paperclip-plugin-telegram 0.2.0 → 0.2.2

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.
Files changed (62) hide show
  1. package/README.md +68 -4
  2. package/dist/acp-bridge.d.ts +34 -0
  3. package/dist/acp-bridge.js +805 -0
  4. package/dist/acp-bridge.js.map +1 -0
  5. package/dist/adapter.d.ts +35 -0
  6. package/dist/adapter.js +75 -0
  7. package/dist/adapter.js.map +1 -0
  8. package/dist/command-registry.d.ts +3 -0
  9. package/dist/command-registry.js +273 -0
  10. package/dist/command-registry.js.map +1 -0
  11. package/dist/commands.d.ts +10 -0
  12. package/dist/commands.js +213 -0
  13. package/dist/commands.js.map +1 -0
  14. package/dist/constants.d.ts +44 -0
  15. package/dist/constants.js +48 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/escalation.d.ts +41 -0
  18. package/dist/escalation.js +254 -0
  19. package/dist/escalation.js.map +1 -0
  20. package/dist/formatters.d.ts +13 -0
  21. package/dist/formatters.js +130 -0
  22. package/dist/formatters.js.map +1 -0
  23. package/dist/index.js +4 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/manifest.d.ts +3 -0
  26. package/dist/manifest.js +230 -0
  27. package/dist/manifest.js.map +1 -0
  28. package/dist/media-pipeline.d.ts +46 -0
  29. package/dist/media-pipeline.js +161 -0
  30. package/dist/media-pipeline.js.map +1 -0
  31. package/dist/telegram-api.d.ts +28 -0
  32. package/dist/telegram-api.js +147 -0
  33. package/dist/telegram-api.js.map +1 -0
  34. package/dist/watch-registry.d.ts +9 -0
  35. package/dist/watch-registry.js +272 -0
  36. package/dist/watch-registry.js.map +1 -0
  37. package/dist/worker.d.ts +1 -0
  38. package/dist/worker.js +548 -0
  39. package/dist/worker.js.map +1 -0
  40. package/package.json +7 -3
  41. package/src/acp-bridge.ts +0 -1273
  42. package/src/adapter.ts +0 -129
  43. package/src/command-registry.ts +0 -482
  44. package/src/commands.ts +0 -346
  45. package/src/constants.ts +0 -51
  46. package/src/escalation.ts +0 -421
  47. package/src/formatters.ts +0 -148
  48. package/src/manifest.ts +0 -246
  49. package/src/media-pipeline.ts +0 -234
  50. package/src/telegram-api.ts +0 -202
  51. package/src/watch-registry.ts +0 -369
  52. package/src/worker.ts +0 -783
  53. package/tests/acp-bridge.test.ts +0 -314
  54. package/tests/command-registry.test.ts +0 -283
  55. package/tests/commands.test.ts +0 -213
  56. package/tests/escalation.test.ts +0 -550
  57. package/tests/formatters.test.ts +0 -185
  58. package/tests/media-pipeline.test.ts +0 -324
  59. package/tests/telegram-api.test.ts +0 -108
  60. package/tests/watch-registry.test.ts +0 -404
  61. package/tsconfig.json +0 -16
  62. /package/{src/index.ts → dist/index.d.ts} +0 -0
@@ -1,213 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { handleCommand, BOT_COMMANDS, handleConnectTopic, getTopicForProject } from "../src/commands.js";
3
- import type { PluginContext } from "@paperclipai/plugin-sdk";
4
-
5
- let sentMessages: Array<{ chatId: string; text: string; options?: Record<string, unknown> }> = [];
6
- let metricsWritten: Array<{ name: string; value: number }> = [];
7
- let stateStore: Record<string, unknown> = {};
8
-
9
- function mockCtx(): PluginContext {
10
- return {
11
- http: {
12
- fetch: vi.fn().mockResolvedValue({
13
- json: () => Promise.resolve({ ok: true }),
14
- }),
15
- },
16
- metrics: {
17
- write: vi.fn(async (name: string, value: number) => {
18
- metricsWritten.push({ name, value });
19
- }),
20
- },
21
- state: {
22
- get: vi.fn(async (key: { stateKey: string }) => stateStore[key.stateKey] ?? null),
23
- set: vi.fn(async (key: { stateKey: string }, value: unknown) => {
24
- stateStore[key.stateKey] = value;
25
- }),
26
- },
27
- logger: {
28
- info: vi.fn(),
29
- warn: vi.fn(),
30
- error: vi.fn(),
31
- },
32
- agents: {
33
- list: vi.fn().mockResolvedValue([
34
- { id: "a1", name: "Builder", status: "active" },
35
- { id: "a2", name: "Tester", status: "paused" },
36
- ]),
37
- },
38
- issues: {
39
- list: vi.fn().mockResolvedValue([
40
- { id: "i1", identifier: "PROJ-1", title: "Fix bug", status: "todo", project: null },
41
- { id: "i2", identifier: "PROJ-2", title: "Add feature", status: "done", project: { name: "Backend" } },
42
- ]),
43
- },
44
- } as unknown as PluginContext;
45
- }
46
-
47
- vi.mock("../src/telegram-api.js", async () => {
48
- const actual = await vi.importActual("../src/telegram-api.js") as Record<string, unknown>;
49
- return {
50
- ...actual,
51
- sendMessage: vi.fn(async (_ctx: unknown, _token: string, chatId: string, text: string, options?: Record<string, unknown>) => {
52
- sentMessages.push({ chatId, text, options });
53
- return 1;
54
- }),
55
- sendChatAction: vi.fn(),
56
- };
57
- });
58
-
59
- beforeEach(() => {
60
- sentMessages = [];
61
- metricsWritten = [];
62
- stateStore = {};
63
- });
64
-
65
- describe("handleCommand", () => {
66
- it("routes /help command", async () => {
67
- const ctx = mockCtx();
68
- await handleCommand(ctx, "token", "123", "help", "");
69
- expect(sentMessages.length).toBe(1);
70
- expect(sentMessages[0].text).toContain("Paperclip Bot Commands");
71
- });
72
-
73
- it("routes /status command and shows agent/issue counts", async () => {
74
- const ctx = mockCtx();
75
- await handleCommand(ctx, "token", "123", "status", "");
76
- expect(sentMessages.length).toBe(1);
77
- expect(sentMessages[0].text).toContain("Paperclip Status");
78
- });
79
-
80
- it("routes /issues command", async () => {
81
- const ctx = mockCtx();
82
- await handleCommand(ctx, "token", "123", "issues", "");
83
- expect(sentMessages.length).toBe(1);
84
- expect(sentMessages[0].text).toContain("Issues");
85
- });
86
-
87
- it("routes /agents command", async () => {
88
- const ctx = mockCtx();
89
- await handleCommand(ctx, "token", "123", "agents", "");
90
- expect(sentMessages.length).toBe(1);
91
- expect(sentMessages[0].text).toContain("Agents");
92
- });
93
-
94
- it("routes /approve without args shows usage", async () => {
95
- const ctx = mockCtx();
96
- await handleCommand(ctx, "token", "123", "approve", "");
97
- expect(sentMessages[0].text).toContain("Usage");
98
- });
99
-
100
- it("routes /approve with id calls API with configurable base URL", async () => {
101
- const ctx = mockCtx();
102
- await handleCommand(ctx, "token", "123", "approve", "apr-1", undefined, "http://example.com");
103
- expect(ctx.http.fetch).toHaveBeenCalledWith(
104
- "http://example.com/api/approvals/apr-1/approve",
105
- expect.any(Object),
106
- );
107
- });
108
-
109
- it("handles unknown command", async () => {
110
- const ctx = mockCtx();
111
- await handleCommand(ctx, "token", "123", "foobar", "");
112
- expect(sentMessages[0].text).toContain("Unknown command");
113
- });
114
-
115
- it("passes messageThreadId for forum topics", async () => {
116
- const ctx = mockCtx();
117
- await handleCommand(ctx, "token", "123", "help", "", 42);
118
- expect(sentMessages[0].options).toMatchObject({ messageThreadId: 42 });
119
- });
120
-
121
- it("increments commands metric", async () => {
122
- const ctx = mockCtx();
123
- await handleCommand(ctx, "token", "123", "help", "");
124
- expect(metricsWritten.some(m => m.name === "telegram_commands_handled")).toBe(true);
125
- });
126
-
127
- it("/connect stores company mapping", async () => {
128
- const ctx = mockCtx();
129
- await handleCommand(ctx, "token", "123", "connect", "MyCompany");
130
- expect(stateStore["chat_123"]).toEqual(
131
- expect.objectContaining({ companyName: "MyCompany" }),
132
- );
133
- });
134
-
135
- it("/connect without args shows usage", async () => {
136
- const ctx = mockCtx();
137
- await handleCommand(ctx, "token", "123", "connect", "");
138
- expect(sentMessages[0].text).toContain("Usage");
139
- });
140
-
141
- it("/issues filters by project name", async () => {
142
- const ctx = mockCtx();
143
- await handleCommand(ctx, "token", "123", "issues", "Backend");
144
- expect(sentMessages[0].text).toContain("PROJ\\-2");
145
- });
146
-
147
- it("/agents shows agent names and status", async () => {
148
- const ctx = mockCtx();
149
- await handleCommand(ctx, "token", "123", "agents", "");
150
- expect(sentMessages[0].text).toContain("Builder");
151
- expect(sentMessages[0].text).toContain("Tester");
152
- });
153
- });
154
-
155
- describe("handleConnectTopic", () => {
156
- it("stores topic mapping for a project", async () => {
157
- const ctx = mockCtx();
158
- await handleConnectTopic(ctx, "token", "123", "Backend 42");
159
- expect(stateStore["topic-map-123"]).toEqual({ Backend: "42" });
160
- });
161
-
162
- it("shows usage when args are insufficient", async () => {
163
- const ctx = mockCtx();
164
- await handleConnectTopic(ctx, "token", "123", "");
165
- expect(sentMessages[0].text).toContain("Usage");
166
- });
167
-
168
- it("appends to existing topic map", async () => {
169
- stateStore["topic-map-123"] = { Frontend: "10" };
170
- const ctx = mockCtx();
171
- await handleConnectTopic(ctx, "token", "123", "Backend 42");
172
- expect(stateStore["topic-map-123"]).toEqual({ Frontend: "10", Backend: "42" });
173
- });
174
- });
175
-
176
- describe("getTopicForProject", () => {
177
- it("returns topic id for mapped project", async () => {
178
- stateStore["topic-map-123"] = { Backend: "42" };
179
- const ctx = mockCtx();
180
- const result = await getTopicForProject(ctx, "123", "Backend");
181
- expect(result).toBe(42);
182
- });
183
-
184
- it("returns undefined for unmapped project", async () => {
185
- stateStore["topic-map-123"] = { Backend: "42" };
186
- const ctx = mockCtx();
187
- const result = await getTopicForProject(ctx, "123", "Frontend");
188
- expect(result).toBeUndefined();
189
- });
190
-
191
- it("returns undefined when no topic map exists", async () => {
192
- const ctx = mockCtx();
193
- const result = await getTopicForProject(ctx, "123", "Backend");
194
- expect(result).toBeUndefined();
195
- });
196
-
197
- it("returns undefined when no project name", async () => {
198
- const ctx = mockCtx();
199
- const result = await getTopicForProject(ctx, "123");
200
- expect(result).toBeUndefined();
201
- });
202
- });
203
-
204
- describe("BOT_COMMANDS", () => {
205
- it("has all expected commands", () => {
206
- const names = BOT_COMMANDS.map(c => c.command);
207
- expect(names).toContain("status");
208
- expect(names).toContain("issues");
209
- expect(names).toContain("agents");
210
- expect(names).toContain("approve");
211
- expect(names).toContain("help");
212
- });
213
- });