@bytesbrains/pi-telegram-bridge 1.2.0 → 1.3.1
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/package.json +1 -1
- package/src/__tests__/index.test.ts +82 -0
- package/src/helpers.ts +1 -1
- package/src/index.ts +16 -2
- package/src/templates.ts +3 -6
package/package.json
CHANGED
|
@@ -577,4 +577,86 @@ describe("session lifecycle", () => {
|
|
|
577
577
|
await new Promise((r) => setTimeout(r, 100));
|
|
578
578
|
expect(mockPollUpdates).toHaveBeenCalledTimes(1);
|
|
579
579
|
});
|
|
580
|
+
|
|
581
|
+
it("does not crash when TELEGRAM_BOT_TOKEN is not set (graceful degradation)", async () => {
|
|
582
|
+
vi.stubEnv("TELEGRAM_BOT_TOKEN", "");
|
|
583
|
+
vi.stubEnv("TELEGRAM_CHAT_ID", "");
|
|
584
|
+
const consoleWarn = vi
|
|
585
|
+
.spyOn(console, "warn")
|
|
586
|
+
.mockImplementation(() => {});
|
|
587
|
+
|
|
588
|
+
const pi = createMockPI();
|
|
589
|
+
telegramBridge(pi as any);
|
|
590
|
+
|
|
591
|
+
const ctx = {
|
|
592
|
+
sessionManager: { getEntries: () => [] },
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// Should NOT throw
|
|
596
|
+
await expect(
|
|
597
|
+
pi._listeners["session_start"][0]({}, ctx),
|
|
598
|
+
).resolves.toBeUndefined();
|
|
599
|
+
|
|
600
|
+
// Should have warned about missing config
|
|
601
|
+
expect(consoleWarn).toHaveBeenCalledWith(
|
|
602
|
+
expect.stringContaining("TELEGRAM_BOT_TOKEN"),
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
// Listener should NOT have been started
|
|
606
|
+
expect(mockPollUpdates).not.toHaveBeenCalled();
|
|
607
|
+
|
|
608
|
+
consoleWarn.mockRestore();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("does not crash when only TELEGRAM_BOT_TOKEN is missing", async () => {
|
|
612
|
+
vi.stubEnv("TELEGRAM_BOT_TOKEN", "");
|
|
613
|
+
// CHAT_ID is set
|
|
614
|
+
const consoleWarn = vi
|
|
615
|
+
.spyOn(console, "warn")
|
|
616
|
+
.mockImplementation(() => {});
|
|
617
|
+
|
|
618
|
+
const pi = createMockPI();
|
|
619
|
+
telegramBridge(pi as any);
|
|
620
|
+
|
|
621
|
+
const ctx = {
|
|
622
|
+
sessionManager: { getEntries: () => [] },
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
await expect(
|
|
626
|
+
pi._listeners["session_start"][0]({}, ctx),
|
|
627
|
+
).resolves.toBeUndefined();
|
|
628
|
+
|
|
629
|
+
expect(consoleWarn).toHaveBeenCalledWith(
|
|
630
|
+
expect.stringContaining("TELEGRAM_BOT_TOKEN"),
|
|
631
|
+
);
|
|
632
|
+
expect(mockPollUpdates).not.toHaveBeenCalled();
|
|
633
|
+
|
|
634
|
+
consoleWarn.mockRestore();
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("does not crash when only TELEGRAM_CHAT_ID is missing", async () => {
|
|
638
|
+
vi.stubEnv("TELEGRAM_CHAT_ID", "");
|
|
639
|
+
// BOT_TOKEN is set
|
|
640
|
+
const consoleWarn = vi
|
|
641
|
+
.spyOn(console, "warn")
|
|
642
|
+
.mockImplementation(() => {});
|
|
643
|
+
|
|
644
|
+
const pi = createMockPI();
|
|
645
|
+
telegramBridge(pi as any);
|
|
646
|
+
|
|
647
|
+
const ctx = {
|
|
648
|
+
sessionManager: { getEntries: () => [] },
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
await expect(
|
|
652
|
+
pi._listeners["session_start"][0]({}, ctx),
|
|
653
|
+
).resolves.toBeUndefined();
|
|
654
|
+
|
|
655
|
+
expect(consoleWarn).toHaveBeenCalledWith(
|
|
656
|
+
expect.stringContaining("TELEGRAM_BOT_TOKEN"),
|
|
657
|
+
);
|
|
658
|
+
expect(mockPollUpdates).not.toHaveBeenCalled();
|
|
659
|
+
|
|
660
|
+
consoleWarn.mockRestore();
|
|
661
|
+
});
|
|
580
662
|
});
|
package/src/helpers.ts
CHANGED
|
@@ -151,7 +151,7 @@ export async function sendPhoto(
|
|
|
151
151
|
const token = getToken();
|
|
152
152
|
const formData = new FormData();
|
|
153
153
|
formData.set("chat_id", getChatId());
|
|
154
|
-
const blob = new Blob([fileData]
|
|
154
|
+
const blob = new Blob([fileData]);
|
|
155
155
|
formData.set("photo", blob, filename);
|
|
156
156
|
if (caption) {
|
|
157
157
|
formData.set("caption", caption);
|
package/src/index.ts
CHANGED
|
@@ -189,9 +189,23 @@ export default function telegramBridge(pi: ExtensionAPI) {
|
|
|
189
189
|
lastUpdateId = (entry.data as { update_id: number }).update_id;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
+
// Skip listener if Telegram is not configured
|
|
193
|
+
if (!process.env.TELEGRAM_BOT_TOKEN || !process.env.TELEGRAM_CHAT_ID) {
|
|
194
|
+
console.warn(
|
|
195
|
+
"pi-telegram-bridge: TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID not set. Listener disabled.",
|
|
196
|
+
);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
192
199
|
// Start background listener
|
|
193
|
-
|
|
194
|
-
|
|
200
|
+
try {
|
|
201
|
+
mentionTrigger = await resolveMentionTrigger();
|
|
202
|
+
startListener();
|
|
203
|
+
} catch (e: unknown) {
|
|
204
|
+
console.warn(
|
|
205
|
+
"pi-telegram-bridge: Failed to start listener:",
|
|
206
|
+
e instanceof Error ? e.message : e,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
195
209
|
});
|
|
196
210
|
|
|
197
211
|
pi.on("session_shutdown", () => {
|
package/src/templates.ts
CHANGED
|
@@ -12,10 +12,7 @@ const BAR = "━".repeat(20);
|
|
|
12
12
|
|
|
13
13
|
/** Escape user text for safe embedding inside HTML tags. */
|
|
14
14
|
function escapeHtml(s: string): string {
|
|
15
|
-
return s
|
|
16
|
-
.replace(/&/g, "&")
|
|
17
|
-
.replace(/</g, "<")
|
|
18
|
-
.replace(/>/g, ">");
|
|
15
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
function bold(s: string) {
|
|
@@ -31,7 +28,7 @@ function strike(s: string) {
|
|
|
31
28
|
return `<s>${escapeHtml(s)}</s>`;
|
|
32
29
|
}
|
|
33
30
|
function labelValue(label: string, value: string) {
|
|
34
|
-
return `${bold(label + ":")} ${
|
|
31
|
+
return `${bold(label + ":")} ${value}`;
|
|
35
32
|
}
|
|
36
33
|
function link(url: string, text: string) {
|
|
37
34
|
return `<a href="${url}">${escapeHtml(text)}</a>`;
|
|
@@ -257,7 +254,7 @@ export function ciPipeline(p: CIPipeline): string {
|
|
|
257
254
|
msg += `${BAR}\n`;
|
|
258
255
|
msg += labelValue("Run", `#${p.runNumber}`) + "\n";
|
|
259
256
|
msg += labelValue("Branch", code(p.branch)) + "\n";
|
|
260
|
-
msg += labelValue("Trigger", p.event) + "\n";
|
|
257
|
+
msg += labelValue("Trigger", escapeHtml(p.event)) + "\n";
|
|
261
258
|
if (p.triggeredBy) msg += labelValue("By", code(p.triggeredBy)) + "\n";
|
|
262
259
|
msg += `${BAR}\n`;
|
|
263
260
|
|