mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-16 12:30:49 +00:00
* fix(line): enforce requireMention gating in group message handler
* fix(line): scope canDetectMention to text messages, pass hasAnyMention
* fix(line): fix TS errors in mentionees type and test casts
* feat(line): register LINE in DOCKS and CHAT_CHANNEL_ORDER
- Add "line" to CHAT_CHANNEL_ORDER and CHAT_CHANNEL_META in registry.ts
- Export resolveLineGroupRequireMention and resolveLineGroupToolPolicy
in group-mentions.ts using the generic resolveChannelGroupRequireMention
and resolveChannelGroupToolsPolicy helpers (same pattern as iMessage)
- Add "line" entry to DOCKS in dock.ts so resolveGroupRequireMention
in the reply stage can correctly read LINE group config
Fixes the third layer of the requireMention bug: previously
getChannelDock("line") returned undefined, causing the reply-stage
resolveGroupRequireMention to fall back to true unconditionally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): pending history, requireMention default, mentionPatterns fallback
- Default requireMention to true (consistent with other channels)
- Add mentionPatterns regex fallback alongside native isSelf/@all detection
- Record unmentioned group messages via recordPendingHistoryEntryIfEnabled
- Inject pending history context in buildLineMessageContext when bot is mentioned
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(line): update tests for requireMention default and pending history
- Add requireMention: false to 6 group tests unrelated to mention gating
(allowlist, replay dedup, inflight dedup, error retry) to preserve
their original intent after the default changed from false to true
- Add test: skips group messages by default when requireMention not configured
- Add test: records unmentioned group messages as pending history
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): use undefined instead of empty string as historyKey sentinel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): deliver pending history via InboundHistory, not Body mutation
- Remove post-hoc ctxPayload.Body injection (BodyForAgent takes priority
in the prompt pipeline, so Body was never reached)
- Pass InboundHistory array to finalizeInboundContext instead, matching
the Telegram pattern rendered by buildInboundUserContextPrefix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): pass agentId to buildMentionRegexes for per-agent mentionPatterns
- Resolve route before mention gating to obtain agentId
- Pass agentId to buildMentionRegexes, matching Telegram behavior
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): clear pending history after handled group turn
- Call clearHistoryEntriesIfEnabled after processMessage for group messages
- Prevents stale skipped messages from replaying on subsequent mentions
- Matches Discord, Signal, Slack, iMessage behavior
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style(line): fix import order and merge orphaned JSDoc in bot-handlers
- Move resolveAgentRoute import from ./local group to ../routing group
- Merge duplicate JSDoc blocks above getLineMentionees into one
Addresses Greptile review comments r2888826724 and r2888826840 on PR #35847.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): read historyLimit from config and guard clear with has()
- bot.ts: resolve historyLimit from cfg.messages.groupChat.historyLimit
with fallback to DEFAULT_GROUP_HISTORY_LIMIT, so setting historyLimit: 0
actually disables pending history accumulation
- bot-handlers.ts: add groupHistories.has(historyKey) guard before
clearHistoryEntriesIfEnabled to prevent writing empty buckets for
groups that have never accumulated pending history (memory leak)
Addresses Codex review comments r2888829146 and r2888829152 on PR #35847.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style(line): apply oxfmt formatting to bot-handlers and bot
Auto-formatted by oxfmt to fix CI format:check failure on PR #35847.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(line): add shouldLogVerbose to globals mock in bot-handlers test
resolveAgentRoute calls shouldLogVerbose() from globals.js; the mock
was missing this export, causing 13 test failures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Address review findings for #35847
---------
Co-authored-by: Kaiyi <me@kaiyi.cool>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Yi-Cheng Wang <yicheng.wang@heph-ai.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
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<void>;
|
|
}
|
|
|
|
export interface LineBot {
|
|
handleWebhook: (body: WebhookRequestBody) => Promise<void>;
|
|
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<string, HistoryEntry[]>();
|
|
|
|
const handleWebhook = async (body: WebhookRequestBody): Promise<void> => {
|
|
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<void> } {
|
|
const { handler } = startLineWebhook({
|
|
channelSecret,
|
|
onEvents: bot.handleWebhook,
|
|
path,
|
|
});
|
|
|
|
return { path, handler };
|
|
}
|