import { resolveZaloAccount } from "./accounts.js"; import type { ZaloFetch } from "./api.js"; import { sendMessage, sendPhoto } from "./api.js"; import { resolveZaloProxyFetch } from "./proxy.js"; import type { OpenClawConfig } from "./runtime-api.js"; import { resolveZaloToken } from "./token.js"; export type ZaloSendOptions = { token?: string; accountId?: string; cfg?: OpenClawConfig; mediaUrl?: string; caption?: string; verbose?: boolean; proxy?: string; }; export type ZaloSendResult = { ok: boolean; messageId?: string; error?: string; }; function toZaloSendResult(response: { ok?: boolean; result?: { message_id?: string }; }): ZaloSendResult { if (response.ok && response.result) { return { ok: true, messageId: response.result.message_id }; } return { ok: false, error: "Failed to send message" }; } async function runZaloSend( failureMessage: string, send: () => Promise<{ ok?: boolean; result?: { message_id?: string } }>, ): Promise { try { const result = toZaloSendResult(await send()); return result.ok ? result : { ok: false, error: failureMessage }; } catch (err) { return { ok: false, error: err instanceof Error ? err.message : String(err) }; } } function resolveSendContext(options: ZaloSendOptions): { token: string; fetcher?: ZaloFetch; } { if (options.cfg) { const account = resolveZaloAccount({ cfg: options.cfg, accountId: options.accountId, }); const token = options.token || account.token; const proxy = options.proxy ?? account.config.proxy; return { token, fetcher: resolveZaloProxyFetch(proxy) }; } const token = options.token ?? resolveZaloToken(undefined, options.accountId).token; const proxy = options.proxy; return { token, fetcher: resolveZaloProxyFetch(proxy) }; } function resolveValidatedSendContext( chatId: string, options: ZaloSendOptions, ): { ok: true; chatId: string; token: string; fetcher?: ZaloFetch } | { ok: false; error: string } { const { token, fetcher } = resolveSendContext(options); if (!token) { return { ok: false, error: "No Zalo bot token configured" }; } const trimmedChatId = chatId?.trim(); if (!trimmedChatId) { return { ok: false, error: "No chat_id provided" }; } return { ok: true, chatId: trimmedChatId, token, fetcher }; } function resolveSendContextOrFailure( chatId: string, options: ZaloSendOptions, ): | { context: { chatId: string; token: string; fetcher?: ZaloFetch } } | { failure: ZaloSendResult } { const context = resolveValidatedSendContext(chatId, options); return context.ok ? { context } : { failure: { ok: false, error: context.error }, }; } export async function sendMessageZalo( chatId: string, text: string, options: ZaloSendOptions = {}, ): Promise { const resolved = resolveSendContextOrFailure(chatId, options); if ("failure" in resolved) { return resolved.failure; } const { context } = resolved; if (options.mediaUrl) { return sendPhotoZalo(context.chatId, options.mediaUrl, { ...options, token: context.token, caption: text || options.caption, }); } return await runZaloSend("Failed to send message", () => sendMessage( context.token, { chat_id: context.chatId, text: text.slice(0, 2000), }, context.fetcher, ), ); } export async function sendPhotoZalo( chatId: string, photoUrl: string, options: ZaloSendOptions = {}, ): Promise { const resolved = resolveSendContextOrFailure(chatId, options); if ("failure" in resolved) { return resolved.failure; } const { context } = resolved; if (!photoUrl?.trim()) { return { ok: false, error: "No photo URL provided" }; } return await runZaloSend("Failed to send photo", () => sendPhoto( context.token, { chat_id: context.chatId, photo: photoUrl.trim(), caption: options.caption?.slice(0, 2000), }, context.fetcher, ), ); }