diff --git a/src/config/config.ts b/src/config/config.ts index 9fbf905ac..4d3f5a10a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -501,6 +501,7 @@ const HeartbeatSchema = z z.literal("last"), z.literal("whatsapp"), z.literal("telegram"), + z.literal("discord"), z.literal("none"), ]) .optional(), diff --git a/src/discord/monitor.ts b/src/discord/monitor.ts index 4974e8458..d8da49ab0 100644 --- a/src/discord/monitor.ts +++ b/src/discord/monitor.ts @@ -197,7 +197,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) { }; if (isDirectMessage) { - const sessionCfg = cfg.inbound?.reply?.session; + const sessionCfg = cfg.session; const mainKey = (sessionCfg?.mainKey ?? "main").trim() || "main"; const storePath = resolveStorePath(sessionCfg?.store); await updateLastRoute({ @@ -274,16 +274,12 @@ async function resolveMedia( ); } const buffer = Buffer.from(await res.arrayBuffer()); - const saved = await saveMediaBuffer( + const mime = await detectMime({ buffer, - detectMime({ - buffer, - headerMime: attachment.contentType ?? res.headers.get("content-type"), - filePath: attachment.name ?? attachment.url, - }), - "inbound", - maxBytes, - ); + headerMime: attachment.contentType ?? res.headers.get("content-type"), + filePath: attachment.name ?? attachment.url, + }); + const saved = await saveMediaBuffer(buffer, mime, "inbound", maxBytes); return { path: saved.path, contentType: saved.contentType, diff --git a/src/gateway/server.ts b/src/gateway/server.ts index 4939a117a..ecab3e46e 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -1361,7 +1361,7 @@ export async function startGatewayServer( wakeMode: "now" | "next-heartbeat"; sessionKey: string; deliver: boolean; - channel: "last" | "whatsapp" | "telegram"; + channel: "last" | "whatsapp" | "telegram" | "discord"; to?: string; thinking?: string; timeoutSeconds?: number; @@ -1445,7 +1445,7 @@ export async function startGatewayServer( wakeMode: "now" | "next-heartbeat"; sessionKey: string; deliver: boolean; - channel: "last" | "whatsapp" | "telegram"; + channel: "last" | "whatsapp" | "telegram" | "discord"; to?: string; thinking?: string; timeoutSeconds?: number; diff --git a/src/infra/heartbeat-runner.ts b/src/infra/heartbeat-runner.ts index 5d937a4c9..5929e5bdc 100644 --- a/src/infra/heartbeat-runner.ts +++ b/src/infra/heartbeat-runner.ts @@ -14,6 +14,7 @@ import { type SessionEntry, saveSessionStore, } from "../config/sessions.js"; +import { sendMessageDiscord } from "../discord/send.js"; import { formatErrorMessage } from "../infra/errors.js"; import { createSubsystemLogger } from "../logging.js"; import { getQueueSize } from "../process/command-queue.js"; @@ -28,10 +29,15 @@ import { setHeartbeatWakeHandler, } from "./heartbeat-wake.js"; -export type HeartbeatTarget = "last" | "whatsapp" | "telegram" | "none"; +export type HeartbeatTarget = + | "last" + | "whatsapp" + | "telegram" + | "discord" + | "none"; export type HeartbeatDeliveryTarget = { - channel: "whatsapp" | "telegram" | "none"; + channel: "whatsapp" | "telegram" | "discord" | "none"; to?: string; reason?: string; }; @@ -40,6 +46,7 @@ type HeartbeatDeps = { runtime?: RuntimeEnv; sendWhatsApp?: typeof sendMessageWhatsApp; sendTelegram?: typeof sendMessageTelegram; + sendDiscord?: typeof sendMessageDiscord; getQueueSize?: (lane?: string) => number; nowMs?: () => number; }; @@ -126,6 +133,7 @@ export function resolveHeartbeatDeliveryTarget(params: { const target: HeartbeatTarget = rawTarget === "whatsapp" || rawTarget === "telegram" || + rawTarget === "discord" || rawTarget === "none" || rawTarget === "last" ? rawTarget @@ -146,10 +154,10 @@ export function resolveHeartbeatDeliveryTarget(params: { : undefined; const lastTo = typeof entry?.lastTo === "string" ? entry.lastTo.trim() : ""; - const channel: "whatsapp" | "telegram" | undefined = + const channel: "whatsapp" | "telegram" | "discord" | undefined = target === "last" ? lastChannel - : target === "whatsapp" || target === "telegram" + : target === "whatsapp" || target === "telegram" || target === "discord" ? target : undefined; @@ -216,11 +224,13 @@ function normalizeHeartbeatReply( } async function deliverHeartbeatReply(params: { - channel: "whatsapp" | "telegram"; + channel: "whatsapp" | "telegram" | "discord"; to: string; text: string; mediaUrls: string[]; - deps: Required>; + deps: Required< + Pick + >; }) { const { channel, to, text, mediaUrls, deps } = params; if (channel === "whatsapp") { @@ -239,17 +249,31 @@ async function deliverHeartbeatReply(params: { return; } - if (mediaUrls.length === 0) { - for (const chunk of chunkText(text, 4000)) { - await deps.sendTelegram(to, chunk, { verbose: false }); + if (channel === "telegram") { + if (mediaUrls.length === 0) { + for (const chunk of chunkText(text, 4000)) { + await deps.sendTelegram(to, chunk, { verbose: false }); + } + return; } + let first = true; + for (const url of mediaUrls) { + const caption = first ? text : ""; + first = false; + await deps.sendTelegram(to, caption, { verbose: false, mediaUrl: url }); + } + return; + } + + if (mediaUrls.length === 0) { + await deps.sendDiscord(to, text, { verbose: false }); return; } let first = true; for (const url of mediaUrls) { const caption = first ? text : ""; first = false; - await deps.sendTelegram(to, caption, { verbose: false, mediaUrl: url }); + await deps.sendDiscord(to, caption, { verbose: false, mediaUrl: url }); } } @@ -354,6 +378,7 @@ export async function runHeartbeatOnce(opts: { const deps = { sendWhatsApp: opts.deps?.sendWhatsApp ?? sendMessageWhatsApp, sendTelegram: opts.deps?.sendTelegram ?? sendMessageTelegram, + sendDiscord: opts.deps?.sendDiscord ?? sendMessageDiscord, }; await deliverHeartbeatReply({ channel: delivery.channel,