Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  channel.ts

  Sprache: JAVA
 

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

import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import { createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
import {
  createOpenGroupPolicyRestrictSendersWarningCollector,
  projectAccountWarningCollector,
} from "openclaw/plugin-sdk/channel-policy";
import { buildProbeChannelStatusSummary } from "openclaw/plugin-sdk/channel-status";
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
import {
  createComputedAccountStatusAdapter,
  createDefaultChannelRuntimeState,
} from "openclaw/plugin-sdk/status-helpers";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import {
  type ResolvedBlueBubblesAccount,
  resolveBlueBubblesEffectiveAllowPrivateNetwork,
} from "./accounts.js";
import { bluebubblesMessageActions } from "./actions.js";
import {
  bluebubblesCapabilities,
  bluebubblesConfigAdapter,
  bluebubblesConfigSchema,
  bluebubblesReload,
  describeBlueBubblesAccount,
  bluebubblesMeta as meta,
} from "./channel-shared.js";
import type { BlueBubblesProbe } from "./channel.runtime.js";
import { createBlueBubblesConversationBindingManager } from "./conversation-bindings.js";
import {
  matchBlueBubblesAcpConversation,
  normalizeBlueBubblesAcpConversationId,
  resolveBlueBubblesConversationIdFromTarget,
} from "./conversation-id.js";
import { bluebubblesDoctor } from "./doctor.js";
import {
  resolveBlueBubblesGroupRequireMention,
  resolveBlueBubblesGroupToolPolicy,
} from "./group-policy.js";
import { createBlueBubblesPairingText } from "./pairing.js";
import type { ChannelAccountSnapshot, ChannelPlugin } from "./runtime-api.js";
import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./secret-contract.js";
import { resolveBlueBubblesOutboundSessionRoute } from "./session-route.js";
import { blueBubblesSetupAdapter } from "./setup-core.js";
import { blueBubblesSetupWizard } from "./setup-surface.js";
import { collectBlueBubblesStatusIssues } from "./status-issues.js";
import {
  extractHandleFromChatGuid,
  inferBlueBubblesTargetChatType,
  looksLikeBlueBubblesExplicitTargetId,
  looksLikeBlueBubblesTargetId,
  normalizeBlueBubblesHandle,
  normalizeBlueBubblesMessagingTarget,
  parseBlueBubblesTarget,
} from "./targets.js";

const loadBlueBubblesChannelRuntime = createLazyRuntimeNamedExport(
  () => import("./channel.runtime.js"),
  "blueBubblesChannelRuntime",
);

const resolveBlueBubblesDmPolicy = createScopedDmSecurityResolver<ResolvedBlueBubblesAccount>({
  channelKey: "bluebubbles",
  resolvePolicy: (account) => account.config.dmPolicy,
  resolveAllowFrom: (account) => account.config.allowFrom,
  policyPathSuffix: "dmPolicy",
  normalizeEntry: (raw) => normalizeBlueBubblesHandle(raw.replace(/^bluebubbles:/i, "")),
});

const collectBlueBubblesSecurityWarnings =
  createOpenGroupPolicyRestrictSendersWarningCollector<ResolvedBlueBubblesAccount>({
    resolveGroupPolicy: (account) => account.config.groupPolicy,
    defaultGroupPolicy: "allowlist",
    surface: "BlueBubbles groups",
    openScope: "any member",
    groupPolicyPath: "channels.bluebubbles.groupPolicy",
    groupAllowFromPath: "channels.bluebubbles.groupAllowFrom",
    mentionGated: false,
  });

export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBubblesProbe> =
  createChatChannelPlugin<ResolvedBlueBubblesAccount, BlueBubblesProbe>({
    base: {
      id: "bluebubbles",
      meta,
      capabilities: bluebubblesCapabilities,
      groups: {
        resolveRequireMention: resolveBlueBubblesGroupRequireMention,
        resolveToolPolicy: resolveBlueBubblesGroupToolPolicy,
      },
      reload: bluebubblesReload,
      configSchema: bluebubblesConfigSchema,
      setupWizard: blueBubblesSetupWizard,
      config: {
        ...bluebubblesConfigAdapter,
        isConfigured: (account) => account.configured,
        describeAccount: (account): ChannelAccountSnapshot => describeBlueBubblesAccount(account),
      },
      doctor: bluebubblesDoctor,
      conversationBindings: {
        supportsCurrentConversationBinding: true,
        createManager: ({ cfg, accountId }) =>
          createBlueBubblesConversationBindingManager({
            cfg,
            accountId: accountId ?? undefined,
          }),
      },
      actions: bluebubblesMessageActions,
      secrets: {
        secretTargetRegistryEntries,
        collectRuntimeConfigAssignments,
      },
      bindings: {
        compileConfiguredBinding: ({ conversationId }) =>
          normalizeBlueBubblesAcpConversationId(conversationId),
        matchInboundConversation: ({ compiledBinding, conversationId }) =>
          matchBlueBubblesAcpConversation({
            bindingConversationId: compiledBinding.conversationId,
            conversationId,
          }),
        resolveCommandConversation: ({ originatingTo, commandTo, fallbackTo }) => {
          const conversationId =
            resolveBlueBubblesConversationIdFromTarget(originatingTo ?? "") ??
            resolveBlueBubblesConversationIdFromTarget(commandTo ?? "") ??
            resolveBlueBubblesConversationIdFromTarget(fallbackTo ?? "");
          return conversationId ? { conversationId } : null;
        },
      },
      messaging: {
        normalizeTarget: normalizeBlueBubblesMessagingTarget,
        inferTargetChatType: ({ to }) => inferBlueBubblesTargetChatType(to),
        resolveOutboundSessionRoute: (params) => resolveBlueBubblesOutboundSessionRoute(params),
        targetResolver: {
          looksLikeId: looksLikeBlueBubblesExplicitTargetId,
          hint: "<handle|chat_guid:GUID|chat_id:ID|chat_identifier:ID>",
          resolveTarget: async ({ normalized }) => {
            const to = normalizeOptionalString(normalized);
            if (!to) {
              return null;
            }
            const chatType = inferBlueBubblesTargetChatType(to);
            if (!chatType) {
              return null;
            }
            return {
              to,
              kind: chatType === "direct" ? "user" : "group",
              source: "normalized" as const,
            };
          },
        },
        formatTargetDisplay: ({ target, display }) => {
          const shouldParseDisplay = (value: string): boolean => {
            if (looksLikeBlueBubblesTargetId(value)) {
              return true;
            }
            return /^(bluebubbles:|chat_guid:|chat_id:|chat_identifier:)/i.test(value);
          };

          // Helper to extract a clean handle from any BlueBubbles target format
          const extractCleanDisplay = (value: string | undefined): string | null => {
            const trimmed = normalizeOptionalString(value);
            if (!trimmed) {
              return null;
            }
            try {
              const parsed = parseBlueBubblesTarget(trimmed);
              if (parsed.kind === "chat_guid") {
                const handle = extractHandleFromChatGuid(parsed.chatGuid);
                if (handle) {
                  return handle;
                }
              }
              if (parsed.kind === "handle") {
                return normalizeBlueBubblesHandle(parsed.to);
              }
            } catch {
              // Fall through
            }
            // Strip common prefixes and try raw extraction
            const stripped = trimmed
              .replace(/^bluebubbles:/i, "")
              .replace(/^chat_guid:/i, "")
              .replace(/^chat_id:/i, "")
              .replace(/^chat_identifier:/i, "");
            const handle = extractHandleFromChatGuid(stripped);
            if (handle) {
              return handle;
            }
            // Don't return raw chat_guid formats - they contain internal routing info
            if (stripped.includes(";-;") || stripped.includes(";+;")) {
              return null;
            }
            return stripped;
          };

          // Try to get a clean display from the display parameter first
          const trimmedDisplay = normalizeOptionalString(display);
          if (trimmedDisplay) {
            if (!shouldParseDisplay(trimmedDisplay)) {
              return trimmedDisplay;
            }
            const cleanDisplay = extractCleanDisplay(trimmedDisplay);
            if (cleanDisplay) {
              return cleanDisplay;
            }
          }

          // Fall back to extracting from target
          const cleanTarget = extractCleanDisplay(target);
          if (cleanTarget) {
            return cleanTarget;
          }

          // Last resort: return display or target as-is
          return normalizeOptionalString(display) || normalizeOptionalString(target) || "";
        },
      },
      setup: blueBubblesSetupAdapter,
      status: createComputedAccountStatusAdapter<ResolvedBlueBubblesAccount, BlueBubblesProbe>({
        defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
        collectStatusIssues: collectBlueBubblesStatusIssues,
        buildChannelSummary: ({ snapshot }) =>
          buildProbeChannelStatusSummary(snapshot, { baseUrl: snapshot.baseUrl ?? null }),
        probeAccount: async ({ account, timeoutMs }) =>
          (await loadBlueBubblesChannelRuntime()).probeBlueBubbles({
            baseUrl: account.baseUrl,
            password: account.config.password ?? null,
            timeoutMs,
            allowPrivateNetwork: resolveBlueBubblesEffectiveAllowPrivateNetwork({
              baseUrl: account.baseUrl,
              config: account.config,
            }),
          }),
        resolveAccountSnapshot: ({ account, runtime, probe }) => {
          const running = runtime?.running ?? false;
          const probeOk = probe?.ok;
          return {
            accountId: account.accountId,
            name: account.name,
            enabled: account.enabled,
            configured: account.configured,
            extra: {
              baseUrl: account.baseUrl,
              connected: probeOk ?? running,
            },
          };
        },
      }),
      gateway: {
        startAccount: async (ctx) => {
          const runtime = await loadBlueBubblesChannelRuntime();
          const account = ctx.account;
          const conversationBindings = createBlueBubblesConversationBindingManager({
            cfg: ctx.cfg,
            accountId: ctx.accountId,
          });
          const webhookPath = runtime.resolveWebhookPathFromConfig(account.config);
          const statusSink = createAccountStatusSink({
            accountId: ctx.accountId,
            setStatus: ctx.setStatus,
          });
          statusSink({
            baseUrl: account.baseUrl,
          });
          ctx.log?.info(`[${account.accountId}] starting provider (webhook=${webhookPath})`);
          try {
            return await runtime.monitorBlueBubblesProvider({
              account,
              config: ctx.cfg,
              runtime: ctx.runtime,
              abortSignal: ctx.abortSignal,
              statusSink,
              webhookPath,
            });
          } finally {
            conversationBindings.stop();
          }
        },
      },
    },
    security: {
      resolveDmPolicy: resolveBlueBubblesDmPolicy,
      collectWarnings: projectAccountWarningCollector<
        ResolvedBlueBubblesAccount,
        { account: ResolvedBlueBubblesAccount }
      >(collectBlueBubblesSecurityWarnings),
    },
    threading: {
      buildToolContext: ({ context, hasRepliedRef }) => ({
        currentChannelId: normalizeOptionalString(context.To),
        currentThreadTs: context.ReplyToIdFull ?? context.ReplyToId,
        hasRepliedRef,
      }),
    },
    pairing: {
      text: createBlueBubblesPairingText(async (id, message, params) => {
        await (await loadBlueBubblesChannelRuntime()).sendMessageBlueBubbles(id, message, params);
      }),
    },
    outbound: {
      base: {
        deliveryMode: "direct",
        textChunkLimit: 4000,
        resolveTarget: ({ to }) => {
          const trimmed = normalizeOptionalString(to);
          if (!trimmed) {
            return {
              ok: false,
              error: new Error("Delivering to BlueBubbles requires --to <handle|chat_guid:GUID>"),
            };
          }
          return { ok: true, to: trimmed };
        },
      },
      attachedResults: {
        channel: "bluebubbles",
        sendText: async ({ cfg, to, text, accountId, replyToId }) => {
          const runtime = await loadBlueBubblesChannelRuntime();
          const rawReplyToId = normalizeOptionalString(replyToId) ?? "";
          const replyToMessageGuid = rawReplyToId
            ? runtime.resolveBlueBubblesMessageId(rawReplyToId, { requireKnownShortId: true })
            : "";
          return await runtime.sendMessageBlueBubbles(to, text, {
            cfg: cfg,
            accountId: accountId ?? undefined,
            replyToMessageGuid: replyToMessageGuid || undefined,
          });
        },
        sendMedia: async (ctx) => {
          const runtime = await loadBlueBubblesChannelRuntime();
          const { cfg, to, text, mediaUrl, accountId, replyToId } = ctx;
          const { mediaPath, mediaBuffer, contentType, filename, caption } = ctx as {
            mediaPath?: string;
            mediaBuffer?: Uint8Array;
            contentType?: string;
            filename?: string;
            caption?: string;
          };
          return await runtime.sendBlueBubblesMedia({
            cfg: cfg,
            to,
            mediaUrl,
            mediaPath,
            mediaBuffer,
            contentType,
            filename,
            caption: caption ?? text ?? undefined,
            replyToId: replyToId ?? null,
            accountId: accountId ?? undefined,
          });
        },
      },
    },
  });

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






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge