Spaces:
Paused
Paused
| /** | |
| * Twitch message sending functions with dependency injection support. | |
| * | |
| * These functions are the primary interface for sending messages to Twitch. | |
| * They support dependency injection via the `deps` parameter for testability. | |
| */ | |
| import type { OpenClawConfig } from "openclaw/plugin-sdk"; | |
| import { getClientManager as getRegistryClientManager } from "./client-manager-registry.js"; | |
| import { DEFAULT_ACCOUNT_ID, getAccountConfig } from "./config.js"; | |
| import { resolveTwitchToken } from "./token.js"; | |
| import { stripMarkdownForTwitch } from "./utils/markdown.js"; | |
| import { generateMessageId, isAccountConfigured, normalizeTwitchChannel } from "./utils/twitch.js"; | |
| /** | |
| * Result from sending a message to Twitch. | |
| */ | |
| export interface SendMessageResult { | |
| /** Whether the send was successful */ | |
| ok: boolean; | |
| /** The message ID (generated for tracking) */ | |
| messageId: string; | |
| /** Error message if the send failed */ | |
| error?: string; | |
| } | |
| /** | |
| * Internal send function used by the outbound adapter. | |
| * | |
| * This function has access to the full OpenClaw config and handles | |
| * account resolution, markdown stripping, and actual message sending. | |
| * | |
| * @param channel - The channel name | |
| * @param text - The message text | |
| * @param cfg - Full OpenClaw configuration | |
| * @param accountId - Account ID to use | |
| * @param stripMarkdown - Whether to strip markdown (default: true) | |
| * @param logger - Logger instance | |
| * @returns Result with message ID and status | |
| * | |
| * @example | |
| * const result = await sendMessageTwitchInternal( | |
| * "#mychannel", | |
| * "Hello Twitch!", | |
| * openclawConfig, | |
| * "default", | |
| * true, | |
| * console, | |
| * ); | |
| */ | |
| export async function sendMessageTwitchInternal( | |
| channel: string, | |
| text: string, | |
| cfg: OpenClawConfig, | |
| accountId: string = DEFAULT_ACCOUNT_ID, | |
| stripMarkdown: boolean = true, | |
| logger: Console = console, | |
| ): Promise<SendMessageResult> { | |
| const account = getAccountConfig(cfg, accountId); | |
| if (!account) { | |
| const availableIds = Object.keys(cfg.channels?.twitch?.accounts ?? {}); | |
| return { | |
| ok: false, | |
| messageId: generateMessageId(), | |
| error: `Account not found: ${accountId}. Available accounts: ${availableIds.join(", ") || "none"}`, | |
| }; | |
| } | |
| const tokenResolution = resolveTwitchToken(cfg, { accountId }); | |
| if (!isAccountConfigured(account, tokenResolution.token)) { | |
| return { | |
| ok: false, | |
| messageId: generateMessageId(), | |
| error: | |
| `Account ${accountId} is not properly configured. ` + | |
| "Required: username, clientId, and token (config or env for default account).", | |
| }; | |
| } | |
| const normalizedChannel = channel || account.channel; | |
| if (!normalizedChannel) { | |
| return { | |
| ok: false, | |
| messageId: generateMessageId(), | |
| error: "No channel specified and no default channel in account config", | |
| }; | |
| } | |
| const cleanedText = stripMarkdown ? stripMarkdownForTwitch(text) : text; | |
| if (!cleanedText) { | |
| return { | |
| ok: true, | |
| messageId: "skipped", | |
| }; | |
| } | |
| const clientManager = getRegistryClientManager(accountId); | |
| if (!clientManager) { | |
| return { | |
| ok: false, | |
| messageId: generateMessageId(), | |
| error: `Client manager not found for account: ${accountId}. Please start the Twitch gateway first.`, | |
| }; | |
| } | |
| try { | |
| const result = await clientManager.sendMessage( | |
| account, | |
| normalizeTwitchChannel(normalizedChannel), | |
| cleanedText, | |
| cfg, | |
| accountId, | |
| ); | |
| if (!result.ok) { | |
| return { | |
| ok: false, | |
| messageId: result.messageId ?? generateMessageId(), | |
| error: result.error ?? "Send failed", | |
| }; | |
| } | |
| return { | |
| ok: true, | |
| messageId: result.messageId ?? generateMessageId(), | |
| }; | |
| } catch (error) { | |
| const errorMsg = error instanceof Error ? error.message : String(error); | |
| logger.error(`Failed to send message: ${errorMsg}`); | |
| return { | |
| ok: false, | |
| messageId: generateMessageId(), | |
| error: errorMsg, | |
| }; | |
| } | |
| } | |