paperclip-plugin-telegram 0.1.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 ADDED
@@ -0,0 +1,116 @@
1
+ # paperclip-plugin-telegram
2
+
3
+ Bidirectional Telegram integration for [Paperclip](https://github.com/paperclipai/paperclip). Push agent notifications to Telegram, receive bot commands, approve requests with inline buttons, and route forum topics to projects.
4
+
5
+ Built on the Paperclip plugin SDK and the domain event bridge ([PR #909](https://github.com/paperclipai/paperclip/pull/909)).
6
+
7
+ ## Features
8
+
9
+ - **Push notifications** on issue created, issue done, approval requested, agent error, run lifecycle
10
+ - **Bot commands**: `/status`, `/issues`, `/agents`, `/approve`, `/help`, `/connect`
11
+ - **Inline buttons** for approve/reject on approval notifications
12
+ - **Inbound routing**: reply to a notification in Telegram and it becomes an issue comment in Paperclip
13
+ - **Per-company chat routing** with `/connect` command
14
+ - **Forum topic routing**: map Telegram topics to Paperclip projects
15
+ - **Daily digest**: scheduled summary of agent activity
16
+ - **MarkdownV2 formatting** with automatic plain text fallback
17
+
18
+ ## Setup
19
+
20
+ ### 1. Create a Telegram bot
21
+
22
+ 1. Open Telegram and chat with [@BotFather](https://t.me/BotFather)
23
+ 2. Run `/newbot` and follow the prompts
24
+ 3. Save the bot token
25
+
26
+ ### 2. Get your chat ID
27
+
28
+ Send a message to your bot, then run:
29
+
30
+ ```bash
31
+ curl "https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates"
32
+ ```
33
+
34
+ Find the `chat.id` field in the response. For groups, the ID is a negative number.
35
+
36
+ ### 3. Install the plugin
37
+
38
+ ```bash
39
+ # From your Paperclip directory
40
+ pnpm paperclipai plugin install /path/to/paperclip-plugin-telegram
41
+ ```
42
+
43
+ ### 4. Configure
44
+
45
+ In your Paperclip instance settings, configure:
46
+
47
+ | Setting | Required | Description |
48
+ |---------|----------|-------------|
49
+ | `telegramBotTokenRef` | Yes | Secret reference to your bot token |
50
+ | `defaultChatId` | Yes | Chat ID for notifications |
51
+ | `approvalsChatId` | No | Separate chat for approvals |
52
+ | `errorsChatId` | No | Separate chat for errors |
53
+ | `enableCommands` | No | Enable bot commands (default: true) |
54
+ | `enableInbound` | No | Route Telegram replies to issues (default: true) |
55
+ | `dailyDigestEnabled` | No | Send daily activity summary |
56
+ | `topicRouting` | No | Map forum topics to projects |
57
+
58
+ ### 5. Add bot to group (optional)
59
+
60
+ If using a group chat:
61
+ 1. Add the bot to your Telegram group
62
+ 2. Disable privacy mode via BotFather (`/setprivacy` -> Disable) if you want the bot to see all messages
63
+ 3. Run `/connect <company-name>` in the group
64
+
65
+ ## How it works
66
+
67
+ ### Notifications (outbound)
68
+
69
+ When Paperclip events fire (issue created, approval needed, agent error), the plugin formats them as Telegram messages with MarkdownV2 and sends to the configured chat.
70
+
71
+ Approval notifications include inline Approve/Reject buttons. Clicking a button calls the Paperclip API directly and updates the message to show the resolution.
72
+
73
+ ### Commands (inbound)
74
+
75
+ The bot registers commands with Telegram's command menu. Users can interact with Paperclip directly:
76
+
77
+ - `/status` - overview of agents and tasks
78
+ - `/issues` - list open issues
79
+ - `/agents` - list agents with status
80
+ - `/approve <id>` - approve a pending request
81
+ - `/connect <company>` - link this chat to a Paperclip company
82
+
83
+ ### Reply routing
84
+
85
+ When a user replies to a bot notification, the plugin looks up which Paperclip entity that notification was about and posts the reply as an issue comment. This turns Telegram threads into a natural conversation interface for Paperclip issues.
86
+
87
+ ## Comparison with PR #407
88
+
89
+ | Feature | PR #407 | This plugin |
90
+ |---------|---------|-------------|
91
+ | Push notifications | Yes | Yes |
92
+ | Receive messages | No | Yes |
93
+ | Bot commands | No | /status, /issues, /agents, /approve |
94
+ | Inline buttons | No | Approve/reject on approvals |
95
+ | Reply routing | No | Replies become issue comments |
96
+ | Topic routing | No | Forum topic = project |
97
+ | Daily digest | No | Yes |
98
+ | Architecture | Monorepo example | Standalone npm package |
99
+
100
+ ## Development
101
+
102
+ ```bash
103
+ pnpm install
104
+ pnpm build
105
+ pnpm typecheck
106
+ ```
107
+
108
+ ## License
109
+
110
+ MIT
111
+
112
+ ## Credits
113
+
114
+ This contribution was developed with AI assistance (Claude Code).
115
+
116
+ Inspired by [OpenClaw's Telegram integration](https://github.com/openclaw/openclaw) (grammY, bidirectional messaging, inline buttons) and adapted for the Paperclip plugin SDK.
@@ -0,0 +1,12 @@
1
+ import type { PluginContext } from "@paperclipai/plugin-sdk";
2
+ type AcpOutputEvent = {
3
+ sessionId: string;
4
+ chatId: string;
5
+ threadId: number;
6
+ text: string;
7
+ done?: boolean;
8
+ };
9
+ export declare function handleAcpCommand(ctx: PluginContext, token: string, chatId: string, args: string, messageThreadId?: number): Promise<void>;
10
+ export declare function routeMessageToAcp(ctx: PluginContext, chatId: string, threadId: number, text: string): Promise<boolean>;
11
+ export declare function handleAcpOutput(ctx: PluginContext, token: string, event: AcpOutputEvent): Promise<void>;
12
+ export {};
@@ -0,0 +1,166 @@
1
+ import { sendMessage, escapeMarkdownV2, sendChatAction } from "./telegram-api.js";
2
+ export async function handleAcpCommand(ctx, token, chatId, args, messageThreadId) {
3
+ const parts = args.trim().split(/\s+/);
4
+ const subcommand = parts[0]?.toLowerCase() ?? "";
5
+ switch (subcommand) {
6
+ case "spawn":
7
+ await handleAcpSpawn(ctx, token, chatId, parts.slice(1).join(" "), messageThreadId);
8
+ break;
9
+ case "status":
10
+ await handleAcpStatus(ctx, token, chatId, messageThreadId);
11
+ break;
12
+ case "cancel":
13
+ await handleAcpCancel(ctx, token, chatId, messageThreadId);
14
+ break;
15
+ case "close":
16
+ await handleAcpClose(ctx, token, chatId, messageThreadId);
17
+ break;
18
+ default:
19
+ await sendMessage(ctx, token, chatId, [
20
+ escapeMarkdownV2("🔌") + " *ACP Commands*",
21
+ "",
22
+ `/acp spawn <agent\\-name> \\- ${escapeMarkdownV2("Start a coding agent session in this thread")}`,
23
+ `/acp status \\- ${escapeMarkdownV2("Show current ACP session status")}`,
24
+ `/acp cancel \\- ${escapeMarkdownV2("Cancel the running agent task")}`,
25
+ `/acp close \\- ${escapeMarkdownV2("End the ACP session and unbind this thread")}`,
26
+ ].join("\n"), { parseMode: "MarkdownV2", messageThreadId });
27
+ }
28
+ }
29
+ async function handleAcpSpawn(ctx, token, chatId, agentName, messageThreadId) {
30
+ if (!agentName.trim()) {
31
+ await sendMessage(ctx, token, chatId, "Usage: /acp spawn <agent-name>", {
32
+ messageThreadId,
33
+ });
34
+ return;
35
+ }
36
+ if (!messageThreadId) {
37
+ await sendMessage(ctx, token, chatId, "ACP sessions must be started inside a topic thread.", { messageThreadId });
38
+ return;
39
+ }
40
+ const existingBinding = await getAcpBinding(ctx, chatId, messageThreadId);
41
+ if (existingBinding) {
42
+ await sendMessage(ctx, token, chatId, `This thread already has an ACP session: \`${escapeMarkdownV2(existingBinding.sessionId)}\``, { parseMode: "MarkdownV2", messageThreadId });
43
+ return;
44
+ }
45
+ await sendChatAction(ctx, token, chatId);
46
+ const sessionId = `acp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
47
+ await ctx.state.set({ scopeKind: "instance", stateKey: `acp_${chatId}_${messageThreadId}` }, { sessionId, agentName: agentName.trim(), boundAt: new Date().toISOString() });
48
+ await ctx.events.emit("acp-message", "", {
49
+ type: "spawn",
50
+ sessionId,
51
+ agentName: agentName.trim(),
52
+ chatId,
53
+ threadId: messageThreadId,
54
+ });
55
+ await sendMessage(ctx, token, chatId, [
56
+ escapeMarkdownV2("🔌") + " *ACP Session Started*",
57
+ "",
58
+ `Agent: *${escapeMarkdownV2(agentName.trim())}*`,
59
+ `Session: \`${escapeMarkdownV2(sessionId)}\``,
60
+ "",
61
+ escapeMarkdownV2("Send messages in this thread to interact with the agent."),
62
+ ].join("\n"), { parseMode: "MarkdownV2", messageThreadId });
63
+ ctx.logger.info("ACP session spawned", { sessionId, agentName: agentName.trim(), chatId, threadId: messageThreadId });
64
+ }
65
+ async function handleAcpStatus(ctx, token, chatId, messageThreadId) {
66
+ if (!messageThreadId) {
67
+ await sendMessage(ctx, token, chatId, "Run /acp status inside a thread with an active session.", {
68
+ messageThreadId,
69
+ });
70
+ return;
71
+ }
72
+ const binding = await getAcpBinding(ctx, chatId, messageThreadId);
73
+ if (!binding) {
74
+ await sendMessage(ctx, token, chatId, "No ACP session bound to this thread.", {
75
+ messageThreadId,
76
+ });
77
+ return;
78
+ }
79
+ await sendMessage(ctx, token, chatId, [
80
+ escapeMarkdownV2("🔌") + " *ACP Session*",
81
+ "",
82
+ `Agent: *${escapeMarkdownV2(binding.agentName)}*`,
83
+ `Session: \`${escapeMarkdownV2(binding.sessionId)}\``,
84
+ `Started: ${escapeMarkdownV2(binding.boundAt)}`,
85
+ ].join("\n"), { parseMode: "MarkdownV2", messageThreadId });
86
+ }
87
+ async function handleAcpCancel(ctx, token, chatId, messageThreadId) {
88
+ if (!messageThreadId) {
89
+ await sendMessage(ctx, token, chatId, "Run /acp cancel inside a thread with an active session.", {
90
+ messageThreadId,
91
+ });
92
+ return;
93
+ }
94
+ const binding = await getAcpBinding(ctx, chatId, messageThreadId);
95
+ if (!binding) {
96
+ await sendMessage(ctx, token, chatId, "No ACP session bound to this thread.", {
97
+ messageThreadId,
98
+ });
99
+ return;
100
+ }
101
+ await ctx.events.emit("acp-message", "", {
102
+ type: "cancel",
103
+ sessionId: binding.sessionId,
104
+ chatId,
105
+ threadId: messageThreadId,
106
+ });
107
+ await sendMessage(ctx, token, chatId, `${escapeMarkdownV2("⏹")} Cancellation requested for session \`${escapeMarkdownV2(binding.sessionId)}\``, { parseMode: "MarkdownV2", messageThreadId });
108
+ ctx.logger.info("ACP cancel requested", { sessionId: binding.sessionId, chatId, threadId: messageThreadId });
109
+ }
110
+ async function handleAcpClose(ctx, token, chatId, messageThreadId) {
111
+ if (!messageThreadId) {
112
+ await sendMessage(ctx, token, chatId, "Run /acp close inside a thread with an active session.", {
113
+ messageThreadId,
114
+ });
115
+ return;
116
+ }
117
+ const binding = await getAcpBinding(ctx, chatId, messageThreadId);
118
+ if (!binding) {
119
+ await sendMessage(ctx, token, chatId, "No ACP session bound to this thread.", {
120
+ messageThreadId,
121
+ });
122
+ return;
123
+ }
124
+ await ctx.events.emit("acp-message", "", {
125
+ type: "close",
126
+ sessionId: binding.sessionId,
127
+ chatId,
128
+ threadId: messageThreadId,
129
+ });
130
+ await ctx.state.set({ scopeKind: "instance", stateKey: `acp_${chatId}_${messageThreadId}` }, null);
131
+ await sendMessage(ctx, token, chatId, `${escapeMarkdownV2("🔌")} ACP session \`${escapeMarkdownV2(binding.sessionId)}\` closed\\.`, { parseMode: "MarkdownV2", messageThreadId });
132
+ ctx.logger.info("ACP session closed", { sessionId: binding.sessionId, chatId, threadId: messageThreadId });
133
+ }
134
+ export async function routeMessageToAcp(ctx, chatId, threadId, text) {
135
+ const binding = await getAcpBinding(ctx, chatId, threadId);
136
+ if (!binding)
137
+ return false;
138
+ await ctx.events.emit("acp-message", "", {
139
+ type: "message",
140
+ sessionId: binding.sessionId,
141
+ chatId,
142
+ threadId,
143
+ text,
144
+ });
145
+ ctx.logger.info("Routed message to ACP session", { sessionId: binding.sessionId, chatId, threadId });
146
+ return true;
147
+ }
148
+ export async function handleAcpOutput(ctx, token, event) {
149
+ const { chatId, threadId, text, done } = event;
150
+ const prefix = done
151
+ ? escapeMarkdownV2("✅")
152
+ : escapeMarkdownV2("🤖");
153
+ const formatted = `${prefix} ${escapeMarkdownV2(text)}`;
154
+ await sendMessage(ctx, token, chatId, formatted, {
155
+ parseMode: "MarkdownV2",
156
+ messageThreadId: threadId,
157
+ });
158
+ }
159
+ async function getAcpBinding(ctx, chatId, threadId) {
160
+ const binding = await ctx.state.get({
161
+ scopeKind: "instance",
162
+ stateKey: `acp_${chatId}_${threadId}`,
163
+ });
164
+ return binding;
165
+ }
166
+ //# sourceMappingURL=acp-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acp-bridge.js","sourceRoot":"","sources":["../src/acp-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { PluginContext } from "@paperclipai/plugin-sdk";
2
+ type BotCommand = {
3
+ command: string;
4
+ description: string;
5
+ };
6
+ export declare const BOT_COMMANDS: BotCommand[];
7
+ export declare function handleCommand(ctx: PluginContext, token: string, chatId: string, command: string, args: string, messageThreadId?: number, baseUrl?: string): Promise<void>;
8
+ export declare function handleConnectTopic(ctx: PluginContext, token: string, chatId: string, args: string, messageThreadId?: number): Promise<void>;
9
+ export declare function getTopicForProject(ctx: PluginContext, chatId: string, projectName?: string): Promise<number | undefined>;
10
+ export {};
@@ -0,0 +1,212 @@
1
+ import { sendMessage, escapeMarkdownV2, sendChatAction } from "./telegram-api.js";
2
+ import { METRIC_NAMES } from "./constants.js";
3
+ import { handleAcpCommand } from "./acp-bridge.js";
4
+ export const BOT_COMMANDS = [
5
+ { command: "status", description: "Company health: active agents, open issues" },
6
+ { command: "issues", description: "List open issues (optionally by project)" },
7
+ { command: "agents", description: "List agents with current status" },
8
+ { command: "approve", description: "Approve a pending request by ID" },
9
+ { command: "help", description: "Show available commands" },
10
+ { command: "acp", description: "Manage ACP coding agent sessions (spawn, status, cancel, close)" },
11
+ ];
12
+ export async function handleCommand(ctx, token, chatId, command, args, messageThreadId, baseUrl) {
13
+ await ctx.metrics.write(METRIC_NAMES.commandsHandled, 1);
14
+ switch (command) {
15
+ case "status":
16
+ await handleStatus(ctx, token, chatId, messageThreadId);
17
+ break;
18
+ case "issues":
19
+ await handleIssues(ctx, token, chatId, args, messageThreadId);
20
+ break;
21
+ case "agents":
22
+ await handleAgents(ctx, token, chatId, messageThreadId);
23
+ break;
24
+ case "approve":
25
+ await handleApprove(ctx, token, chatId, args, messageThreadId, baseUrl);
26
+ break;
27
+ case "help":
28
+ await handleHelp(ctx, token, chatId, messageThreadId);
29
+ break;
30
+ case "connect":
31
+ await handleConnect(ctx, token, chatId, args, messageThreadId);
32
+ break;
33
+ case "connect-topic":
34
+ await handleConnectTopic(ctx, token, chatId, args, messageThreadId);
35
+ break;
36
+ case "acp":
37
+ await handleAcpCommand(ctx, token, chatId, args, messageThreadId);
38
+ break;
39
+ default:
40
+ await sendMessage(ctx, token, chatId, `Unknown command: /${command}. Try /help`, {
41
+ messageThreadId,
42
+ });
43
+ }
44
+ }
45
+ async function handleStatus(ctx, token, chatId, messageThreadId) {
46
+ await sendChatAction(ctx, token, chatId);
47
+ try {
48
+ const companyId = await resolveCompanyId(ctx, chatId);
49
+ const agents = await ctx.agents.list({ companyId });
50
+ const activeAgents = agents.filter((a) => a.status === "active");
51
+ const issues = await ctx.issues.list({ companyId, limit: 10 });
52
+ const doneIssues = issues.filter((i) => i.status === "done");
53
+ const lines = [
54
+ escapeMarkdownV2("📊") + " *Paperclip Status*",
55
+ "",
56
+ `${escapeMarkdownV2("🤖")} Active agents: *${activeAgents.length}*/${escapeMarkdownV2(String(agents.length))}`,
57
+ `${escapeMarkdownV2("📋")} Recent issues: *${escapeMarkdownV2(String(issues.length))}* \\(${escapeMarkdownV2(String(doneIssues.length))} done\\)`,
58
+ ];
59
+ await sendMessage(ctx, token, chatId, lines.join("\n"), {
60
+ parseMode: "MarkdownV2",
61
+ messageThreadId,
62
+ });
63
+ }
64
+ catch {
65
+ await sendMessage(ctx, token, chatId, escapeMarkdownV2("📊") + " *Paperclip Status*\n\n" + escapeMarkdownV2("Could not fetch status. Make sure this chat is linked to a company with /connect."), {
66
+ parseMode: "MarkdownV2",
67
+ messageThreadId,
68
+ });
69
+ }
70
+ }
71
+ async function handleIssues(ctx, token, chatId, projectFilter, messageThreadId) {
72
+ await sendChatAction(ctx, token, chatId);
73
+ try {
74
+ const companyId = await resolveCompanyId(ctx, chatId);
75
+ const issues = await ctx.issues.list({ companyId, limit: 10 });
76
+ const filtered = projectFilter
77
+ ? issues.filter((i) => {
78
+ const projName = i.project?.name ?? "";
79
+ return projName.toLowerCase().includes(projectFilter.toLowerCase());
80
+ })
81
+ : issues;
82
+ if (filtered.length === 0) {
83
+ const filter = projectFilter ? ` for project "${projectFilter}"` : "";
84
+ await sendMessage(ctx, token, chatId, `No issues found${filter}.`, { messageThreadId });
85
+ return;
86
+ }
87
+ const statusEmoji = { done: "✅", todo: "📋", in_progress: "🔄", backlog: "📥" };
88
+ const lines = [escapeMarkdownV2("📋") + " *Open Issues*", ""];
89
+ for (const issue of filtered) {
90
+ const emoji = statusEmoji[issue.status] ?? "📋";
91
+ const id = issue.identifier ?? issue.id;
92
+ lines.push(`${escapeMarkdownV2(emoji)} ${escapeMarkdownV2(id)} \\- ${escapeMarkdownV2(issue.title)}`);
93
+ }
94
+ await sendMessage(ctx, token, chatId, lines.join("\n"), {
95
+ parseMode: "MarkdownV2",
96
+ messageThreadId,
97
+ });
98
+ }
99
+ catch {
100
+ const filter = projectFilter ? ` for project "${projectFilter}"` : "";
101
+ await sendMessage(ctx, token, chatId, `Could not fetch issues${filter}. Make sure this chat is linked with /connect.`, { messageThreadId });
102
+ }
103
+ }
104
+ async function handleAgents(ctx, token, chatId, messageThreadId) {
105
+ await sendChatAction(ctx, token, chatId);
106
+ try {
107
+ const companyId = await resolveCompanyId(ctx, chatId);
108
+ const agents = await ctx.agents.list({ companyId });
109
+ if (agents.length === 0) {
110
+ await sendMessage(ctx, token, chatId, "No agents found.", { messageThreadId });
111
+ return;
112
+ }
113
+ const statusEmoji = { active: "🟢", error: "🔴", paused: "🟡", idle: "⚪", running: "🔵" };
114
+ const lines = [escapeMarkdownV2("🤖") + " *Agents*", ""];
115
+ for (const agent of agents) {
116
+ const emoji = statusEmoji[agent.status] ?? "⚪";
117
+ lines.push(`${escapeMarkdownV2(emoji)} *${escapeMarkdownV2(agent.name)}* \\- ${escapeMarkdownV2(agent.status)}`);
118
+ }
119
+ await sendMessage(ctx, token, chatId, lines.join("\n"), {
120
+ parseMode: "MarkdownV2",
121
+ messageThreadId,
122
+ });
123
+ }
124
+ catch {
125
+ await sendMessage(ctx, token, chatId, "Could not fetch agents. Make sure this chat is linked with /connect.", { messageThreadId });
126
+ }
127
+ }
128
+ async function handleApprove(ctx, token, chatId, approvalId, messageThreadId, baseUrl = "http://localhost:3100") {
129
+ if (!approvalId.trim()) {
130
+ await sendMessage(ctx, token, chatId, "Usage: /approve <approval-id>", {
131
+ messageThreadId,
132
+ });
133
+ return;
134
+ }
135
+ try {
136
+ await ctx.http.fetch(`${baseUrl}/api/approvals/${approvalId.trim()}/approve`, {
137
+ method: "POST",
138
+ headers: { "Content-Type": "application/json" },
139
+ body: JSON.stringify({ decidedByUserId: `telegram:${chatId}` }),
140
+ });
141
+ await sendMessage(ctx, token, chatId, `${escapeMarkdownV2("✅")} *Approved*: \`${escapeMarkdownV2(approvalId.trim())}\``, { parseMode: "MarkdownV2", messageThreadId });
142
+ }
143
+ catch (err) {
144
+ await sendMessage(ctx, token, chatId, `Failed to approve ${approvalId}: ${err instanceof Error ? err.message : String(err)}`, { messageThreadId });
145
+ }
146
+ }
147
+ async function handleHelp(ctx, token, chatId, messageThreadId) {
148
+ const lines = [
149
+ escapeMarkdownV2("📎") + " *Paperclip Bot Commands*",
150
+ "",
151
+ ...BOT_COMMANDS.map((cmd) => `/${escapeMarkdownV2(cmd.command)} \\- ${escapeMarkdownV2(cmd.description)}`),
152
+ "",
153
+ `/${escapeMarkdownV2("connect")} \\- ${escapeMarkdownV2("Link this chat to a Paperclip company")}`,
154
+ `/${escapeMarkdownV2("connect-topic")} \\- ${escapeMarkdownV2("Map a project to a forum topic")}`,
155
+ ];
156
+ await sendMessage(ctx, token, chatId, lines.join("\n"), {
157
+ parseMode: "MarkdownV2",
158
+ messageThreadId,
159
+ });
160
+ }
161
+ async function handleConnect(ctx, token, chatId, companyName, messageThreadId) {
162
+ if (!companyName.trim()) {
163
+ await sendMessage(ctx, token, chatId, "Usage: /connect <company-name>", {
164
+ messageThreadId,
165
+ });
166
+ return;
167
+ }
168
+ await ctx.state.set({ scopeKind: "instance", stateKey: `chat_${chatId}` }, { companyName: companyName.trim(), linkedAt: new Date().toISOString() });
169
+ await sendMessage(ctx, token, chatId, `${escapeMarkdownV2("🔗")} ${escapeMarkdownV2("Linked this chat to company:")} *${escapeMarkdownV2(companyName.trim())}*`, { parseMode: "MarkdownV2", messageThreadId });
170
+ ctx.logger.info("Chat linked to company", { chatId, companyName: companyName.trim() });
171
+ }
172
+ export async function handleConnectTopic(ctx, token, chatId, args, messageThreadId) {
173
+ const parts = args.trim().split(/\s+/);
174
+ if (parts.length < 2) {
175
+ await sendMessage(ctx, token, chatId, "Usage: /connect\\-topic <project\\-name> <topic\\-id>", {
176
+ parseMode: "MarkdownV2",
177
+ messageThreadId,
178
+ });
179
+ return;
180
+ }
181
+ const topicId = parts.pop();
182
+ const projectName = parts.join(" ");
183
+ const existing = (await ctx.state.get({
184
+ scopeKind: "instance",
185
+ stateKey: `topic-map-${chatId}`,
186
+ }));
187
+ const topicMap = existing ?? {};
188
+ topicMap[projectName] = topicId;
189
+ await ctx.state.set({ scopeKind: "instance", stateKey: `topic-map-${chatId}` }, topicMap);
190
+ await sendMessage(ctx, token, chatId, `${escapeMarkdownV2("🔗")} ${escapeMarkdownV2(`Mapped project "${projectName}" to topic ${topicId}`)}`, { parseMode: "MarkdownV2", messageThreadId });
191
+ ctx.logger.info("Topic mapped", { chatId, projectName, topicId });
192
+ }
193
+ export async function getTopicForProject(ctx, chatId, projectName) {
194
+ if (!projectName)
195
+ return undefined;
196
+ const topicMap = (await ctx.state.get({
197
+ scopeKind: "instance",
198
+ stateKey: `topic-map-${chatId}`,
199
+ }));
200
+ if (!topicMap)
201
+ return undefined;
202
+ const topicId = topicMap[projectName];
203
+ return topicId ? Number(topicId) : undefined;
204
+ }
205
+ async function resolveCompanyId(ctx, chatId) {
206
+ const mapping = await ctx.state.get({
207
+ scopeKind: "instance",
208
+ stateKey: `chat_${chatId}`,
209
+ });
210
+ return mapping?.companyName ?? chatId;
211
+ }
212
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export declare const PLUGIN_ID = "paperclip-plugin-telegram";
2
+ export declare const PLUGIN_VERSION = "0.1.0";
3
+ export declare const DEFAULT_CONFIG: {
4
+ readonly telegramBotTokenRef: "";
5
+ readonly defaultChatId: "";
6
+ readonly approvalsChatId: "";
7
+ readonly errorsChatId: "";
8
+ readonly paperclipBaseUrl: "http://localhost:3100";
9
+ readonly notifyOnIssueCreated: true;
10
+ readonly notifyOnIssueDone: true;
11
+ readonly notifyOnApprovalCreated: true;
12
+ readonly notifyOnAgentError: true;
13
+ readonly enableCommands: true;
14
+ readonly enableInbound: true;
15
+ readonly dailyDigestEnabled: false;
16
+ readonly dailyDigestTime: "09:00";
17
+ readonly topicRouting: false;
18
+ };
19
+ export declare const METRIC_NAMES: {
20
+ readonly sent: "telegram_notifications_sent";
21
+ readonly failed: "telegram_notification_failures";
22
+ readonly commandsHandled: "telegram_commands_handled";
23
+ readonly inboundRouted: "telegram_inbound_routed";
24
+ };
@@ -0,0 +1,25 @@
1
+ export const PLUGIN_ID = "paperclip-plugin-telegram";
2
+ export const PLUGIN_VERSION = "0.1.0";
3
+ export const DEFAULT_CONFIG = {
4
+ telegramBotTokenRef: "",
5
+ defaultChatId: "",
6
+ approvalsChatId: "",
7
+ errorsChatId: "",
8
+ paperclipBaseUrl: "http://localhost:3100",
9
+ notifyOnIssueCreated: true,
10
+ notifyOnIssueDone: true,
11
+ notifyOnApprovalCreated: true,
12
+ notifyOnAgentError: true,
13
+ enableCommands: true,
14
+ enableInbound: true,
15
+ dailyDigestEnabled: false,
16
+ dailyDigestTime: "09:00",
17
+ topicRouting: false,
18
+ };
19
+ export const METRIC_NAMES = {
20
+ sent: "telegram_notifications_sent",
21
+ failed: "telegram_notification_failures",
22
+ commandsHandled: "telegram_commands_handled",
23
+ inboundRouted: "telegram_inbound_routed",
24
+ };
25
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,2BAA2B,CAAC;AACrD,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AAEtC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,mBAAmB,EAAE,EAAE;IACvB,aAAa,EAAE,EAAE;IACjB,eAAe,EAAE,EAAE;IACnB,YAAY,EAAE,EAAE;IAChB,gBAAgB,EAAE,uBAAuB;IACzC,oBAAoB,EAAE,IAAI;IAC1B,iBAAiB,EAAE,IAAI;IACvB,uBAAuB,EAAE,IAAI;IAC7B,kBAAkB,EAAE,IAAI;IACxB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,IAAI;IACnB,kBAAkB,EAAE,KAAK;IACzB,eAAe,EAAE,OAAO;IACxB,YAAY,EAAE,KAAK;CACX,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,6BAA6B;IACnC,MAAM,EAAE,gCAAgC;IACxC,eAAe,EAAE,2BAA2B;IAC5C,aAAa,EAAE,yBAAyB;CAChC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { PluginEvent } from "@paperclipai/plugin-sdk";
2
+ import type { SendMessageOptions } from "./telegram-api.js";
3
+ type FormattedMessage = {
4
+ text: string;
5
+ options: SendMessageOptions;
6
+ };
7
+ export declare function formatIssueCreated(event: PluginEvent): FormattedMessage;
8
+ export declare function formatIssueDone(event: PluginEvent): FormattedMessage;
9
+ export declare function formatApprovalCreated(event: PluginEvent): FormattedMessage;
10
+ export declare function formatAgentError(event: PluginEvent): FormattedMessage;
11
+ export declare function formatAgentRunStarted(event: PluginEvent): FormattedMessage;
12
+ export declare function formatAgentRunFinished(event: PluginEvent): FormattedMessage;
13
+ export {};