@zhin.js/adapter-telegram 5.0.2 → 5.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +61 -144
  3. package/adapters/telegram.ts +27 -0
  4. package/agent/tools/create_invite.ts +2 -2
  5. package/agent/tools/list_admins.ts +2 -2
  6. package/agent/tools/member_count.ts +2 -2
  7. package/agent/tools/pin_message.ts +2 -2
  8. package/agent/tools/react.ts +2 -2
  9. package/agent/tools/send_poll.ts +2 -2
  10. package/agent/tools/send_sticker.ts +2 -2
  11. package/agent/tools/set_description.ts +2 -2
  12. package/agent/tools/set_permissions.ts +2 -2
  13. package/agent/tools/unpin_message.ts +2 -2
  14. package/lib/endpoint.d.ts +65 -0
  15. package/lib/endpoint.js +332 -0
  16. package/lib/index.d.ts +4 -0
  17. package/lib/index.js +4 -0
  18. package/lib/platform-permit.d.ts +18 -0
  19. package/lib/{src/platform-permit.js → platform-permit.js} +1 -2
  20. package/lib/polling.d.ts +9 -0
  21. package/lib/polling.js +52 -0
  22. package/lib/protocol.d.ts +250 -0
  23. package/lib/protocol.js +324 -0
  24. package/lib/telegram-agent-deps.d.ts +28 -0
  25. package/lib/telegram-agent-deps.js +30 -0
  26. package/lib/webhook.d.ts +13 -0
  27. package/lib/webhook.js +45 -0
  28. package/package.json +43 -40
  29. package/plugin.ts +13 -0
  30. package/schema.json +39 -0
  31. package/src/endpoint.ts +339 -1103
  32. package/src/index.ts +40 -186
  33. package/src/platform-permit.ts +1 -1
  34. package/src/polling.ts +71 -0
  35. package/src/protocol.ts +573 -0
  36. package/src/telegram-agent-deps.ts +52 -11
  37. package/src/webhook.ts +67 -0
  38. package/client/Dashboard.tsx +0 -295
  39. package/client/index.tsx +0 -11
  40. package/client/tsconfig.json +0 -7
  41. package/client/utils/api.ts +0 -30
  42. package/dist/index.js +0 -32
  43. package/lib/agent/tools/create_invite.js +0 -20
  44. package/lib/agent/tools/create_invite.js.map +0 -1
  45. package/lib/agent/tools/list_admins.js +0 -26
  46. package/lib/agent/tools/list_admins.js.map +0 -1
  47. package/lib/agent/tools/member_count.js +0 -18
  48. package/lib/agent/tools/member_count.js.map +0 -1
  49. package/lib/agent/tools/pin_message.js +0 -21
  50. package/lib/agent/tools/pin_message.js.map +0 -1
  51. package/lib/agent/tools/react.js +0 -20
  52. package/lib/agent/tools/react.js.map +0 -1
  53. package/lib/agent/tools/send_poll.js +0 -32
  54. package/lib/agent/tools/send_poll.js.map +0 -1
  55. package/lib/agent/tools/send_sticker.js +0 -19
  56. package/lib/agent/tools/send_sticker.js.map +0 -1
  57. package/lib/agent/tools/set_description.js +0 -19
  58. package/lib/agent/tools/set_description.js.map +0 -1
  59. package/lib/agent/tools/set_permissions.js +0 -34
  60. package/lib/agent/tools/set_permissions.js.map +0 -1
  61. package/lib/agent/tools/unpin_message.js +0 -21
  62. package/lib/agent/tools/unpin_message.js.map +0 -1
  63. package/lib/src/adapter.js +0 -58
  64. package/lib/src/adapter.js.map +0 -1
  65. package/lib/src/endpoint.js +0 -1046
  66. package/lib/src/endpoint.js.map +0 -1
  67. package/lib/src/index.js +0 -215
  68. package/lib/src/index.js.map +0 -1
  69. package/lib/src/platform-permit.js.map +0 -1
  70. package/lib/src/segment-mapper.js +0 -2
  71. package/lib/src/segment-mapper.js.map +0 -1
  72. package/lib/src/telegram-agent-deps.js +0 -10
  73. package/lib/src/telegram-agent-deps.js.map +0 -1
  74. package/lib/src/types.js +0 -2
  75. package/lib/src/types.js.map +0 -1
  76. package/plugin.yml +0 -3
  77. package/src/adapter.ts +0 -66
  78. package/src/segment-mapper.ts +0 -1
  79. package/src/types.ts +0 -32
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Telegram Bot API protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
3
+ * Canonicalization is owned by gateway/core before endpoint.send.
4
+ */
5
+ export function resolveTelegramConfig(config = {}) {
6
+ const entry = config.endpoints?.find((item) => item.context === 'telegram');
7
+ const token = config.token
8
+ ?? entry?.token
9
+ ?? process.env.TELEGRAM_TOKEN
10
+ ?? process.env.TELEGRAM_BOT_TOKEN;
11
+ if (!token) {
12
+ throw new TypeError('Telegram adapter requires token (plugins.<key>.token or endpoints with context: telegram)');
13
+ }
14
+ const name = (typeof config.name === 'string' && config.name)
15
+ || (typeof entry?.name === 'string' && entry.name)
16
+ || process.env.TELEGRAM_BOT_NAME
17
+ || 'telegram-bot';
18
+ const polling = config.polling ?? entry?.polling;
19
+ const webhookSource = config.webhook ?? entry?.webhook;
20
+ // Match legacy: polling defaults true; webhook only when polling === false.
21
+ const mode = polling === false ? 'webhook' : 'polling';
22
+ const apiBaseUrl = (config.apiBaseUrl
23
+ ?? entry?.apiBaseUrl
24
+ ?? 'https://api.telegram.org').replace(/\/$/, '');
25
+ const allowedUpdates = config.allowedUpdates
26
+ ?? entry?.allowedUpdates
27
+ ?? ['message', 'callback_query'];
28
+ const webhook = mode === 'webhook'
29
+ ? {
30
+ domain: webhookSource?.domain ?? '',
31
+ path: normalizeWebhookPath(webhookSource?.path ?? '/telegram/webhook'),
32
+ secretToken: webhookSource?.secretToken
33
+ ?? process.env.TELEGRAM_WEBHOOK_SECRET
34
+ ?? undefined,
35
+ }
36
+ : undefined;
37
+ return {
38
+ context: 'telegram',
39
+ name,
40
+ token,
41
+ mode,
42
+ allowedUpdates: [...allowedUpdates],
43
+ apiBaseUrl,
44
+ webhook,
45
+ };
46
+ }
47
+ export function normalizeWebhookPath(path) {
48
+ const trimmed = path.trim() || '/telegram/webhook';
49
+ return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
50
+ }
51
+ export function buildWebhookUrl(webhook) {
52
+ const domain = webhook.domain.replace(/\/$/, '');
53
+ if (!domain) {
54
+ throw new TypeError('Telegram webhook mode requires webhook.domain');
55
+ }
56
+ return `${domain}${webhook.path}`;
57
+ }
58
+ export async function readTextBody(request, options = {}) {
59
+ const limit = options.limit ?? 1_048_576;
60
+ const chunks = [];
61
+ let size = 0;
62
+ for await (const chunk of request) {
63
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
64
+ size += buffer.length;
65
+ if (size > limit) {
66
+ request.destroy();
67
+ throw new Error(`Request body exceeds ${limit} bytes`);
68
+ }
69
+ chunks.push(buffer);
70
+ }
71
+ return Buffer.concat(chunks).toString('utf8');
72
+ }
73
+ export function botApiUrl(config, method) {
74
+ return `${config.apiBaseUrl}/bot${config.token}/${method}`;
75
+ }
76
+ export function resolveChannel(msg) {
77
+ return {
78
+ channelType: msg.chat.type === 'private' ? 'private' : 'group',
79
+ channelId: String(msg.chat.id),
80
+ };
81
+ }
82
+ export function senderDisplayName(user) {
83
+ if (!user)
84
+ return 'Unknown';
85
+ return user.username || user.first_name || String(user.id);
86
+ }
87
+ /** Build inbound text for MessageGateway.receive. */
88
+ export function formatInboundContent(msg) {
89
+ if (msg.text)
90
+ return msg.text;
91
+ if (msg.caption)
92
+ return msg.caption;
93
+ if (msg.photo?.length)
94
+ return '[image]';
95
+ if (msg.video)
96
+ return '[video]';
97
+ if (msg.audio)
98
+ return '[audio]';
99
+ if (msg.voice)
100
+ return '[voice]';
101
+ if (msg.document) {
102
+ return msg.document.file_name ? `[file: ${msg.document.file_name}]` : '[file]';
103
+ }
104
+ if (msg.sticker) {
105
+ return msg.sticker.emoji ? `[sticker: ${msg.sticker.emoji}]` : '[sticker]';
106
+ }
107
+ if (msg.location) {
108
+ return `[location: ${msg.location.latitude},${msg.location.longitude}]`;
109
+ }
110
+ return '';
111
+ }
112
+ export function formatCallbackContent(query) {
113
+ return query.data ? `[action: ${query.data}]` : '[action]';
114
+ }
115
+ /**
116
+ * Wire-encode an already-rendered outbound payload into Telegram Bot API actions.
117
+ * Segment canonicalization is intentionally not done here.
118
+ */
119
+ export function formatOutboundActions(target, payload) {
120
+ const chatId = typeof target === 'number' ? target : (/^-?\d+$/.test(target) ? Number(target) : target);
121
+ if (typeof payload === 'string') {
122
+ const text = payload.trim();
123
+ if (!text)
124
+ throw new Error('No Telegram content to send');
125
+ return [{ method: 'sendMessage', params: { chat_id: chatId, text } }];
126
+ }
127
+ const items = Array.isArray(payload)
128
+ ? payload
129
+ : payload && typeof payload === 'object' && 'type' in payload
130
+ ? [payload]
131
+ : [];
132
+ if (items.length === 0) {
133
+ const text = payload == null
134
+ ? ''
135
+ : typeof payload === 'object'
136
+ ? JSON.stringify(payload)
137
+ : String(payload);
138
+ if (!text.trim())
139
+ throw new Error('No Telegram content to send');
140
+ return [{ method: 'sendMessage', params: { chat_id: chatId, text: text.trim() } }];
141
+ }
142
+ let textContent = '';
143
+ let replyTo;
144
+ let keyboard;
145
+ const actions = [];
146
+ const replyParams = () => (replyTo != null ? { reply_parameters: { message_id: replyTo } } : {});
147
+ const mediaSource = (data) => {
148
+ if (typeof data.file_id === 'string' && data.file_id)
149
+ return data.file_id;
150
+ if (typeof data.url === 'string' && data.url)
151
+ return data.url;
152
+ if (typeof data.file === 'string' && data.file)
153
+ return data.file;
154
+ return undefined;
155
+ };
156
+ for (const item of items) {
157
+ if (typeof item === 'string') {
158
+ textContent += item;
159
+ continue;
160
+ }
161
+ const data = item.data ?? {};
162
+ switch (item.type) {
163
+ case 'text':
164
+ textContent += String(data.text ?? data.content ?? '');
165
+ break;
166
+ case 'at':
167
+ if (data.id)
168
+ textContent += `@${String(data.name || data.id)}`;
169
+ break;
170
+ case 'reply': {
171
+ const id = Number(data.id ?? data.message_id);
172
+ if (Number.isFinite(id))
173
+ replyTo = id;
174
+ break;
175
+ }
176
+ case 'keyboard': {
177
+ const rows = Array.isArray(data.rows) ? data.rows : [];
178
+ keyboard = rows.map((row) => {
179
+ const buttons = Array.isArray(row) ? row : [];
180
+ return buttons.map((btn) => {
181
+ const record = btn && typeof btn === 'object'
182
+ ? btn
183
+ : {};
184
+ return {
185
+ text: String(record.label ?? record.text ?? ''),
186
+ callback_data: String(record.payload ?? record.callback_data ?? '').slice(0, 64),
187
+ };
188
+ });
189
+ });
190
+ break;
191
+ }
192
+ case 'image': {
193
+ const photo = mediaSource(data);
194
+ if (photo) {
195
+ actions.push({
196
+ method: 'sendPhoto',
197
+ params: {
198
+ chat_id: chatId,
199
+ photo,
200
+ caption: textContent.trim() || undefined,
201
+ ...replyParams(),
202
+ },
203
+ });
204
+ textContent = '';
205
+ }
206
+ break;
207
+ }
208
+ case 'video': {
209
+ const video = mediaSource(data);
210
+ if (video) {
211
+ actions.push({
212
+ method: 'sendVideo',
213
+ params: {
214
+ chat_id: chatId,
215
+ video,
216
+ caption: textContent.trim() || undefined,
217
+ ...replyParams(),
218
+ },
219
+ });
220
+ textContent = '';
221
+ }
222
+ break;
223
+ }
224
+ case 'audio': {
225
+ const audio = mediaSource(data);
226
+ if (audio) {
227
+ actions.push({
228
+ method: 'sendAudio',
229
+ params: {
230
+ chat_id: chatId,
231
+ audio,
232
+ caption: textContent.trim() || undefined,
233
+ ...replyParams(),
234
+ },
235
+ });
236
+ textContent = '';
237
+ }
238
+ break;
239
+ }
240
+ case 'voice': {
241
+ const voice = mediaSource(data);
242
+ if (voice) {
243
+ actions.push({
244
+ method: 'sendVoice',
245
+ params: {
246
+ chat_id: chatId,
247
+ voice,
248
+ caption: textContent.trim() || undefined,
249
+ ...replyParams(),
250
+ },
251
+ });
252
+ textContent = '';
253
+ }
254
+ break;
255
+ }
256
+ case 'file': {
257
+ const document = mediaSource(data);
258
+ if (document) {
259
+ actions.push({
260
+ method: 'sendDocument',
261
+ params: {
262
+ chat_id: chatId,
263
+ document,
264
+ caption: textContent.trim() || undefined,
265
+ ...replyParams(),
266
+ },
267
+ });
268
+ textContent = '';
269
+ }
270
+ break;
271
+ }
272
+ case 'sticker': {
273
+ const sticker = typeof data.file_id === 'string' ? data.file_id : mediaSource(data);
274
+ if (sticker) {
275
+ actions.push({
276
+ method: 'sendSticker',
277
+ params: { chat_id: chatId, sticker, ...replyParams() },
278
+ });
279
+ }
280
+ break;
281
+ }
282
+ case 'location': {
283
+ actions.push({
284
+ method: 'sendLocation',
285
+ params: {
286
+ chat_id: chatId,
287
+ latitude: Number(data.latitude ?? 0),
288
+ longitude: Number(data.longitude ?? 0),
289
+ ...replyParams(),
290
+ },
291
+ });
292
+ break;
293
+ }
294
+ default:
295
+ textContent += String(data.text ?? `[${item.type}]`);
296
+ }
297
+ }
298
+ if (actions.length === 0) {
299
+ const text = textContent.trim() || (keyboard ? ' ' : '');
300
+ if (!text && !keyboard)
301
+ throw new Error('No Telegram content to send');
302
+ return [{
303
+ method: 'sendMessage',
304
+ params: {
305
+ chat_id: chatId,
306
+ text: text || ' ',
307
+ ...replyParams(),
308
+ ...(keyboard ? { reply_markup: { inline_keyboard: keyboard } } : {}),
309
+ },
310
+ }];
311
+ }
312
+ if (textContent.trim() || keyboard) {
313
+ actions.unshift({
314
+ method: 'sendMessage',
315
+ params: {
316
+ chat_id: chatId,
317
+ text: textContent.trim() || ' ',
318
+ ...replyParams(),
319
+ ...(keyboard ? { reply_markup: { inline_keyboard: keyboard } } : {}),
320
+ },
321
+ });
322
+ }
323
+ return actions;
324
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Agent tool deps for telegram.
3
+ * Endpoints register themselves on start; tools look up by config name / endpoint id.
4
+ */
5
+ import type { TelegramChatMember } from './protocol.js';
6
+ export interface TelegramAgentEndpoint {
7
+ pinMessage(chatId: number, messageId: number): Promise<boolean>;
8
+ unpinMessage(chatId: number, messageId?: number): Promise<boolean>;
9
+ setChatDescription(chatId: number, description: string): Promise<boolean>;
10
+ setMessageReaction(chatId: number, messageId: number, reaction: string): Promise<boolean>;
11
+ getChatMemberCount(chatId: number): Promise<number>;
12
+ getChatAdmins(chatId: number): Promise<TelegramChatMember[]>;
13
+ sendStickerMessage(chatId: number, sticker: string): Promise<{
14
+ message_id: number;
15
+ }>;
16
+ setChatPermissionsAll(chatId: number, permissions: Record<string, boolean | undefined>): Promise<boolean>;
17
+ createInviteLink(chatId: number): Promise<string>;
18
+ sendPoll(chatId: number, question: string, options: string[], isAnonymous?: boolean, allowsMultipleAnswers?: boolean): Promise<{
19
+ message_id: number;
20
+ }>;
21
+ }
22
+ export interface TelegramAgentDeps {
23
+ getEndpoint: (endpointId: string) => TelegramAgentEndpoint;
24
+ }
25
+ export declare function registerTelegramAgentEndpoint(endpointId: string, endpoint: TelegramAgentEndpoint): () => void;
26
+ /** Optional override used by tests / transitional callers. Pass `null` to clear. */
27
+ export declare function setTelegramAgentDeps(deps: TelegramAgentDeps | null): void;
28
+ export declare function getTelegramAgentDeps(): TelegramAgentDeps;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Agent tool deps for telegram.
3
+ * Endpoints register themselves on start; tools look up by config name / endpoint id.
4
+ */
5
+ const endpoints = new Map();
6
+ let override = null;
7
+ export function registerTelegramAgentEndpoint(endpointId, endpoint) {
8
+ endpoints.set(endpointId, endpoint);
9
+ return () => {
10
+ if (endpoints.get(endpointId) === endpoint) {
11
+ endpoints.delete(endpointId);
12
+ }
13
+ };
14
+ }
15
+ /** Optional override used by tests / transitional callers. Pass `null` to clear. */
16
+ export function setTelegramAgentDeps(deps) {
17
+ override = deps;
18
+ }
19
+ export function getTelegramAgentDeps() {
20
+ if (override)
21
+ return override;
22
+ return {
23
+ getEndpoint(endpointId) {
24
+ const registered = endpoints.get(endpointId);
25
+ if (!registered)
26
+ throw new Error(`Endpoint ${endpointId} 不存在`);
27
+ return registered;
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Telegram webhook HTTP: secret token → parse → handle update.
3
+ */
4
+ import type { IncomingMessage, ServerResponse } from 'node:http';
5
+ import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
6
+ import { type ResolvedTelegramConfig, type TelegramUpdate } from './protocol.js';
7
+ export interface TelegramWebhookHandler {
8
+ readonly config: ResolvedTelegramConfig;
9
+ readonly isOpen: boolean;
10
+ handleUpdate(update: TelegramUpdate): void;
11
+ }
12
+ export declare function registerTelegramWebhookRoutes(http: HttpHost, handler: TelegramWebhookHandler): HttpRouteRegistration[];
13
+ export declare function handleTelegramWebhookRequest(request: IncomingMessage, response: ServerResponse, handler: TelegramWebhookHandler): Promise<void>;
package/lib/webhook.js ADDED
@@ -0,0 +1,45 @@
1
+ import { getLogger } from '@zhin.js/logger';
2
+ import { readTextBody } from './protocol.js';
3
+ const logger = getLogger('telegram');
4
+ export function registerTelegramWebhookRoutes(http, handler) {
5
+ const path = handler.config.webhook.path;
6
+ return [
7
+ http.route('POST', path, async (request, response) => {
8
+ await handleTelegramWebhookRequest(request, response, handler);
9
+ }, { summary: 'Telegram Bot API webhook', tags: ['telegram'] }),
10
+ ];
11
+ }
12
+ export async function handleTelegramWebhookRequest(request, response, handler) {
13
+ try {
14
+ const secret = handler.config.webhook?.secretToken;
15
+ if (secret) {
16
+ const header = request.headers['x-telegram-bot-api-secret-token'];
17
+ const token = Array.isArray(header) ? header[0] : header;
18
+ if (token !== secret) {
19
+ response.writeHead(403, { 'Content-Type': 'application/json' });
20
+ response.end(JSON.stringify({ ok: false, description: 'Invalid secret token' }));
21
+ return;
22
+ }
23
+ }
24
+ const rawBody = await readTextBody(request);
25
+ let update;
26
+ try {
27
+ update = JSON.parse(rawBody);
28
+ }
29
+ catch {
30
+ response.writeHead(200, { 'Content-Type': 'application/json' });
31
+ response.end(JSON.stringify({ ok: true }));
32
+ return;
33
+ }
34
+ if (handler.isOpen) {
35
+ handler.handleUpdate(update);
36
+ }
37
+ response.writeHead(200, { 'Content-Type': 'application/json' });
38
+ response.end(JSON.stringify({ ok: true }));
39
+ }
40
+ catch (error) {
41
+ logger.error('Telegram webhook error:', error);
42
+ response.writeHead(200, { 'Content-Type': 'application/json' });
43
+ response.end(JSON.stringify({ ok: true }));
44
+ }
45
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-telegram",
3
- "version": "5.0.2",
4
- "description": "Zhin.js adapter for Telegram",
3
+ "version": "5.0.3",
4
+ "description": "Zhin.js Telegram Bot API adapter for Plugin Runtime (long-poll getUpdates)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -32,58 +32,47 @@
32
32
  "directory": "plugins/adapters/telegram"
33
33
  },
34
34
  "dependencies": {
35
- "telegraf": "^4.16.3"
36
- },
37
- "devDependencies": {
38
- "@types/node": "^26.1.0",
39
- "@types/react": "^19.2.17",
40
- "@types/react-dom": "^19.2.3",
41
- "lucide-react": "^1.22.0",
42
- "typescript": "^6.0.3",
43
- "zod": "^4.4.3",
44
- "@zhin.js/contract": "1.0.3",
45
- "@zhin.js/host-router": "2.0.3",
46
- "@zhin.js/client": "2.0.5",
47
- "@zhin.js/host-api": "2.0.5",
48
- "@zhin.js/cli": "1.0.93",
49
- "@zhin.js/logger": "1.0.74",
50
- "zhin.js": "4.1.2",
51
- "@zhin.js/agent": "1.0.3"
35
+ "@zhin.js/adapter": "1.0.1",
36
+ "@zhin.js/core": "1.3.5",
37
+ "@zhin.js/host-http": "1.0.1",
38
+ "@zhin.js/logger": "1.0.75",
39
+ "@zhin.js/plugin-runtime": "1.0.1"
52
40
  },
53
41
  "peerDependencies": {
54
42
  "zod": "^4.0.0",
55
- "@zhin.js/host-api": "2.0.5",
56
- "@zhin.js/contract": "1.0.3",
57
- "@zhin.js/client": "2.0.5",
58
- "@zhin.js/host-router": "2.0.3",
59
- "@zhin.js/agent": "1.0.3",
60
- "@zhin.js/logger": "1.0.74",
61
- "zhin.js": "4.1.2"
43
+ "@zhin.js/adapter": "1.0.1",
44
+ "@zhin.js/agent": "1.0.4",
45
+ "@zhin.js/core": "1.3.5",
46
+ "@zhin.js/plugin-runtime": "1.0.1",
47
+ "zhin.js": "4.1.3"
62
48
  },
63
49
  "peerDependenciesMeta": {
64
- "@zhin.js/agent": {
65
- "optional": true
66
- },
67
- "@zhin.js/client": {
50
+ "zhin.js": {
68
51
  "optional": true
69
52
  },
70
- "@zhin.js/host-router": {
71
- "optional": true
72
- },
73
- "@zhin.js/host-api": {
53
+ "@zhin.js/agent": {
74
54
  "optional": true
75
55
  },
76
56
  "zod": {
77
57
  "optional": true
78
58
  }
79
59
  },
60
+ "devDependencies": {
61
+ "@types/node": "^26.1.0",
62
+ "typescript": "^6.0.3",
63
+ "vitest": "^4.1.10",
64
+ "zod": "^4.4.3",
65
+ "@zhin.js/agent": "1.0.4",
66
+ "@zhin.js/host-http": "1.0.1",
67
+ "zhin.js": "4.1.3"
68
+ },
80
69
  "files": [
70
+ "adapters",
71
+ "plugin.ts",
72
+ "schema.json",
81
73
  "src",
82
74
  "lib",
83
- "client",
84
- "dist",
85
75
  "agent",
86
- "plugin.yml",
87
76
  "README.md",
88
77
  "CHANGELOG.md"
89
78
  ],
@@ -94,9 +83,23 @@
94
83
  "engines": {
95
84
  "node": "^20.19.0 || >=22.12.0"
96
85
  },
86
+ "zhin": {
87
+ "protocol": 1,
88
+ "type": "plugin",
89
+ "entry": "./plugin.ts",
90
+ "engine": "^1.0.0",
91
+ "runtime": "trusted",
92
+ "features": [
93
+ {
94
+ "package": "@zhin.js/adapter",
95
+ "api": "^1.0.0"
96
+ }
97
+ ],
98
+ "plugins": []
99
+ },
97
100
  "scripts": {
98
- "build": "zhin build",
99
- "clean": "rimraf lib dist",
100
- "build:node": "tsc"
101
+ "build": "tsc",
102
+ "clean": "rimraf lib",
103
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/telegram/tests"
101
104
  }
102
105
  }
package/plugin.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { definePlugin } from '@zhin.js/plugin-runtime';
2
+ import { registerTelegramPlatformPermitChecker } from './src/platform-permit.js';
3
+
4
+ export default definePlugin({
5
+ name: 'telegram',
6
+ metadata: {
7
+ displayName: 'Telegram Bot API Adapter',
8
+ },
9
+ setup() {
10
+ // 平台权限门禁:chat_creator / chat_administrator / pin_messages 等(agent 工具 platformPermit)
11
+ return registerTelegramPlatformPermitChecker();
12
+ },
13
+ });
package/schema.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "name": {
7
+ "type": "string",
8
+ "default": "telegram-bot"
9
+ },
10
+ "token": {
11
+ "type": "string"
12
+ },
13
+ "polling": {
14
+ "type": "boolean",
15
+ "default": true,
16
+ "description": "Long-poll getUpdates (default). Set false for webhook via httpHostToken."
17
+ },
18
+ "webhook": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "description": "Webhook settings when polling is false.",
22
+ "properties": {
23
+ "domain": { "type": "string" },
24
+ "path": { "type": "string", "default": "/telegram/webhook" },
25
+ "secretToken": { "type": "string", "description": "Verified via X-Telegram-Bot-Api-Secret-Token." }
26
+ }
27
+ },
28
+ "allowedUpdates": {
29
+ "type": "array",
30
+ "items": { "type": "string" },
31
+ "default": ["message", "callback_query"]
32
+ },
33
+ "apiBaseUrl": {
34
+ "type": "string",
35
+ "default": "https://api.telegram.org"
36
+ }
37
+ },
38
+ "required": ["token"]
39
+ }