import type { WebhookRequestBody } from "@line/bot-sdk"; import type { Request, Response, NextFunction } from "express"; import { DEFAULT_GROUP_HISTORY_LIMIT, type HistoryEntry } from "../auto-reply/reply/history.js"; import type { OpenClawConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js"; import { logVerbose } from "../globals.js"; import { createNonExitingRuntime, type RuntimeEnv } from "../runtime.js"; import { resolveLineAccount } from "./accounts.js"; import { createLineWebhookReplayCache, handleLineWebhookEvents } from "./bot-handlers.js"; import type { LineInboundContext } from "./bot-message-context.js"; import type { ResolvedLineAccount } from "./types.js"; import { startLineWebhook } from "./webhook.js"; export interface LineBotOptions { channelAccessToken: string; channelSecret: string; accountId?: string; runtime?: RuntimeEnv; config?: OpenClawConfig; mediaMaxMb?: number; onMessage?: (ctx: LineInboundContext) => Promise; } export interface LineBot { handleWebhook: (body: WebhookRequestBody) => Promise; account: ResolvedLineAccount; } export function createLineBot(opts: LineBotOptions): LineBot { const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime(); const cfg = opts.config ?? loadConfig(); const account = resolveLineAccount({ cfg, accountId: opts.accountId, }); const mediaMaxBytes = (opts.mediaMaxMb ?? account.config.mediaMaxMb ?? 10) * 1024 * 1024; const processMessage = opts.onMessage ?? (async () => { logVerbose("line: no message handler configured"); }); const replayCache = createLineWebhookReplayCache(); const groupHistories = new Map(); const handleWebhook = async (body: WebhookRequestBody): Promise => { if (!body.events || body.events.length === 0) { return; } await handleLineWebhookEvents(body.events, { cfg, account, runtime, mediaMaxBytes, processMessage, replayCache, groupHistories, historyLimit: cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT, }); }; return { handleWebhook, account, }; } export function createLineWebhookCallback( bot: LineBot, channelSecret: string, path = "/line/webhook", ): { path: string; handler: (req: Request, res: Response, _next: NextFunction) => Promise } { const { handler } = startLineWebhook({ channelSecret, onEvents: bot.handleWebhook, path, }); return { path, handler }; }