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

Quelle  utils.ts

  Sprache: JAVA
 

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

import { formatErrorMessage as sharedFormatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { normalizeShip } from "../targets.js";

// Cite types for message references
export interface ChanCite {
  chan: { nest: string; where: string };
}
export interface GroupCite {
  group: string;
}
export interface DeskCite {
  desk: { flag: string; where: string };
}
export interface BaitCite {
  bait: { group: string; graph: string; where: string };
}
export type Cite = ChanCite | GroupCite | DeskCite | BaitCite;

export interface ParsedCite {
  type: "chan" | "group" | "desk" | "bait";
  nest?: string;
  author?: string;
  postId?: string;
  group?: string;
  flag?: string;
  where?: string;
}

// Extract all cites from message content
export function extractCites(content: unknown): ParsedCite[] {
  if (!content || !Array.isArray(content)) {
    return [];
  }

  const cites: ParsedCite[] = [];

  for (const verse of content) {
    if (verse?.block?.cite && typeof verse.block.cite === "object") {
      const cite = verse.block.cite;

      if (cite.chan && typeof cite.chan === "object") {
        const { nest, where } = cite.chan;
        const whereMatch = where?.match(/\/msg\/(~[a-z-]+)\/(.+)/);
        cites.push({
          type: "chan",
          nest,
          where,
          author: whereMatch?.[1],
          postId: whereMatch?.[2],
        });
      } else if (cite.group && typeof cite.group === "string") {
        cites.push({ type: "group", group: cite.group });
      } else if (cite.desk && typeof cite.desk === "object") {
        cites.push({ type: "desk", flag: cite.desk.flag, where: cite.desk.where });
      } else if (cite.bait && typeof cite.bait === "object") {
        cites.push({
          type: "bait",
          group: cite.bait.group,
          nest: cite.bait.graph,
          where: cite.bait.where,
        });
      }
    }
  }

  return cites;
}

export function formatModelName(modelString?: string | null): string {
  if (!modelString) {
    return "AI";
  }
  const modelName = modelString.includes("/") ? modelString.split("/")[1] : modelString;
  const modelMappings: Record<string, string> = {
    "claude-opus-4-5": "Claude Opus 4.5",
    "claude-sonnet-4-5": "Claude Sonnet 4.5",
    "claude-sonnet-3-5": "Claude Sonnet 3.5",
    "gpt-4o": "GPT-4o",
    "gpt-4-turbo": "GPT-4 Turbo",
    "gpt-4": "GPT-4",
    "gemini-2.0-flash": "Gemini 2.0 Flash",
    "gemini-pro": "Gemini Pro",
  };

  if (modelMappings[modelName]) {
    return modelMappings[modelName];
  }
  return modelName
    .replace(/-/g, " ")
    .split(" ")
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
    .join(" ");
}

export function isBotMentioned(
  messageText: string,
  botShipName: string,
  nickname?: string,
): boolean {
  if (!messageText || !botShipName) {
    return false;
  }

  // Check for @all mention
  if (/@all\b/i.test(messageText)) {
    return true;
  }

  // Check for ship mention
  const normalizedBotShip = normalizeShip(botShipName);
  const escapedShip = normalizedBotShip.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const mentionPattern = new RegExp(`(^|\\s)${escapedShip}(?=\\s|$)`, "i");
  if (mentionPattern.test(messageText)) {
    return true;
  }

  // Check for nickname mention (case-insensitive, word boundary)
  if (nickname) {
    const escapedNickname = nickname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const nicknamePattern = new RegExp(`(^|\\s)${escapedNickname}(?=\\s|$|[,!?.])`, "i");
    if (nicknamePattern.test(messageText)) {
      return true;
    }
  }

  return false;
}

/**
 * Strip bot ship mention from message text for command detection.
 * "~bot-ship /status" → "/status"
 */
export function stripBotMention(messageText: string, botShipName: string): string {
  if (!messageText || !botShipName) {
    return messageText;
  }
  return messageText.replace(normalizeShip(botShipName), "").trim();
}

export function isDmAllowed(senderShip: string, allowlist: string[] | undefined): boolean {
  if (!allowlist || allowlist.length === 0) {
    return false;
  }
  const normalizedSender = normalizeShip(senderShip);
  return allowlist.map((ship) => normalizeShip(ship)).some((ship) => ship === normalizedSender);
}

/**
 * Check if a group invite from a ship should be auto-accepted.
 *
 * SECURITY: Fail-safe to deny. If allowlist is empty or undefined,
 * ALL invites are rejected - even if autoAcceptGroupInvites is enabled.
 * This prevents misconfigured bots from accepting malicious invites.
 */
export function isGroupInviteAllowed(
  inviterShip: string,
  allowlist: string[] | undefined,
): boolean {
  // SECURITY: Fail-safe to deny when no allowlist configured
  if (!allowlist || allowlist.length === 0) {
    return false;
  }
  const normalizedInviter = normalizeShip(inviterShip);
  return allowlist.map((ship) => normalizeShip(ship)).some((ship) => ship === normalizedInviter);
}

/**
 * Resolve quoted/cited content only after the caller has passed authorization.
 * Unauthorized paths must keep raw text and must not trigger cross-channel cite fetches.
 */
export async function resolveAuthorizedMessageText(params: {
  rawText: string;
  content: unknown;
  authorizedForCites: boolean;
  resolveAllCites: (content: unknown) => Promise<string>;
}): Promise<string> {
  const { rawText, content, authorizedForCites, resolveAllCites } = params;
  if (!authorizedForCites) {
    return rawText;
  }
  const citedContent = await resolveAllCites(content);
  return citedContent + rawText;
}

export const asRecord = asNullableObjectRecord;
export const formatErrorMessage = sharedFormatErrorMessage;
export const readString = readStringField;

function asNullableObjectRecord(value: unknown): Record<string, unknown> | null {
  return value && typeof value === "object" && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : null;
}

function readStringField(
  record: Record<string, unknown> | null | undefined,
  field: string,
): string | undefined {
  const value = record?.[field];
  return typeof value === "string" ? value : undefined;
}

// Helper to recursively extract text from inline content
function renderInlineItem(
  item: unknown,
  options?: {
    linkMode?: "content-or-href" | "href";
    allowBreak?: boolean;
    allowBlockquote?: boolean;
  },
): string {
  if (typeof item === "string") {
    return item;
  }
  const record = asRecord(item);
  if (!record) {
    return "";
  }
  const ship = readString(record, "ship");
  if (ship) {
    return ship;
  }
  if ("sect" in record) {
    const sect = record.sect;
    if (typeof sect === "string") {
      return `@${sect || "all"}`;
    }
    if (sect === null) {
      return "@all";
    }
  }
  if (options?.allowBreak && "break" in record) {
    return "\n";
  }
  const inlineCode = readString(record, "inline-code");
  if (inlineCode) {
    return `\`${inlineCode}\``;
  }
  const code = readString(record, "code");
  if (code) {
    return `\`${code}\``;
  }
  const link = asRecord(record.link);
  const linkHref = link ? readString(link, "href") : undefined;
  if (link && linkHref) {
    const linkContent = readString(link, "content");
    return options?.linkMode === "href" ? linkHref : linkContent || linkHref;
  }
  if (Array.isArray(record.bold)) {
    return `**${extractInlineText(record.bold)}**`;
  }
  if (Array.isArray(record.italics)) {
    return `*${extractInlineText(record.italics)}*`;
  }
  if (Array.isArray(record.strike)) {
    return `~~${extractInlineText(record.strike)}~~`;
  }
  if (options?.allowBlockquote && Array.isArray(record.blockquote)) {
    return `> ${extractInlineText(record.blockquote)}`;
  }
  return "";
}

function extractInlineText(items: readonly unknown[]): string {
  return items.map((item) => renderInlineItem(item)).join("");
}

export function extractMessageText(content: unknown): string {
  if (!content || !Array.isArray(content)) {
    return "";
  }

  return content
    .map((verse) => {
      const verseRecord = asRecord(verse);
      if (!verseRecord) {
        return "";
      }

      // Handle inline content (text, ships, links, etc.)
      if (Array.isArray(verseRecord.inline)) {
        return verseRecord.inline
          .map((item) =>
            renderInlineItem(item, {
              linkMode: "href",
              allowBreak: true,
              allowBlockquote: true,
            }),
          )
          .join("");
      }

      // Handle block content (images, code blocks, etc.)
      const block = asRecord(verseRecord.block);
      if (block) {
        const image = asRecord(block.image);

        // Image blocks
        if (image) {
          const imageSrc = readString(image, "src");
          if (imageSrc) {
            const altText = readString(image, "alt");
            const alt = altText ? ` (${altText})` : "";
            return `\n${imageSrc}${alt}\n`;
          }
        }

        // Code blocks
        const codeBlock = asRecord(block.code);
        if (codeBlock) {
          const lang = readString(codeBlock, "lang") ?? "";
          const code = readString(codeBlock, "code") ?? "";
          return `\n\`\`\`${lang}\n${code}\n\`\`\`\n`;
        }

        // Header blocks
        const header = asRecord(block.header);
        if (header) {
          const headerContent = Array.isArray(header.content) ? header.content : [];
          const text =
            headerContent.map((item) => (typeof item === "string" ? item : "")).join("") || "";
          return `\n## ${text}\n`;
        }

        // Cite/quote blocks - parse the reference structure
        const cite = asRecord(block.cite);
        if (cite) {
          const chanCite = asRecord(cite.chan);

          // ChanCite - reference to a channel message
          if (chanCite) {
            const nest = readString(chanCite, "nest");
            const where = readString(chanCite, "where");
            // where is typically /msg/~author/timestamp
            const whereMatch = where?.match(/\/msg\/(~[a-z-]+)\/(.+)/);
            if (whereMatch) {
              const [, author, _postId] = whereMatch;
              return `\n> [quoted: ${author} in ${nest}]\n`;
            }
            return `\n> [quoted from ${nest}]\n`;
          }

          // GroupCite - reference to a group
          const group = readString(cite, "group");
          if (group) {
            return `\n> [ref: group ${group}]\n`;
          }

          // DeskCite - reference to an app/desk
          const desk = asRecord(cite.desk);
          if (desk) {
            const flag = readString(desk, "flag");
            if (flag) {
              return `\n> [ref: ${flag}]\n`;
            }
          }

          // BaitCite - reference with group+graph context
          const bait = asRecord(cite.bait);
          if (bait) {
            const graph = readString(bait, "graph");
            const groupName = readString(bait, "group");
            if (graph && groupName) {
              return `\n> [ref: ${graph} in ${groupName}]\n`;
            }
          }

          return `\n> [quoted message]\n`;
        }
      }

      return "";
    })
    .join("\n")
    .trim();
}

export function isSummarizationRequest(messageText: string): boolean {
  const patterns = [
    /summarize\s+(this\s+)?(channel|chat|conversation)/i,
    /what\s+did\s+i\s+miss/i,
    /catch\s+me\s+up/i,
    /channel\s+summary/i,
    /tldr/i,
  ];
  return patterns.some((pattern) => pattern.test(messageText));
}

export function formatChangesDate(daysAgo = 5): string {
  const now = new Date();
  const targetDate = new Date(now.getTime() - daysAgo * 24 * 60 * 60 * 1000);
  const year = targetDate.getFullYear();
  const month = targetDate.getMonth() + 1;
  const day = targetDate.getDate();
  return `~${year}.${month}.${day}..20.19.51..9b9d`;
}

¤ Dauer der Verarbeitung: 0.22 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.