@llblab/pi-telegram 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +15 -0
- package/README.md +16 -13
- package/docs/architecture.md +26 -16
- package/index.ts +199 -251
- package/lib/api.ts +277 -42
- package/lib/commands.ts +87 -0
- package/lib/media.ts +70 -1
- package/lib/polling.ts +25 -5
- package/lib/preview.ts +239 -0
- package/lib/rendering.ts +686 -49
- package/lib/replies.ts +2 -181
- package/lib/turns.ts +86 -0
- package/lib/types.ts +137 -0
- package/lib/updates.ts +64 -2
- package/package.json +1 -1
- package/tests/api.test.ts +243 -1
- package/tests/commands.test.ts +85 -0
- package/tests/media.test.ts +90 -1
- package/tests/menu.test.ts +46 -15
- package/tests/polling.test.ts +73 -0
- package/tests/preview.test.ts +480 -0
- package/tests/queue.test.ts +3 -0
- package/tests/rendering.test.ts +175 -2
- package/tests/replies.test.ts +2 -222
- package/tests/turns.test.ts +115 -0
- package/tests/updates.test.ts +72 -7
package/tests/replies.test.ts
CHANGED
|
@@ -1,239 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Regression tests for
|
|
3
|
-
* Covers
|
|
2
|
+
* Regression tests for Telegram reply delivery helpers
|
|
3
|
+
* Covers rendered-message transport, chunk delivery, and plain or markdown final reply sending
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import assert from "node:assert/strict";
|
|
7
7
|
import test from "node:test";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
-
buildTelegramPreviewFinalText,
|
|
11
|
-
buildTelegramPreviewFlushText,
|
|
12
10
|
buildTelegramReplyTransport,
|
|
13
|
-
clearTelegramPreview,
|
|
14
11
|
editTelegramRenderedMessage,
|
|
15
|
-
finalizeTelegramMarkdownPreview,
|
|
16
|
-
finalizeTelegramPreview,
|
|
17
|
-
flushTelegramPreview,
|
|
18
12
|
sendTelegramMarkdownReply,
|
|
19
13
|
sendTelegramPlainReply,
|
|
20
14
|
sendTelegramRenderedChunks,
|
|
21
|
-
shouldUseTelegramDraftPreview,
|
|
22
15
|
} from "../lib/replies.ts";
|
|
23
16
|
|
|
24
|
-
function createPreviewRuntimeHarness(state?: {
|
|
25
|
-
mode: "draft" | "message";
|
|
26
|
-
draftId?: number;
|
|
27
|
-
messageId?: number;
|
|
28
|
-
pendingText: string;
|
|
29
|
-
lastSentText: string;
|
|
30
|
-
flushTimer?: ReturnType<typeof setTimeout>;
|
|
31
|
-
}) {
|
|
32
|
-
let previewState = state;
|
|
33
|
-
let draftSupport: "unknown" | "supported" | "unsupported" = "unknown";
|
|
34
|
-
let nextDraftId = 10;
|
|
35
|
-
const events: string[] = [];
|
|
36
|
-
return {
|
|
37
|
-
events,
|
|
38
|
-
getState: () => previewState,
|
|
39
|
-
getDraftSupport: () => draftSupport,
|
|
40
|
-
setDraftSupport: (support: "unknown" | "supported" | "unsupported") => {
|
|
41
|
-
draftSupport = support;
|
|
42
|
-
},
|
|
43
|
-
deps: {
|
|
44
|
-
getState: () => previewState,
|
|
45
|
-
setState: (nextState: typeof previewState) => {
|
|
46
|
-
previewState = nextState;
|
|
47
|
-
},
|
|
48
|
-
clearScheduledFlush: (nextState: NonNullable<typeof previewState>) => {
|
|
49
|
-
if (!nextState.flushTimer) return;
|
|
50
|
-
clearTimeout(nextState.flushTimer);
|
|
51
|
-
nextState.flushTimer = undefined;
|
|
52
|
-
events.push("clear-timer");
|
|
53
|
-
},
|
|
54
|
-
maxMessageLength: 5,
|
|
55
|
-
renderPreviewText: (markdown: string) => markdown.replaceAll("*", ""),
|
|
56
|
-
getDraftSupport: () => draftSupport,
|
|
57
|
-
setDraftSupport: (support: "unknown" | "supported" | "unsupported") => {
|
|
58
|
-
draftSupport = support;
|
|
59
|
-
},
|
|
60
|
-
allocateDraftId: () => nextDraftId++,
|
|
61
|
-
sendDraft: async (chatId: number, draftId: number, text: string) => {
|
|
62
|
-
events.push(`draft:${chatId}:${draftId}:${text}`);
|
|
63
|
-
},
|
|
64
|
-
sendMessage: async (chatId: number, text: string) => {
|
|
65
|
-
events.push(`send:${chatId}:${text}`);
|
|
66
|
-
return { message_id: 77 };
|
|
67
|
-
},
|
|
68
|
-
editMessageText: async (
|
|
69
|
-
chatId: number,
|
|
70
|
-
messageId: number,
|
|
71
|
-
text: string,
|
|
72
|
-
) => {
|
|
73
|
-
events.push(`edit:${chatId}:${messageId}:${text}`);
|
|
74
|
-
},
|
|
75
|
-
renderTelegramMessage: (text: string, options?: { mode?: string }) => [
|
|
76
|
-
{ text: `${options?.mode ?? "plain"}:${text}` },
|
|
77
|
-
],
|
|
78
|
-
sendRenderedChunks: async (
|
|
79
|
-
chatId: number,
|
|
80
|
-
chunks: Array<{ text: string }>,
|
|
81
|
-
) => {
|
|
82
|
-
events.push(
|
|
83
|
-
`render-send:${chatId}:${chunks.map((chunk) => chunk.text).join("|")}`,
|
|
84
|
-
);
|
|
85
|
-
return 88;
|
|
86
|
-
},
|
|
87
|
-
editRenderedMessage: async (
|
|
88
|
-
chatId: number,
|
|
89
|
-
messageId: number,
|
|
90
|
-
chunks: Array<{ text: string }>,
|
|
91
|
-
) => {
|
|
92
|
-
events.push(
|
|
93
|
-
`render-edit:${chatId}:${messageId}:${chunks.map((chunk) => chunk.text).join("|")}`,
|
|
94
|
-
);
|
|
95
|
-
return messageId;
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
test("Reply previews build flush text only when the preview changed", () => {
|
|
102
|
-
assert.equal(
|
|
103
|
-
buildTelegramPreviewFlushText({
|
|
104
|
-
state: {
|
|
105
|
-
mode: "draft",
|
|
106
|
-
pendingText: "**hello**",
|
|
107
|
-
lastSentText: "",
|
|
108
|
-
},
|
|
109
|
-
maxMessageLength: 4096,
|
|
110
|
-
renderPreviewText: (markdown) => markdown.replaceAll("*", ""),
|
|
111
|
-
}),
|
|
112
|
-
"hello",
|
|
113
|
-
);
|
|
114
|
-
assert.equal(
|
|
115
|
-
buildTelegramPreviewFlushText({
|
|
116
|
-
state: {
|
|
117
|
-
mode: "draft",
|
|
118
|
-
pendingText: "**hello**",
|
|
119
|
-
lastSentText: "hello",
|
|
120
|
-
},
|
|
121
|
-
maxMessageLength: 4096,
|
|
122
|
-
renderPreviewText: (markdown) => markdown.replaceAll("*", ""),
|
|
123
|
-
}),
|
|
124
|
-
undefined,
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("Reply previews truncate long flush text and compute final text fallback", () => {
|
|
129
|
-
assert.equal(
|
|
130
|
-
buildTelegramPreviewFlushText({
|
|
131
|
-
state: {
|
|
132
|
-
mode: "message",
|
|
133
|
-
pendingText: "abcdef",
|
|
134
|
-
lastSentText: "",
|
|
135
|
-
},
|
|
136
|
-
maxMessageLength: 3,
|
|
137
|
-
renderPreviewText: (markdown) => markdown,
|
|
138
|
-
}),
|
|
139
|
-
"abc",
|
|
140
|
-
);
|
|
141
|
-
assert.equal(
|
|
142
|
-
buildTelegramPreviewFinalText({
|
|
143
|
-
mode: "message",
|
|
144
|
-
pendingText: " ",
|
|
145
|
-
lastSentText: "saved",
|
|
146
|
-
}),
|
|
147
|
-
"saved",
|
|
148
|
-
);
|
|
149
|
-
assert.equal(
|
|
150
|
-
buildTelegramPreviewFinalText({
|
|
151
|
-
mode: "message",
|
|
152
|
-
pendingText: " ",
|
|
153
|
-
lastSentText: " ",
|
|
154
|
-
}),
|
|
155
|
-
undefined,
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("Reply previews use drafts unless support is explicitly disabled", () => {
|
|
160
|
-
assert.equal(
|
|
161
|
-
shouldUseTelegramDraftPreview({ draftSupport: "unknown" }),
|
|
162
|
-
true,
|
|
163
|
-
);
|
|
164
|
-
assert.equal(
|
|
165
|
-
shouldUseTelegramDraftPreview({ draftSupport: "supported" }),
|
|
166
|
-
true,
|
|
167
|
-
);
|
|
168
|
-
assert.equal(
|
|
169
|
-
shouldUseTelegramDraftPreview({ draftSupport: "unsupported" }),
|
|
170
|
-
false,
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("Reply preview runtime prefers draft updates and can clear draft previews", async () => {
|
|
175
|
-
const harness = createPreviewRuntimeHarness({
|
|
176
|
-
mode: "draft",
|
|
177
|
-
pendingText: "**hello**",
|
|
178
|
-
lastSentText: "",
|
|
179
|
-
flushTimer: setTimeout(() => {}, 1000),
|
|
180
|
-
});
|
|
181
|
-
await flushTelegramPreview(7, harness.deps);
|
|
182
|
-
assert.deepEqual(harness.events, ["draft:7:10:hello"]);
|
|
183
|
-
assert.equal(harness.getState()?.mode, "draft");
|
|
184
|
-
assert.equal(harness.getState()?.draftId, 10);
|
|
185
|
-
assert.equal(harness.getState()?.lastSentText, "hello");
|
|
186
|
-
assert.equal(harness.getDraftSupport(), "supported");
|
|
187
|
-
await clearTelegramPreview(7, harness.deps);
|
|
188
|
-
assert.deepEqual(harness.events, ["draft:7:10:hello", "draft:7:10:"]);
|
|
189
|
-
assert.equal(harness.getState(), undefined);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test("Reply preview runtime falls back to editable messages when draft delivery fails", async () => {
|
|
193
|
-
const harness = createPreviewRuntimeHarness({
|
|
194
|
-
mode: "draft",
|
|
195
|
-
pendingText: "abcdef",
|
|
196
|
-
lastSentText: "",
|
|
197
|
-
});
|
|
198
|
-
harness.deps.sendDraft = async () => {
|
|
199
|
-
throw new Error("draft unsupported");
|
|
200
|
-
};
|
|
201
|
-
await flushTelegramPreview(7, harness.deps);
|
|
202
|
-
assert.deepEqual(harness.events, ["send:7:abcde"]);
|
|
203
|
-
assert.equal(harness.getState()?.mode, "message");
|
|
204
|
-
assert.equal(harness.getState()?.messageId, 77);
|
|
205
|
-
assert.equal(harness.getDraftSupport(), "unsupported");
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test("Reply preview runtime finalizes plain and markdown previews", async () => {
|
|
209
|
-
const plainHarness = createPreviewRuntimeHarness({
|
|
210
|
-
mode: "message",
|
|
211
|
-
messageId: 44,
|
|
212
|
-
pendingText: "done",
|
|
213
|
-
lastSentText: "",
|
|
214
|
-
});
|
|
215
|
-
plainHarness.setDraftSupport("unsupported");
|
|
216
|
-
assert.equal(await finalizeTelegramPreview(7, plainHarness.deps), true);
|
|
217
|
-
assert.deepEqual(plainHarness.events, ["edit:7:44:done"]);
|
|
218
|
-
assert.equal(plainHarness.getState(), undefined);
|
|
219
|
-
const markdownHarness = createPreviewRuntimeHarness({
|
|
220
|
-
mode: "message",
|
|
221
|
-
messageId: 55,
|
|
222
|
-
pendingText: "done",
|
|
223
|
-
lastSentText: "",
|
|
224
|
-
});
|
|
225
|
-
markdownHarness.setDraftSupport("unsupported");
|
|
226
|
-
assert.equal(
|
|
227
|
-
await finalizeTelegramMarkdownPreview(7, "**done**", markdownHarness.deps),
|
|
228
|
-
true,
|
|
229
|
-
);
|
|
230
|
-
assert.deepEqual(markdownHarness.events, [
|
|
231
|
-
"edit:7:55:done",
|
|
232
|
-
"render-edit:7:55:markdown:**done**",
|
|
233
|
-
]);
|
|
234
|
-
assert.equal(markdownHarness.getState(), undefined);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
17
|
test("Reply transport forwards send and edit operations through delivery helpers", async () => {
|
|
238
18
|
const events: string[] = [];
|
|
239
19
|
const transport = buildTelegramReplyTransport({
|
package/tests/turns.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildTelegramTurnPrompt,
|
|
12
12
|
formatTelegramTurnStatusSummary,
|
|
13
13
|
truncateTelegramQueueSummary,
|
|
14
|
+
updateTelegramPromptTurnText,
|
|
14
15
|
} from "../lib/turns.ts";
|
|
15
16
|
|
|
16
17
|
test("Turn helpers truncate queue summaries predictably", () => {
|
|
@@ -69,6 +70,120 @@ test("Turn helpers summarize text and attachment-only turns", () => {
|
|
|
69
70
|
);
|
|
70
71
|
});
|
|
71
72
|
|
|
73
|
+
test("Turn helpers update queued prompt text for edited Telegram messages", () => {
|
|
74
|
+
const turn = {
|
|
75
|
+
kind: "prompt" as const,
|
|
76
|
+
chatId: 99,
|
|
77
|
+
replyToMessageId: 10,
|
|
78
|
+
sourceMessageIds: [10],
|
|
79
|
+
queueOrder: 1,
|
|
80
|
+
queueLane: "default" as const,
|
|
81
|
+
laneOrder: 1,
|
|
82
|
+
queuedAttachments: [],
|
|
83
|
+
content: [{ type: "text" as const, text: "[telegram] old" }],
|
|
84
|
+
historyText: "old",
|
|
85
|
+
statusSummary: "old",
|
|
86
|
+
};
|
|
87
|
+
const updated = updateTelegramPromptTurnText({
|
|
88
|
+
turn,
|
|
89
|
+
telegramPrefix: "[telegram]",
|
|
90
|
+
rawText: "new edited message",
|
|
91
|
+
});
|
|
92
|
+
assert.equal(updated.content[0]?.type, "text");
|
|
93
|
+
assert.equal(
|
|
94
|
+
(updated.content[0] as { type: "text"; text: string }).text,
|
|
95
|
+
"[telegram] new edited message",
|
|
96
|
+
);
|
|
97
|
+
assert.equal(updated.historyText, "new edited message");
|
|
98
|
+
assert.equal(updated.statusSummary, "new edited message");
|
|
99
|
+
assert.notEqual(updated, turn);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("Turn helpers preserve queued prompt attachments when captions are edited", () => {
|
|
103
|
+
const turn = {
|
|
104
|
+
kind: "prompt" as const,
|
|
105
|
+
chatId: 99,
|
|
106
|
+
replyToMessageId: 10,
|
|
107
|
+
sourceMessageIds: [10],
|
|
108
|
+
queueOrder: 1,
|
|
109
|
+
queueLane: "default" as const,
|
|
110
|
+
laneOrder: 1,
|
|
111
|
+
queuedAttachments: [],
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "text" as const,
|
|
115
|
+
text:
|
|
116
|
+
"[telegram] old caption\n\n" +
|
|
117
|
+
"Telegram attachments were saved locally:\n" +
|
|
118
|
+
"- /tmp/demo.png\n" +
|
|
119
|
+
"- /tmp/report.txt",
|
|
120
|
+
},
|
|
121
|
+
{ type: "image" as const, data: "abc", mimeType: "image/png" },
|
|
122
|
+
],
|
|
123
|
+
historyText: "old caption\nAttachments:\n- /tmp/demo.png\n- /tmp/report.txt",
|
|
124
|
+
statusSummary: "old caption",
|
|
125
|
+
};
|
|
126
|
+
const updated = updateTelegramPromptTurnText({
|
|
127
|
+
turn,
|
|
128
|
+
telegramPrefix: "[telegram]",
|
|
129
|
+
rawText: "new caption",
|
|
130
|
+
});
|
|
131
|
+
assert.equal(
|
|
132
|
+
(updated.content[0] as { type: "text"; text: string }).text,
|
|
133
|
+
"[telegram] new caption\n\n" +
|
|
134
|
+
"Telegram attachments were saved locally:\n" +
|
|
135
|
+
"- /tmp/demo.png\n" +
|
|
136
|
+
"- /tmp/report.txt",
|
|
137
|
+
);
|
|
138
|
+
assert.deepEqual(updated.content[1], turn.content[1]);
|
|
139
|
+
assert.equal(
|
|
140
|
+
updated.historyText,
|
|
141
|
+
"new caption\nAttachments:\n- /tmp/demo.png\n- /tmp/report.txt",
|
|
142
|
+
);
|
|
143
|
+
assert.equal(updated.statusSummary, "new caption");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("Turn helpers preserve abort-history prompt context when queued turns are edited", () => {
|
|
147
|
+
const turn = {
|
|
148
|
+
kind: "prompt" as const,
|
|
149
|
+
chatId: 99,
|
|
150
|
+
replyToMessageId: 10,
|
|
151
|
+
sourceMessageIds: [10],
|
|
152
|
+
queueOrder: 1,
|
|
153
|
+
queueLane: "default" as const,
|
|
154
|
+
laneOrder: 1,
|
|
155
|
+
queuedAttachments: [],
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text" as const,
|
|
159
|
+
text:
|
|
160
|
+
"[telegram]\n\n" +
|
|
161
|
+
"Earlier Telegram messages arrived after an aborted turn. " +
|
|
162
|
+
"Treat them as prior user messages, in order:\n\n" +
|
|
163
|
+
"1. older Current Telegram message: quote\n\n" +
|
|
164
|
+
"Current Telegram message:\nold current",
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
historyText: "old current",
|
|
168
|
+
statusSummary: "old current",
|
|
169
|
+
};
|
|
170
|
+
const updated = updateTelegramPromptTurnText({
|
|
171
|
+
turn,
|
|
172
|
+
telegramPrefix: "[telegram]",
|
|
173
|
+
rawText: "new current",
|
|
174
|
+
});
|
|
175
|
+
assert.equal(
|
|
176
|
+
(updated.content[0] as { type: "text"; text: string }).text,
|
|
177
|
+
"[telegram]\n\n" +
|
|
178
|
+
"Earlier Telegram messages arrived after an aborted turn. " +
|
|
179
|
+
"Treat them as prior user messages, in order:\n\n" +
|
|
180
|
+
"1. older Current Telegram message: quote\n\n" +
|
|
181
|
+
"Current Telegram message:\nnew current",
|
|
182
|
+
);
|
|
183
|
+
assert.equal(updated.historyText, "new current");
|
|
184
|
+
assert.equal(updated.statusSummary, "new current");
|
|
185
|
+
});
|
|
186
|
+
|
|
72
187
|
test("Turn helpers assemble prompt turns with text, ids, history, and image payloads", async () => {
|
|
73
188
|
const turn = await buildTelegramPromptTurn({
|
|
74
189
|
telegramPrefix: "[telegram]",
|
package/tests/updates.test.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
executeTelegramUpdatePlan,
|
|
16
16
|
extractDeletedTelegramMessageIds,
|
|
17
17
|
getAuthorizedTelegramCallbackQuery,
|
|
18
|
+
getAuthorizedTelegramEditedMessage,
|
|
18
19
|
getAuthorizedTelegramMessage,
|
|
19
20
|
getTelegramAuthorizationState,
|
|
20
21
|
normalizeTelegramReactionEmoji,
|
|
@@ -74,7 +75,7 @@ test("Update routing extracts only private human callback queries", () => {
|
|
|
74
75
|
assert.ok(query);
|
|
75
76
|
});
|
|
76
77
|
|
|
77
|
-
test("Update routing extracts private human messages
|
|
78
|
+
test("Update routing extracts private human messages and edited messages separately", () => {
|
|
78
79
|
assert.equal(
|
|
79
80
|
getAuthorizedTelegramMessage({
|
|
80
81
|
message: {
|
|
@@ -85,12 +86,19 @@ test("Update routing extracts private human messages from message or edited_mess
|
|
|
85
86
|
undefined,
|
|
86
87
|
);
|
|
87
88
|
const directMessage = getAuthorizedTelegramMessage({
|
|
88
|
-
|
|
89
|
+
message: {
|
|
89
90
|
chat: { type: "private" },
|
|
90
91
|
from: { id: 1, is_bot: false },
|
|
91
92
|
},
|
|
92
93
|
});
|
|
93
94
|
assert.ok(directMessage);
|
|
95
|
+
const editedMessage = getAuthorizedTelegramEditedMessage({
|
|
96
|
+
edited_message: {
|
|
97
|
+
chat: { type: "private" },
|
|
98
|
+
from: { id: 1, is_bot: false },
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
assert.ok(editedMessage);
|
|
94
102
|
});
|
|
95
103
|
|
|
96
104
|
test("Update flow prioritizes deleted business-message handling over other update kinds", () => {
|
|
@@ -107,7 +115,7 @@ test("Update flow prioritizes deleted business-message handling over other updat
|
|
|
107
115
|
assert.deepEqual(action, { kind: "deleted", messageIds: [1, 2] });
|
|
108
116
|
});
|
|
109
117
|
|
|
110
|
-
test("Update flow returns authorized callback and
|
|
118
|
+
test("Update flow returns authorized callback, message, and edit actions", () => {
|
|
111
119
|
const callbackAction = buildTelegramUpdateFlowAction(
|
|
112
120
|
{
|
|
113
121
|
callback_query: {
|
|
@@ -119,7 +127,9 @@ test("Update flow returns authorized callback and message actions", () => {
|
|
|
119
127
|
);
|
|
120
128
|
assert.equal(callbackAction.kind, "callback");
|
|
121
129
|
assert.deepEqual(
|
|
122
|
-
callbackAction.kind === "callback"
|
|
130
|
+
callbackAction.kind === "callback"
|
|
131
|
+
? callbackAction.authorization
|
|
132
|
+
: undefined,
|
|
123
133
|
{ kind: "allow" },
|
|
124
134
|
);
|
|
125
135
|
const messageAction = buildTelegramUpdateFlowAction({
|
|
@@ -133,6 +143,16 @@ test("Update flow returns authorized callback and message actions", () => {
|
|
|
133
143
|
messageAction.kind === "message" ? messageAction.authorization : undefined,
|
|
134
144
|
{ kind: "pair", userId: 9 },
|
|
135
145
|
);
|
|
146
|
+
const editAction = buildTelegramUpdateFlowAction(
|
|
147
|
+
{
|
|
148
|
+
edited_message: {
|
|
149
|
+
chat: { type: "private" },
|
|
150
|
+
from: { id: 9, is_bot: false },
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
9,
|
|
154
|
+
);
|
|
155
|
+
assert.equal(editAction.kind, "edited-message");
|
|
136
156
|
});
|
|
137
157
|
|
|
138
158
|
test("Update flow ignores unauthorized transport shapes and preserves reaction events", () => {
|
|
@@ -223,10 +243,10 @@ test("Update runtime executes delete and reaction plans through the right side e
|
|
|
223
243
|
{
|
|
224
244
|
ctx: {} as never,
|
|
225
245
|
removePendingMediaGroupMessages: (ids) => {
|
|
226
|
-
events.push(`media:${ids.join(
|
|
246
|
+
events.push(`media:${ids.join(",")}`);
|
|
227
247
|
},
|
|
228
248
|
removeQueuedTelegramTurnsByMessageIds: (ids) => {
|
|
229
|
-
events.push(`queue:${ids.join(
|
|
249
|
+
events.push(`queue:${ids.join(",")}`);
|
|
230
250
|
return ids.length;
|
|
231
251
|
},
|
|
232
252
|
handleAuthorizedTelegramReactionUpdate: async () => {
|
|
@@ -237,6 +257,7 @@ test("Update runtime executes delete and reaction plans through the right side e
|
|
|
237
257
|
handleAuthorizedTelegramCallbackQuery: async () => {},
|
|
238
258
|
sendTextReply: async () => undefined,
|
|
239
259
|
handleAuthorizedTelegramMessage: async () => {},
|
|
260
|
+
handleAuthorizedTelegramEditedMessage: async () => {},
|
|
240
261
|
},
|
|
241
262
|
);
|
|
242
263
|
assert.deepEqual(events, ["media:1,2", "queue:1,2"]);
|
|
@@ -271,9 +292,47 @@ test("Update runtime can execute directly from raw updates", async () => {
|
|
|
271
292
|
handleAuthorizedTelegramMessage: async () => {
|
|
272
293
|
events.push("message");
|
|
273
294
|
},
|
|
295
|
+
handleAuthorizedTelegramEditedMessage: async () => {
|
|
296
|
+
events.push("edited-message");
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
);
|
|
300
|
+
assert.deepEqual(events, [
|
|
301
|
+
"pair",
|
|
302
|
+
"reply:Telegram bridge paired with this account.",
|
|
303
|
+
"message",
|
|
304
|
+
]);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("Update runtime routes edited messages without creating normal message turns", async () => {
|
|
308
|
+
const events: string[] = [];
|
|
309
|
+
await executeTelegramUpdate(
|
|
310
|
+
{
|
|
311
|
+
edited_message: {
|
|
312
|
+
chat: { id: 10, type: "private" },
|
|
313
|
+
message_id: 20,
|
|
314
|
+
from: { id: 7, is_bot: false },
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
7,
|
|
318
|
+
{
|
|
319
|
+
ctx: {} as never,
|
|
320
|
+
removePendingMediaGroupMessages: () => {},
|
|
321
|
+
removeQueuedTelegramTurnsByMessageIds: () => 0,
|
|
322
|
+
handleAuthorizedTelegramReactionUpdate: async () => {},
|
|
323
|
+
pairTelegramUserIfNeeded: async () => false,
|
|
324
|
+
answerCallbackQuery: async () => {},
|
|
325
|
+
handleAuthorizedTelegramCallbackQuery: async () => {},
|
|
326
|
+
sendTextReply: async () => undefined,
|
|
327
|
+
handleAuthorizedTelegramMessage: async () => {
|
|
328
|
+
events.push("message");
|
|
329
|
+
},
|
|
330
|
+
handleAuthorizedTelegramEditedMessage: async () => {
|
|
331
|
+
events.push("edited-message");
|
|
332
|
+
},
|
|
274
333
|
},
|
|
275
334
|
);
|
|
276
|
-
assert.deepEqual(events, ["
|
|
335
|
+
assert.deepEqual(events, ["edited-message"]);
|
|
277
336
|
});
|
|
278
337
|
|
|
279
338
|
test("Update runtime handles callback deny and message pair flows", async () => {
|
|
@@ -311,6 +370,9 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
|
|
311
370
|
handleAuthorizedTelegramMessage: async () => {
|
|
312
371
|
events.push("message");
|
|
313
372
|
},
|
|
373
|
+
handleAuthorizedTelegramEditedMessage: async () => {
|
|
374
|
+
events.push("edited-message");
|
|
375
|
+
},
|
|
314
376
|
},
|
|
315
377
|
);
|
|
316
378
|
await executeTelegramUpdatePlan(
|
|
@@ -340,6 +402,9 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
|
|
340
402
|
handleAuthorizedTelegramMessage: async () => {
|
|
341
403
|
events.push("message");
|
|
342
404
|
},
|
|
405
|
+
handleAuthorizedTelegramEditedMessage: async () => {
|
|
406
|
+
events.push("edited-message");
|
|
407
|
+
},
|
|
343
408
|
},
|
|
344
409
|
);
|
|
345
410
|
assert.deepEqual(events, [
|