paperclip-plugin-telegram 0.2.1 → 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.
- package/dist/acp-bridge.d.ts +34 -0
- package/dist/acp-bridge.js +805 -0
- package/dist/acp-bridge.js.map +1 -0
- package/dist/adapter.d.ts +35 -0
- package/dist/adapter.js +75 -0
- package/dist/adapter.js.map +1 -0
- package/dist/command-registry.d.ts +3 -0
- package/dist/command-registry.js +273 -0
- package/dist/command-registry.js.map +1 -0
- package/dist/commands.d.ts +10 -0
- package/dist/commands.js +213 -0
- package/dist/commands.js.map +1 -0
- package/dist/constants.d.ts +44 -0
- package/dist/constants.js +48 -0
- package/dist/constants.js.map +1 -0
- package/dist/escalation.d.ts +41 -0
- package/dist/escalation.js +254 -0
- package/dist/escalation.js.map +1 -0
- package/dist/formatters.d.ts +13 -0
- package/dist/formatters.js +130 -0
- package/dist/formatters.js.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +3 -0
- package/dist/manifest.js +230 -0
- package/dist/manifest.js.map +1 -0
- package/dist/media-pipeline.d.ts +46 -0
- package/dist/media-pipeline.js +161 -0
- package/dist/media-pipeline.js.map +1 -0
- package/dist/telegram-api.d.ts +28 -0
- package/dist/telegram-api.js +147 -0
- package/dist/telegram-api.js.map +1 -0
- package/dist/watch-registry.d.ts +9 -0
- package/dist/watch-registry.js +272 -0
- package/dist/watch-registry.js.map +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +548 -0
- package/dist/worker.js.map +1 -0
- package/package.json +7 -3
- package/src/acp-bridge.ts +0 -1273
- package/src/adapter.ts +0 -129
- package/src/command-registry.ts +0 -482
- package/src/commands.ts +0 -346
- package/src/constants.ts +0 -51
- package/src/escalation.ts +0 -421
- package/src/formatters.ts +0 -148
- package/src/manifest.ts +0 -246
- package/src/media-pipeline.ts +0 -234
- package/src/telegram-api.ts +0 -202
- package/src/watch-registry.ts +0 -369
- package/src/worker.ts +0 -783
- package/tests/acp-bridge.test.ts +0 -314
- package/tests/command-registry.test.ts +0 -283
- package/tests/commands.test.ts +0 -213
- package/tests/escalation.test.ts +0 -550
- package/tests/formatters.test.ts +0 -185
- package/tests/media-pipeline.test.ts +0 -324
- package/tests/telegram-api.test.ts +0 -108
- package/tests/watch-registry.test.ts +0 -404
- package/tsconfig.json +0 -16
- /package/{src/index.ts → dist/index.d.ts} +0 -0
package/tests/commands.test.ts
DELETED
|
@@ -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
|
-
});
|