paperclip-plugin-telegram 0.3.0 → 0.5.0
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/README.md +57 -3
- package/dist/allowlist.d.ts +27 -0
- package/dist/allowlist.js +34 -0
- package/dist/allowlist.js.map +1 -0
- package/dist/approval-routing.d.ts +2 -0
- package/dist/approval-routing.js +7 -0
- package/dist/approval-routing.js.map +1 -0
- package/dist/command-registry.js +1 -1
- package/dist/command-registry.js.map +1 -1
- package/dist/commands.d.ts +3 -2
- package/dist/commands.js +252 -31
- package/dist/commands.js.map +1 -1
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -1
- package/dist/formatters.d.ts +1 -0
- package/dist/formatters.js +28 -0
- package/dist/formatters.js.map +1 -1
- package/dist/manifest.js +79 -2
- package/dist/manifest.js.map +1 -1
- package/dist/paperclip-api.d.ts +3 -0
- package/dist/paperclip-api.js +34 -0
- package/dist/paperclip-api.js.map +1 -0
- package/dist/polling-offset.d.ts +4 -0
- package/dist/polling-offset.js +24 -0
- package/dist/polling-offset.js.map +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +320 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/worker.js +289 -32
- package/dist/worker.js.map +1 -1
- package/package.json +8 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/worker.js
CHANGED
|
@@ -1,14 +1,125 @@
|
|
|
1
1
|
import { definePlugin, runWorker, } from "@paperclipai/plugin-sdk";
|
|
2
2
|
import { sendMessage, editMessage, answerCallbackQuery, setMyCommands, escapeMarkdownV2, isForum, GENERAL_TOPIC_THREAD_ID, } from "./telegram-api.js";
|
|
3
|
-
import { formatIssueCreated, formatIssueDone, formatApprovalCreated, formatAgentError, formatAgentRunStarted, formatAgentRunFinished, } from "./formatters.js";
|
|
4
|
-
import { handleCommand,
|
|
3
|
+
import { formatIssueCreated, formatIssueDone, formatIssueAssigned, formatApprovalCreated, formatAgentError, formatAgentRunStarted, formatAgentRunFinished, } from "./formatters.js";
|
|
4
|
+
import { handleCommand, resolveNotificationThreadId, BOT_COMMANDS } from "./commands.js";
|
|
5
5
|
import { routeMessageToAgent, handleHandoffToolCall, handleDiscussToolCall, handleHandoffApproval, handleHandoffRejection, setupAcpOutputListener, } from "./acp-bridge.js";
|
|
6
6
|
import { handleMediaMessage } from "./media-pipeline.js";
|
|
7
|
+
import { getPersistedTelegramUpdateOffset, persistTelegramUpdateOffset } from "./polling-offset.js";
|
|
7
8
|
import { handleCommandsCommand, tryCustomCommand } from "./command-registry.js";
|
|
8
9
|
import { handleRegisterWatch, checkWatches } from "./watch-registry.js";
|
|
9
10
|
import { METRIC_NAMES } from "./constants.js";
|
|
10
11
|
import { EscalationManager } from "./escalation.js";
|
|
12
|
+
import { isTelegramUpdateAllowed, validateTelegramAllowlists } from "./allowlist.js";
|
|
13
|
+
import { shouldNotifyApproval } from "./approval-routing.js";
|
|
14
|
+
import { buildPaperclipAuthHeaders, fetchPaperclipApi } from "./paperclip-api.js";
|
|
11
15
|
const TELEGRAM_API = "https://api.telegram.org";
|
|
16
|
+
const BOARD_ACCESS_SCOPE = {
|
|
17
|
+
scopeKind: "instance",
|
|
18
|
+
stateKey: "telegram.board-access.v1",
|
|
19
|
+
};
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function asNonEmptyString(value) {
|
|
24
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
25
|
+
}
|
|
26
|
+
function normalizeBoardAccessState(value) {
|
|
27
|
+
const record = isRecord(value) ? value : {};
|
|
28
|
+
return {
|
|
29
|
+
paperclipBoardApiTokenRef: asNonEmptyString(record.paperclipBoardApiTokenRef),
|
|
30
|
+
identity: asNonEmptyString(record.identity),
|
|
31
|
+
companyId: asNonEmptyString(record.companyId),
|
|
32
|
+
updatedAt: asNonEmptyString(record.updatedAt),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function loadBoardAccessState(ctx) {
|
|
36
|
+
return normalizeBoardAccessState(await ctx.state.get(BOARD_ACCESS_SCOPE));
|
|
37
|
+
}
|
|
38
|
+
async function persistBoardAccessState(ctx, state) {
|
|
39
|
+
const nextState = normalizeBoardAccessState(state);
|
|
40
|
+
await ctx.state.set(BOARD_ACCESS_SCOPE, nextState);
|
|
41
|
+
return {
|
|
42
|
+
...nextState,
|
|
43
|
+
configured: Boolean(nextState.paperclipBoardApiTokenRef),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function getBoardAccessRegistration(state) {
|
|
47
|
+
return {
|
|
48
|
+
...state,
|
|
49
|
+
configured: Boolean(state.paperclipBoardApiTokenRef),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async function resolveBoardApiToken(ctx, config, companyId) {
|
|
53
|
+
const boardAccessState = await loadBoardAccessState(ctx);
|
|
54
|
+
const candidates = [];
|
|
55
|
+
if (boardAccessState.paperclipBoardApiTokenRef &&
|
|
56
|
+
(!companyId || !boardAccessState.companyId || boardAccessState.companyId === companyId)) {
|
|
57
|
+
candidates.push({
|
|
58
|
+
source: "board-access",
|
|
59
|
+
ref: boardAccessState.paperclipBoardApiTokenRef,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (config.paperclipBoardApiTokenRef) {
|
|
63
|
+
candidates.push({
|
|
64
|
+
source: "config",
|
|
65
|
+
ref: config.paperclipBoardApiTokenRef,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
for (const candidate of candidates) {
|
|
70
|
+
if (seen.has(candidate.ref))
|
|
71
|
+
continue;
|
|
72
|
+
seen.add(candidate.ref);
|
|
73
|
+
try {
|
|
74
|
+
return await ctx.secrets.resolve(candidate.ref);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
ctx.logger.warn("Failed to resolve board API token secret", {
|
|
78
|
+
source: candidate.source,
|
|
79
|
+
companyId,
|
|
80
|
+
error: String(err),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
async function resolveCallbackCompanyId(ctx, query) {
|
|
87
|
+
const chatId = query.message?.chat.id ? String(query.message.chat.id) : null;
|
|
88
|
+
const messageId = query.message?.message_id;
|
|
89
|
+
if (!chatId || !messageId)
|
|
90
|
+
return null;
|
|
91
|
+
const mapping = await ctx.state.get({
|
|
92
|
+
scopeKind: "instance",
|
|
93
|
+
stateKey: `msg_${chatId}_${messageId}`,
|
|
94
|
+
});
|
|
95
|
+
return mapping?.companyId ?? null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Shared 5s sliding-window dedupe for issue.updated handlers.
|
|
99
|
+
*
|
|
100
|
+
* Paperclip's core can emit duplicate `issue.updated` plugin events for a
|
|
101
|
+
* single PATCH (the route's logActivity plus side-effects from heartbeat
|
|
102
|
+
* reconciliation), so handlers must dedupe to avoid sending the same
|
|
103
|
+
* Telegram message twice.
|
|
104
|
+
*/
|
|
105
|
+
function makeUpdateDedupe(windowMs = 5_000, maxEntries = 500) {
|
|
106
|
+
const seen = new Map();
|
|
107
|
+
return (key) => {
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
const last = seen.get(key);
|
|
110
|
+
if (last !== undefined && now - last < windowMs)
|
|
111
|
+
return false;
|
|
112
|
+
seen.set(key, now);
|
|
113
|
+
if (seen.size > maxEntries) {
|
|
114
|
+
const cutoff = now - windowMs;
|
|
115
|
+
for (const [k, ts] of seen) {
|
|
116
|
+
if (ts < cutoff)
|
|
117
|
+
seen.delete(k);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
12
123
|
async function resolveChat(ctx, companyId, fallback) {
|
|
13
124
|
const override = await ctx.state.get({
|
|
14
125
|
scopeKind: "company",
|
|
@@ -17,6 +128,32 @@ async function resolveChat(ctx, companyId, fallback) {
|
|
|
17
128
|
});
|
|
18
129
|
return override ?? fallback ?? null;
|
|
19
130
|
}
|
|
131
|
+
function parseTopicId(value) {
|
|
132
|
+
const trimmed = value?.trim();
|
|
133
|
+
if (!trimmed)
|
|
134
|
+
return undefined;
|
|
135
|
+
if (!/^\d+$/.test(trimmed))
|
|
136
|
+
return undefined;
|
|
137
|
+
return Number(trimmed);
|
|
138
|
+
}
|
|
139
|
+
function validateConfiguredTopicIds(config) {
|
|
140
|
+
const errors = [];
|
|
141
|
+
for (const key of ["approvalsTopicId", "errorsTopicId", "digestTopicId"]) {
|
|
142
|
+
const value = config[key];
|
|
143
|
+
if (value === undefined || value === null || value === "")
|
|
144
|
+
continue;
|
|
145
|
+
if (typeof value !== "string" || !parseTopicId(value)) {
|
|
146
|
+
errors.push(`${key} must be a numeric Telegram forum topic ID string.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return errors;
|
|
150
|
+
}
|
|
151
|
+
async function resolveDigestThreadId(ctx, token, chatId, configuredTopicId) {
|
|
152
|
+
const configured = parseTopicId(configuredTopicId);
|
|
153
|
+
if (configured)
|
|
154
|
+
return configured;
|
|
155
|
+
return await isForum(ctx, token, chatId) ? GENERAL_TOPIC_THREAD_ID : undefined;
|
|
156
|
+
}
|
|
20
157
|
async function resolveCompanyId(ctx, chatId) {
|
|
21
158
|
const mapping = await ctx.state.get({
|
|
22
159
|
scopeKind: "instance",
|
|
@@ -31,6 +168,20 @@ const plugin = definePlugin({
|
|
|
31
168
|
const config = rawConfig;
|
|
32
169
|
const baseUrl = config.paperclipBaseUrl || "http://localhost:3100";
|
|
33
170
|
const publicUrl = config.paperclipPublicUrl || baseUrl;
|
|
171
|
+
ctx.data.register("board-access.read", async () => getBoardAccessRegistration(await loadBoardAccessState(ctx)));
|
|
172
|
+
ctx.actions.register("board-access.update", async (params) => {
|
|
173
|
+
const record = isRecord(params) ? params : {};
|
|
174
|
+
const paperclipBoardApiTokenRef = asNonEmptyString(record.paperclipBoardApiTokenRef);
|
|
175
|
+
const identity = asNonEmptyString(record.identity);
|
|
176
|
+
const companyId = asNonEmptyString(record.companyId);
|
|
177
|
+
const now = new Date().toISOString();
|
|
178
|
+
return persistBoardAccessState(ctx, {
|
|
179
|
+
paperclipBoardApiTokenRef,
|
|
180
|
+
identity,
|
|
181
|
+
companyId,
|
|
182
|
+
updatedAt: now,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
34
185
|
if (!config.telegramBotTokenRef) {
|
|
35
186
|
ctx.logger.warn("No telegramBotTokenRef configured, plugin disabled");
|
|
36
187
|
return;
|
|
@@ -60,7 +211,7 @@ const plugin = definePlugin({
|
|
|
60
211
|
}
|
|
61
212
|
// --- Long polling for inbound messages ---
|
|
62
213
|
let pollingActive = true;
|
|
63
|
-
let lastUpdateId =
|
|
214
|
+
let lastUpdateId = await getPersistedTelegramUpdateOffset(ctx);
|
|
64
215
|
async function pollUpdates() {
|
|
65
216
|
while (pollingActive) {
|
|
66
217
|
try {
|
|
@@ -68,8 +219,28 @@ const plugin = definePlugin({
|
|
|
68
219
|
const data = (await res.json());
|
|
69
220
|
if (data.ok && data.result) {
|
|
70
221
|
for (const update of data.result) {
|
|
71
|
-
|
|
72
|
-
|
|
222
|
+
try {
|
|
223
|
+
await handleUpdate(ctx, token, config, update, baseUrl, publicUrl);
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
ctx.logger.error("Telegram update handling failed", {
|
|
227
|
+
updateId: update.update_id,
|
|
228
|
+
error: String(err),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const nextUpdateId = Math.max(lastUpdateId, update.update_id);
|
|
232
|
+
if (nextUpdateId > lastUpdateId) {
|
|
233
|
+
lastUpdateId = nextUpdateId;
|
|
234
|
+
try {
|
|
235
|
+
await persistTelegramUpdateOffset(ctx, lastUpdateId);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
ctx.logger.error("Failed to persist Telegram polling offset", {
|
|
239
|
+
updateId: lastUpdateId,
|
|
240
|
+
error: String(err),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
73
244
|
}
|
|
74
245
|
}
|
|
75
246
|
}
|
|
@@ -99,25 +270,35 @@ const plugin = definePlugin({
|
|
|
99
270
|
}
|
|
100
271
|
return { baseUrl: publicUrl, issuePrefix: prefix || undefined };
|
|
101
272
|
}
|
|
102
|
-
const notify = async (event, formatter, overrideChatId) => {
|
|
273
|
+
const notify = async (event, formatter, overrideChatId, overrideTopicId) => {
|
|
103
274
|
const chatId = await resolveChat(ctx, event.companyId, overrideChatId || config.defaultChatId);
|
|
104
275
|
if (!chatId)
|
|
105
276
|
return;
|
|
106
277
|
const linksOpts = await resolveIssueLinksOpts(event.companyId);
|
|
107
278
|
const msg = formatter(event, linksOpts);
|
|
108
|
-
let messageThreadId;
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
const projectName = payload.projectName ? String(payload.projectName) : undefined;
|
|
112
|
-
messageThreadId = await getTopicForProject(ctx, chatId, projectName);
|
|
113
|
-
}
|
|
114
|
-
// For forum groups, fall back to General topic if no specific topic mapping
|
|
115
|
-
if (!messageThreadId && await isForum(ctx, token, chatId)) {
|
|
116
|
-
messageThreadId = GENERAL_TOPIC_THREAD_ID;
|
|
279
|
+
let messageThreadId = parseTopicId(overrideTopicId);
|
|
280
|
+
if (!messageThreadId) {
|
|
281
|
+
messageThreadId = await resolveNotificationThreadId(ctx, chatId, event, config.topicRouting);
|
|
117
282
|
}
|
|
118
283
|
if (messageThreadId) {
|
|
119
284
|
msg.options.messageThreadId = messageThreadId;
|
|
120
285
|
}
|
|
286
|
+
// Issue threading — if we've already sent a message for this entity in this
|
|
287
|
+
// chat+topic, reply to that anchor so all updates about a single entity stack
|
|
288
|
+
// as one Telegram thread on mobile (created → comments → done).
|
|
289
|
+
const anchorKey = event.entityId
|
|
290
|
+
? `anchor_${chatId}_${event.entityType}_${event.entityId}`
|
|
291
|
+
: null;
|
|
292
|
+
if (anchorKey) {
|
|
293
|
+
const anchor = (await ctx.state.get({
|
|
294
|
+
scopeKind: "instance",
|
|
295
|
+
stateKey: anchorKey,
|
|
296
|
+
}));
|
|
297
|
+
// Only thread when targeting the same topic — Telegram rejects cross-topic replies.
|
|
298
|
+
if (anchor?.messageId && anchor.messageThreadId === messageThreadId) {
|
|
299
|
+
msg.options.replyToMessageId = anchor.messageId;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
121
302
|
const messageId = await sendMessage(ctx, token, chatId, msg.text, msg.options);
|
|
122
303
|
if (messageId) {
|
|
123
304
|
await ctx.state.set({
|
|
@@ -135,16 +316,30 @@ const plugin = definePlugin({
|
|
|
135
316
|
entityType: "plugin",
|
|
136
317
|
entityId: event.entityId,
|
|
137
318
|
});
|
|
319
|
+
// First-message-per-entity: store the anchor so future notifications about the
|
|
320
|
+
// same entity reply to this one. Never overwritten — the first message stays root.
|
|
321
|
+
if (anchorKey) {
|
|
322
|
+
const existing = (await ctx.state.get({
|
|
323
|
+
scopeKind: "instance",
|
|
324
|
+
stateKey: anchorKey,
|
|
325
|
+
}));
|
|
326
|
+
if (!existing) {
|
|
327
|
+
await ctx.state.set({ scopeKind: "instance", stateKey: anchorKey }, { messageId, messageThreadId });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
138
330
|
}
|
|
139
331
|
};
|
|
140
332
|
if (config.notifyOnIssueCreated) {
|
|
141
333
|
ctx.events.on("issue.created", (event) => notify(event, formatIssueCreated));
|
|
142
334
|
}
|
|
143
335
|
if (config.notifyOnIssueDone) {
|
|
336
|
+
const doneDedupe = makeUpdateDedupe();
|
|
144
337
|
ctx.events.on("issue.updated", async (event) => {
|
|
145
338
|
const payload = event.payload;
|
|
146
339
|
if (payload.status !== "done")
|
|
147
340
|
return;
|
|
341
|
+
if (!doneDedupe(`done|${event.entityId}`))
|
|
342
|
+
return;
|
|
148
343
|
// Enrich with title if missing (issue.updated events often omit it)
|
|
149
344
|
if (!payload.title && event.entityId) {
|
|
150
345
|
try {
|
|
@@ -168,8 +363,47 @@ const plugin = definePlugin({
|
|
|
168
363
|
await notify(event, formatIssueDone);
|
|
169
364
|
});
|
|
170
365
|
}
|
|
366
|
+
if (config.notifyOnIssueAssigned) {
|
|
367
|
+
const assignmentDedupe = makeUpdateDedupe();
|
|
368
|
+
ctx.events.on("issue.updated", async (event) => {
|
|
369
|
+
const payload = event.payload;
|
|
370
|
+
const prev = payload._previous ?? {};
|
|
371
|
+
const userChanged = "assigneeUserId" in payload && payload.assigneeUserId !== prev.assigneeUserId;
|
|
372
|
+
const agentChanged = "assigneeAgentId" in payload && payload.assigneeAgentId !== prev.assigneeAgentId;
|
|
373
|
+
if (!userChanged && !agentChanged)
|
|
374
|
+
return;
|
|
375
|
+
if (config.onlyNotifyIfAssignedTo && payload.assigneeUserId !== config.onlyNotifyIfAssignedTo) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const dedupeKey = [
|
|
379
|
+
"assigned",
|
|
380
|
+
event.entityId,
|
|
381
|
+
String(prev.assigneeUserId ?? ""),
|
|
382
|
+
String(payload.assigneeUserId ?? ""),
|
|
383
|
+
String(prev.assigneeAgentId ?? ""),
|
|
384
|
+
String(payload.assigneeAgentId ?? ""),
|
|
385
|
+
].join("|");
|
|
386
|
+
if (!assignmentDedupe(dedupeKey))
|
|
387
|
+
return;
|
|
388
|
+
if ((!payload.title || !payload.assigneeName) && event.entityId) {
|
|
389
|
+
try {
|
|
390
|
+
const issue = await ctx.issues.get(event.entityId, event.companyId);
|
|
391
|
+
if (issue) {
|
|
392
|
+
payload.title ??= issue.title;
|
|
393
|
+
const name = issue.assigneeName;
|
|
394
|
+
if (name)
|
|
395
|
+
payload.assigneeName ??= name;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
catch { /* best effort */ }
|
|
399
|
+
}
|
|
400
|
+
await notify(event, formatIssueAssigned);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
171
403
|
if (config.notifyOnApprovalCreated) {
|
|
172
404
|
ctx.events.on("approval.created", async (event) => {
|
|
405
|
+
if (!shouldNotifyApproval(event, config.onlyNotifyBoardApprovals))
|
|
406
|
+
return;
|
|
173
407
|
const payload = event.payload;
|
|
174
408
|
// Enrich with linked issue details (event only has issueIds)
|
|
175
409
|
const issueIds = Array.isArray(payload.issueIds) ? payload.issueIds : [];
|
|
@@ -210,11 +444,11 @@ const plugin = definePlugin({
|
|
|
210
444
|
? `${approvalType} — ${agentLabel}`
|
|
211
445
|
: approvalType;
|
|
212
446
|
}
|
|
213
|
-
await notify(event, formatApprovalCreated, config.approvalsChatId);
|
|
447
|
+
await notify(event, formatApprovalCreated, config.approvalsChatId, config.approvalsTopicId);
|
|
214
448
|
});
|
|
215
449
|
}
|
|
216
450
|
if (config.notifyOnAgentError) {
|
|
217
|
-
ctx.events.on("agent.run.failed", (event) => notify(event, formatAgentError, config.errorsChatId));
|
|
451
|
+
ctx.events.on("agent.run.failed", (event) => notify(event, formatAgentError, config.errorsChatId, config.errorsTopicId));
|
|
218
452
|
}
|
|
219
453
|
ctx.events.on("agent.run.started", (event) => notify(event, formatAgentRunStarted));
|
|
220
454
|
ctx.events.on("agent.run.finished", (event) => notify(event, formatAgentRunFinished));
|
|
@@ -270,7 +504,7 @@ const plugin = definePlugin({
|
|
|
270
504
|
return;
|
|
271
505
|
const companies = await ctx.companies.list();
|
|
272
506
|
for (const company of companies) {
|
|
273
|
-
const chatId = await resolveChat(ctx, company.id, config.defaultChatId);
|
|
507
|
+
const chatId = await resolveChat(ctx, company.id, config.digestChatId || config.defaultChatId);
|
|
274
508
|
if (!chatId)
|
|
275
509
|
continue;
|
|
276
510
|
try {
|
|
@@ -321,9 +555,7 @@ const plugin = definePlugin({
|
|
|
321
555
|
for (const i of blocked.slice(0, 10))
|
|
322
556
|
lines.push(formatIssueItem(i));
|
|
323
557
|
}
|
|
324
|
-
const digestThreadId = await
|
|
325
|
-
? GENERAL_TOPIC_THREAD_ID
|
|
326
|
-
: undefined;
|
|
558
|
+
const digestThreadId = await resolveDigestThreadId(ctx, token, chatId, config.digestTopicId);
|
|
327
559
|
await sendMessage(ctx, token, chatId, lines.join("\n"), {
|
|
328
560
|
parseMode: "MarkdownV2",
|
|
329
561
|
messageThreadId: digestThreadId,
|
|
@@ -336,9 +568,7 @@ const plugin = definePlugin({
|
|
|
336
568
|
"",
|
|
337
569
|
escapeMarkdownV2("Could not generate digest. Check plugin logs for details."),
|
|
338
570
|
].join("\n");
|
|
339
|
-
const errorThreadId = await
|
|
340
|
-
? GENERAL_TOPIC_THREAD_ID
|
|
341
|
-
: undefined;
|
|
571
|
+
const errorThreadId = await resolveDigestThreadId(ctx, token, chatId, config.errorsTopicId || config.digestTopicId);
|
|
342
572
|
await sendMessage(ctx, token, chatId, text, {
|
|
343
573
|
parseMode: "MarkdownV2",
|
|
344
574
|
messageThreadId: errorThreadId,
|
|
@@ -531,15 +761,35 @@ const plugin = definePlugin({
|
|
|
531
761
|
if (!config.telegramBotTokenRef || typeof config.telegramBotTokenRef !== "string") {
|
|
532
762
|
return { ok: false, errors: ["telegramBotTokenRef is required"] };
|
|
533
763
|
}
|
|
764
|
+
const allowlistErrors = validateTelegramAllowlists(config);
|
|
765
|
+
if (allowlistErrors.length > 0) {
|
|
766
|
+
return { ok: false, errors: allowlistErrors };
|
|
767
|
+
}
|
|
768
|
+
const topicErrors = validateConfiguredTopicIds(config);
|
|
769
|
+
if (topicErrors.length > 0) {
|
|
770
|
+
return { ok: false, errors: topicErrors };
|
|
771
|
+
}
|
|
534
772
|
return { ok: true };
|
|
535
773
|
},
|
|
536
774
|
async onHealth() {
|
|
537
775
|
return { status: "ok" };
|
|
538
776
|
},
|
|
539
777
|
});
|
|
540
|
-
async function handleUpdate(ctx, token, config, update, baseUrl, publicUrl) {
|
|
778
|
+
async function handleUpdate(ctx, token, config, update, baseUrl, publicUrl, boardApiToken) {
|
|
779
|
+
if (!isTelegramUpdateAllowed(config, update)) {
|
|
780
|
+
const fromId = update.message?.from?.id ?? update.callback_query?.from.id;
|
|
781
|
+
const chatId = update.message?.chat.id ?? update.callback_query?.message?.chat.id;
|
|
782
|
+
ctx.logger.warn("Blocked unauthorized Telegram update", {
|
|
783
|
+
updateId: update.update_id,
|
|
784
|
+
fromId,
|
|
785
|
+
chatId,
|
|
786
|
+
});
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
541
789
|
if (update.callback_query) {
|
|
542
|
-
await
|
|
790
|
+
const companyId = await resolveCallbackCompanyId(ctx, update.callback_query);
|
|
791
|
+
const boardApiToken = await resolveBoardApiToken(ctx, config, companyId);
|
|
792
|
+
await handleCallbackQuery(ctx, token, update.callback_query, baseUrl, boardApiToken);
|
|
543
793
|
return;
|
|
544
794
|
}
|
|
545
795
|
const msg = update.message;
|
|
@@ -589,7 +839,8 @@ async function handleUpdate(ctx, token, config, update, baseUrl, publicUrl) {
|
|
|
589
839
|
if (handledCustom)
|
|
590
840
|
return;
|
|
591
841
|
// Built-in commands
|
|
592
|
-
|
|
842
|
+
const boardApiToken = command === "approve" ? await resolveBoardApiToken(ctx, config, companyId) : undefined;
|
|
843
|
+
await handleCommand(ctx, token, chatId, command, args, threadId, baseUrl, publicUrl, companyId, boardApiToken);
|
|
593
844
|
return;
|
|
594
845
|
}
|
|
595
846
|
if (config.enableInbound && msg.reply_to_message?.from?.is_bot) {
|
|
@@ -634,7 +885,7 @@ async function handleUpdate(ctx, token, config, update, baseUrl, publicUrl) {
|
|
|
634
885
|
}
|
|
635
886
|
}
|
|
636
887
|
}
|
|
637
|
-
async function handleCallbackQuery(ctx, token, query, baseUrl) {
|
|
888
|
+
async function handleCallbackQuery(ctx, token, query, baseUrl, boardApiToken) {
|
|
638
889
|
const data = query.data;
|
|
639
890
|
if (!data)
|
|
640
891
|
return;
|
|
@@ -645,9 +896,12 @@ async function handleCallbackQuery(ctx, token, query, baseUrl) {
|
|
|
645
896
|
const approvalId = data.replace("approve_", "");
|
|
646
897
|
ctx.logger.info("Approval button clicked", { approvalId, actor });
|
|
647
898
|
try {
|
|
648
|
-
await ctx
|
|
899
|
+
await fetchPaperclipApi(ctx, `${baseUrl}/api/approvals/${approvalId}/approve`, {
|
|
649
900
|
method: "POST",
|
|
650
|
-
headers: {
|
|
901
|
+
headers: {
|
|
902
|
+
"Content-Type": "application/json",
|
|
903
|
+
...buildPaperclipAuthHeaders(boardApiToken),
|
|
904
|
+
},
|
|
651
905
|
body: JSON.stringify({ decidedByUserId: `telegram:${actor}` }),
|
|
652
906
|
});
|
|
653
907
|
await answerCallbackQuery(ctx, token, query.id, "Approved");
|
|
@@ -673,9 +927,12 @@ async function handleCallbackQuery(ctx, token, query, baseUrl) {
|
|
|
673
927
|
const approvalId = data.replace("reject_", "");
|
|
674
928
|
ctx.logger.info("Rejection button clicked", { approvalId, actor });
|
|
675
929
|
try {
|
|
676
|
-
await ctx
|
|
930
|
+
await fetchPaperclipApi(ctx, `${baseUrl}/api/approvals/${approvalId}/reject`, {
|
|
677
931
|
method: "POST",
|
|
678
|
-
headers: {
|
|
932
|
+
headers: {
|
|
933
|
+
"Content-Type": "application/json",
|
|
934
|
+
...buildPaperclipAuthHeaders(boardApiToken),
|
|
935
|
+
},
|
|
679
936
|
body: JSON.stringify({ decidedByUserId: `telegram:${actor}` }),
|
|
680
937
|
});
|
|
681
938
|
await answerCallbackQuery(ctx, token, query.id, "Rejected");
|