2026-01-30 03:15:10 +01:00
|
|
|
import type { OpenClawConfig } from "../../config/config.js";
|
2026-01-08 00:50:29 +00:00
|
|
|
import type { ReplyToMode } from "../../config/types.js";
|
|
|
|
|
import type { OriginatingChannelType } from "../templating.js";
|
|
|
|
|
import type { ReplyPayload } from "../types.js";
|
2026-02-01 10:03:47 +09:00
|
|
|
import { getChannelDock } from "../../channels/dock.js";
|
|
|
|
|
import { normalizeChannelId } from "../../channels/plugins/index.js";
|
2026-01-08 00:50:29 +00:00
|
|
|
|
|
|
|
|
export function resolveReplyToMode(
|
2026-01-30 03:15:10 +01:00
|
|
|
cfg: OpenClawConfig,
|
2026-01-08 00:50:29 +00:00
|
|
|
channel?: OriginatingChannelType,
|
2026-01-11 11:45:25 +00:00
|
|
|
accountId?: string | null,
|
2026-01-23 07:13:23 +02:00
|
|
|
chatType?: string | null,
|
2026-01-08 00:50:29 +00:00
|
|
|
): ReplyToMode {
|
2026-01-13 06:16:43 +00:00
|
|
|
const provider = normalizeChannelId(channel);
|
2026-01-31 16:19:20 +09:00
|
|
|
if (!provider) {
|
|
|
|
|
return "all";
|
|
|
|
|
}
|
2026-01-13 06:16:43 +00:00
|
|
|
const resolved = getChannelDock(provider)?.threading?.resolveReplyToMode?.({
|
2026-01-11 11:45:25 +00:00
|
|
|
cfg,
|
|
|
|
|
accountId,
|
2026-01-23 07:13:23 +02:00
|
|
|
chatType,
|
2026-01-11 11:45:25 +00:00
|
|
|
});
|
|
|
|
|
return resolved ?? "all";
|
2026-01-08 00:50:29 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-09 14:59:36 +01:00
|
|
|
export function createReplyToModeFilter(
|
|
|
|
|
mode: ReplyToMode,
|
|
|
|
|
opts: { allowTagsWhenOff?: boolean } = {},
|
|
|
|
|
) {
|
2026-01-08 00:50:29 +00:00
|
|
|
let hasThreaded = false;
|
|
|
|
|
return (payload: ReplyPayload): ReplyPayload => {
|
2026-01-31 16:19:20 +09:00
|
|
|
if (!payload.replyToId) {
|
|
|
|
|
return payload;
|
|
|
|
|
}
|
2026-01-08 00:50:29 +00:00
|
|
|
if (mode === "off") {
|
2026-01-31 16:19:20 +09:00
|
|
|
if (opts.allowTagsWhenOff && payload.replyToTag) {
|
|
|
|
|
return payload;
|
|
|
|
|
}
|
2026-01-08 00:50:29 +00:00
|
|
|
return { ...payload, replyToId: undefined };
|
|
|
|
|
}
|
2026-01-31 16:19:20 +09:00
|
|
|
if (mode === "all") {
|
|
|
|
|
return payload;
|
|
|
|
|
}
|
2026-01-08 00:50:29 +00:00
|
|
|
if (hasThreaded) {
|
|
|
|
|
return { ...payload, replyToId: undefined };
|
|
|
|
|
}
|
|
|
|
|
hasThreaded = true;
|
|
|
|
|
return payload;
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-01-09 16:01:47 +00:00
|
|
|
|
|
|
|
|
export function createReplyToModeFilterForChannel(
|
|
|
|
|
mode: ReplyToMode,
|
|
|
|
|
channel?: OriginatingChannelType,
|
|
|
|
|
) {
|
2026-01-13 06:16:43 +00:00
|
|
|
const provider = normalizeChannelId(channel);
|
2026-02-14 06:29:42 -06:00
|
|
|
// Always honour explicit [[reply_to_*]] tags even when replyToMode is "off".
|
|
|
|
|
// Per-channel opt-out is possible but the safe default is to allow them.
|
2026-01-11 11:45:25 +00:00
|
|
|
const allowTagsWhenOff = provider
|
2026-02-14 06:29:42 -06:00
|
|
|
? (getChannelDock(provider)?.threading?.allowTagsWhenOff ?? true)
|
|
|
|
|
: true;
|
2026-01-09 16:01:47 +00:00
|
|
|
return createReplyToModeFilter(mode, {
|
2026-01-11 11:45:25 +00:00
|
|
|
allowTagsWhenOff,
|
2026-01-09 16:01:47 +00:00
|
|
|
});
|
|
|
|
|
}
|