Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  channel.ts

  Sprache: JAVA
 

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,
          });
        },
      },
    },
  });

Messung V0.5 in Prozent
C=99 H=100 G=99

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-06-06) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik