@krischoichoi/channel-telegram 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +129 -0
  2. package/lib/adapter.d.ts +94 -0
  3. package/lib/adapter.d.ts.map +1 -0
  4. package/lib/adapter.js +417 -0
  5. package/lib/adapter.js.map +1 -0
  6. package/lib/api-error.d.ts +74 -0
  7. package/lib/api-error.d.ts.map +1 -0
  8. package/lib/api-error.js +103 -0
  9. package/lib/api-error.js.map +1 -0
  10. package/lib/config.d.ts +115 -0
  11. package/lib/config.d.ts.map +1 -0
  12. package/lib/config.js +67 -0
  13. package/lib/config.js.map +1 -0
  14. package/lib/definition.d.ts +59 -0
  15. package/lib/definition.d.ts.map +1 -0
  16. package/lib/definition.js +158 -0
  17. package/lib/definition.js.map +1 -0
  18. package/lib/inbound.d.ts +61 -0
  19. package/lib/inbound.d.ts.map +1 -0
  20. package/lib/inbound.js +125 -0
  21. package/lib/inbound.js.map +1 -0
  22. package/lib/index.d.ts +52 -0
  23. package/lib/index.d.ts.map +1 -0
  24. package/lib/index.js +114 -0
  25. package/lib/index.js.map +1 -0
  26. package/lib/manifest.d.ts +16 -0
  27. package/lib/manifest.d.ts.map +1 -0
  28. package/lib/manifest.js +26 -0
  29. package/lib/manifest.js.map +1 -0
  30. package/lib/mapper.d.ts +51 -0
  31. package/lib/mapper.d.ts.map +1 -0
  32. package/lib/mapper.js +314 -0
  33. package/lib/mapper.js.map +1 -0
  34. package/lib/media-hydrator.d.ts +46 -0
  35. package/lib/media-hydrator.d.ts.map +1 -0
  36. package/lib/media-hydrator.js +85 -0
  37. package/lib/media-hydrator.js.map +1 -0
  38. package/lib/outbound.d.ts +42 -0
  39. package/lib/outbound.d.ts.map +1 -0
  40. package/lib/outbound.js +215 -0
  41. package/lib/outbound.js.map +1 -0
  42. package/lib/render/html.d.ts +13 -0
  43. package/lib/render/html.d.ts.map +1 -0
  44. package/lib/render/html.js +110 -0
  45. package/lib/render/html.js.map +1 -0
  46. package/lib/render/index.d.ts +64 -0
  47. package/lib/render/index.d.ts.map +1 -0
  48. package/lib/render/index.js +76 -0
  49. package/lib/render/index.js.map +1 -0
  50. package/lib/render/markdown.d.ts +38 -0
  51. package/lib/render/markdown.d.ts.map +1 -0
  52. package/lib/render/markdown.js +187 -0
  53. package/lib/render/markdown.js.map +1 -0
  54. package/lib/render/plain.d.ts +43 -0
  55. package/lib/render/plain.d.ts.map +1 -0
  56. package/lib/render/plain.js +97 -0
  57. package/lib/render/plain.js.map +1 -0
  58. package/lib/render/segment.d.ts +39 -0
  59. package/lib/render/segment.d.ts.map +1 -0
  60. package/lib/render/segment.js +117 -0
  61. package/lib/render/segment.js.map +1 -0
  62. package/lib/rich-message.d.ts +75 -0
  63. package/lib/rich-message.d.ts.map +1 -0
  64. package/lib/rich-message.js +11 -0
  65. package/lib/rich-message.js.map +1 -0
  66. package/lib/rich-streaming-reply.d.ts +69 -0
  67. package/lib/rich-streaming-reply.d.ts.map +1 -0
  68. package/lib/rich-streaming-reply.js +207 -0
  69. package/lib/rich-streaming-reply.js.map +1 -0
  70. package/lib/streaming-reply.d.ts +98 -0
  71. package/lib/streaming-reply.d.ts.map +1 -0
  72. package/lib/streaming-reply.js +274 -0
  73. package/lib/streaming-reply.js.map +1 -0
  74. package/lib/transport.d.ts +38 -0
  75. package/lib/transport.d.ts.map +1 -0
  76. package/lib/transport.js +91 -0
  77. package/lib/transport.js.map +1 -0
  78. package/lib/upstream.d.ts +174 -0
  79. package/lib/upstream.d.ts.map +1 -0
  80. package/lib/upstream.js +452 -0
  81. package/lib/upstream.js.map +1 -0
  82. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @krischoichoi/channel-telegram
2
+
3
+ Telegram Bot API channel adapter for DeepSeek Harness.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @krischoichoi/channel-telegram
9
+ ```
10
+
11
+ ## Bundled community channel
12
+
13
+ This package is part of the community-maintained `@krischoichoi/dsh-channels` bundle.
14
+ It is not an official DeepSeek Harness or Telegram package.
15
+ It implements the same Channel Contract as weixin / qq / dingtalk / lark,
16
+ with no changes to channel-core, channel-harness or the other adapters.
17
+ Setup and credentials go through the Channel Control Plane; the bot token is
18
+ stored via `ctx.credentials` (`tokenRef`), never in profile config.
19
+
20
+ > The bundle patch inserts this adapter as `channels-telegram` and the Web settings
21
+ > panel shows it with the other bundled channels.
22
+
23
+
24
+ Contract coverage:
25
+
26
+ - `ChannelAdapter` contract (`start` / `stop` / `send` / `getHealth` / capabilities)
27
+ - `runChannelAdapterContract` — the testkit's full contract suite passes
28
+ - Cordis plugin shape (`name` / `inject` / `apply` via `ctx.effect`)
29
+ - Schemastery `Config` with an injectable `HttpTransport` (offline fake in tests)
30
+ - Fixture-driven mapper tests (`fixtures/telegram/*.json`, Bot API 10.2 shapes)
31
+ - M4 governance: `readonly manifest` class field + `manifest.ts` for
32
+ `channels doctor` compatibility checks
33
+
34
+ The adapter remains `experimental` until its real-platform live gate passes;
35
+ offline contract tests and fixtures do not by themselves justify `tested`.
36
+
37
+ ## Pointing it at a real bot
38
+
39
+ In your profile config:
40
+
41
+ ```json
42
+ {
43
+ "plugins": {
44
+ "channel-telegram": {
45
+ "enabled": true,
46
+ "accountId": "main",
47
+ "baseUrl": "https://api.telegram.org",
48
+ "tokenRef": "TELEGRAM_BOT_TOKEN",
49
+ "longPollTimeoutMs": 25000
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ The token is a **secret** resolved through `ctx.credentials` (`tokenRef`): it is never logged (bearer-style path segments are
56
+ redacted in transport error messages) and it only ever appears in the Bot API
57
+ request path built by the upstream driver. It is never written into fixture
58
+ files.
59
+
60
+ Inbound delivery currently uses Telegram Bot API long polling (`getUpdates`),
61
+ matching OpenClaw's local-install default. Startup removes an existing webhook
62
+ before polling because Telegram makes webhook and `getUpdates` delivery mutually
63
+ exclusive. This is an operational takeover of the Bot's update receiver: do not
64
+ reuse the same Bot for another webhook consumer. A hosted webhook transport is
65
+ not implemented yet.
66
+
67
+ ## Capabilities
68
+
69
+ | capability | value |
70
+ | ------------ | --------- |
71
+ | text / image / file / audio / video | ✅ |
72
+ | markdown | ✅ Rich Markdown |
73
+ | reactions | ❌ |
74
+ | cards | ❌ |
75
+ | threads | ✅ |
76
+ | interactive actions | ✅ (inline keyboard buttons via `callback_query`) |
77
+ | streaming | DM Rich Draft; group plain preview + rich final edit; set `streaming.enabled: false` for buffered |
78
+
79
+ ## Known limits
80
+
81
+ - Minimum supported upstream is Telegram Bot API 10.2. Older or pinned custom
82
+ Bot API servers are not supported; use `formatting.mode: plain` only as an
83
+ explicit presentation choice, not as an old-server compatibility mode.
84
+ - `getUpdates` subscribes to `message` and `callback_query`. Button interactions
85
+ are currently intended only for callback queries carrying `message.chat`;
86
+ inline-message callbacks without chat context are not a supported routing
87
+ surface and require a mapper hardening change before release.
88
+ - The ordinary message mapper still needs a complete zod trust-boundary schema;
89
+ the current partial envelope validation and TypeScript casts are an identified
90
+ release blocker, not evidence that arbitrary Telegram updates are supported.
91
+ - Media sends validate the Bot API `ok` envelope and returned `message_id`; an
92
+ `ok: false` response is surfaced as a delivery failure. Offline coverage does
93
+ not replace the required real-bot live gate.
94
+ - Inbound image, file, audio, and video media hydrate bytes through
95
+ `getFile`/download when declared as byte-capable. Failures retain
96
+ `resourceRef` plus ingress-failure metadata.
97
+ - Telegram albums (`media_group_id`) are intentionally delivered one update at
98
+ a time. Each image is downloaded, dispatched, retried and acknowledged
99
+ independently; no cross-update buffering or delayed album aggregation is
100
+ performed.
101
+ - Media captions are preserved as a text part before the image or document, so
102
+ the model receives both the caption and the shared attachment representation.
103
+ - Media outbound accepts trusted `localData` via `multipart/form-data`, a
104
+ public `url`, or a platform `file_id` (`resourceRef`). Multiple supported
105
+ media parts are sent sequentially; an unsupported part fails the send rather
106
+ than being silently dropped.
107
+ - Inbound media maps Telegram `file_id` to the contract's `resourceRef` carrier
108
+ (an opaque platform handle), never to `url` — `url` is reserved for real
109
+ `http(s)` URLs.
110
+ - `beginAuth`/`pollAuth` are omitted: auth is token-driven (getMe() check at start).
111
+ - Forum topics preserve `message_thread_id`; `chat.type === 'channel'`
112
+ currently maps to a `dm` conversation.
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ pnpm --filter @krischoichoi/channel-telegram build
118
+ pnpm --filter @krischoichoi/channel-telegram typecheck
119
+ pnpm --filter @krischoichoi/channel-telegram test
120
+ ```
121
+
122
+ ## Related
123
+
124
+ - [Repository root](../../README.md)
125
+ - [Adapter authoring guide](../../docs/adapter-authoring.md)
126
+
127
+ ## License
128
+
129
+ [MIT](../../LICENSE)
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Telegram channel adapter.
3
+ *
4
+ * Maps the Telegram Bot API (through the upstream driver) to the Channel
5
+ * Contract. All network/lifecycle resources live behind the upstream driver
6
+ * and are aborted via the adapter context signal. The adapter never touches
7
+ * Harness Agent APIs.
8
+ *
9
+ * Auth is token-driven: when a token is resolved (via `deps.token`, the
10
+ * credential seam, or the deprecated plaintext `config.token`), `start`
11
+ * verifies it with getMe() and launches the long-poll receive loop; without a
12
+ * token the adapter reports health 'down' and skips the loop entirely.
13
+ * `beginAuth`/`pollAuth` are intentionally not implemented — there is no
14
+ * interactive flow in V1; token rotation can slot in behind the same optional
15
+ * contract methods later.
16
+ *
17
+ * Streaming is `edit`: ReplyRouter opens a `TelegramStreamingReply` which sends
18
+ * one message and edits it in place with `editMessageText` as full-text
19
+ * previews arrive. Set `config.streaming.enabled: false` to force the buffered
20
+ * send-once strategy.
21
+ */
22
+ import type { ChannelAdapter, ChannelAdapterContext, ChannelCapabilities, ChannelHealth, ChannelTarget, CreateReplyOptions, OutboundMessage, ReplyHandle, SendResult, StreamingMode } from '@krischoichoi/channel-core';
23
+ import type { TelegramConfig } from './config.js';
24
+ import { type HttpTransport } from './transport.js';
25
+ import { type TelegramManifest } from './manifest.js';
26
+ export interface TelegramAdapterDeps {
27
+ transport?: HttpTransport;
28
+ /**
29
+ * Resolved Telegram Bot API token. The plugin resolves it via
30
+ * `ctx.credentials` and hands it in; profile config never carries it.
31
+ */
32
+ token?: string;
33
+ /** Injectable clock (tests). */
34
+ now?: () => number;
35
+ }
36
+ export declare class TelegramAdapter implements ChannelAdapter {
37
+ private readonly config;
38
+ readonly id = "telegram";
39
+ /** Upstream compatibility manifest (read structurally by `channels doctor`). */
40
+ readonly manifest: TelegramManifest;
41
+ readonly capabilities: ChannelCapabilities;
42
+ readonly outboxCapabilities: {
43
+ proactiveText: boolean;
44
+ proactiveMedia: boolean;
45
+ };
46
+ private ctx?;
47
+ private upstream;
48
+ private inbound;
49
+ private outbound;
50
+ private started;
51
+ private stopped;
52
+ private receiveLoop?;
53
+ private receiveAbort?;
54
+ private removeContextAbortListener?;
55
+ /** One start/refresh lifecycle per chat/topic target, including in-flight starts. */
56
+ private readonly typingStates;
57
+ private connected;
58
+ private authState;
59
+ /** Public bot identity used only for reliable inbound mention activation. */
60
+ private botIdentity?;
61
+ /** getUpdates acknowledgement cursor, shared with the driver across retries. */
62
+ private readonly cursor;
63
+ private readonly now;
64
+ /** Resolved token used by the upstream driver (deps credential wins over legacy config). */
65
+ private readonly token;
66
+ /** Resolved once so outbound, streaming and explicit edits use one policy. */
67
+ private readonly formattingMode;
68
+ constructor(config: TelegramConfig, deps?: TelegramAdapterDeps);
69
+ start(ctx: ChannelAdapterContext): Promise<void>;
70
+ private createInboundProcessor;
71
+ stop(): Promise<void>;
72
+ send(target: ChannelTarget, message: OutboundMessage): Promise<SendResult>;
73
+ /** Start Telegram's transient typing indicator and refresh it per chat/topic. */
74
+ startTypingForTarget(target: ChannelTarget): Promise<void>;
75
+ /** Telegram has no explicit stop action; clearing refresh lets it expire. */
76
+ stopTypingForTarget(target: ChannelTarget): Promise<void>;
77
+ private sendTyping;
78
+ private clearTypingStates;
79
+ resolveStreamingMode(target: ChannelTarget): StreamingMode;
80
+ createReply(target: ChannelTarget, _options?: CreateReplyOptions): Promise<ReplyHandle>;
81
+ /**
82
+ * Optional in-place edit of an already-sent message (plan §15.3): either
83
+ * update the interactive keyboard (`editMessageReplyMarkup`) or the text
84
+ * (`editMessageText`).
85
+ */
86
+ edit(target: ChannelTarget, messageId: string, message: OutboundMessage): Promise<SendResult>;
87
+ getHealth(): Promise<ChannelHealth>;
88
+ /** Long-poll receive loop with exponential backoff on failure. */
89
+ private startReceiveLoop;
90
+ private runReceiveLoop;
91
+ private emitAuth;
92
+ private emitConnection;
93
+ }
94
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
package/lib/adapter.js ADDED
@@ -0,0 +1,417 @@
1
+ import { ChannelError } from '@krischoichoi/channel-core';
2
+ import { FetchTransport } from './transport.js';
3
+ import { HttpTelegramUpstream, } from './upstream.js';
4
+ import { InboundProcessor } from './inbound.js';
5
+ import { OutboundSender } from './outbound.js';
6
+ import { TelegramStreamingReply } from './streaming-reply.js';
7
+ import { TelegramRichStreamingReply } from './rich-streaming-reply.js';
8
+ import { actionsToReplyMarkup } from './outbound.js';
9
+ import { manifest as telegramManifest } from './manifest.js';
10
+ import { resolveMode } from './render/index.js';
11
+ export class TelegramAdapter {
12
+ config;
13
+ id = 'telegram';
14
+ /** Upstream compatibility manifest (read structurally by `channels doctor`). */
15
+ manifest = telegramManifest;
16
+ capabilities = {
17
+ text: true,
18
+ image: true,
19
+ file: true,
20
+ audio: true,
21
+ video: true,
22
+ // Rich Markdown (Bot API 10.1/10.2 sendRichMessage / HTML / MarkdownV2) is
23
+ // genuinely rendered, not just parse_mode in a body (plan §Phase 3).
24
+ markdown: true,
25
+ cards: false,
26
+ reactions: false,
27
+ threads: true,
28
+ // Inline buttons + callback_query interactions are supported (plan §5).
29
+ interactiveActions: true,
30
+ // Telegram can edit sent messages with editMessageText and can stream rich
31
+ // drafts natively for DMs; `resolveStreamingMode` picks per target.
32
+ streaming: 'edit',
33
+ maxTextLength: 4096,
34
+ // Directional media precision. Inbound:
35
+ // every binary kind is hydrated to real bytes (`resourceRef` -> getFile ->
36
+ // /file/bot... -> localData) before emit. Outbound: sendPhoto /
37
+ // sendDocument / sendAudio / sendVideo all accept byte uploads
38
+ // (multipart) or references, so every kind can send real bytes.
39
+ // Platform-level size limits (e.g. Bot API cloud upload caps) are a
40
+ // separate concern from this directional declaration.
41
+ media: {
42
+ inbound: {
43
+ image: 'bytes',
44
+ file: 'bytes',
45
+ audio: 'bytes',
46
+ video: 'bytes',
47
+ },
48
+ outbound: {
49
+ image: 'bytes',
50
+ file: 'bytes',
51
+ audio: 'bytes',
52
+ video: 'bytes',
53
+ },
54
+ },
55
+ };
56
+ outboxCapabilities = { proactiveText: true, proactiveMedia: true };
57
+ ctx;
58
+ upstream;
59
+ inbound;
60
+ outbound;
61
+ started = false;
62
+ stopped = false;
63
+ receiveLoop;
64
+ receiveAbort;
65
+ removeContextAbortListener;
66
+ /** One start/refresh lifecycle per chat/topic target, including in-flight starts. */
67
+ typingStates = new Map();
68
+ connected = false;
69
+ authState = 'unauthenticated';
70
+ /** Public bot identity used only for reliable inbound mention activation. */
71
+ botIdentity;
72
+ /** getUpdates acknowledgement cursor, shared with the driver across retries. */
73
+ cursor = { offset: 0 };
74
+ now;
75
+ /** Resolved token used by the upstream driver (deps credential wins over legacy config). */
76
+ token;
77
+ /** Resolved once so outbound, streaming and explicit edits use one policy. */
78
+ formattingMode;
79
+ constructor(config, deps = {}) {
80
+ this.config = config;
81
+ this.now = deps.now ?? Date.now;
82
+ const transport = deps.transport ?? new FetchTransport(config.baseUrl, { timeoutMs: config.timeoutMs });
83
+ const token = deps.token ?? config.token;
84
+ this.upstream = new HttpTelegramUpstream({
85
+ transport,
86
+ token,
87
+ longPollTimeoutMs: config.longPollTimeoutMs,
88
+ });
89
+ this.token = token;
90
+ this.formattingMode = resolveMode(config.formatting.mode);
91
+ }
92
+ async start(ctx) {
93
+ if (this.started)
94
+ return;
95
+ this.ctx = ctx;
96
+ this.stopped = false;
97
+ this.connected = false;
98
+ this.botIdentity = undefined;
99
+ this.cursor.offset = 0;
100
+ this.inbound = this.createInboundProcessor();
101
+ this.receiveAbort = new AbortController();
102
+ const abortReceive = () => this.receiveAbort?.abort();
103
+ if (ctx.signal.aborted)
104
+ abortReceive();
105
+ else {
106
+ ctx.signal.addEventListener('abort', abortReceive, { once: true });
107
+ this.removeContextAbortListener = () => ctx.signal.removeEventListener('abort', abortReceive);
108
+ }
109
+ this.outbound = new OutboundSender(this.upstream, ctx.logger, {
110
+ formatting: { ...this.config.formatting, mode: this.formattingMode },
111
+ });
112
+ if (this.token) {
113
+ try {
114
+ const bot = await this.upstream.getMe();
115
+ this.botIdentity = {
116
+ id: bot.id,
117
+ ...(bot.username ? { username: bot.username } : {}),
118
+ };
119
+ // Polling starts only after getMe, so replace the pre-auth processor
120
+ // with one carrying the trusted identity used by mention detection.
121
+ this.inbound = this.createInboundProcessor();
122
+ this.authState = 'authenticated';
123
+ this.emitAuth('authenticated');
124
+ }
125
+ catch (error) {
126
+ this.authState = 'failed';
127
+ this.emitAuth('failed');
128
+ this.ctx.logger.error('[channel-telegram] auth check failed; receive loop disabled', error instanceof Error ? error.message : error);
129
+ this.started = true;
130
+ return;
131
+ }
132
+ try {
133
+ // getUpdates and webhooks are mutually exclusive. Preserve queued
134
+ // updates while switching this bot to the local polling transport.
135
+ await this.upstream.deleteWebhook();
136
+ this.emitConnection('connecting');
137
+ this.startReceiveLoop();
138
+ }
139
+ catch (error) {
140
+ this.ctx.logger.error('[channel-telegram] polling setup failed; receive loop disabled', error instanceof Error ? error.message : error);
141
+ this.emitConnection('disconnected');
142
+ }
143
+ }
144
+ else {
145
+ this.authState = 'unauthenticated';
146
+ this.emitAuth('unknown');
147
+ }
148
+ this.started = true;
149
+ }
150
+ createInboundProcessor() {
151
+ return new InboundProcessor({
152
+ ctx: this.ctx,
153
+ meta: {
154
+ channel: this.id,
155
+ accountId: this.config.accountId,
156
+ ...(this.botIdentity ? { bot: this.botIdentity } : {}),
157
+ },
158
+ dedupEnabled: this.config.dedup.enabled,
159
+ dedupWindowMs: this.config.dedup.windowMs,
160
+ files: this.upstream,
161
+ maxDownloadBytes: this.config.maxDownloadBytes,
162
+ now: this.now,
163
+ // Best-effort callback ACK (clears the button spinner; never blocks on
164
+ // agent resolution — plan §12.2).
165
+ ackCallback: (callbackId) => this.upstream.answerCallbackQuery({ callback_query_id: callbackId }),
166
+ });
167
+ }
168
+ async stop() {
169
+ if (!this.started || this.stopped)
170
+ return;
171
+ this.stopped = true;
172
+ this.started = false;
173
+ this.connected = false;
174
+ this.clearTypingStates();
175
+ this.receiveAbort?.abort();
176
+ this.receiveAbort = undefined;
177
+ this.removeContextAbortListener?.();
178
+ this.removeContextAbortListener = undefined;
179
+ const loop = this.receiveLoop;
180
+ this.receiveLoop = undefined;
181
+ if (loop)
182
+ await loop.catch(() => undefined);
183
+ this.emitConnection('closed');
184
+ }
185
+ async send(target, message) {
186
+ if (!this.started || !this.outbound) {
187
+ throw new ChannelError('CHANNEL_NOT_STARTED', 'telegram adapter is not started');
188
+ }
189
+ return this.outbound.send(target, message);
190
+ }
191
+ /** Start Telegram's transient typing indicator and refresh it per chat/topic. */
192
+ async startTypingForTarget(target) {
193
+ if (!this.started || this.stopped || !this.config.typing.enabled)
194
+ return;
195
+ const key = typingKey(target);
196
+ if (this.typingStates.has(key))
197
+ return;
198
+ const state = {};
199
+ this.typingStates.set(key, state);
200
+ // Let the lifecycle caller decide how to handle an immediate failure. Do
201
+ // not create a refresh timer unless Telegram accepted the initial action.
202
+ try {
203
+ await this.sendTyping(target);
204
+ if (!this.started || this.stopped || this.typingStates.get(key) !== state)
205
+ return;
206
+ state.timer = setInterval(() => {
207
+ // Refresh failures are isolated from the timer; the harness owns the
208
+ // best-effort boundary for the immediate action below.
209
+ void this.sendTyping(target).catch((error) => {
210
+ this.ctx?.logger.debug('[channel-telegram] typing refresh failed', error instanceof Error ? error.message : error);
211
+ });
212
+ }, this.config.typing.refreshMs);
213
+ }
214
+ catch (error) {
215
+ if (this.typingStates.get(key) === state)
216
+ this.typingStates.delete(key);
217
+ throw error;
218
+ }
219
+ }
220
+ /** Telegram has no explicit stop action; clearing refresh lets it expire. */
221
+ async stopTypingForTarget(target) {
222
+ const key = typingKey(target);
223
+ const state = this.typingStates.get(key);
224
+ if (!state)
225
+ return;
226
+ this.typingStates.delete(key);
227
+ if (state.timer)
228
+ clearInterval(state.timer);
229
+ }
230
+ async sendTyping(target) {
231
+ await this.upstream.sendChatAction(target.conversationId, 'typing', {
232
+ ...(target.threadId ? { messageThreadId: target.threadId } : {}),
233
+ });
234
+ }
235
+ clearTypingStates() {
236
+ for (const state of this.typingStates.values()) {
237
+ if (state.timer)
238
+ clearInterval(state.timer);
239
+ }
240
+ this.typingStates.clear();
241
+ }
242
+ resolveStreamingMode(target) {
243
+ // `streaming.enabled: false` is a hard off-switch: it forces the buffered
244
+ // send-once strategy even though streaming is available.
245
+ if (!this.config.streaming.enabled)
246
+ return 'buffered';
247
+ // Private DMs with rich output use the native sendRichMessageDraft flow.
248
+ const rich = this.formattingMode === 'rich-markdown';
249
+ if (rich && target.conversationType === 'dm')
250
+ return 'native';
251
+ // Everything else edits one message in place.
252
+ return 'edit';
253
+ }
254
+ async createReply(target, _options) {
255
+ if (!this.started || !this.ctx) {
256
+ throw new ChannelError('CHANNEL_NOT_STARTED', 'telegram adapter is not started');
257
+ }
258
+ const mode = this.formattingMode;
259
+ const rich = mode === 'rich-markdown';
260
+ // Native rich draft streaming for private DMs (plan §6.1).
261
+ if (rich && target.conversationType === 'dm') {
262
+ const reply = new TelegramRichStreamingReply(this.upstream, target, this.config.streaming.placeholder, { formatting: { mode } });
263
+ await reply.start();
264
+ return reply;
265
+ }
266
+ // Group (or plain config): plain edit preview; finish upgrades the same
267
+ // message to a Rich Message when rich output is active (plan §6.2).
268
+ const reply = new TelegramStreamingReply(this.upstream, target, this.config.streaming.placeholder, { formatting: { mode }, richFinal: rich && target.conversationType !== 'dm' });
269
+ // Send the placeholder immediately so the user sees the bot is working;
270
+ // subsequent full-text previews edit this same message in place.
271
+ await reply.start();
272
+ return reply;
273
+ }
274
+ /**
275
+ * Optional in-place edit of an already-sent message (plan §15.3): either
276
+ * update the interactive keyboard (`editMessageReplyMarkup`) or the text
277
+ * (`editMessageText`).
278
+ */
279
+ async edit(target, messageId, message) {
280
+ if (!this.started) {
281
+ throw new ChannelError('CHANNEL_NOT_STARTED', 'telegram adapter is not started');
282
+ }
283
+ if (message.text !== undefined) {
284
+ const markup = message.actions === undefined
285
+ ? undefined
286
+ : actionsToReplyMarkup(message.actions) ?? { inline_keyboard: [] };
287
+ const mode = this.formattingMode;
288
+ const raw = mode === 'rich-markdown'
289
+ ? await this.upstream.editMessageRich(target.conversationId, messageId, { markdown: message.text }, markup)
290
+ : await this.upstream.editMessageText(target.conversationId, messageId, message.text, markup ? { replyMarkup: markup } : undefined);
291
+ return { delivered: true, raw };
292
+ }
293
+ if (message.actions !== undefined) {
294
+ const markup = actionsToReplyMarkup(message.actions) ?? { inline_keyboard: [] };
295
+ const raw = await this.upstream.editMessageReplyMarkup(target.conversationId, messageId, markup);
296
+ return { delivered: true, raw };
297
+ }
298
+ return { delivered: true };
299
+ }
300
+ async getHealth() {
301
+ if (!this.started) {
302
+ return { status: 'down', detail: 'telegram adapter is not started', authenticated: false };
303
+ }
304
+ if (this.authState === 'authenticated' && this.connected) {
305
+ return { status: 'ok', detail: 'connected', connection: 'connected', authenticated: true };
306
+ }
307
+ if (this.authState === 'authenticated') {
308
+ return {
309
+ status: 'down',
310
+ detail: 'authenticated but receive loop down',
311
+ connection: 'disconnected',
312
+ authenticated: true,
313
+ };
314
+ }
315
+ if (this.authState === 'failed') {
316
+ return { status: 'down', detail: 'authentication failed', authenticated: false };
317
+ }
318
+ return { status: 'down', detail: 'not configured: no bot token', authenticated: false };
319
+ }
320
+ /** Long-poll receive loop with exponential backoff on failure. */
321
+ startReceiveLoop() {
322
+ if (this.receiveLoop)
323
+ return;
324
+ this.receiveLoop = this.runReceiveLoop();
325
+ }
326
+ async runReceiveLoop() {
327
+ let attempt = 0;
328
+ const signal = this.receiveAbort?.signal;
329
+ if (!signal)
330
+ return;
331
+ while (!this.stopped && !signal.aborted) {
332
+ try {
333
+ await this.upstream.getUpdates(this.cursor, signal, async (raw) => {
334
+ try {
335
+ await this.inbound.handle(raw);
336
+ }
337
+ catch (error) {
338
+ // Reset transient processor state before retrying the unacked
339
+ // update after the receive loop reconnects.
340
+ this.inbound = this.createInboundProcessor();
341
+ throw error;
342
+ }
343
+ }, () => {
344
+ if (!this.connected) {
345
+ this.connected = true;
346
+ this.emitConnection('connected');
347
+ }
348
+ });
349
+ attempt = 0;
350
+ }
351
+ catch (error) {
352
+ if (this.stopped || signal.aborted)
353
+ break;
354
+ attempt += 1;
355
+ this.connected = false;
356
+ this.emitConnection('reconnecting');
357
+ if (!this.config.reconnect.enabled || attempt > this.config.reconnect.maxRetries) {
358
+ this.ctx.logger.warn('[channel-telegram] reconnect budget exhausted');
359
+ this.emitConnection('disconnected');
360
+ break;
361
+ }
362
+ const delay = Math.min(this.config.reconnect.baseDelayMs * 2 ** Math.min(attempt - 1, 8), this.config.reconnect.maxDelayMs);
363
+ this.ctx.logger.warn(`[channel-telegram] receive loop error; retry in ${delay}ms`, error);
364
+ await sleep(delay, signal);
365
+ }
366
+ }
367
+ this.connected = false;
368
+ if (!this.stopped && !signal.aborted) {
369
+ this.emitConnection('disconnected');
370
+ }
371
+ }
372
+ emitAuth(state) {
373
+ if (!this.ctx)
374
+ return;
375
+ void this.ctx
376
+ .emit({
377
+ type: 'auth.changed',
378
+ channel: this.id,
379
+ accountId: this.config.accountId,
380
+ state,
381
+ })
382
+ .catch(() => undefined);
383
+ }
384
+ emitConnection(state) {
385
+ if (!this.ctx)
386
+ return;
387
+ void this.ctx
388
+ .emit({
389
+ type: 'connection.changed',
390
+ channel: this.id,
391
+ accountId: this.config.accountId,
392
+ state,
393
+ })
394
+ .catch(() => undefined);
395
+ }
396
+ }
397
+ function sleep(ms, signal) {
398
+ return new Promise((resolve) => {
399
+ if (signal?.aborted) {
400
+ resolve();
401
+ return;
402
+ }
403
+ const timer = setTimeout(() => {
404
+ signal?.removeEventListener('abort', onAbort);
405
+ resolve();
406
+ }, ms);
407
+ const onAbort = () => {
408
+ clearTimeout(timer);
409
+ resolve();
410
+ };
411
+ signal?.addEventListener('abort', onAbort, { once: true });
412
+ });
413
+ }
414
+ function typingKey(target) {
415
+ return JSON.stringify([target.conversationId, target.threadId ?? null]);
416
+ }
417
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}