@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,332 @@
1
+ import { formatCompact, getLogger } from '@zhin.js/logger';
2
+ import { runTelegramPollLoop } from './polling.js';
3
+ import { normalizeTelegramChatMember } from './platform-permit.js';
4
+ import { botApiUrl, buildWebhookUrl, formatCallbackContent, formatInboundContent, formatOutboundActions, resolveChannel, senderDisplayName, } from './protocol.js';
5
+ import { registerTelegramAgentEndpoint } from './telegram-agent-deps.js';
6
+ import { registerTelegramWebhookRoutes } from './webhook.js';
7
+ const logger = getLogger('telegram');
8
+ const CHAT_MEMBER_CACHE_TTL_MS = 60_000;
9
+ const CHAT_MEMBER_CACHE_MAX = 2_000;
10
+ export class TelegramEndpoint {
11
+ #options;
12
+ #fetch;
13
+ #pollAbort;
14
+ #pollPromise;
15
+ #routeReleases = [];
16
+ #open = false;
17
+ #started = false;
18
+ #unregisterAgent;
19
+ #updateOffset = 0;
20
+ #botUserId;
21
+ #botUsername;
22
+ #chatMemberCache = new Map();
23
+ constructor(options) {
24
+ this.#options = options;
25
+ this.#fetch = options.fetch ?? globalThis.fetch;
26
+ }
27
+ /** Used by webhook handler. */
28
+ get isOpen() {
29
+ return this.#open;
30
+ }
31
+ get config() {
32
+ return this.#options.config;
33
+ }
34
+ get allowedUpdates() {
35
+ return this.#options.config.allowedUpdates;
36
+ }
37
+ getUpdateOffset() {
38
+ return this.#updateOffset;
39
+ }
40
+ setUpdateOffset(offset) {
41
+ this.#updateOffset = offset;
42
+ }
43
+ async start() {
44
+ if (this.#started)
45
+ return;
46
+ this.#started = true;
47
+ try {
48
+ this.#unregisterAgent = registerTelegramAgentEndpoint(this.#options.config.name, this);
49
+ const me = await this.callApi('getMe');
50
+ this.#botUserId = me.id;
51
+ this.#botUsername = me.username;
52
+ if (this.#options.config.mode === 'webhook') {
53
+ if (!this.#options.http) {
54
+ throw new TypeError('Telegram webhook mode requires httpHostToken');
55
+ }
56
+ this.#routeReleases.push(...registerTelegramWebhookRoutes(this.#options.http, this));
57
+ const webhook = this.#options.config.webhook;
58
+ const url = buildWebhookUrl(webhook);
59
+ await this.callApi('setWebhook', {
60
+ url,
61
+ allowed_updates: this.#options.config.allowedUpdates,
62
+ ...(webhook.secretToken ? { secret_token: webhook.secretToken } : {}),
63
+ });
64
+ logger.info(formatCompact({
65
+ op: 'connect',
66
+ endpoint: this.#options.config.name,
67
+ mode: 'webhook',
68
+ path: webhook.path,
69
+ username: me.username,
70
+ }));
71
+ return;
72
+ }
73
+ await this.callApi('deleteWebhook', { drop_pending_updates: false });
74
+ this.#pollAbort = new AbortController();
75
+ this.#pollPromise = runTelegramPollLoop(this, this.#pollAbort.signal);
76
+ logger.info(formatCompact({
77
+ op: 'connect',
78
+ endpoint: this.#options.config.name,
79
+ mode: 'polling',
80
+ username: me.username,
81
+ }));
82
+ }
83
+ catch (error) {
84
+ await this.stop();
85
+ logger.error('Failed to connect Telegram bot:', error);
86
+ throw error;
87
+ }
88
+ }
89
+ open() {
90
+ this.#open = true;
91
+ }
92
+ close() {
93
+ this.#open = false;
94
+ }
95
+ async stop() {
96
+ this.#open = false;
97
+ this.#pollAbort?.abort();
98
+ try {
99
+ await this.#pollPromise;
100
+ }
101
+ catch {
102
+ /* poll loop exit */
103
+ }
104
+ for (const release of this.#routeReleases.splice(0))
105
+ release();
106
+ this.#unregisterAgent?.();
107
+ this.#unregisterAgent = undefined;
108
+ this.#chatMemberCache.clear();
109
+ this.#started = false;
110
+ logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
111
+ }
112
+ async send({ target, payload }) {
113
+ const actions = formatOutboundActions(target, payload);
114
+ let lastId = '';
115
+ for (const action of actions) {
116
+ const result = await this.callApi(action.method, action.params);
117
+ if (result.message_id != null)
118
+ lastId = String(result.message_id);
119
+ }
120
+ return lastId || `telegram-${Date.now()}`;
121
+ }
122
+ /** Test / internal: admit a message when open. */
123
+ admit(msg) {
124
+ if (!this.#open)
125
+ return;
126
+ const { channelId } = resolveChannel(msg);
127
+ void this.#admitWithSenderRole(msg, channelId).catch((err) => {
128
+ logger.warn(formatCompact({
129
+ op: 'telegram_gateway_receive_failed',
130
+ target: channelId,
131
+ error: err instanceof Error ? err.message : String(err),
132
+ }));
133
+ });
134
+ }
135
+ async #admitWithSenderRole(msg, channelId) {
136
+ const permit = await this.#resolveGroupSenderPermit(msg);
137
+ // 新 Runtime Message.content 为纯文本:@ 本机只能经 metadata 传递
138
+ const mentioned = this.#isBotMentioned(msg);
139
+ await this.#options.gateway.receive({
140
+ adapter: this.#options.id,
141
+ target: channelId,
142
+ content: formatInboundContent(msg),
143
+ sender: senderDisplayName(msg.from),
144
+ id: String(msg.message_id),
145
+ metadata: Object.freeze({
146
+ endpoint: this.#options.config.name,
147
+ chatType: msg.chat.type,
148
+ userId: msg.from?.id,
149
+ date: msg.date,
150
+ ...(permit?.role ? { senderRole: permit.role } : {}),
151
+ ...(permit?.permissions.length ? { senderPermissions: [...permit.permissions] } : {}),
152
+ ...(mentioned ? { mentioned: true } : {}),
153
+ }),
154
+ });
155
+ }
156
+ /** entities 里 mention 文本命中 bot username(getMe 缓存),或 text_mention 指向 bot 用户。 */
157
+ #isBotMentioned(msg) {
158
+ if (!msg.entities?.length)
159
+ return false;
160
+ for (const entity of msg.entities) {
161
+ if (entity.type === 'text_mention') {
162
+ if (this.#botUserId != null && entity.user?.id === this.#botUserId)
163
+ return true;
164
+ continue;
165
+ }
166
+ if (entity.type !== 'mention' || !this.#botUsername)
167
+ continue;
168
+ const slice = (msg.text ?? '').slice(entity.offset, entity.offset + entity.length);
169
+ if (slice.toLowerCase() === `@${this.#botUsername.toLowerCase()}`)
170
+ return true;
171
+ }
172
+ return false;
173
+ }
174
+ /** 群消息 sender role 解析:getChatMember + 60s 缓存(对齐旧 enrichGroupSender)。 */
175
+ async #resolveGroupSenderPermit(msg) {
176
+ if (msg.chat.type === 'private' || !msg.from?.id)
177
+ return undefined;
178
+ const chatId = Number(msg.chat.id);
179
+ const userId = msg.from.id;
180
+ const key = `${chatId}:${userId}`;
181
+ const now = Date.now();
182
+ this.#sweepChatMemberCache(now);
183
+ const cached = this.#chatMemberCache.get(key);
184
+ if (cached && now - cached.at < CHAT_MEMBER_CACHE_TTL_MS)
185
+ return cached;
186
+ try {
187
+ const member = await this.callApi('getChatMember', {
188
+ chat_id: chatId,
189
+ user_id: userId,
190
+ });
191
+ const normalized = normalizeTelegramChatMember(member);
192
+ const entry = { at: now, ...normalized };
193
+ this.#chatMemberCache.set(key, entry);
194
+ return entry;
195
+ }
196
+ catch {
197
+ // 保守拒绝:无角色快照
198
+ return undefined;
199
+ }
200
+ }
201
+ #sweepChatMemberCache(now) {
202
+ for (const [key, entry] of this.#chatMemberCache) {
203
+ if (now - entry.at >= CHAT_MEMBER_CACHE_TTL_MS)
204
+ this.#chatMemberCache.delete(key);
205
+ }
206
+ if (this.#chatMemberCache.size > CHAT_MEMBER_CACHE_MAX) {
207
+ const excess = this.#chatMemberCache.size - CHAT_MEMBER_CACHE_MAX;
208
+ let removed = 0;
209
+ for (const [key] of this.#chatMemberCache) {
210
+ if (removed >= excess)
211
+ break;
212
+ this.#chatMemberCache.delete(key);
213
+ removed++;
214
+ }
215
+ }
216
+ }
217
+ /** Test / internal: admit a callback query when open. */
218
+ admitCallback(query) {
219
+ if (!this.#open)
220
+ return;
221
+ const msg = query.message;
222
+ const channelId = msg ? resolveChannel(msg).channelId : String(query.from.id);
223
+ void this.#options.gateway.receive({
224
+ adapter: this.#options.id,
225
+ target: channelId,
226
+ content: formatCallbackContent(query),
227
+ sender: senderDisplayName(query.from),
228
+ id: query.id,
229
+ metadata: Object.freeze({
230
+ endpoint: this.#options.config.name,
231
+ eventType: 'callback_query',
232
+ payload: query.data,
233
+ sourceMessageId: msg ? String(msg.message_id) : undefined,
234
+ }),
235
+ }).catch((err) => {
236
+ logger.warn(formatCompact({
237
+ op: 'telegram_gateway_receive_failed',
238
+ target: channelId,
239
+ error: err instanceof Error ? err.message : String(err),
240
+ }));
241
+ });
242
+ }
243
+ /** Used by webhook / polling handlers. */
244
+ handleUpdate(update) {
245
+ if (update.message) {
246
+ this.admit(update.message);
247
+ return;
248
+ }
249
+ if (update.callback_query) {
250
+ const query = update.callback_query;
251
+ if (query.data) {
252
+ void this.callApi('answerCallbackQuery', { callback_query_id: query.id }).catch(() => {
253
+ /* already answered */
254
+ });
255
+ }
256
+ this.admitCallback(query);
257
+ }
258
+ }
259
+ async callApi(method, params = {}, signal) {
260
+ const url = botApiUrl(this.#options.config, method);
261
+ const response = await this.#fetch(url, {
262
+ method: 'POST',
263
+ headers: { 'Content-Type': 'application/json' },
264
+ body: JSON.stringify(params),
265
+ signal,
266
+ });
267
+ const text = await response.text();
268
+ let body;
269
+ try {
270
+ body = JSON.parse(text);
271
+ }
272
+ catch {
273
+ throw new Error(`Telegram API ${method} invalid JSON (${response.status}): ${text.slice(0, 200)}`);
274
+ }
275
+ if (!body.ok) {
276
+ throw new Error(`Telegram API ${method} failed (${body.error_code ?? response.status}): ${body.description ?? text}`);
277
+ }
278
+ return body.result;
279
+ }
280
+ // ── Agent tool surface ──────────────────────────────────────────────
281
+ async pinMessage(chatId, messageId) {
282
+ await this.callApi('pinChatMessage', { chat_id: chatId, message_id: messageId });
283
+ return true;
284
+ }
285
+ async unpinMessage(chatId, messageId) {
286
+ if (messageId != null) {
287
+ await this.callApi('unpinChatMessage', { chat_id: chatId, message_id: messageId });
288
+ }
289
+ else {
290
+ await this.callApi('unpinAllChatMessages', { chat_id: chatId });
291
+ }
292
+ return true;
293
+ }
294
+ async setChatDescription(chatId, description) {
295
+ await this.callApi('setChatDescription', { chat_id: chatId, description });
296
+ return true;
297
+ }
298
+ async setMessageReaction(chatId, messageId, reaction) {
299
+ await this.callApi('setMessageReaction', {
300
+ chat_id: chatId,
301
+ message_id: messageId,
302
+ reaction: [{ type: 'emoji', emoji: reaction }],
303
+ });
304
+ return true;
305
+ }
306
+ async getChatMemberCount(chatId) {
307
+ return this.callApi('getChatMemberCount', { chat_id: chatId });
308
+ }
309
+ async getChatAdmins(chatId) {
310
+ return this.callApi('getChatAdministrators', { chat_id: chatId });
311
+ }
312
+ async sendStickerMessage(chatId, sticker) {
313
+ return this.callApi('sendSticker', { chat_id: chatId, sticker });
314
+ }
315
+ async setChatPermissionsAll(chatId, permissions) {
316
+ await this.callApi('setChatPermissions', { chat_id: chatId, permissions });
317
+ return true;
318
+ }
319
+ async createInviteLink(chatId) {
320
+ const link = await this.callApi('createChatInviteLink', { chat_id: chatId });
321
+ return link.invite_link;
322
+ }
323
+ async sendPoll(chatId, question, options, isAnonymous = true, allowsMultipleAnswers = false) {
324
+ return this.callApi('sendPoll', {
325
+ chat_id: chatId,
326
+ question,
327
+ options,
328
+ is_anonymous: isAnonymous,
329
+ allows_multiple_answers: allowsMultipleAnswers,
330
+ });
331
+ }
332
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { TelegramEndpoint, type TelegramEndpointOptions, type TelegramFetch, } from './endpoint.js';
2
+ export { botApiUrl, formatCallbackContent, formatInboundContent, formatOutboundActions, normalizeWebhookPath, resolveChannel, resolveTelegramConfig, senderDisplayName, type ResolvedTelegramConfig, type TelegramAdapterConfig, type TelegramCallbackQuery, type TelegramChat, type TelegramChatMember, type TelegramMessage, type TelegramOutboundAction, type TelegramUpdate, type TelegramUser, type TelegramWireSegment, } from './protocol.js';
3
+ export { getTelegramAgentDeps, registerTelegramAgentEndpoint, setTelegramAgentDeps, type TelegramAgentDeps, type TelegramAgentEndpoint, } from './telegram-agent-deps.js';
4
+ export { checkTelegramPlatformPermit, normalizeTelegramChatMember, platformPermit, registerTelegramPlatformPermitChecker, telegramGroupPermitResolver, } from './platform-permit.js';
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { TelegramEndpoint, } from './endpoint.js';
2
+ export { botApiUrl, formatCallbackContent, formatInboundContent, formatOutboundActions, normalizeWebhookPath, resolveChannel, resolveTelegramConfig, senderDisplayName, } from './protocol.js';
3
+ export { getTelegramAgentDeps, registerTelegramAgentEndpoint, setTelegramAgentDeps, } from './telegram-agent-deps.js';
4
+ export { checkTelegramPlatformPermit, normalizeTelegramChatMember, platformPermit, registerTelegramPlatformPermitChecker, telegramGroupPermitResolver, } from './platform-permit.js';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Telegram platform permit — ChatMember 状态 + 细粒度权限
3
+ */
4
+ import { type Message } from '@zhin.js/core';
5
+ export declare function platformPermit(perm: string): string;
6
+ export declare function telegramGroupPermitResolver(logicalPerm: string): string;
7
+ export declare function normalizeTelegramChatMember(member: {
8
+ status?: string;
9
+ can_restrict_members?: boolean;
10
+ can_pin_messages?: boolean;
11
+ can_delete_messages?: boolean;
12
+ can_manage_chat?: boolean;
13
+ }): {
14
+ role?: string;
15
+ permissions: string[];
16
+ };
17
+ export declare function checkTelegramPlatformPermit(perm: string, message: Message<any>): boolean;
18
+ export declare function registerTelegramPlatformPermitChecker(): () => void;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Telegram platform permit — ChatMember 状态 + 细粒度权限
3
3
  */
4
- import { registerPlatformPermitChecker } from 'zhin.js';
4
+ import { registerPlatformPermitChecker } from '@zhin.js/core';
5
5
  const ADAPTER = 'telegram';
6
6
  export function platformPermit(perm) {
7
7
  return `platform(${ADAPTER},${perm})`;
@@ -59,4 +59,3 @@ export function checkTelegramPlatformPermit(perm, message) {
59
59
  export function registerTelegramPlatformPermitChecker() {
60
60
  return registerPlatformPermitChecker(ADAPTER, checkTelegramPlatformPermit);
61
61
  }
62
- //# sourceMappingURL=platform-permit.js.map
@@ -0,0 +1,9 @@
1
+ import type { TelegramUpdate } from './protocol.js';
2
+ export interface TelegramPollingHost {
3
+ readonly allowedUpdates: readonly string[];
4
+ callApi<T>(method: string, params?: Record<string, unknown>, signal?: AbortSignal): Promise<T>;
5
+ getUpdateOffset(): number;
6
+ setUpdateOffset(offset: number): void;
7
+ handleUpdate(update: TelegramUpdate): void;
8
+ }
9
+ export declare function runTelegramPollLoop(host: TelegramPollingHost, abortSignal: AbortSignal): Promise<void>;
package/lib/polling.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Telegram long-polling loop for getUpdates.
3
+ */
4
+ import { formatCompact, getLogger } from '@zhin.js/logger';
5
+ const logger = getLogger('telegram');
6
+ const RETRY_DELAY_MS = 2_000;
7
+ const BACKOFF_DELAY_MS = 10_000;
8
+ const MAX_CONSECUTIVE_FAILURES = 5;
9
+ const DEFAULT_POLL_TIMEOUT_SEC = 30;
10
+ export async function runTelegramPollLoop(host, abortSignal) {
11
+ let consecutiveFailures = 0;
12
+ while (!abortSignal.aborted) {
13
+ try {
14
+ const updates = await host.callApi('getUpdates', {
15
+ offset: host.getUpdateOffset() || undefined,
16
+ timeout: DEFAULT_POLL_TIMEOUT_SEC,
17
+ allowed_updates: host.allowedUpdates,
18
+ }, abortSignal);
19
+ consecutiveFailures = 0;
20
+ for (const update of updates) {
21
+ host.setUpdateOffset(update.update_id + 1);
22
+ host.handleUpdate(update);
23
+ }
24
+ }
25
+ catch (err) {
26
+ if (abortSignal.aborted)
27
+ return;
28
+ consecutiveFailures += 1;
29
+ logger.error(formatCompact({
30
+ op: 'poll',
31
+ ok: false,
32
+ error: err instanceof Error ? err.message : String(err),
33
+ }));
34
+ await sleep(consecutiveFailures >= MAX_CONSECUTIVE_FAILURES ? BACKOFF_DELAY_MS : RETRY_DELAY_MS, abortSignal);
35
+ if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES)
36
+ consecutiveFailures = 0;
37
+ }
38
+ }
39
+ }
40
+ function sleep(ms, signal) {
41
+ return new Promise((resolve) => {
42
+ if (signal?.aborted) {
43
+ resolve();
44
+ return;
45
+ }
46
+ const timer = setTimeout(resolve, ms);
47
+ signal?.addEventListener('abort', () => {
48
+ clearTimeout(timer);
49
+ resolve();
50
+ }, { once: true });
51
+ });
52
+ }
@@ -0,0 +1,250 @@
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
+ import type { IncomingMessage } from 'node:http';
6
+ /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
7
+ export interface TelegramAdapterConfig {
8
+ readonly name?: string;
9
+ readonly token?: string;
10
+ /** Default true. `false` selects webhook mode (requires httpHostToken). */
11
+ readonly polling?: boolean;
12
+ readonly webhook?: {
13
+ readonly domain?: string;
14
+ readonly path?: string;
15
+ readonly secretToken?: string;
16
+ };
17
+ readonly allowedUpdates?: readonly string[];
18
+ readonly apiBaseUrl?: string;
19
+ /** Transitional: legacy root `endpoints[]` with `context: telegram`. */
20
+ readonly endpoints?: ReadonlyArray<Partial<ResolvedTelegramConfig> & {
21
+ readonly context?: string;
22
+ readonly polling?: boolean;
23
+ readonly webhook?: TelegramAdapterConfig['webhook'];
24
+ readonly allowedUpdates?: readonly string[];
25
+ readonly apiBaseUrl?: string;
26
+ }>;
27
+ }
28
+ export interface ResolvedTelegramConfig {
29
+ readonly context: 'telegram';
30
+ readonly name: string;
31
+ readonly token: string;
32
+ readonly mode: 'polling' | 'webhook';
33
+ readonly allowedUpdates: readonly string[];
34
+ readonly apiBaseUrl: string;
35
+ readonly webhook?: {
36
+ readonly domain: string;
37
+ readonly path: string;
38
+ readonly secretToken?: string;
39
+ };
40
+ }
41
+ export interface TelegramUser {
42
+ readonly id: number;
43
+ readonly is_bot?: boolean;
44
+ readonly first_name?: string;
45
+ readonly last_name?: string;
46
+ readonly username?: string;
47
+ }
48
+ export interface TelegramChat {
49
+ readonly id: number;
50
+ readonly type: 'private' | 'group' | 'supergroup' | 'channel';
51
+ readonly title?: string;
52
+ readonly username?: string;
53
+ }
54
+ export interface TelegramMessageEntity {
55
+ readonly type: string;
56
+ readonly offset: number;
57
+ readonly length: number;
58
+ readonly url?: string;
59
+ readonly user?: TelegramUser;
60
+ }
61
+ export interface TelegramPhotoSize {
62
+ readonly file_id: string;
63
+ readonly file_unique_id?: string;
64
+ readonly width?: number;
65
+ readonly height?: number;
66
+ readonly file_size?: number;
67
+ }
68
+ export interface TelegramMessage {
69
+ readonly message_id: number;
70
+ readonly date: number;
71
+ readonly chat: TelegramChat;
72
+ readonly from?: TelegramUser;
73
+ readonly text?: string;
74
+ readonly caption?: string;
75
+ readonly entities?: readonly TelegramMessageEntity[];
76
+ readonly reply_to_message?: TelegramMessage;
77
+ readonly photo?: readonly TelegramPhotoSize[];
78
+ readonly video?: {
79
+ readonly file_id: string;
80
+ readonly file_unique_id?: string;
81
+ readonly width?: number;
82
+ readonly height?: number;
83
+ readonly duration?: number;
84
+ readonly file_size?: number;
85
+ };
86
+ readonly audio?: {
87
+ readonly file_id: string;
88
+ readonly file_unique_id?: string;
89
+ readonly duration?: number;
90
+ readonly performer?: string;
91
+ readonly title?: string;
92
+ readonly file_size?: number;
93
+ };
94
+ readonly voice?: {
95
+ readonly file_id: string;
96
+ readonly file_unique_id?: string;
97
+ readonly duration?: number;
98
+ readonly file_size?: number;
99
+ };
100
+ readonly document?: {
101
+ readonly file_id: string;
102
+ readonly file_unique_id?: string;
103
+ readonly file_name?: string;
104
+ readonly mime_type?: string;
105
+ readonly file_size?: number;
106
+ };
107
+ readonly sticker?: {
108
+ readonly file_id: string;
109
+ readonly file_unique_id?: string;
110
+ readonly width?: number;
111
+ readonly height?: number;
112
+ readonly is_animated?: boolean;
113
+ readonly is_video?: boolean;
114
+ readonly emoji?: string;
115
+ };
116
+ readonly location?: {
117
+ readonly longitude: number;
118
+ readonly latitude: number;
119
+ };
120
+ }
121
+ export interface TelegramCallbackQuery {
122
+ readonly id: string;
123
+ readonly from: TelegramUser;
124
+ readonly data?: string;
125
+ readonly message?: TelegramMessage;
126
+ }
127
+ export interface TelegramUpdate {
128
+ readonly update_id: number;
129
+ readonly message?: TelegramMessage;
130
+ readonly edited_message?: TelegramMessage;
131
+ readonly callback_query?: TelegramCallbackQuery;
132
+ }
133
+ export interface TelegramChatMember {
134
+ readonly status: string;
135
+ readonly user: TelegramUser;
136
+ readonly can_restrict_members?: boolean;
137
+ readonly can_pin_messages?: boolean;
138
+ readonly can_delete_messages?: boolean;
139
+ readonly can_manage_chat?: boolean;
140
+ }
141
+ export interface TelegramWireSegment {
142
+ readonly type: string;
143
+ readonly data?: Record<string, unknown>;
144
+ }
145
+ export interface TelegramInlineButton {
146
+ readonly text: string;
147
+ readonly callback_data: string;
148
+ }
149
+ export type TelegramOutboundAction = {
150
+ readonly method: 'sendMessage';
151
+ readonly params: {
152
+ readonly chat_id: number | string;
153
+ readonly text: string;
154
+ readonly reply_parameters?: {
155
+ readonly message_id: number;
156
+ };
157
+ readonly reply_markup?: {
158
+ readonly inline_keyboard: TelegramInlineButton[][];
159
+ };
160
+ };
161
+ } | {
162
+ readonly method: 'sendPhoto';
163
+ readonly params: {
164
+ readonly chat_id: number | string;
165
+ readonly photo: string;
166
+ readonly caption?: string;
167
+ readonly reply_parameters?: {
168
+ readonly message_id: number;
169
+ };
170
+ };
171
+ } | {
172
+ readonly method: 'sendVideo';
173
+ readonly params: {
174
+ readonly chat_id: number | string;
175
+ readonly video: string;
176
+ readonly caption?: string;
177
+ readonly reply_parameters?: {
178
+ readonly message_id: number;
179
+ };
180
+ };
181
+ } | {
182
+ readonly method: 'sendAudio';
183
+ readonly params: {
184
+ readonly chat_id: number | string;
185
+ readonly audio: string;
186
+ readonly caption?: string;
187
+ readonly reply_parameters?: {
188
+ readonly message_id: number;
189
+ };
190
+ };
191
+ } | {
192
+ readonly method: 'sendVoice';
193
+ readonly params: {
194
+ readonly chat_id: number | string;
195
+ readonly voice: string;
196
+ readonly caption?: string;
197
+ readonly reply_parameters?: {
198
+ readonly message_id: number;
199
+ };
200
+ };
201
+ } | {
202
+ readonly method: 'sendDocument';
203
+ readonly params: {
204
+ readonly chat_id: number | string;
205
+ readonly document: string;
206
+ readonly caption?: string;
207
+ readonly reply_parameters?: {
208
+ readonly message_id: number;
209
+ };
210
+ };
211
+ } | {
212
+ readonly method: 'sendSticker';
213
+ readonly params: {
214
+ readonly chat_id: number | string;
215
+ readonly sticker: string;
216
+ readonly reply_parameters?: {
217
+ readonly message_id: number;
218
+ };
219
+ };
220
+ } | {
221
+ readonly method: 'sendLocation';
222
+ readonly params: {
223
+ readonly chat_id: number | string;
224
+ readonly latitude: number;
225
+ readonly longitude: number;
226
+ readonly reply_parameters?: {
227
+ readonly message_id: number;
228
+ };
229
+ };
230
+ };
231
+ export declare function resolveTelegramConfig(config?: TelegramAdapterConfig): ResolvedTelegramConfig;
232
+ export declare function normalizeWebhookPath(path: string): string;
233
+ export declare function buildWebhookUrl(webhook: NonNullable<ResolvedTelegramConfig['webhook']>): string;
234
+ export declare function readTextBody(request: IncomingMessage, options?: {
235
+ readonly limit?: number;
236
+ }): Promise<string>;
237
+ export declare function botApiUrl(config: Pick<ResolvedTelegramConfig, 'apiBaseUrl' | 'token'>, method: string): string;
238
+ export declare function resolveChannel(msg: Pick<TelegramMessage, 'chat'>): {
239
+ readonly channelType: 'private' | 'group';
240
+ readonly channelId: string;
241
+ };
242
+ export declare function senderDisplayName(user?: TelegramUser): string;
243
+ /** Build inbound text for MessageGateway.receive. */
244
+ export declare function formatInboundContent(msg: TelegramMessage): string;
245
+ export declare function formatCallbackContent(query: TelegramCallbackQuery): string;
246
+ /**
247
+ * Wire-encode an already-rendered outbound payload into Telegram Bot API actions.
248
+ * Segment canonicalization is intentionally not done here.
249
+ */
250
+ export declare function formatOutboundActions(target: string | number, payload: unknown): TelegramOutboundAction[];