pi-harness-runtime 1.1.145 → 1.1.147
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 +113 -0
- package/index.ts +136 -6
- package/package.json +2 -2
- 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/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/src/adapters/email-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/email-adapter.js.map +1 -0
- package/packages/notification/dist/src/adapters/line-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/adapters/line-adapter.js.map +1 -0
- package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts.map +1 -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.map +1 -0
- package/packages/notification/dist/src/adapters/webhook-adapter.js.map +1 -0
- package/packages/notification/dist/src/base-adapter.d.ts.map +1 -0
- package/packages/notification/dist/src/base-adapter.js.map +1 -0
- package/packages/notification/dist/{index.d.ts → src/index.d.ts} +2 -0
- package/packages/notification/dist/src/index.d.ts.map +1 -0
- package/packages/notification/dist/{index.js → src/index.js} +2 -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/{notification-center.js → src/notification-center.js} +103 -1
- 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/{types.d.ts → src/types.d.ts} +37 -0
- package/packages/notification/dist/src/types.d.ts.map +1 -0
- package/packages/notification/dist/src/types.js.map +1 -0
- 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
- package/packages/notification/dist/adapters/email-adapter.d.ts.map +0 -1
- package/packages/notification/dist/adapters/email-adapter.js.map +0 -1
- package/packages/notification/dist/adapters/line-adapter.d.ts.map +0 -1
- package/packages/notification/dist/adapters/line-adapter.js.map +0 -1
- package/packages/notification/dist/adapters/ntfy-adapter.d.ts.map +0 -1
- package/packages/notification/dist/adapters/ntfy-adapter.js.map +0 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts +0 -19
- package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +0 -1
- package/packages/notification/dist/adapters/telegram-adapter.js +0 -97
- package/packages/notification/dist/adapters/telegram-adapter.js.map +0 -1
- package/packages/notification/dist/adapters/webhook-adapter.d.ts.map +0 -1
- package/packages/notification/dist/adapters/webhook-adapter.js.map +0 -1
- package/packages/notification/dist/base-adapter.d.ts.map +0 -1
- package/packages/notification/dist/base-adapter.js.map +0 -1
- package/packages/notification/dist/index.d.ts.map +0 -1
- package/packages/notification/dist/index.js.map +0 -1
- package/packages/notification/dist/notification-center.d.ts +0 -50
- package/packages/notification/dist/notification-center.d.ts.map +0 -1
- package/packages/notification/dist/notification-center.js.map +0 -1
- package/packages/notification/dist/types.d.ts.map +0 -1
- package/packages/notification/dist/types.js.map +0 -1
- /package/packages/notification/dist/{adapters → src/adapters}/email-adapter.d.ts +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/email-adapter.js +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/line-adapter.d.ts +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/line-adapter.js +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/ntfy-adapter.d.ts +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/ntfy-adapter.js +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/webhook-adapter.d.ts +0 -0
- /package/packages/notification/dist/{adapters → src/adapters}/webhook-adapter.js +0 -0
- /package/packages/notification/dist/{base-adapter.d.ts → src/base-adapter.d.ts} +0 -0
- /package/packages/notification/dist/{base-adapter.js → src/base-adapter.js} +0 -0
- /package/packages/notification/dist/{types.js → src/types.js} +0 -0
|
@@ -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;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Policy Engine for intelligent routing
|
|
3
|
+
*
|
|
4
|
+
* Decides:
|
|
5
|
+
* 1. Should we notify at all?
|
|
6
|
+
* 2. What channel(s)?
|
|
7
|
+
* 3. Should we include approval buttons?
|
|
8
|
+
* 4. What's the priority level?
|
|
9
|
+
*
|
|
10
|
+
* Can be extended with Jev for AI-powered decisions:
|
|
11
|
+
* import { Jev } from "@type-safe/ai";
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface NotificationContext {
|
|
15
|
+
event: string;
|
|
16
|
+
jobId?: string;
|
|
17
|
+
requirement?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
severity?: "low" | "medium" | "high" | "critical";
|
|
20
|
+
requiresApproval?: boolean;
|
|
21
|
+
userId?: number;
|
|
22
|
+
timestamp?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface NotificationPolicy {
|
|
26
|
+
shouldNotify: boolean;
|
|
27
|
+
channels: ("telegram" | "tui" | "log")[];
|
|
28
|
+
includeButtons: boolean;
|
|
29
|
+
priority: "low" | "normal" | "high" | "urgent";
|
|
30
|
+
message?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UserPreferences {
|
|
34
|
+
telegramEnabled: boolean;
|
|
35
|
+
telegramChatId?: string;
|
|
36
|
+
telegramBotToken?: string;
|
|
37
|
+
tuiEnabled: boolean;
|
|
38
|
+
notifyOnError: boolean;
|
|
39
|
+
notifyOnCompletion: boolean;
|
|
40
|
+
notifyOnApprovalRequired: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Default preferences
|
|
44
|
+
const DEFAULT_PREFERENCES: UserPreferences = {
|
|
45
|
+
telegramEnabled: false,
|
|
46
|
+
tuiEnabled: true,
|
|
47
|
+
notifyOnError: true,
|
|
48
|
+
notifyOnCompletion: false,
|
|
49
|
+
notifyOnApprovalRequired: true,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Events that require user approval
|
|
54
|
+
*/
|
|
55
|
+
const APPROVAL_REQUIRED_EVENTS = new Set([
|
|
56
|
+
"HumanReviewNeeded",
|
|
57
|
+
"WaitingForUserInput",
|
|
58
|
+
"ApprovalRequired",
|
|
59
|
+
"ConfirmDeploy",
|
|
60
|
+
"ConfirmDelete",
|
|
61
|
+
"ConfirmCriticalAction",
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* High-priority events (always notify)
|
|
66
|
+
*/
|
|
67
|
+
const HIGH_PRIORITY_EVENTS = new Set([
|
|
68
|
+
"QuotaExhausted",
|
|
69
|
+
"CriticalError",
|
|
70
|
+
"HumanReviewNeeded",
|
|
71
|
+
"WaitingForUserInput",
|
|
72
|
+
"JobFailed",
|
|
73
|
+
"SystemAlert",
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Notification policy engine
|
|
78
|
+
*/
|
|
79
|
+
export class NotificationPolicyEngine {
|
|
80
|
+
private preferences: UserPreferences;
|
|
81
|
+
|
|
82
|
+
constructor(preferences?: Partial<UserPreferences>) {
|
|
83
|
+
this.preferences = { ...DEFAULT_PREFERENCES, ...preferences };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Update user preferences
|
|
88
|
+
*/
|
|
89
|
+
updatePreferences(prefs: Partial<UserPreferences>): void {
|
|
90
|
+
this.preferences = { ...this.preferences, ...prefs };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Evaluate notification policy
|
|
95
|
+
*/
|
|
96
|
+
evaluate(context: NotificationContext): NotificationPolicy {
|
|
97
|
+
const event = context.event;
|
|
98
|
+
const severity = context.severity || this.inferSeverity(event);
|
|
99
|
+
const needsApproval = APPROVAL_REQUIRED_EVENTS.has(event);
|
|
100
|
+
|
|
101
|
+
// Build channels
|
|
102
|
+
const channels: ("telegram" | "tui" | "log")[] = [];
|
|
103
|
+
|
|
104
|
+
if (this.preferences.telegramEnabled && this.preferences.telegramChatId) {
|
|
105
|
+
channels.push("telegram");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.preferences.tuiEnabled) {
|
|
109
|
+
channels.push("tui");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Decide based on event type and severity
|
|
113
|
+
let shouldNotify = false;
|
|
114
|
+
let includeButtons = false;
|
|
115
|
+
let priority: NotificationPolicy["priority"] = "normal";
|
|
116
|
+
|
|
117
|
+
switch (event) {
|
|
118
|
+
case "HumanReviewNeeded":
|
|
119
|
+
case "WaitingForUserInput":
|
|
120
|
+
case "ApprovalRequired":
|
|
121
|
+
shouldNotify = this.preferences.notifyOnApprovalRequired;
|
|
122
|
+
includeButtons = true;
|
|
123
|
+
priority = "high";
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case "CriticalError":
|
|
127
|
+
case "SystemAlert":
|
|
128
|
+
shouldNotify = true;
|
|
129
|
+
includeButtons = needsApproval;
|
|
130
|
+
priority = "urgent";
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case "QuotaExhausted":
|
|
134
|
+
shouldNotify = true;
|
|
135
|
+
includeButtons = false;
|
|
136
|
+
priority = "high";
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
case "JobCompleted":
|
|
140
|
+
shouldNotify = this.preferences.notifyOnCompletion;
|
|
141
|
+
includeButtons = false;
|
|
142
|
+
priority = "low";
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case "Error":
|
|
146
|
+
case "JobFailed":
|
|
147
|
+
shouldNotify = this.preferences.notifyOnError;
|
|
148
|
+
includeButtons = needsApproval;
|
|
149
|
+
priority = severity === "high" || severity === "critical" ? "high" : "normal";
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
default:
|
|
153
|
+
shouldNotify = severity === "high" || severity === "critical";
|
|
154
|
+
includeButtons = needsApproval;
|
|
155
|
+
priority = severity === "critical" ? "urgent" : severity === "high" ? "high" : "low";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Always notify for high-priority events
|
|
159
|
+
if (HIGH_PRIORITY_EVENTS.has(event)) {
|
|
160
|
+
shouldNotify = true;
|
|
161
|
+
if (priority !== "urgent") {
|
|
162
|
+
priority = "high";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
shouldNotify,
|
|
168
|
+
channels: channels.length > 0 ? channels : ["log"],
|
|
169
|
+
includeButtons,
|
|
170
|
+
priority,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Infer severity from event name
|
|
176
|
+
*/
|
|
177
|
+
private inferSeverity(event: string): "low" | "medium" | "high" | "critical" {
|
|
178
|
+
if (event.includes("Critical") || event.includes("Emergency")) {
|
|
179
|
+
return "critical";
|
|
180
|
+
}
|
|
181
|
+
if (event.includes("Error") || event.includes("Failed") || event.includes("Exhausted")) {
|
|
182
|
+
return "high";
|
|
183
|
+
}
|
|
184
|
+
if (event.includes("Warning") || event.includes("Alert")) {
|
|
185
|
+
return "medium";
|
|
186
|
+
}
|
|
187
|
+
return "low";
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Check if an event requires approval buttons
|
|
193
|
+
*/
|
|
194
|
+
export function requiresApproval(event: string): boolean {
|
|
195
|
+
return APPROVAL_REQUIRED_EVENTS.has(event);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Check if an event is high priority
|
|
200
|
+
*/
|
|
201
|
+
export function isHighPriority(event: string): boolean {
|
|
202
|
+
return HIGH_PRIORITY_EVENTS.has(event);
|
|
203
|
+
}
|