pi-harness-runtime 1.1.146 → 1.1.148
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/CHANGELOG.md +2 -0
- package/README.md +113 -0
- package/index.ts +136 -6
- package/package.json +4 -3
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/dist/src/extension.js +186 -0
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts +38 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.js +159 -5
- package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
- package/packages/notification/dist/index.d.ts +2 -0
- package/packages/notification/dist/index.d.ts.map +1 -1
- package/packages/notification/dist/index.js +2 -0
- package/packages/notification/dist/index.js.map +1 -1
- package/packages/notification/dist/notification-center.d.ts +40 -1
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +103 -1
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/dist/notification-policy.d.ts +66 -0
- package/packages/notification/dist/notification-policy.d.ts.map +1 -0
- package/packages/notification/dist/notification-policy.js +153 -0
- package/packages/notification/dist/notification-policy.js.map +1 -0
- package/packages/notification/dist/src/adapters/email-adapter.d.ts +17 -0
- package/packages/notification/dist/src/adapters/email-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/email-adapter.js +89 -0
- package/packages/notification/dist/src/adapters/email-adapter.js.map +1 -0
- package/packages/notification/dist/src/adapters/line-adapter.d.ts +17 -0
- package/packages/notification/dist/src/adapters/line-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/line-adapter.js +99 -0
- package/packages/notification/dist/src/adapters/line-adapter.js.map +1 -0
- package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts +17 -0
- package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/ntfy-adapter.js +84 -0
- package/packages/notification/dist/src/adapters/ntfy-adapter.js.map +1 -0
- package/packages/notification/dist/src/adapters/telegram-adapter.d.ts +56 -0
- package/packages/notification/dist/src/adapters/telegram-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/telegram-adapter.js +251 -0
- package/packages/notification/dist/src/adapters/telegram-adapter.js.map +1 -0
- package/packages/notification/dist/src/adapters/webhook-adapter.d.ts +15 -0
- package/packages/notification/dist/src/adapters/webhook-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/webhook-adapter.js +64 -0
- package/packages/notification/dist/src/adapters/webhook-adapter.js.map +1 -0
- package/packages/notification/dist/src/base-adapter.d.ts +36 -0
- package/packages/notification/dist/src/base-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/base-adapter.js +39 -0
- package/packages/notification/dist/src/base-adapter.js.map +1 -0
- package/packages/notification/dist/src/index.d.ts +12 -0
- package/packages/notification/dist/src/index.d.ts.map +1 -0
- package/packages/notification/dist/src/index.js +12 -0
- package/packages/notification/dist/src/index.js.map +1 -0
- package/packages/notification/dist/src/notification-center.d.ts +89 -0
- package/packages/notification/dist/src/notification-center.d.ts.map +1 -0
- package/packages/notification/dist/src/notification-center.js +356 -0
- package/packages/notification/dist/src/notification-center.js.map +1 -0
- package/packages/notification/dist/src/notification-policy.d.ts +66 -0
- package/packages/notification/dist/src/notification-policy.d.ts.map +1 -0
- package/packages/notification/dist/src/notification-policy.js +153 -0
- package/packages/notification/dist/src/notification-policy.js.map +1 -0
- package/packages/notification/dist/src/telegram-webhook-handler.d.ts +86 -0
- package/packages/notification/dist/src/telegram-webhook-handler.d.ts.map +1 -0
- package/packages/notification/dist/src/telegram-webhook-handler.js +149 -0
- package/packages/notification/dist/src/telegram-webhook-handler.js.map +1 -0
- package/packages/notification/dist/src/types.d.ts +106 -0
- package/packages/notification/dist/src/types.d.ts.map +1 -0
- package/packages/notification/dist/src/types.js +7 -0
- package/packages/notification/dist/src/types.js.map +1 -0
- package/packages/notification/dist/telegram-webhook-handler.d.ts +86 -0
- package/packages/notification/dist/telegram-webhook-handler.d.ts.map +1 -0
- package/packages/notification/dist/telegram-webhook-handler.js +149 -0
- package/packages/notification/dist/telegram-webhook-handler.js.map +1 -0
- package/packages/notification/dist/types.d.ts +37 -0
- package/packages/notification/dist/types.d.ts.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/adapters/telegram-adapter.ts +187 -5
- package/packages/notification/src/index.ts +2 -0
- package/packages/notification/src/notification-center.ts +139 -0
- package/packages/notification/src/notification-policy.ts +203 -0
- package/packages/notification/src/telegram-webhook-handler.ts +211 -0
- package/packages/notification/src/types.ts +42 -0
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/dist/index.d.ts.map +1 -1
- package/packages/paperclip-integration/dist/index.js +30 -18
- package/packages/paperclip-integration/dist/index.js.map +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/paperclip-integration/src/index.ts +35 -20
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/telegram-callback-processor.ts +180 -0
- package/scripts/telegram-webhook-server.ts +387 -0
- package/scripts/test-telegram-approval.ts +64 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Webhook Handler — RFC-0022
|
|
3
|
+
*
|
|
4
|
+
* Handles incoming webhook updates from Telegram (callback queries, messages).
|
|
5
|
+
* This enables 2-way communication with inline keyboard buttons.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Predefined action prefixes for callback data
|
|
9
|
+
*/
|
|
10
|
+
export const CallbackActions = {
|
|
11
|
+
/** Approve/resume action */
|
|
12
|
+
APPROVE: "approve",
|
|
13
|
+
/** Reject/cancel action */
|
|
14
|
+
REJECT: "reject",
|
|
15
|
+
/** View details action */
|
|
16
|
+
VIEW: "view",
|
|
17
|
+
/** Custom action prefix */
|
|
18
|
+
CUSTOM: "custom",
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Parse callback data string into structured object
|
|
22
|
+
* Format: "action_targetId" or "action_targetId_key1=val1,key2=val2"
|
|
23
|
+
*/
|
|
24
|
+
export function parseCallbackData(data) {
|
|
25
|
+
const parts = data.split("_");
|
|
26
|
+
const action = parts[0] || "";
|
|
27
|
+
const targetId = parts[1] || "";
|
|
28
|
+
let payload;
|
|
29
|
+
// Check for additional payload
|
|
30
|
+
const payloadPart = parts.slice(2).join("_");
|
|
31
|
+
if (payloadPart && payloadPart.includes("=")) {
|
|
32
|
+
payload = {};
|
|
33
|
+
for (const pair of payloadPart.split(",")) {
|
|
34
|
+
const [key, value] = pair.split("=");
|
|
35
|
+
if (key && value) {
|
|
36
|
+
payload[key] = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { action, targetId, payload };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build callback data string from components
|
|
44
|
+
*/
|
|
45
|
+
export function buildCallbackData(action, targetId, payload) {
|
|
46
|
+
let data = `${action}_${targetId}`;
|
|
47
|
+
if (payload && Object.keys(payload).length > 0) {
|
|
48
|
+
const pairs = Object.entries(payload)
|
|
49
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
50
|
+
.join(",");
|
|
51
|
+
data += `_${pairs}`;
|
|
52
|
+
}
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a standard Yes/No callback handler
|
|
57
|
+
*/
|
|
58
|
+
export function createYesNoHandler(options) {
|
|
59
|
+
return async (data, query) => {
|
|
60
|
+
const parsed = parseCallbackData(data);
|
|
61
|
+
const { action, targetId } = parsed;
|
|
62
|
+
console.log(`[TelegramWebhook] Callback: action=${action}, target=${targetId}, from=${query.from.username || query.from.first_name}`);
|
|
63
|
+
switch (action) {
|
|
64
|
+
case CallbackActions.APPROVE:
|
|
65
|
+
if (options.onApprove) {
|
|
66
|
+
await options.onApprove(targetId, query);
|
|
67
|
+
}
|
|
68
|
+
else if (options.jobManager) {
|
|
69
|
+
await options.jobManager.resume(targetId);
|
|
70
|
+
console.log(`[TelegramWebhook] Job resumed: ${targetId}`);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
case CallbackActions.REJECT:
|
|
74
|
+
if (options.onReject) {
|
|
75
|
+
await options.onReject(targetId, query);
|
|
76
|
+
}
|
|
77
|
+
else if (options.jobManager) {
|
|
78
|
+
await options.jobManager.cancel(targetId);
|
|
79
|
+
console.log(`[TelegramWebhook] Job cancelled: ${targetId}`);
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case CallbackActions.VIEW:
|
|
83
|
+
if (options.onCustom) {
|
|
84
|
+
await options.onCustom(action, targetId, query);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
if (options.onCustom) {
|
|
89
|
+
await options.onCustom(action, targetId, query);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.warn(`[TelegramWebhook] Unknown action: ${action}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Telegram webhook route handler for Express/Fastify/etc.
|
|
99
|
+
*/
|
|
100
|
+
export function createTelegramWebhookHandler(adapter, secretToken) {
|
|
101
|
+
return async (req, res) => {
|
|
102
|
+
// Verify secret token
|
|
103
|
+
if (secretToken) {
|
|
104
|
+
const receivedToken = req.headers["x-telegram-bot-api-secret-token"];
|
|
105
|
+
if (receivedToken !== secretToken) {
|
|
106
|
+
console.error("[TelegramWebhook] Unauthorized webhook request");
|
|
107
|
+
res.sendStatus(403);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await adapter.handleWebhookUpdate(req.body);
|
|
113
|
+
res.sendStatus(200);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error("[TelegramWebhook] Error processing update:", error);
|
|
117
|
+
res.sendStatus(500);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Notification payload helper with inline keyboard support
|
|
123
|
+
*/
|
|
124
|
+
export function createInteractivePayload(event, jobId, title, message, buttons, details) {
|
|
125
|
+
return {
|
|
126
|
+
event,
|
|
127
|
+
jobId,
|
|
128
|
+
timestamp: new Date().toISOString(),
|
|
129
|
+
title,
|
|
130
|
+
message,
|
|
131
|
+
details: {
|
|
132
|
+
...details,
|
|
133
|
+
_callbackButtons: buttons.map((b) => ({
|
|
134
|
+
text: b.text,
|
|
135
|
+
callbackData: buildCallbackData(b.action, b.targetId),
|
|
136
|
+
})),
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Update TelegramConfig to include action buttons from payload details
|
|
142
|
+
*/
|
|
143
|
+
export function extractButtonsFromPayload(details) {
|
|
144
|
+
if (!details)
|
|
145
|
+
return undefined;
|
|
146
|
+
const cb = details._callbackButtons;
|
|
147
|
+
return cb?.length ? cb : undefined;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=telegram-webhook-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-webhook-handler.js","sourceRoot":"","sources":["../src/telegram-webhook-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,7 +29,44 @@ export interface TelegramConfig {
|
|
|
29
29
|
botToken: string;
|
|
30
30
|
chatId: string;
|
|
31
31
|
parseMode?: "MarkdownV2" | "HTML" | "Markdown";
|
|
32
|
+
enableInlineKeyboard?: boolean;
|
|
33
|
+
actionButtons?: InlineKeyboardButton[];
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Inline keyboard button for Telegram interactive messages
|
|
37
|
+
*/
|
|
38
|
+
export interface InlineKeyboardButton {
|
|
39
|
+
/** Button text displayed to user */
|
|
40
|
+
text: string;
|
|
41
|
+
/** Callback data sent when button is clicked */
|
|
42
|
+
callbackData: string;
|
|
43
|
+
/** Optional URL to open when button is clicked (mutually exclusive with callbackData) */
|
|
44
|
+
url?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Callback query received from Telegram inline keyboard
|
|
48
|
+
*/
|
|
49
|
+
export interface TelegramCallbackQuery {
|
|
50
|
+
id: string;
|
|
51
|
+
from: {
|
|
52
|
+
id: number;
|
|
53
|
+
is_bot: boolean;
|
|
54
|
+
first_name: string;
|
|
55
|
+
username?: string;
|
|
56
|
+
};
|
|
57
|
+
chat_instance: string;
|
|
58
|
+
data: string;
|
|
59
|
+
message?: {
|
|
60
|
+
chat: {
|
|
61
|
+
id: number;
|
|
62
|
+
};
|
|
63
|
+
message_id: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Callback handler function type
|
|
68
|
+
*/
|
|
69
|
+
export type TelegramCallbackHandler = (callbackData: string, query: TelegramCallbackQuery) => Promise<void> | void;
|
|
33
70
|
export interface NtfyConfig {
|
|
34
71
|
server: string;
|
|
35
72
|
topic: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,iBAAiB,GAC1B,YAAY,GACZ,eAAe,GACf,YAAY,GACZ,aAAa,GACb,iBAAiB,GACjB,kBAAkB,GAClB,sBAAsB,GACtB,WAAW,GACX,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,qBAAqB,GACrB,qBAAqB,GACrB,mBAAmB,GACnB,OAAO,CAAC;AAEX,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAElC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IAEtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,uBAAuB,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;CAC/E;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEzF,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
/// <reference types="node" />
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
|
+
InlineKeyboardButton,
|
|
10
11
|
NotificationPayload,
|
|
11
12
|
NotificationResult,
|
|
13
|
+
TelegramCallbackHandler,
|
|
14
|
+
TelegramCallbackQuery,
|
|
12
15
|
TelegramConfig,
|
|
13
16
|
} from "../types.js";
|
|
14
17
|
import { BaseChannelAdapter } from "../base-adapter.js";
|
|
@@ -17,11 +20,27 @@ export class TelegramAdapter extends BaseChannelAdapter {
|
|
|
17
20
|
readonly id = "telegram";
|
|
18
21
|
readonly type = "telegram";
|
|
19
22
|
private _botUsername: string | undefined;
|
|
23
|
+
private _callbackHandler: TelegramCallbackHandler | undefined;
|
|
24
|
+
private _webhookSecret: string | undefined;
|
|
20
25
|
|
|
21
26
|
constructor(config: TelegramConfig) {
|
|
22
27
|
super({ id: "telegram", type: "telegram", enabled: true, config });
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Register a callback handler for inline keyboard button clicks
|
|
32
|
+
*/
|
|
33
|
+
setCallbackHandler(handler: TelegramCallbackHandler): void {
|
|
34
|
+
this._callbackHandler = handler;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set webhook secret for verification
|
|
39
|
+
*/
|
|
40
|
+
setWebhookSecret(secret: string): void {
|
|
41
|
+
this._webhookSecret = secret;
|
|
42
|
+
}
|
|
43
|
+
|
|
25
44
|
get botUsername(): string | undefined {
|
|
26
45
|
return this._botUsername;
|
|
27
46
|
}
|
|
@@ -43,21 +62,32 @@ return this._botUsername;
|
|
|
43
62
|
}
|
|
44
63
|
}
|
|
45
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Send a message with optional inline keyboard buttons
|
|
67
|
+
*/
|
|
46
68
|
async send(payload: NotificationPayload): Promise<NotificationResult> {
|
|
47
69
|
try {
|
|
48
70
|
const cfg = this.config.config as TelegramConfig;
|
|
49
71
|
const message = this.formatMessage(payload);
|
|
50
72
|
|
|
73
|
+
// Build request body
|
|
74
|
+
const body: Record<string, unknown> = {
|
|
75
|
+
chat_id: cfg.chatId,
|
|
76
|
+
text: message,
|
|
77
|
+
parse_mode: cfg.parseMode ?? "MarkdownV2",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Add inline keyboard if enabled and buttons are configured
|
|
81
|
+
if (cfg.enableInlineKeyboard && (cfg.actionButtons?.length ?? 0) > 0) {
|
|
82
|
+
body.reply_markup = this.buildInlineKeyboard(cfg.actionButtons!);
|
|
83
|
+
}
|
|
84
|
+
|
|
51
85
|
const response = await fetch(
|
|
52
86
|
`https://api.telegram.org/bot${cfg.botToken}/sendMessage`,
|
|
53
87
|
{
|
|
54
88
|
method: "POST",
|
|
55
89
|
headers: { "Content-Type": "application/json" },
|
|
56
|
-
body: JSON.stringify(
|
|
57
|
-
chat_id: cfg.chatId,
|
|
58
|
-
text: message,
|
|
59
|
-
parse_mode: cfg.parseMode ?? "MarkdownV2",
|
|
60
|
-
}),
|
|
90
|
+
body: JSON.stringify(body),
|
|
61
91
|
},
|
|
62
92
|
);
|
|
63
93
|
|
|
@@ -80,6 +110,158 @@ return this._botUsername;
|
|
|
80
110
|
}
|
|
81
111
|
}
|
|
82
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Build inline keyboard markup from button configuration
|
|
115
|
+
*/
|
|
116
|
+
private buildInlineKeyboard(
|
|
117
|
+
buttons: InlineKeyboardButton[],
|
|
118
|
+
): { inline_keyboard: Array<Array<{ text: string; callback_data?: string; url?: string }>> } {
|
|
119
|
+
// Group buttons into rows of up to 3 buttons each (Telegram limit)
|
|
120
|
+
const ROW_SIZE = 3;
|
|
121
|
+
const rows: Array<Array<{ text: string; callback_data?: string; url?: string }>> = [];
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < buttons.length; i += ROW_SIZE) {
|
|
124
|
+
const row = buttons.slice(i, i + ROW_SIZE).map((btn) => ({
|
|
125
|
+
text: btn.text,
|
|
126
|
+
...(btn.url ? { url: btn.url } : { callback_data: btn.callbackData }),
|
|
127
|
+
}));
|
|
128
|
+
rows.push(row);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { inline_keyboard: rows };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Process incoming webhook update from Telegram
|
|
136
|
+
*/
|
|
137
|
+
async handleWebhookUpdate(update: Record<string, unknown>): Promise<void> {
|
|
138
|
+
// Verify secret token if set
|
|
139
|
+
if (this._webhookSecret) {
|
|
140
|
+
const secretToken = (update as { secret_token?: string }).secret_token;
|
|
141
|
+
if (secretToken !== this._webhookSecret) {
|
|
142
|
+
console.error("[TelegramAdapter] Invalid webhook secret");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle callback query (button click)
|
|
148
|
+
const callbackQuery = update.callback_query as Record<string, unknown> | undefined;
|
|
149
|
+
if (callbackQuery && this._callbackHandler) {
|
|
150
|
+
const query = this.parseCallbackQuery(callbackQuery);
|
|
151
|
+
if (query) {
|
|
152
|
+
await this._callbackHandler(query.data, query);
|
|
153
|
+
await this.answerCallbackQuery(query.id);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Handle regular messages (future: user can type commands)
|
|
158
|
+
const message = update.message as Record<string, unknown> | undefined;
|
|
159
|
+
if (message) {
|
|
160
|
+
console.log("[TelegramAdapter] Received message:", message.text);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parse callback query from webhook update
|
|
166
|
+
*/
|
|
167
|
+
private parseCallbackQuery(
|
|
168
|
+
raw: Record<string, unknown>,
|
|
169
|
+
): TelegramCallbackQuery | null {
|
|
170
|
+
try {
|
|
171
|
+
const from = raw.from as Record<string, unknown>;
|
|
172
|
+
const message = raw.message as Record<string, unknown> | undefined;
|
|
173
|
+
const chat = message?.chat as Record<string, unknown> | undefined;
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
id: String(raw.id),
|
|
177
|
+
from: {
|
|
178
|
+
id: Number(from?.id),
|
|
179
|
+
is_bot: Boolean(from?.is_bot),
|
|
180
|
+
first_name: String(from?.first_name ?? ""),
|
|
181
|
+
username: from?.username ? String(from.username) : undefined,
|
|
182
|
+
},
|
|
183
|
+
chat_instance: String(raw.chat_instance ?? ""),
|
|
184
|
+
data: String(raw.data ?? ""),
|
|
185
|
+
message: message
|
|
186
|
+
? {
|
|
187
|
+
chat: { id: Number(chat?.id) },
|
|
188
|
+
message_id: Number(message?.message_id),
|
|
189
|
+
}
|
|
190
|
+
: undefined,
|
|
191
|
+
};
|
|
192
|
+
} catch {
|
|
193
|
+
console.error("[TelegramAdapter] Failed to parse callback query");
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Answer a callback query to remove loading state
|
|
200
|
+
*/
|
|
201
|
+
private async answerCallbackQuery(callbackQueryId: string): Promise<void> {
|
|
202
|
+
try {
|
|
203
|
+
const cfg = this.config.config as TelegramConfig;
|
|
204
|
+
await fetch(
|
|
205
|
+
`https://api.telegram.org/bot${cfg.botToken}/answerCallbackQuery`,
|
|
206
|
+
{
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: { "Content-Type": "application/json" },
|
|
209
|
+
body: JSON.stringify({ callback_query_id: callbackQueryId }),
|
|
210
|
+
},
|
|
211
|
+
);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error("[TelegramAdapter] Failed to answer callback query:", error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Setup webhook for receiving updates
|
|
219
|
+
*/
|
|
220
|
+
async setupWebhook(webhookUrl: string): Promise<boolean> {
|
|
221
|
+
try {
|
|
222
|
+
const cfg = this.config.config as TelegramConfig;
|
|
223
|
+
const response = await fetch(
|
|
224
|
+
`https://api.telegram.org/bot${cfg.botToken}/setWebhook`,
|
|
225
|
+
{
|
|
226
|
+
method: "POST",
|
|
227
|
+
headers: { "Content-Type": "application/json" },
|
|
228
|
+
body: JSON.stringify({
|
|
229
|
+
url: webhookUrl,
|
|
230
|
+
secret_token: this._webhookSecret,
|
|
231
|
+
}),
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
console.error("[TelegramAdapter] Webhook setup failed:", await response.text());
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.log(`[TelegramAdapter] Webhook set to: ${webhookUrl}`);
|
|
241
|
+
return true;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error("[TelegramAdapter] Webhook setup error:", error);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get current webhook info
|
|
250
|
+
*/
|
|
251
|
+
async getWebhookInfo(): Promise<Record<string, unknown> | null> {
|
|
252
|
+
try {
|
|
253
|
+
const cfg = this.config.config as TelegramConfig;
|
|
254
|
+
const response = await fetch(
|
|
255
|
+
`https://api.telegram.org/bot${cfg.botToken}/getWebhookInfo`,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
if (!response.ok) return null;
|
|
259
|
+
return (await response.json()) as Record<string, unknown>;
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
83
265
|
private formatMessage(payload: NotificationPayload): string {
|
|
84
266
|
const emoji = this.getEmoji(payload.event);
|
|
85
267
|
const title = `${emoji} ${payload.title}`;
|
|
@@ -7,4 +7,6 @@
|
|
|
7
7
|
export * from "./notification-center.js";
|
|
8
8
|
export * from "./base-adapter.js";
|
|
9
9
|
export * from "./types.js";
|
|
10
|
+
export * from "./telegram-webhook-handler.js";
|
|
10
11
|
export { LineAdapter } from "./adapters/line-adapter.js";
|
|
12
|
+
export { TelegramAdapter } from "./adapters/telegram-adapter.js";
|
|
@@ -10,15 +10,19 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
|
+
InlineKeyboardButton,
|
|
13
14
|
NotificationEvent,
|
|
14
15
|
NotificationPayload,
|
|
15
16
|
NotificationConfig,
|
|
16
17
|
NotificationChannelConfig,
|
|
17
18
|
NotificationResult,
|
|
18
19
|
NotificationContext,
|
|
20
|
+
TelegramCallbackHandler,
|
|
21
|
+
TelegramCallbackQuery,
|
|
19
22
|
} from "./types.js";
|
|
20
23
|
import type { ChannelAdapter } from "./base-adapter.js";
|
|
21
24
|
import { TelegramAdapter } from "./adapters/telegram-adapter.js";
|
|
25
|
+
import { buildCallbackData, parseCallbackData, CallbackActions } from "./telegram-webhook-handler.js";
|
|
22
26
|
import { LineAdapter } from "./adapters/line-adapter.js";
|
|
23
27
|
import { NtfyAdapter } from "./adapters/ntfy-adapter.js";
|
|
24
28
|
import { EmailAdapter } from "./adapters/email-adapter.js";
|
|
@@ -158,6 +162,132 @@ export class NotificationCenter {
|
|
|
158
162
|
return undefined;
|
|
159
163
|
}
|
|
160
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Get Telegram adapter for advanced operations (webhook setup, etc.)
|
|
167
|
+
*/
|
|
168
|
+
getTelegramAdapter(): TelegramAdapter | undefined {
|
|
169
|
+
const adapter = this.adapters.get("telegram");
|
|
170
|
+
if (adapter instanceof TelegramAdapter) {
|
|
171
|
+
return adapter;
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Set callback handler for Telegram inline keyboard button clicks
|
|
178
|
+
*/
|
|
179
|
+
setTelegramCallbackHandler(handler: TelegramCallbackHandler): void {
|
|
180
|
+
const adapter = this.adapters.get("telegram");
|
|
181
|
+
if (adapter instanceof TelegramAdapter) {
|
|
182
|
+
adapter.setCallbackHandler(handler);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Set webhook secret for Telegram updates verification
|
|
188
|
+
*/
|
|
189
|
+
setTelegramWebhookSecret(secret: string): void {
|
|
190
|
+
const adapter = this.adapters.get("telegram");
|
|
191
|
+
if (adapter instanceof TelegramAdapter) {
|
|
192
|
+
adapter.setWebhookSecret(secret);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Setup Telegram webhook for receiving updates
|
|
198
|
+
*/
|
|
199
|
+
async setupTelegramWebhook(webhookUrl: string): Promise<boolean> {
|
|
200
|
+
const adapter = this.adapters.get("telegram");
|
|
201
|
+
if (adapter instanceof TelegramAdapter) {
|
|
202
|
+
return await adapter.setupWebhook(webhookUrl);
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Send notification with interactive Yes/No buttons
|
|
209
|
+
*/
|
|
210
|
+
async notifyWithApproval(
|
|
211
|
+
event: NotificationEvent,
|
|
212
|
+
context: NotificationContext,
|
|
213
|
+
options?: {
|
|
214
|
+
/** Custom button labels */
|
|
215
|
+
approveLabel?: string;
|
|
216
|
+
rejectLabel?: string;
|
|
217
|
+
/** Additional details for the message */
|
|
218
|
+
additionalDetails?: Record<string, unknown>;
|
|
219
|
+
},
|
|
220
|
+
): Promise<NotificationResult[]> {
|
|
221
|
+
const approveLabel = options?.approveLabel ?? "Yes";
|
|
222
|
+
const rejectLabel = options?.rejectLabel ?? "No";
|
|
223
|
+
|
|
224
|
+
// Build buttons
|
|
225
|
+
const buttons: InlineKeyboardButton[] = [
|
|
226
|
+
{ text: `\u2705 ${approveLabel}`, callbackData: buildCallbackData(CallbackActions.APPROVE, context.jobId) },
|
|
227
|
+
{ text: `\u274C ${rejectLabel}`, callbackData: buildCallbackData(CallbackActions.REJECT, context.jobId) },
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
// Send to each channel with buttons (Telegram only)
|
|
231
|
+
const results: NotificationResult[] = [];
|
|
232
|
+
|
|
233
|
+
for (const [id, adapter] of this.adapters.entries()) {
|
|
234
|
+
if (adapter instanceof TelegramAdapter) {
|
|
235
|
+
try {
|
|
236
|
+
const payload = this.buildPayload(event, context, {
|
|
237
|
+
enableInlineKeyboard: true,
|
|
238
|
+
actionButtons: buttons,
|
|
239
|
+
...(options?.additionalDetails && { additionalDetails: options.additionalDetails }),
|
|
240
|
+
});
|
|
241
|
+
const result = await adapter.send(payload);
|
|
242
|
+
results.push(result);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
results.push({ success: false, channel: id, error: String(error) });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return results;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Send interactive notification with custom buttons
|
|
254
|
+
*/
|
|
255
|
+
async notifyWithButtons(
|
|
256
|
+
event: NotificationEvent,
|
|
257
|
+
context: NotificationContext,
|
|
258
|
+
buttons: Array<{ text: string; action: string; targetId: string; url?: string }>,
|
|
259
|
+
options?: {
|
|
260
|
+
/** Additional details for the message */
|
|
261
|
+
additionalDetails?: Record<string, unknown>;
|
|
262
|
+
},
|
|
263
|
+
): Promise<NotificationResult[]> {
|
|
264
|
+
const results: NotificationResult[] = [];
|
|
265
|
+
|
|
266
|
+
for (const [id, adapter] of this.adapters.entries()) {
|
|
267
|
+
if (adapter instanceof TelegramAdapter) {
|
|
268
|
+
try {
|
|
269
|
+
const formattedButtons: InlineKeyboardButton[] = buttons.map((b) => ({
|
|
270
|
+
text: b.text,
|
|
271
|
+
callbackData: buildCallbackData(b.action, b.targetId),
|
|
272
|
+
url: b.url,
|
|
273
|
+
}));
|
|
274
|
+
|
|
275
|
+
const payload = this.buildPayload(event, context, {
|
|
276
|
+
enableInlineKeyboard: true,
|
|
277
|
+
actionButtons: formattedButtons,
|
|
278
|
+
...(options?.additionalDetails && { additionalDetails: options.additionalDetails }),
|
|
279
|
+
});
|
|
280
|
+
const result = await adapter.send(payload);
|
|
281
|
+
results.push(result);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
results.push({ success: false, channel: id, error: String(error) });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return results;
|
|
289
|
+
}
|
|
290
|
+
|
|
161
291
|
// --- Private Methods ------------------------------------------------
|
|
162
292
|
|
|
163
293
|
private createAdapter(
|
|
@@ -192,6 +322,11 @@ export class NotificationCenter {
|
|
|
192
322
|
private buildPayload(
|
|
193
323
|
event: NotificationEvent,
|
|
194
324
|
context: NotificationContext,
|
|
325
|
+
options?: {
|
|
326
|
+
enableInlineKeyboard?: boolean;
|
|
327
|
+
actionButtons?: InlineKeyboardButton[];
|
|
328
|
+
additionalDetails?: Record<string, unknown>;
|
|
329
|
+
},
|
|
195
330
|
): NotificationPayload {
|
|
196
331
|
const { title, message } = this.getEventContent(event, context);
|
|
197
332
|
|
|
@@ -207,6 +342,10 @@ export class NotificationCenter {
|
|
|
207
342
|
...(context.taskId && { taskId: context.taskId }),
|
|
208
343
|
...(context.taskTitle && { taskTitle: context.taskTitle }),
|
|
209
344
|
...(context.error && { error: context.error }),
|
|
345
|
+
// Pass inline keyboard options to Telegram adapter
|
|
346
|
+
...(options?.enableInlineKeyboard && { _enableInlineKeyboard: true }),
|
|
347
|
+
...(options?.actionButtons && { _actionButtons: options.actionButtons }),
|
|
348
|
+
...(options?.additionalDetails && options.additionalDetails),
|
|
210
349
|
},
|
|
211
350
|
};
|
|
212
351
|
}
|