Skip to content

Commit de68d19

Browse files
mmapseleqtrizit
authored andcommitted
fix(telegram): respect history mode during forum recovery
1 parent 6c559e4 commit de68d19

4 files changed

Lines changed: 125 additions & 7 deletions

File tree

extensions/telegram/src/bot-message-context.session.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
import type { TelegramContext } from "./bot/types.js";
4848
import { resolveTelegramGroupPromptSettings } from "./group-config-helpers.js";
4949
import {
50+
type TelegramGroupHistoryContextMode,
5051
includesRecentTelegramGroupHistoryContext,
5152
resolveTelegramGroupHistoryContextModeForAccount,
5253
} from "./group-history-context.js";
@@ -215,6 +216,7 @@ export async function buildTelegramInboundContextPayload(params: {
215216
historyKey?: string;
216217
historyLimit: number;
217218
groupHistories: Map<string, HistoryEntry[]>;
219+
groupHistoryContextMode?: TelegramGroupHistoryContextMode;
218220
groupConfig?: TelegramGroupConfig | TelegramDirectConfig;
219221
topicConfig?: TelegramTopicConfig;
220222
effectiveWasMentioned: boolean;
@@ -264,6 +266,7 @@ export async function buildTelegramInboundContextPayload(params: {
264266
historyKey,
265267
historyLimit,
266268
groupHistories,
269+
groupHistoryContextMode,
267270
groupConfig,
268271
topicConfig,
269272
effectiveWasMentioned,
@@ -419,10 +422,11 @@ export async function buildTelegramInboundContextPayload(params: {
419422
const includeRecentGroupHistoryContext =
420423
isGroup &&
421424
includesRecentTelegramGroupHistoryContext(
422-
resolveTelegramGroupHistoryContextModeForAccount({
423-
cfg,
424-
accountId: route.accountId,
425-
}),
425+
groupHistoryContextMode ??
426+
resolveTelegramGroupHistoryContextModeForAccount({
427+
cfg,
428+
accountId: route.accountId,
429+
}),
426430
);
427431
let combinedBody = body;
428432
if (includeRecentGroupHistoryContext && historyKey && historyLimit > 0) {

extensions/telegram/src/bot-message-context.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ import {
4545
} from "./conversation-route.js";
4646
import { enforceTelegramDmAccess } from "./dm-access.js";
4747
import { evaluateTelegramGroupBaseAccess } from "./group-access.js";
48+
import {
49+
resolveTelegramGroupHistoryContextModeForAccount,
50+
type TelegramGroupHistoryContextMode,
51+
} from "./group-history-context.js";
4852
import {
4953
buildTelegramStatusReactionVariants,
5054
type TelegramReactionEmoji,
@@ -106,6 +110,7 @@ export type TelegramMessageContext = {
106110
historyKey?: string;
107111
historyLimit: BuildTelegramMessageContextParams["historyLimit"];
108112
groupHistories: BuildTelegramMessageContextParams["groupHistories"];
113+
groupHistoryContextMode?: TelegramGroupHistoryContextMode;
109114
route: ReturnType<typeof resolveTelegramConversationRoute>["route"];
110115
skillFilter: TelegramMessageContextPayload["skillFilter"];
111116
sendTyping: () => Promise<void>;
@@ -482,6 +487,13 @@ export const buildTelegramMessageContext = async ({
482487
return null;
483488
}
484489

490+
const groupHistoryContextMode = isGroup
491+
? resolveTelegramGroupHistoryContextModeForAccount({
492+
cfg,
493+
accountId: route.accountId,
494+
})
495+
: undefined;
496+
485497
if (!(await ensureConfiguredBindingReady())) {
486498
return null;
487499
}
@@ -517,6 +529,7 @@ export const buildTelegramMessageContext = async ({
517529
historyKey: bodyResult.historyKey ?? "",
518530
historyLimit,
519531
groupHistories,
532+
groupHistoryContextMode,
520533
groupConfig,
521534
topicConfig,
522535
effectiveWasMentioned: bodyResult.effectiveWasMentioned,
@@ -653,6 +666,7 @@ export const buildTelegramMessageContext = async ({
653666
historyKey: bodyResult.historyKey ?? "",
654667
historyLimit,
655668
groupHistories,
669+
groupHistoryContextMode,
656670
route,
657671
skillFilter,
658672
sendTyping,

extensions/telegram/src/bot-message-dispatch.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
716716
historyKey: oldHistoryKey,
717717
historyLimit: 10,
718718
groupHistories,
719+
groupHistoryContextMode: "recent",
719720
sendChatActionHandler,
720721
turn: {
721722
storePath: "/tmp/openclaw/telegram-sessions.json",
@@ -776,6 +777,90 @@ describe("dispatchTelegramMessage draft streaming", () => {
776777
expect(deliverReplies).not.toHaveBeenCalled();
777778
});
778779

780+
it.each(["mention-only", "none"] as const)(
781+
"does not recover forum history context when mode is %s",
782+
async (groupHistoryContextMode) => {
783+
const oldHistoryKey = "-1003774691294:topic:1";
784+
const recoveredHistoryKey = "-1003774691294:topic:3731";
785+
const groupHistories = new Map([
786+
[oldHistoryKey, [{ sender: "Alice", body: "general topic context", timestamp: 1 }]],
787+
[recoveredHistoryKey, [{ sender: "Bob", body: "recovered topic context", timestamp: 2 }]],
788+
]);
789+
deliverInboundReplyWithMessageSendContext.mockResolvedValue({
790+
status: "handled_visible",
791+
delivery: {
792+
messageIds: ["3731"],
793+
visibleReplySent: true,
794+
},
795+
});
796+
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
797+
await dispatcherOptions.deliver({ text: "topic final" }, { kind: "final" });
798+
return { queuedFinal: true };
799+
});
800+
801+
await dispatchWithContext({
802+
context: createContext({
803+
ctxPayload: {
804+
Body:
805+
"[Chat messages since your last reply - for context]\n" +
806+
"general topic context\n" +
807+
"[Current message - respond to this]\n" +
808+
"current topic question",
809+
BodyForAgent:
810+
"[Chat messages since your last reply - for context]\n" +
811+
"general topic context\n" +
812+
"[Current message - respond to this]\n" +
813+
"current topic question",
814+
ChatType: "group",
815+
From: "telegram:group:-1003774691294:topic:1",
816+
MessageThreadId: 1,
817+
OriginatingTo: "telegram:-1003774691294",
818+
SessionKey: "agent:main:telegram:group:-1003774691294:topic:3731",
819+
To: "telegram:-1003774691294",
820+
TransportThreadId: 1,
821+
} as unknown as TelegramMessageContext["ctxPayload"],
822+
msg: {
823+
chat: { id: -1003774691294, type: "supergroup" },
824+
message_id: 27787,
825+
message_thread_id: undefined,
826+
} as unknown as TelegramMessageContext["msg"],
827+
primaryCtx: {
828+
message: { chat: { id: -1003774691294, type: "supergroup" } },
829+
} as unknown as TelegramMessageContext["primaryCtx"],
830+
chatId: -1003774691294,
831+
isGroup: true,
832+
replyThreadId: undefined,
833+
resolvedThreadId: undefined,
834+
threadSpec: { id: 1, scope: "forum" },
835+
historyKey: oldHistoryKey,
836+
historyLimit: 10,
837+
groupHistories,
838+
groupHistoryContextMode,
839+
}),
840+
replyToMode: "off",
841+
streamMode: "off",
842+
});
843+
844+
const outbound = expectRecordFields(mockCallArg(deliverInboundReplyWithMessageSendContext), {
845+
threadId: 3731,
846+
});
847+
expectRecordFields(outbound.ctxPayload, {
848+
From: "telegram:group:-1003774691294:topic:3731",
849+
MessageThreadId: 3731,
850+
OriginatingTo: "telegram:-1003774691294:topic:3731",
851+
TransportThreadId: 3731,
852+
To: "telegram:-1003774691294:topic:3731",
853+
});
854+
const outboundCtxPayload = expectRecordFields(outbound.ctxPayload, {});
855+
expect(outboundCtxPayload.InboundHistory).toBeUndefined();
856+
expect(outboundCtxPayload.Body).toBe("current topic question");
857+
expect(outboundCtxPayload.Body).not.toContain("recovered topic context");
858+
expect(outboundCtxPayload.Body).not.toContain("general topic context");
859+
expect(outboundCtxPayload.BodyForAgent).toBe("current topic question");
860+
expect(deliverReplies).not.toHaveBeenCalled();
861+
},
862+
);
863+
779864
it("does not recover forum thread context from malformed payload thread ids", async () => {
780865
const generalHistoryKey = "-1003774691294:topic:1";
781866
const spoofedHistoryKey = "-1003774691294:topic:3731";
@@ -955,6 +1040,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
9551040
historyKey: oldHistoryKey,
9561041
historyLimit: 10,
9571042
groupHistories,
1043+
groupHistoryContextMode: "recent",
9581044
}),
9591045
replyToMode: "off",
9601046
streamMode: "off",

extensions/telegram/src/bot-message-dispatch.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ import {
107107
shouldSuppressTelegramError,
108108
} from "./error-policy.js";
109109
import { shouldSuppressLocalTelegramExecApprovalPrompt } from "./exec-approvals.js";
110+
import { includesRecentTelegramGroupHistoryContext } from "./group-history-context.js";
110111
import { beginTelegramInboundEventDeliveryCorrelation } from "./inbound-event-delivery.js";
111112
import {
112113
createLaneDeliveryStateTracker,
@@ -552,14 +553,26 @@ function extractCurrentTelegramBody(body: string | undefined): string {
552553
return body.slice(markerIndex + CURRENT_MESSAGE_MARKER.length).trimStart();
553554
}
554555

556+
function includesRecoveredTelegramGroupHistoryContext(context: TelegramMessageContext): boolean {
557+
return Boolean(
558+
context.isGroup &&
559+
context.groupHistoryContextMode &&
560+
includesRecentTelegramGroupHistoryContext(context.groupHistoryContextMode),
561+
);
562+
}
563+
555564
function buildRecoveredTelegramBody(params: {
556565
cfg: OpenClawConfig;
557566
context: TelegramMessageContext;
558567
currentMessage: string;
559568
historyKey?: string;
560569
threadSpec: TelegramThreadSpec;
561570
}): string {
562-
if (!params.context.isGroup || !params.historyKey || params.context.historyLimit <= 0) {
571+
if (
572+
!includesRecoveredTelegramGroupHistoryContext(params.context) ||
573+
!params.historyKey ||
574+
params.context.historyLimit <= 0
575+
) {
563576
return params.currentMessage;
564577
}
565578
const groupLabel = buildGroupLabel(
@@ -621,7 +634,7 @@ function migrateRecoveredTelegramRoomEventHistory(params: {
621634
const originalHistoryKey = params.context.historyKey;
622635
const recoveredHistoryKey = params.recoveredHistoryKey;
623636
if (
624-
!params.context.isGroup ||
637+
!includesRecoveredTelegramGroupHistoryContext(params.context) ||
625638
params.context.ctxPayload.InboundEventKind !== "room_event" ||
626639
!originalHistoryKey ||
627640
!recoveredHistoryKey ||
@@ -688,12 +701,13 @@ function resolveDispatchTelegramContext(params: {
688701
const recoveredHistoryKey = params.context.isGroup
689702
? buildTelegramGroupPeerId(params.context.chatId, threadSpec.id)
690703
: params.context.historyKey;
704+
const includeRecoveredGroupHistory = includesRecoveredTelegramGroupHistoryContext(params.context);
691705
migrateRecoveredTelegramRoomEventHistory({
692706
context: params.context,
693707
recoveredHistoryKey,
694708
});
695709
const recoveredInboundHistory =
696-
params.context.isGroup && recoveredHistoryKey && params.context.historyLimit > 0
710+
includeRecoveredGroupHistory && recoveredHistoryKey && params.context.historyLimit > 0
697711
? createChannelHistoryWindow({
698712
historyMap: params.context.groupHistories,
699713
}).buildInboundHistory({

0 commit comments

Comments
 (0)