Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Openclaw/extensions/line/src/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 11 kB image not shown  

Quelle  reply-payload-transform.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import {
  createAgendaCard,
  createAppleTvRemoteCard,
  createDeviceControlCard,
  createEventCard,
  createMediaPlayerCard,
} from "./flex-templates.js";
import type { LineChannelData } from "./types.js";

/**
 * Parse LINE-specific directives from text and extract them into ReplyPayload fields.
 *
 * Supported directives:
 * - [[quick_replies: option1, option2, option3]]
 * - [[location: title | address | latitude | longitude]]
 * - [[confirm: question | yes_label | no_label]]
 * - [[buttons: title | text | btn1:data1, btn2:data2]]
 * - [[media_player: title | artist | source | imageUrl | playing/paused]]
 * - [[event: title | date | time | location | description]]
 * - [[agenda: title | event1_title:event1_time, event2_title:event2_time, ...]]
 * - [[device: name | type | status | ctrl1:data1, ctrl2:data2]]
 * - [[appletv_remote: name | status]]
 */
export function parseLineDirectives(payload: ReplyPayload): ReplyPayload {
  let text = payload.text;
  if (!text) {
    return payload;
  }

  const result: ReplyPayload = { ...payload };
  const lineData: LineChannelData = {
    ...(result.channelData?.line as LineChannelData | undefined),
  };
  const toSlug = (value: string): string =>
    normalizeLowercaseStringOrEmpty(value)
      .replace(/[^a-z0-9]+/g, "_")
      .replace(/^_+|_+$/g, "") || "device";
  const lineActionData = (action: string, extras?: Record<string, string>): string => {
    const base = [`line.action=${encodeURIComponent(action)}`];
    if (extras) {
      for (const [key, value] of Object.entries(extras)) {
        base.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
      }
    }
    return base.join("&");
  };

  const quickRepliesMatch = text.match(/\[\[quick_replies:\s*([^\]]+)\]\]/i);
  if (quickRepliesMatch) {
    const options = quickRepliesMatch[1]
      .split(",")
      .map((s) => s.trim())
      .filter(Boolean);
    if (options.length > 0) {
      lineData.quickReplies = [...(lineData.quickReplies || []), ...options];
    }
    text = text.replace(quickRepliesMatch[0], "").trim();
  }

  const locationMatch = text.match(/\[\[location:\s*([^\]]+)\]\]/i);
  if (locationMatch && !lineData.location) {
    const parts = locationMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 4) {
      const [title, address, latStr, lonStr] = parts;
      const latitude = Number.parseFloat(latStr);
      const longitude = Number.parseFloat(lonStr);
      if (!Number.isNaN(latitude) && !Number.isNaN(longitude)) {
        lineData.location = {
          title: title || "Location",
          address: address || "",
          latitude,
          longitude,
        };
      }
    }
    text = text.replace(locationMatch[0], "").trim();
  }

  const confirmMatch = text.match(/\[\[confirm:\s*([^\]]+)\]\]/i);
  if (confirmMatch && !lineData.templateMessage) {
    const parts = confirmMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 3) {
      const [question, yesPart, noPart] = parts;
      const [yesLabel, yesData] = yesPart.includes(":")
        ? yesPart.split(":").map((s) => s.trim())
        : [yesPart, normalizeLowercaseStringOrEmpty(yesPart)];
      const [noLabel, noData] = noPart.includes(":")
        ? noPart.split(":").map((s) => s.trim())
        : [noPart, normalizeLowercaseStringOrEmpty(noPart)];

      lineData.templateMessage = {
        type: "confirm",
        text: question,
        confirmLabel: yesLabel,
        confirmData: yesData,
        cancelLabel: noLabel,
        cancelData: noData,
        altText: question,
      };
    }
    text = text.replace(confirmMatch[0], "").trim();
  }

  const buttonsMatch = text.match(/\[\[buttons:\s*([^\]]+)\]\]/i);
  if (buttonsMatch && !lineData.templateMessage) {
    const parts = buttonsMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 3) {
      const [title, bodyText, actionsStr] = parts;

      const actions = actionsStr.split(",").map((actionStr) => {
        const trimmed = actionStr.trim();
        const colonIndex = (() => {
          const index = trimmed.indexOf(":");
          if (index === -1) {
            return -1;
          }
          const lower = normalizeLowercaseStringOrEmpty(trimmed);
          if (lower.startsWith("http://") || lower.startsWith("https://")) {
            return -1;
          }
          return index;
        })();

        let label: string;
        let data: string;

        if (colonIndex === -1) {
          label = trimmed;
          data = trimmed;
        } else {
          label = trimmed.slice(0, colonIndex).trim();
          data = trimmed.slice(colonIndex + 1).trim();
        }

        if (data.startsWith("http://") || data.startsWith("https://")) {
          return { type: "uri" as const, label, uri: data };
        }
        if (data.includes("=")) {
          return { type: "postback" as const, label, data };
        }
        return { type: "message" as const, label, data: data || label };
      });

      if (actions.length > 0) {
        lineData.templateMessage = {
          type: "buttons",
          title,
          text: bodyText,
          actions: actions.slice(0, 4),
          altText: `${title}: ${bodyText}`,
        };
      }
    }
    text = text.replace(buttonsMatch[0], "").trim();
  }

  const mediaPlayerMatch = text.match(/\[\[media_player:\s*([^\]]+)\]\]/i);
  if (mediaPlayerMatch && !lineData.flexMessage) {
    const parts = mediaPlayerMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 1) {
      const [title, artist, source, imageUrl, statusStr] = parts;
      const isPlaying = normalizeLowercaseStringOrEmpty(statusStr) === "playing";
      const validImageUrl = imageUrl?.startsWith("https://") ? imageUrl : undefined;
      const deviceKey = toSlug(source || title || "media");
      const card = createMediaPlayerCard({
        title: title || "Unknown Track",
        subtitle: artist || undefined,
        source: source || undefined,
        imageUrl: validImageUrl,
        isPlaying: statusStr ? isPlaying : undefined,
        controls: {
          previous: { data: lineActionData("previous", { "line.device": deviceKey }) },
          play: { data: lineActionData("play", { "line.device": deviceKey }) },
          pause: { data: lineActionData("pause", { "line.device": deviceKey }) },
          next: { data: lineActionData("next", { "line.device": deviceKey }) },
        },
      });

      lineData.flexMessage = {
        altText: `�� ${title}${artist ? ` - ${artist}` : ""}`,
        contents: card,
      };
    }
    text = text.replace(mediaPlayerMatch[0], "").trim();
  }

  const eventMatch = text.match(/\[\[event:\s*([^\]]+)\]\]/i);
  if (eventMatch && !lineData.flexMessage) {
    const parts = eventMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 2) {
      const [title, date, time, location, description] = parts;

      const card = createEventCard({
        title: title || "Event",
        date: date || "TBD",
        time: time || undefined,
        location: location || undefined,
        description: description || undefined,
      });

      lineData.flexMessage = {
        altText: `�� ${title} - ${date}${time ? ` ${time}` : ""}`,
        contents: card,
      };
    }
    text = text.replace(eventMatch[0], "").trim();
  }

  const appleTvMatch = text.match(/\[\[appletv_remote:\s*([^\]]+)\]\]/i);
  if (appleTvMatch && !lineData.flexMessage) {
    const parts = appleTvMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 1) {
      const [deviceName, status] = parts;
      const deviceKey = toSlug(deviceName || "apple_tv");

      const card = createAppleTvRemoteCard({
        deviceName: deviceName || "Apple TV",
        status: status || undefined,
        actionData: {
          up: lineActionData("up", { "line.device": deviceKey }),
          down: lineActionData("down", { "line.device": deviceKey }),
          left: lineActionData("left", { "line.device": deviceKey }),
          right: lineActionData("right", { "line.device": deviceKey }),
          select: lineActionData("select", { "line.device": deviceKey }),
          menu: lineActionData("menu", { "line.device": deviceKey }),
          home: lineActionData("home", { "line.device": deviceKey }),
          play: lineActionData("play", { "line.device": deviceKey }),
          pause: lineActionData("pause", { "line.device": deviceKey }),
          volumeUp: lineActionData("volume_up", { "line.device": deviceKey }),
          volumeDown: lineActionData("volume_down", { "line.device": deviceKey }),
          mute: lineActionData("mute", { "line.device": deviceKey }),
        },
      });

      lineData.flexMessage = {
        altText: `�� ${deviceName || "Apple TV"} Remote`,
        contents: card,
      };
    }
    text = text.replace(appleTvMatch[0], "").trim();
  }

  const agendaMatch = text.match(/\[\[agenda:\s*([^\]]+)\]\]/i);
  if (agendaMatch && !lineData.flexMessage) {
    const parts = agendaMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 2) {
      const [title, eventsStr] = parts;
      const events = eventsStr.split(",").map((eventStr) => {
        const trimmed = eventStr.trim();
        const colonIdx = trimmed.lastIndexOf(":");
        if (colonIdx > 0) {
          return {
            title: trimmed.slice(0, colonIdx).trim(),
            time: trimmed.slice(colonIdx + 1).trim(),
          };
        }
        return { title: trimmed };
      });

      const card = createAgendaCard({
        title: title || "Agenda",
        events,
      });

      lineData.flexMessage = {
        altText: `�� ${title} (${events.length} events)`,
        contents: card,
      };
    }
    text = text.replace(agendaMatch[0], "").trim();
  }

  const deviceMatch = text.match(/\[\[device:\s*([^\]]+)\]\]/i);
  if (deviceMatch && !lineData.flexMessage) {
    const parts = deviceMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 1) {
      const [deviceName, deviceType, status, controlsStr] = parts;
      const deviceKey = toSlug(deviceName || "device");
      const controls = controlsStr
        ? controlsStr.split(",").map((ctrlStr) => {
            const [label, data] = ctrlStr.split(":").map((s) => s.trim());
            const action = data || normalizeLowercaseStringOrEmpty(label).replace(/\s+/g, "_");
            return { label, data: lineActionData(action, { "line.device": deviceKey }) };
          })
        : [];

      const card = createDeviceControlCard({
        deviceName: deviceName || "Device",
        deviceType: deviceType || undefined,
        status: status || undefined,
        controls,
      });

      lineData.flexMessage = {
        altText: `�� ${deviceName}${status ? `: ${status}` : ""}`,
        contents: card,
      };
    }
    text = text.replace(deviceMatch[0], "").trim();
  }

  text = text.replace(/\n{3,}/g, "\n\n").trim();

  result.text = text || undefined;
  if (Object.keys(lineData).length > 0) {
    result.channelData = { ...result.channelData, line: lineData };
  }
  return result;
}

export function hasLineDirectives(text: string): boolean {
  return /\[\[(quick_replies|location|confirm|buttons|media_player|event|agenda|device|appletv_remote):/i.test(
    text,
  );
}

¤ Dauer der Verarbeitung: 0.43 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.