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

Quelle  messages.ts

  Sprache: JAVA
 

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

/**
 * Message sending API for the QQ Open Platform.
 *
 * Key design improvements:
 * - Unified `sendMessage(scope, ...)` replaces `sendC2CMessage` + `sendGroupMessage`.
 * - `onMessageSent` hook is scoped to the instance, not a module-level global.
 * - Markdown support flag is per-instance, not a global Map.
 */

import type {
  ChatScope,
  MessageResponse,
  OutboundMeta,
  EngineLogger,
  InlineKeyboard,
} from "../types.js";
import { formatErrorMessage } from "../utils/format.js";
import { ApiClient } from "./api-client.js";
import {
  messagePath,
  channelMessagePath,
  dmMessagePath,
  gatewayPath,
  interactionPath,
  getNextMsgSeq,
} from "./routes.js";
import { TokenManager } from "./token.js";

export interface MessageApiConfig {
  /** Whether the QQ Bot has markdown permission. */
  markdownSupport: boolean;
  /** Logger for diagnostics. */
  logger?: EngineLogger;
}

type OnMessageSentCallback = (refIdx: string, meta: OutboundMeta) => void;

/**
 * Message sending module.
 *
 * Usage:
 * ```ts
 * const api = new MessageApi(client, tokenMgr, { markdownSupport: true });
 * await api.sendMessage('c2c', openid, 'Hello!', { appId, clientSecret, msgId });
 * ```
 */
export class MessageApi {
  private readonly client: ApiClient;
  private readonly tokenManager: TokenManager;
  private readonly markdownSupport: boolean;
  private readonly logger?: EngineLogger;
  private messageSentHook: OnMessageSentCallback | null = null;

  constructor(client: ApiClient, tokenManager: TokenManager, config: MessageApiConfig) {
    this.client = client;
    this.tokenManager = tokenManager;
    this.markdownSupport = config.markdownSupport;
    this.logger = config.logger;
  }

  /** Register a callback invoked when a sent message returns a ref_idx. */
  onMessageSent(callback: OnMessageSentCallback): void {
    this.messageSentHook = callback;
  }

  /**
   * Notify the registered hook about a sent message.
   * Use this for media sends that bypass `sendAndNotify`.
   */
  notifyMessageSent(refIdx: string, meta: OutboundMeta): void {
    if (this.messageSentHook) {
      try {
        this.messageSentHook(refIdx, meta);
      } catch (err) {
        this.logger?.error?.(
          `[qqbot:messages] onMessageSent hook error: ${formatErrorMessage(err)}`,
        );
      }
    }
  }

  // ---- Unified message sending ----

  /**
   * Send a text message to a C2C or Group target.
   *
   * Automatically constructs the correct path, body format (markdown vs plain),
   * and message sequence number.
   */
  async sendMessage(
    scope: ChatScope,
    targetId: string,
    content: string,
    creds: Credentials,
    opts?: {
      msgId?: string;
      messageReference?: string;
      inlineKeyboard?: InlineKeyboard;
    },
  ): Promise<MessageResponse> {
    const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);
    const msgSeq = opts?.msgId ? getNextMsgSeq(opts.msgId) : 1;
    const body = this.buildMessageBody(
      content,
      opts?.msgId,
      msgSeq,
      opts?.messageReference,
      opts?.inlineKeyboard,
    );
    const path = messagePath(scope, targetId);
    return this.sendAndNotify(creds.appId, token, "POST", path, body, { text: content });
  }

  /** Send a proactive (no msgId) message to a C2C or Group target. */
  async sendProactiveMessage(
    scope: ChatScope,
    targetId: string,
    content: string,
    creds: Credentials,
  ): Promise<MessageResponse> {
    if (!content?.trim()) {
      throw new Error("Proactive message content must not be empty");
    }
    const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);
    const body = this.buildProactiveBody(content);
    const path = messagePath(scope, targetId);
    return this.sendAndNotify(creds.appId, token, "POST", path, body, { text: content });
  }

  // ---- Channel / DM ----

  /** Send a channel message. */
  async sendChannelMessage(opts: {
    channelId: string;
    content: string;
    creds: Credentials;
    msgId?: string;
  }): Promise<MessageResponse> {
    const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);
    return this.client.request<MessageResponse>(token, "POST", channelMessagePath(opts.channelId), {
      content: opts.content,
      ...(opts.msgId ? { msg_id: opts.msgId } : {}),
    });
  }

  /** Send a DM (guild direct message). */
  async sendDmMessage(opts: {
    guildId: string;
    content: string;
    creds: Credentials;
    msgId?: string;
  }): Promise<MessageResponse> {
    const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);
    return this.client.request<MessageResponse>(token, "POST", dmMessagePath(opts.guildId), {
      content: opts.content,
      ...(opts.msgId ? { msg_id: opts.msgId } : {}),
    });
  }

  // ---- C2C Input Notify ----

  /** Send a typing indicator to a C2C user. */
  async sendInputNotify(opts: {
    openid: string;
    creds: Credentials;
    msgId?: string;
    inputSecond?: number;
  }): Promise<{ refIdx?: string }> {
    const inputSecond = opts.inputSecond ?? 60;
    const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);
    const msgSeq = opts.msgId ? getNextMsgSeq(opts.msgId) : 1;
    const response = await this.client.request<{ ext_info?: { ref_idx?: string } }>(
      token,
      "POST",
      messagePath("c2c", opts.openid),
      {
        msg_type: 6,
        input_notify: { input_type: 1, input_second: inputSecond },
        msg_seq: msgSeq,
        ...(opts.msgId ? { msg_id: opts.msgId } : {}),
      },
    );
    return { refIdx: response.ext_info?.ref_idx };
  }

  // ---- Interaction ----

  /** Acknowledge an INTERACTION_CREATE event. */
  async acknowledgeInteraction(
    interactionId: string,
    creds: Credentials,
    code: 0 | 1 | 2 | 3 | 4 | 5 = 0,
  ): Promise<void> {
    const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);
    await this.client.request(token, "PUT", interactionPath(interactionId), { code });
  }

  // ---- Gateway ----

  /** Get the WebSocket gateway URL. */
  async getGatewayUrl(creds: Credentials): Promise<string> {
    const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);
    const data = await this.client.request<{ url: string }>(token, "GET", gatewayPath());
    return data.url;
  }

  // ---- Internal ----

  private async sendAndNotify(
    appId: string,
    accessToken: string,
    method: string,
    path: string,
    body: unknown,
    meta: OutboundMeta,
  ): Promise<MessageResponse> {
    const result = await this.client.request<MessageResponse>(accessToken, method, path, body);
    if (result.ext_info?.ref_idx && this.messageSentHook) {
      try {
        this.messageSentHook(result.ext_info.ref_idx, meta);
      } catch (err) {
        this.logger?.error?.(
          `[qqbot:messages] onMessageSent hook error: ${formatErrorMessage(err)}`,
        );
      }
    }
    return result;
  }

  private buildMessageBody(
    content: string,
    msgId: string | undefined,
    msgSeq: number,
    messageReference?: string,
    inlineKeyboard?: InlineKeyboard,
  ): Record<string, unknown> {
    const body: Record<string, unknown> = this.markdownSupport
      ? { markdown: { content }, msg_type: 2, msg_seq: msgSeq }
      : { content, msg_type: 0, msg_seq: msgSeq };

    if (msgId) {
      body.msg_id = msgId;
    }
    if (messageReference && !this.markdownSupport) {
      body.message_reference = { message_id: messageReference };
    }
    if (inlineKeyboard) {
      body.keyboard = inlineKeyboard;
    }
    return body;
  }

  private buildProactiveBody(content: string): Record<string, unknown> {
    return this.markdownSupport ? { markdown: { content }, msg_type: 2 } : { content, msg_type: 0 };
  }
}

// ---- Shared helpers ----

/** Credentials needed to authenticate API requests. */
export interface Credentials {
  appId: string;
  clientSecret: string;
}

// Re-export getNextMsgSeq for consumers that import from messages.ts.
export { getNextMsgSeq } from "./routes.js";

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