@ai-devkit/channel-connector 0.10.0 โ†’ 0.10.1

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.
@@ -47,6 +47,12 @@ vi.mock('telegraf', ()=>{
47
47
  function getMockBot() {
48
48
  return telegrafModule.__mockBot;
49
49
  }
50
+ function expectHtmlChunkWithinLimit(chunk) {
51
+ expect(chunk.length).toBeLessThanOrEqual(4096);
52
+ expect(chunk).not.toMatch(/&(?:l|lt|g|gt|a|am|amp|q|qu|quo|quot)$/);
53
+ expect((chunk.match(/<pre>/g) ?? []).length).toBe((chunk.match(/<\/pre>/g) ?? []).length);
54
+ expect((chunk.match(/<code(?: class="language-[^"]+")?>/g) ?? []).length).toBe((chunk.match(/<\/code>/g) ?? []).length);
55
+ }
50
56
  describe('TelegramAdapter', ()=>{
51
57
  let adapter;
52
58
  beforeEach(()=>{
@@ -148,6 +154,72 @@ describe('TelegramAdapter', ()=>{
148
154
  });
149
155
  }
150
156
  });
157
+ it('should split long fenced code into independently valid HTML code blocks', async ()=>{
158
+ const bot = getMockBot();
159
+ const codeLine = 'const sample = "<code>tag</code>" && value < limit;\n';
160
+ const longMessage = `Before\n\n\`\`\`ts\n${codeLine.repeat(100)}\`\`\`\n\nAfter`;
161
+ await adapter.sendMessage('12345', longMessage);
162
+ expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
163
+ for (const call of bot.telegram.sendMessage.mock.calls){
164
+ const chunk = call[1];
165
+ expect(call[2]).toEqual({
166
+ parse_mode: 'HTML'
167
+ });
168
+ expectHtmlChunkWithinLimit(chunk);
169
+ if (chunk.includes('<pre>')) {
170
+ expect(chunk).toContain('<code class="language-ts">');
171
+ expect(chunk).toContain('&lt;code&gt;tag&lt;/code&gt;');
172
+ }
173
+ }
174
+ });
175
+ it('should split nested list fenced code without sending partial HTML tags', async ()=>{
176
+ const bot = getMockBot();
177
+ const codeLine = ' return "<code>tag</code>" && input < output;\n';
178
+ const longMessage = [
179
+ '- parent item',
180
+ ' - child item with code',
181
+ ' ```js',
182
+ codeLine.repeat(100).trimEnd(),
183
+ ' ```',
184
+ '- final item'
185
+ ].join('\n');
186
+ await adapter.sendMessage('12345', longMessage);
187
+ expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
188
+ for (const call of bot.telegram.sendMessage.mock.calls){
189
+ const chunk = call[1];
190
+ expect(call[2]).toEqual({
191
+ parse_mode: 'HTML'
192
+ });
193
+ expectHtmlChunkWithinLimit(chunk);
194
+ }
195
+ });
196
+ it('should split long paragraphs at readable markdown boundaries', async ()=>{
197
+ const bot = getMockBot();
198
+ const sentence = 'This is a long sentence with enough words to make chunking choose sentence boundaries. ';
199
+ const longMessage = sentence.repeat(80);
200
+ await adapter.sendMessage('12345', longMessage);
201
+ expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
202
+ for (const call of bot.telegram.sendMessage.mock.calls){
203
+ const chunk = call[1];
204
+ expect(call[2]).toEqual({
205
+ parse_mode: 'HTML'
206
+ });
207
+ expectHtmlChunkWithinLimit(chunk);
208
+ expect(chunk.endsWith('.') || chunk.endsWith('.\n\n') || chunk.endsWith(' ')).toBe(true);
209
+ }
210
+ });
211
+ it('should keep emoji chunks within the configured JavaScript string length limit', async ()=>{
212
+ const bot = getMockBot();
213
+ const longMessage = `Status ${'๐Ÿงช'.repeat(2500)}`;
214
+ await adapter.sendMessage('12345', longMessage);
215
+ expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
216
+ for (const call of bot.telegram.sendMessage.mock.calls){
217
+ expect(call[1].length).toBeLessThanOrEqual(4096);
218
+ expect(call[2]).toEqual({
219
+ parse_mode: 'HTML'
220
+ });
221
+ }
222
+ });
151
223
  it('should hard split at 4096 when no newlines available', async ()=>{
152
224
  const bot = getMockBot();
153
225
  const longMessage = 'A'.repeat(5000);
@@ -164,7 +236,7 @@ describe('TelegramAdapter', ()=>{
164
236
  await adapter.sendMessage('12345', message);
165
237
  // First chunk should end at a \n\n boundary, not mid-paragraph
166
238
  const firstChunk = bot.telegram.sendMessage.mock.calls[0][1];
167
- expect(firstChunk.endsWith('\n\n')).toBe(true);
239
+ expect(firstChunk).toBe(`${paragraph}\n\n${paragraph}`);
168
240
  });
169
241
  it('should send short messages in a single call', async ()=>{
170
242
  const bot = getMockBot();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/adapters/TelegramAdapter.test.ts"],"sourcesContent":["import * as telegrafModule from 'telegraf';\nimport * as telegramHtml from '../../utils/telegramHtml.js';\nimport { TelegramAdapter } from '../../adapters/TelegramAdapter.js';\nimport type { IncomingMessage } from '../../types.js';\n\n// Mock telegraf\nvi.mock('telegraf', () => {\n const handlers: Record<string, (...args: any[]) => any> = {};\n const mockBot = {\n launch: vi.fn().mockResolvedValue(undefined),\n stop: vi.fn().mockResolvedValue(undefined),\n on: vi.fn((event: string, handler: (...args: any[]) => any) => {\n handlers[event] = handler;\n }),\n telegram: {\n sendMessage: vi.fn().mockResolvedValue(undefined),\n getMe: vi.fn().mockResolvedValue({ username: 'test_bot' }),\n },\n _handlers: handlers,\n _triggerText: async (chatId: number, userId: number, text: string) => {\n const ctx = {\n message: {\n chat: { id: chatId },\n from: { id: userId },\n text,\n date: Math.floor(Date.now() / 1000),\n },\n reply: vi.fn().mockResolvedValue(undefined),\n };\n if (handlers['text']) {\n await handlers['text'](ctx);\n }\n return ctx;\n },\n };\n return {\n Telegraf: vi.fn(function () { return mockBot; }),\n __mockBot: mockBot,\n };\n});\n\nfunction getMockBot() {\n return (telegrafModule as unknown as { __mockBot: ReturnType<typeof vi.fn>['mock'] & Record<string, unknown> }).__mockBot;\n}\n\ndescribe('TelegramAdapter', () => {\n let adapter: TelegramAdapter;\n\n beforeEach(() => {\n vi.clearAllMocks();\n adapter = new TelegramAdapter({ botToken: 'test-token-123' });\n });\n\n describe('type', () => {\n it('should return \"telegram\"', () => {\n expect(adapter.type).toBe('telegram');\n });\n });\n\n describe('start', () => {\n it('should launch the telegraf bot', async () => {\n const bot = getMockBot();\n await adapter.start();\n expect(bot.launch).toHaveBeenCalled();\n });\n });\n\n describe('stop', () => {\n it('should stop the telegraf bot', async () => {\n const bot = getMockBot();\n await adapter.start();\n await adapter.stop();\n expect(bot.stop).toHaveBeenCalled();\n });\n });\n\n describe('onMessage', () => {\n it('should silently ignore messages when no handler is registered', async () => {\n // Don't register a handler\n await adapter.start();\n\n const bot = getMockBot();\n const ctx = await bot._triggerText(12345, 67890, 'hello');\n\n // Should not throw or reply\n expect(ctx.reply).not.toHaveBeenCalled();\n });\n\n it('should handle non-Error thrown by handler', async () => {\n const handler = vi.fn().mockRejectedValue('string error');\n adapter.onMessage(handler);\n await adapter.start();\n\n const bot = getMockBot();\n const ctx = await bot._triggerText(12345, 67890, 'hello');\n\n expect(ctx.reply).toHaveBeenCalledWith(\n 'Error processing message: Unknown error'\n );\n });\n\n it('should call handler with IncomingMessage on incoming text', async () => {\n const handler = vi.fn().mockResolvedValue(undefined);\n adapter.onMessage(handler);\n await adapter.start();\n\n const bot = getMockBot();\n await bot._triggerText(12345, 67890, 'hello agent');\n\n expect(handler).toHaveBeenCalledTimes(1);\n const msg: IncomingMessage = handler.mock.calls[0][0];\n expect(msg.channelType).toBe('telegram');\n expect(msg.chatId).toBe('12345');\n expect(msg.userId).toBe('67890');\n expect(msg.text).toBe('hello agent');\n expect(msg.timestamp).toBeInstanceOf(Date);\n });\n\n it('should handle handler errors gracefully', async () => {\n const handler = vi.fn().mockRejectedValue(new Error('handler failed'));\n adapter.onMessage(handler);\n await adapter.start();\n\n const bot = getMockBot();\n const ctx = await bot._triggerText(12345, 67890, 'hello');\n\n // Should not throw, and should reply with error\n expect(ctx.reply).toHaveBeenCalledWith(\n expect.stringContaining('Error processing message')\n );\n });\n });\n\n describe('sendMessage', () => {\n it('should send plain text with parse_mode HTML', async () => {\n const bot = getMockBot();\n await adapter.sendMessage('12345', 'hello from bot');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledWith(\n '12345',\n 'hello from bot',\n { parse_mode: 'HTML' }\n );\n });\n\n it('should render markdown as Telegram HTML', async () => {\n const bot = getMockBot();\n await adapter.sendMessage('12345', '**bold** and *italic* and `code`');\n\n const sent = bot.telegram.sendMessage.mock.calls[0][1];\n expect(sent).toContain('<b>bold</b>');\n expect(sent).toContain('<i>italic</i>');\n expect(sent).toContain('<code>code</code>');\n expect(bot.telegram.sendMessage.mock.calls[0][2]).toEqual({\n parse_mode: 'HTML',\n });\n });\n\n it('should chunk messages exceeding 4096 chars', async () => {\n const bot = getMockBot();\n const line = 'A'.repeat(100) + '\\n';\n const longMessage = line.repeat(50); // 5050 chars\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);\n for (const call of bot.telegram.sendMessage.mock.calls) {\n expect(call[1].length).toBeLessThanOrEqual(4096);\n expect(call[2]).toEqual({ parse_mode: 'HTML' });\n }\n });\n\n it('should hard split at 4096 when no newlines available', async () => {\n const bot = getMockBot();\n const longMessage = 'A'.repeat(5000);\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBe(2);\n expect(bot.telegram.sendMessage.mock.calls[0][1].length).toBe(4096);\n expect(bot.telegram.sendMessage.mock.calls[1][1].length).toBe(904);\n });\n\n it('should prefer paragraph (\\\\n\\\\n) over single \\\\n when chunking', async () => {\n const bot = getMockBot();\n // 4 paragraphs of ~1500 chars each, total > 4096\n const paragraph = 'A'.repeat(1500);\n const message = `${paragraph}\\n\\n${paragraph}\\n\\n${paragraph}\\n\\n${paragraph}`;\n\n await adapter.sendMessage('12345', message);\n\n // First chunk should end at a \\n\\n boundary, not mid-paragraph\n const firstChunk = bot.telegram.sendMessage.mock.calls[0][1];\n expect(firstChunk.endsWith('\\n\\n')).toBe(true);\n });\n\n it('should send short messages in a single call', async () => {\n const bot = getMockBot();\n await adapter.sendMessage('12345', 'short message');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n });\n\n it('should retry as plain text when Telegram rejects HTML with parse-entities error', async () => {\n const bot = getMockBot();\n const parseError = Object.assign(new Error('400: Bad Request'), {\n description: \"Bad Request: can't parse entities: Unsupported start tag \\\"foo\\\"\",\n });\n bot.telegram.sendMessage\n .mockRejectedValueOnce(parseError)\n .mockResolvedValueOnce(undefined);\n\n await adapter.sendMessage('12345', '**hello**');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(2);\n\n // First call: rendered HTML with parse_mode\n const [, htmlChunk, htmlOpts] = bot.telegram.sendMessage.mock.calls[0];\n expect(htmlChunk).toContain('<b>hello</b>');\n expect(htmlOpts).toEqual({ parse_mode: 'HTML' });\n\n // Second call: same content, plain text (tags stripped, no parse_mode)\n const [, plainChunk, plainOpts] = bot.telegram.sendMessage.mock.calls[1];\n expect(plainChunk).toBe('hello');\n expect(plainOpts).toBeUndefined();\n });\n\n it('should detect parse-entities error from \"message\" field too', async () => {\n const bot = getMockBot();\n // Some error shapes carry the marker on `message` rather than `description`\n const parseError = new Error(\"can't parse entities\");\n bot.telegram.sendMessage\n .mockRejectedValueOnce(parseError)\n .mockResolvedValueOnce(undefined);\n\n await adapter.sendMessage('12345', '**hi**');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(2);\n });\n\n it('should decode HTML entities when falling back to plain text', async () => {\n const bot = getMockBot();\n const parseError = Object.assign(new Error('400'), {\n description: \"Bad Request: can't parse entities\",\n });\n bot.telegram.sendMessage\n .mockRejectedValueOnce(parseError)\n .mockResolvedValueOnce(undefined);\n\n // Source has chars that escapeHtml encodes; fallback should decode them\n await adapter.sendMessage('12345', 'a < b && c > d');\n\n const [, plainChunk] = bot.telegram.sendMessage.mock.calls[1];\n expect(plainChunk).toContain('a < b && c > d');\n expect(plainChunk).not.toContain('&lt;');\n expect(plainChunk).not.toContain('&amp;');\n });\n\n it('should deliver nested lists without throwing', async () => {\n const bot = getMockBot();\n\n await adapter.sendMessage('12345', '- agent\\n - Status');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n expect(bot.telegram.sendMessage.mock.calls[0][1]).toContain('Status');\n });\n\n it('should send plain text when markdown formatting throws', async () => {\n const bot = getMockBot();\n const formatter = vi.spyOn(telegramHtml, 'markdownToTelegramHtml')\n .mockImplementationOnce(() => {\n throw new Error('formatter failed');\n });\n\n await adapter.sendMessage('12345', '- agent\\n - Status');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n expect(bot.telegram.sendMessage.mock.calls[0][0]).toBe('12345');\n expect(bot.telegram.sendMessage.mock.calls[0][1]).toBe('- agent\\n - Status');\n expect(bot.telegram.sendMessage.mock.calls[0][2]).toBeUndefined();\n\n formatter.mockRestore();\n });\n\n it('should propagate non-parse-entities errors without falling back', async () => {\n const bot = getMockBot();\n const otherError = Object.assign(new Error('403'), {\n description: 'Forbidden: bot was blocked by the user',\n });\n bot.telegram.sendMessage.mockRejectedValueOnce(otherError);\n\n await expect(adapter.sendMessage('12345', 'hi')).rejects.toBe(otherError);\n\n // Only the HTML attempt should have happened โ€” no fallback retry\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n });\n });\n\n describe('isHealthy', () => {\n it('should return true after start', async () => {\n await adapter.start();\n expect(await adapter.isHealthy()).toBe(true);\n });\n\n it('should return false before start', async () => {\n expect(await adapter.isHealthy()).toBe(false);\n });\n\n it('should return false after stop', async () => {\n await adapter.start();\n await adapter.stop();\n expect(await adapter.isHealthy()).toBe(false);\n });\n });\n});\n"],"names":["telegrafModule","telegramHtml","TelegramAdapter","vi","mock","handlers","mockBot","launch","fn","mockResolvedValue","undefined","stop","on","event","handler","telegram","sendMessage","getMe","username","_handlers","_triggerText","chatId","userId","text","ctx","message","chat","id","from","date","Math","floor","Date","now","reply","Telegraf","__mockBot","getMockBot","describe","adapter","beforeEach","clearAllMocks","botToken","it","expect","type","toBe","bot","start","toHaveBeenCalled","not","mockRejectedValue","onMessage","toHaveBeenCalledWith","toHaveBeenCalledTimes","msg","calls","channelType","timestamp","toBeInstanceOf","Error","stringContaining","parse_mode","sent","toContain","toEqual","line","repeat","longMessage","length","toBeGreaterThan","call","toBeLessThanOrEqual","paragraph","firstChunk","endsWith","parseError","Object","assign","description","mockRejectedValueOnce","mockResolvedValueOnce","htmlChunk","htmlOpts","plainChunk","plainOpts","toBeUndefined","formatter","spyOn","mockImplementationOnce","mockRestore","otherError","rejects","isHealthy"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/adapters/TelegramAdapter.test.ts"],"sourcesContent":["import * as telegrafModule from 'telegraf';\nimport * as telegramHtml from '../../utils/telegramHtml.js';\nimport { TelegramAdapter } from '../../adapters/TelegramAdapter.js';\nimport type { IncomingMessage } from '../../types.js';\n\n// Mock telegraf\nvi.mock('telegraf', () => {\n const handlers: Record<string, (...args: any[]) => any> = {};\n const mockBot = {\n launch: vi.fn().mockResolvedValue(undefined),\n stop: vi.fn().mockResolvedValue(undefined),\n on: vi.fn((event: string, handler: (...args: any[]) => any) => {\n handlers[event] = handler;\n }),\n telegram: {\n sendMessage: vi.fn().mockResolvedValue(undefined),\n getMe: vi.fn().mockResolvedValue({ username: 'test_bot' }),\n },\n _handlers: handlers,\n _triggerText: async (chatId: number, userId: number, text: string) => {\n const ctx = {\n message: {\n chat: { id: chatId },\n from: { id: userId },\n text,\n date: Math.floor(Date.now() / 1000),\n },\n reply: vi.fn().mockResolvedValue(undefined),\n };\n if (handlers['text']) {\n await handlers['text'](ctx);\n }\n return ctx;\n },\n };\n return {\n Telegraf: vi.fn(function () { return mockBot; }),\n __mockBot: mockBot,\n };\n});\n\nfunction getMockBot() {\n return (telegrafModule as unknown as { __mockBot: ReturnType<typeof vi.fn>['mock'] & Record<string, unknown> }).__mockBot;\n}\n\nfunction expectHtmlChunkWithinLimit(chunk: string): void {\n expect(chunk.length).toBeLessThanOrEqual(4096);\n expect(chunk).not.toMatch(/&(?:l|lt|g|gt|a|am|amp|q|qu|quo|quot)$/);\n expect((chunk.match(/<pre>/g) ?? []).length).toBe((chunk.match(/<\\/pre>/g) ?? []).length);\n expect((chunk.match(/<code(?: class=\"language-[^\"]+\")?>/g) ?? []).length).toBe((chunk.match(/<\\/code>/g) ?? []).length);\n}\n\ndescribe('TelegramAdapter', () => {\n let adapter: TelegramAdapter;\n\n beforeEach(() => {\n vi.clearAllMocks();\n adapter = new TelegramAdapter({ botToken: 'test-token-123' });\n });\n\n describe('type', () => {\n it('should return \"telegram\"', () => {\n expect(adapter.type).toBe('telegram');\n });\n });\n\n describe('start', () => {\n it('should launch the telegraf bot', async () => {\n const bot = getMockBot();\n await adapter.start();\n expect(bot.launch).toHaveBeenCalled();\n });\n });\n\n describe('stop', () => {\n it('should stop the telegraf bot', async () => {\n const bot = getMockBot();\n await adapter.start();\n await adapter.stop();\n expect(bot.stop).toHaveBeenCalled();\n });\n });\n\n describe('onMessage', () => {\n it('should silently ignore messages when no handler is registered', async () => {\n // Don't register a handler\n await adapter.start();\n\n const bot = getMockBot();\n const ctx = await bot._triggerText(12345, 67890, 'hello');\n\n // Should not throw or reply\n expect(ctx.reply).not.toHaveBeenCalled();\n });\n\n it('should handle non-Error thrown by handler', async () => {\n const handler = vi.fn().mockRejectedValue('string error');\n adapter.onMessage(handler);\n await adapter.start();\n\n const bot = getMockBot();\n const ctx = await bot._triggerText(12345, 67890, 'hello');\n\n expect(ctx.reply).toHaveBeenCalledWith(\n 'Error processing message: Unknown error'\n );\n });\n\n it('should call handler with IncomingMessage on incoming text', async () => {\n const handler = vi.fn().mockResolvedValue(undefined);\n adapter.onMessage(handler);\n await adapter.start();\n\n const bot = getMockBot();\n await bot._triggerText(12345, 67890, 'hello agent');\n\n expect(handler).toHaveBeenCalledTimes(1);\n const msg: IncomingMessage = handler.mock.calls[0][0];\n expect(msg.channelType).toBe('telegram');\n expect(msg.chatId).toBe('12345');\n expect(msg.userId).toBe('67890');\n expect(msg.text).toBe('hello agent');\n expect(msg.timestamp).toBeInstanceOf(Date);\n });\n\n it('should handle handler errors gracefully', async () => {\n const handler = vi.fn().mockRejectedValue(new Error('handler failed'));\n adapter.onMessage(handler);\n await adapter.start();\n\n const bot = getMockBot();\n const ctx = await bot._triggerText(12345, 67890, 'hello');\n\n // Should not throw, and should reply with error\n expect(ctx.reply).toHaveBeenCalledWith(\n expect.stringContaining('Error processing message')\n );\n });\n });\n\n describe('sendMessage', () => {\n it('should send plain text with parse_mode HTML', async () => {\n const bot = getMockBot();\n await adapter.sendMessage('12345', 'hello from bot');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledWith(\n '12345',\n 'hello from bot',\n { parse_mode: 'HTML' }\n );\n });\n\n it('should render markdown as Telegram HTML', async () => {\n const bot = getMockBot();\n await adapter.sendMessage('12345', '**bold** and *italic* and `code`');\n\n const sent = bot.telegram.sendMessage.mock.calls[0][1];\n expect(sent).toContain('<b>bold</b>');\n expect(sent).toContain('<i>italic</i>');\n expect(sent).toContain('<code>code</code>');\n expect(bot.telegram.sendMessage.mock.calls[0][2]).toEqual({\n parse_mode: 'HTML',\n });\n });\n\n it('should chunk messages exceeding 4096 chars', async () => {\n const bot = getMockBot();\n const line = 'A'.repeat(100) + '\\n';\n const longMessage = line.repeat(50); // 5050 chars\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);\n for (const call of bot.telegram.sendMessage.mock.calls) {\n expect(call[1].length).toBeLessThanOrEqual(4096);\n expect(call[2]).toEqual({ parse_mode: 'HTML' });\n }\n });\n\n it('should split long fenced code into independently valid HTML code blocks', async () => {\n const bot = getMockBot();\n const codeLine = 'const sample = \"<code>tag</code>\" && value < limit;\\n';\n const longMessage = `Before\\n\\n\\`\\`\\`ts\\n${codeLine.repeat(100)}\\`\\`\\`\\n\\nAfter`;\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);\n for (const call of bot.telegram.sendMessage.mock.calls) {\n const chunk = call[1];\n expect(call[2]).toEqual({ parse_mode: 'HTML' });\n expectHtmlChunkWithinLimit(chunk);\n if (chunk.includes('<pre>')) {\n expect(chunk).toContain('<code class=\"language-ts\">');\n expect(chunk).toContain('&lt;code&gt;tag&lt;/code&gt;');\n }\n }\n });\n\n it('should split nested list fenced code without sending partial HTML tags', async () => {\n const bot = getMockBot();\n const codeLine = ' return \"<code>tag</code>\" && input < output;\\n';\n const longMessage = [\n '- parent item',\n ' - child item with code',\n ' ```js',\n codeLine.repeat(100).trimEnd(),\n ' ```',\n '- final item',\n ].join('\\n');\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);\n for (const call of bot.telegram.sendMessage.mock.calls) {\n const chunk = call[1];\n expect(call[2]).toEqual({ parse_mode: 'HTML' });\n expectHtmlChunkWithinLimit(chunk);\n }\n });\n\n it('should split long paragraphs at readable markdown boundaries', async () => {\n const bot = getMockBot();\n const sentence = 'This is a long sentence with enough words to make chunking choose sentence boundaries. ';\n const longMessage = sentence.repeat(80);\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);\n for (const call of bot.telegram.sendMessage.mock.calls) {\n const chunk = call[1];\n expect(call[2]).toEqual({ parse_mode: 'HTML' });\n expectHtmlChunkWithinLimit(chunk);\n expect(chunk.endsWith('.') || chunk.endsWith('.\\n\\n') || chunk.endsWith(' ')).toBe(true);\n }\n });\n\n it('should keep emoji chunks within the configured JavaScript string length limit', async () => {\n const bot = getMockBot();\n const longMessage = `Status ${'๐Ÿงช'.repeat(2500)}`;\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);\n for (const call of bot.telegram.sendMessage.mock.calls) {\n expect(call[1].length).toBeLessThanOrEqual(4096);\n expect(call[2]).toEqual({ parse_mode: 'HTML' });\n }\n });\n\n it('should hard split at 4096 when no newlines available', async () => {\n const bot = getMockBot();\n const longMessage = 'A'.repeat(5000);\n\n await adapter.sendMessage('12345', longMessage);\n\n expect(bot.telegram.sendMessage.mock.calls.length).toBe(2);\n expect(bot.telegram.sendMessage.mock.calls[0][1].length).toBe(4096);\n expect(bot.telegram.sendMessage.mock.calls[1][1].length).toBe(904);\n });\n\n it('should prefer paragraph (\\\\n\\\\n) over single \\\\n when chunking', async () => {\n const bot = getMockBot();\n // 4 paragraphs of ~1500 chars each, total > 4096\n const paragraph = 'A'.repeat(1500);\n const message = `${paragraph}\\n\\n${paragraph}\\n\\n${paragraph}\\n\\n${paragraph}`;\n\n await adapter.sendMessage('12345', message);\n\n // First chunk should end at a \\n\\n boundary, not mid-paragraph\n const firstChunk = bot.telegram.sendMessage.mock.calls[0][1];\n expect(firstChunk).toBe(`${paragraph}\\n\\n${paragraph}`);\n });\n\n it('should send short messages in a single call', async () => {\n const bot = getMockBot();\n await adapter.sendMessage('12345', 'short message');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n });\n\n it('should retry as plain text when Telegram rejects HTML with parse-entities error', async () => {\n const bot = getMockBot();\n const parseError = Object.assign(new Error('400: Bad Request'), {\n description: \"Bad Request: can't parse entities: Unsupported start tag \\\"foo\\\"\",\n });\n bot.telegram.sendMessage\n .mockRejectedValueOnce(parseError)\n .mockResolvedValueOnce(undefined);\n\n await adapter.sendMessage('12345', '**hello**');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(2);\n\n // First call: rendered HTML with parse_mode\n const [, htmlChunk, htmlOpts] = bot.telegram.sendMessage.mock.calls[0];\n expect(htmlChunk).toContain('<b>hello</b>');\n expect(htmlOpts).toEqual({ parse_mode: 'HTML' });\n\n // Second call: same content, plain text (tags stripped, no parse_mode)\n const [, plainChunk, plainOpts] = bot.telegram.sendMessage.mock.calls[1];\n expect(plainChunk).toBe('hello');\n expect(plainOpts).toBeUndefined();\n });\n\n it('should detect parse-entities error from \"message\" field too', async () => {\n const bot = getMockBot();\n // Some error shapes carry the marker on `message` rather than `description`\n const parseError = new Error(\"can't parse entities\");\n bot.telegram.sendMessage\n .mockRejectedValueOnce(parseError)\n .mockResolvedValueOnce(undefined);\n\n await adapter.sendMessage('12345', '**hi**');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(2);\n });\n\n it('should decode HTML entities when falling back to plain text', async () => {\n const bot = getMockBot();\n const parseError = Object.assign(new Error('400'), {\n description: \"Bad Request: can't parse entities\",\n });\n bot.telegram.sendMessage\n .mockRejectedValueOnce(parseError)\n .mockResolvedValueOnce(undefined);\n\n // Source has chars that escapeHtml encodes; fallback should decode them\n await adapter.sendMessage('12345', 'a < b && c > d');\n\n const [, plainChunk] = bot.telegram.sendMessage.mock.calls[1];\n expect(plainChunk).toContain('a < b && c > d');\n expect(plainChunk).not.toContain('&lt;');\n expect(plainChunk).not.toContain('&amp;');\n });\n\n it('should deliver nested lists without throwing', async () => {\n const bot = getMockBot();\n\n await adapter.sendMessage('12345', '- agent\\n - Status');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n expect(bot.telegram.sendMessage.mock.calls[0][1]).toContain('Status');\n });\n\n it('should send plain text when markdown formatting throws', async () => {\n const bot = getMockBot();\n const formatter = vi.spyOn(telegramHtml, 'markdownToTelegramHtml')\n .mockImplementationOnce(() => {\n throw new Error('formatter failed');\n });\n\n await adapter.sendMessage('12345', '- agent\\n - Status');\n\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n expect(bot.telegram.sendMessage.mock.calls[0][0]).toBe('12345');\n expect(bot.telegram.sendMessage.mock.calls[0][1]).toBe('- agent\\n - Status');\n expect(bot.telegram.sendMessage.mock.calls[0][2]).toBeUndefined();\n\n formatter.mockRestore();\n });\n\n it('should propagate non-parse-entities errors without falling back', async () => {\n const bot = getMockBot();\n const otherError = Object.assign(new Error('403'), {\n description: 'Forbidden: bot was blocked by the user',\n });\n bot.telegram.sendMessage.mockRejectedValueOnce(otherError);\n\n await expect(adapter.sendMessage('12345', 'hi')).rejects.toBe(otherError);\n\n // Only the HTML attempt should have happened โ€” no fallback retry\n expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);\n });\n });\n\n describe('isHealthy', () => {\n it('should return true after start', async () => {\n await adapter.start();\n expect(await adapter.isHealthy()).toBe(true);\n });\n\n it('should return false before start', async () => {\n expect(await adapter.isHealthy()).toBe(false);\n });\n\n it('should return false after stop', async () => {\n await adapter.start();\n await adapter.stop();\n expect(await adapter.isHealthy()).toBe(false);\n });\n });\n});\n"],"names":["telegrafModule","telegramHtml","TelegramAdapter","vi","mock","handlers","mockBot","launch","fn","mockResolvedValue","undefined","stop","on","event","handler","telegram","sendMessage","getMe","username","_handlers","_triggerText","chatId","userId","text","ctx","message","chat","id","from","date","Math","floor","Date","now","reply","Telegraf","__mockBot","getMockBot","expectHtmlChunkWithinLimit","chunk","expect","length","toBeLessThanOrEqual","not","toMatch","match","toBe","describe","adapter","beforeEach","clearAllMocks","botToken","it","type","bot","start","toHaveBeenCalled","mockRejectedValue","onMessage","toHaveBeenCalledWith","toHaveBeenCalledTimes","msg","calls","channelType","timestamp","toBeInstanceOf","Error","stringContaining","parse_mode","sent","toContain","toEqual","line","repeat","longMessage","toBeGreaterThan","call","codeLine","includes","trimEnd","join","sentence","endsWith","paragraph","firstChunk","parseError","Object","assign","description","mockRejectedValueOnce","mockResolvedValueOnce","htmlChunk","htmlOpts","plainChunk","plainOpts","toBeUndefined","formatter","spyOn","mockImplementationOnce","mockRestore","otherError","rejects","isHealthy"],"mappings":"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"}
@@ -18,9 +18,8 @@ export declare class TelegramAdapter implements ChannelAdapter {
18
18
  stop(): Promise<void>;
19
19
  /**
20
20
  * Input is treated as markdown and rendered as Telegram-compatible HTML.
21
- * Long messages are chunked at paragraph boundaries when possible; very
22
- * long single blocks (e.g. a `<pre>` over 4096 chars) may still split
23
- * mid-tag and produce a partial render in the second chunk.
21
+ * Long messages are chunked as markdown source before rendering so each
22
+ * Telegram HTML payload is independently valid.
24
23
  */
25
24
  sendMessage(chatId: string, text: string): Promise<void>;
26
25
  onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"TelegramAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/TelegramAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAChD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAGhD,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,cAAyB;IAEtC,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,cAAc,CAA0D;IAChF,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,sBAAsB;IAIrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B;;;;;OAKG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9D,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAI3D,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAGtC"}
1
+ {"version":3,"file":"TelegramAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/TelegramAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAChD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAShD,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,cAAc;IAClD,QAAQ,CAAC,IAAI,cAAyB;IAEtC,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,cAAc,CAA0D;IAChF,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,sBAAsB;IAIrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B;;;;OAIG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B9D,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAI3D,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAGtC"}
@@ -1,8 +1,10 @@
1
1
  import { Telegraf } from 'telegraf';
2
+ import { Marked } from 'marked';
2
3
  import { markdownToTelegramHtml } from '../utils/telegramHtml.js';
3
4
  export const TELEGRAM_CHANNEL_TYPE = 'telegram';
4
5
  export const TELEGRAM_MAX_MESSAGE_LENGTH = 4096;
5
6
  const TELEGRAM_PARSE_MODE = 'HTML';
7
+ const markdownLexer = new Marked();
6
8
  /**
7
9
  * Telegram Bot API adapter using telegraf with long polling.
8
10
  */ export class TelegramAdapter {
@@ -39,29 +41,32 @@ const TELEGRAM_PARSE_MODE = 'HTML';
39
41
  }
40
42
  /**
41
43
  * Input is treated as markdown and rendered as Telegram-compatible HTML.
42
- * Long messages are chunked at paragraph boundaries when possible; very
43
- * long single blocks (e.g. a `<pre>` over 4096 chars) may still split
44
- * mid-tag and produce a partial render in the second chunk.
44
+ * Long messages are chunked as markdown source before rendering so each
45
+ * Telegram HTML payload is independently valid.
45
46
  */ async sendMessage(chatId, text) {
46
- let html;
47
+ let chunks;
47
48
  try {
48
- html = markdownToTelegramHtml(text);
49
+ chunks = chunkMarkdownForTelegram(text, TELEGRAM_MAX_MESSAGE_LENGTH);
49
50
  } catch {
50
51
  for (const chunk of chunkMessage(text, TELEGRAM_MAX_MESSAGE_LENGTH)){
51
52
  await this.bot.telegram.sendMessage(chatId, chunk);
52
53
  }
53
54
  return;
54
55
  }
55
- for (const chunk of chunkMessage(html, TELEGRAM_MAX_MESSAGE_LENGTH)){
56
+ for (const chunk of chunks){
57
+ if (!chunk.html) {
58
+ await this.bot.telegram.sendMessage(chatId, chunk.text);
59
+ continue;
60
+ }
56
61
  try {
57
- await this.bot.telegram.sendMessage(chatId, chunk, {
62
+ await this.bot.telegram.sendMessage(chatId, chunk.text, {
58
63
  parse_mode: TELEGRAM_PARSE_MODE
59
64
  });
60
65
  } catch (error) {
61
66
  if (!isParseEntitiesError(error)) throw error;
62
67
  // Telegram rejected the rendered HTML โ€” fall back to plain text
63
68
  // so the user still gets the content (just unformatted).
64
- await this.bot.telegram.sendMessage(chatId, htmlToPlainText(chunk));
69
+ await this.bot.telegram.sendMessage(chatId, htmlToPlainText(chunk.text));
65
70
  }
66
71
  }
67
72
  }
@@ -81,6 +86,224 @@ function isParseEntitiesError(error) {
81
86
  function htmlToPlainText(html) {
82
87
  return html.replace(/<[^>]+>/g, '').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&amp;/g, '&');
83
88
  }
89
+ function chunkMarkdownForTelegram(markdown, maxLen) {
90
+ const markdownChunks = splitMarkdownSource(markdown, maxLen);
91
+ const chunks = [];
92
+ for (const markdownChunk of markdownChunks){
93
+ const html = markdownToTelegramHtml(markdownChunk);
94
+ if (html.length <= maxLen) {
95
+ if (html.length > 0) chunks.push({
96
+ text: html,
97
+ html: true
98
+ });
99
+ continue;
100
+ }
101
+ for (const plainChunk of chunkMessage(markdownChunk, maxLen)){
102
+ if (plainChunk.length > 0) chunks.push({
103
+ text: plainChunk,
104
+ html: false
105
+ });
106
+ }
107
+ }
108
+ return chunks;
109
+ }
110
+ function splitMarkdownSource(markdown, maxLen, depth = 0) {
111
+ if (markdown.length === 0) return [];
112
+ if (renderedLengthFits(markdown, maxLen)) return [
113
+ markdown
114
+ ];
115
+ if (depth > 6) return splitPlainMarkdownText(markdown, maxLen);
116
+ const chunks = [];
117
+ let current = '';
118
+ const tokens = markdownLexer.lexer(markdown);
119
+ for (const token of tokens){
120
+ const raw = token.raw ?? '';
121
+ if (raw.length === 0) continue;
122
+ const candidate = current + raw;
123
+ if (candidate.length > 0 && renderedLengthFits(candidate, maxLen)) {
124
+ current = candidate;
125
+ continue;
126
+ }
127
+ if (current.length > 0) {
128
+ chunks.push(current);
129
+ current = '';
130
+ }
131
+ if (renderedLengthFits(raw, maxLen)) {
132
+ current = raw;
133
+ } else {
134
+ chunks.push(...splitOversizedToken(token, maxLen, depth + 1));
135
+ }
136
+ }
137
+ if (current.length > 0) chunks.push(current);
138
+ return chunks.flatMap((chunk)=>renderedLengthFits(chunk, maxLen) ? [
139
+ chunk
140
+ ] : splitPlainMarkdownText(chunk, maxLen));
141
+ }
142
+ function splitOversizedToken(token, maxLen, depth) {
143
+ switch(token.type){
144
+ case 'code':
145
+ return splitCodeToken(token, maxLen);
146
+ case 'list':
147
+ return splitListToken(token, maxLen, depth);
148
+ case 'paragraph':
149
+ case 'text':
150
+ return splitPlainMarkdownText(token.raw, maxLen);
151
+ default:
152
+ if ('tokens' in token && Array.isArray(token.tokens) && token.raw !== undefined) {
153
+ return splitMarkdownSource(token.raw, maxLen, depth);
154
+ }
155
+ return splitPlainMarkdownText(token.raw ?? '', maxLen);
156
+ }
157
+ }
158
+ function splitListToken(token, maxLen, depth) {
159
+ const chunks = [];
160
+ let current = '';
161
+ for (const item of token.items){
162
+ const raw = item.raw;
163
+ const candidate = current + raw;
164
+ if (candidate.length > 0 && renderedLengthFits(candidate, maxLen)) {
165
+ current = candidate;
166
+ continue;
167
+ }
168
+ if (current.length > 0) {
169
+ chunks.push(current);
170
+ current = '';
171
+ }
172
+ if (renderedLengthFits(raw, maxLen)) {
173
+ current = raw;
174
+ } else {
175
+ chunks.push(...splitMarkdownSource(raw, maxLen, depth + 1));
176
+ }
177
+ }
178
+ if (current.length > 0) chunks.push(current);
179
+ return chunks;
180
+ }
181
+ function splitCodeToken(token, maxLen) {
182
+ const fence = token.raw.startsWith('~~~') ? '~~~' : '```';
183
+ const lang = token.lang ? token.lang.split(/\s/)[0] : '';
184
+ const lines = token.text.split('\n');
185
+ const chunks = [];
186
+ let currentLines = [];
187
+ const renderFence = (codeLines)=>`${fence}${lang}\n${codeLines.join('\n')}\n${fence}\n\n`;
188
+ for (const line of lines){
189
+ const candidateLines = [
190
+ ...currentLines,
191
+ line
192
+ ];
193
+ if (renderedLengthFits(renderFence(candidateLines), maxLen)) {
194
+ currentLines = candidateLines;
195
+ continue;
196
+ }
197
+ if (currentLines.length > 0) {
198
+ chunks.push(renderFence(currentLines));
199
+ currentLines = [];
200
+ }
201
+ if (renderedLengthFits(renderFence([
202
+ line
203
+ ]), maxLen)) {
204
+ currentLines = [
205
+ line
206
+ ];
207
+ } else {
208
+ for (const segment of splitCodeLine(line, fence, lang, maxLen)){
209
+ chunks.push(renderFence([
210
+ segment
211
+ ]));
212
+ }
213
+ }
214
+ }
215
+ if (currentLines.length > 0) chunks.push(renderFence(currentLines));
216
+ return chunks;
217
+ }
218
+ function splitCodeLine(line, fence, lang, maxLen) {
219
+ const segments = [];
220
+ let current = '';
221
+ const renderFence = (value)=>`${fence}${lang}\n${value}\n${fence}\n\n`;
222
+ for (const char of Array.from(line)){
223
+ const candidate = current + char;
224
+ if (renderedLengthFits(renderFence(candidate), maxLen)) {
225
+ current = candidate;
226
+ continue;
227
+ }
228
+ if (current.length > 0) {
229
+ segments.push(current);
230
+ current = '';
231
+ }
232
+ if (renderedLengthFits(renderFence(char), maxLen)) {
233
+ current = char;
234
+ } else {
235
+ segments.push(char);
236
+ }
237
+ }
238
+ if (current.length > 0) segments.push(current);
239
+ return segments;
240
+ }
241
+ function splitPlainMarkdownText(markdown, maxLen) {
242
+ if (markdown.length === 0) return [];
243
+ if (renderedLengthFits(markdown, maxLen)) return [
244
+ markdown
245
+ ];
246
+ const newlineUnits = markdown.split(/(?<=\n)/u);
247
+ if (newlineUnits.length > 1) {
248
+ return packMarkdownUnits(newlineUnits, maxLen, splitPlainMarkdownText);
249
+ }
250
+ const sentenceUnits = markdown.match(/[^.!?]+[.!?]+(?:\s+|$)|[^.!?]+$/gu);
251
+ if (sentenceUnits && sentenceUnits.length > 1) {
252
+ return packMarkdownUnits(sentenceUnits, maxLen, splitPlainMarkdownText);
253
+ }
254
+ const wordUnits = markdown.match(/\S+\s*/gu);
255
+ if (wordUnits && wordUnits.length > 1) {
256
+ return packMarkdownUnits(wordUnits, maxLen, splitPlainMarkdownText);
257
+ }
258
+ return splitByCodePoint(markdown, maxLen);
259
+ }
260
+ function packMarkdownUnits(units, maxLen, splitOversized) {
261
+ const chunks = [];
262
+ let current = '';
263
+ for (const unit of units){
264
+ const candidate = current + unit;
265
+ if (renderedLengthFits(candidate, maxLen)) {
266
+ current = candidate;
267
+ continue;
268
+ }
269
+ if (current.length > 0) {
270
+ chunks.push(current);
271
+ current = '';
272
+ }
273
+ if (renderedLengthFits(unit, maxLen)) {
274
+ current = unit;
275
+ } else {
276
+ chunks.push(...splitOversized(unit, maxLen));
277
+ }
278
+ }
279
+ if (current.length > 0) chunks.push(current);
280
+ return chunks;
281
+ }
282
+ function splitByCodePoint(text, maxLen) {
283
+ const chunks = [];
284
+ let current = '';
285
+ for (const char of Array.from(text)){
286
+ const candidate = current + char;
287
+ if (renderedLengthFits(candidate, maxLen)) {
288
+ current = candidate;
289
+ continue;
290
+ }
291
+ if (current.length > 0) {
292
+ chunks.push(current);
293
+ current = '';
294
+ }
295
+ if (renderedLengthFits(char, maxLen)) {
296
+ current = char;
297
+ } else {
298
+ chunks.push(char);
299
+ }
300
+ }
301
+ if (current.length > 0) chunks.push(current);
302
+ return chunks;
303
+ }
304
+ function renderedLengthFits(markdown, maxLen) {
305
+ return markdownToTelegramHtml(markdown).length <= maxLen;
306
+ }
84
307
  /**
85
308
  * Split text into chunks of maxLen or fewer characters. Prefers paragraph
86
309
  * boundaries (\n\n), then single newlines (\n), then hard-splits at maxLen.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/TelegramAdapter.ts"],"sourcesContent":["import { Telegraf } from 'telegraf';\nimport type { ChannelAdapter } from './ChannelAdapter.js';\nimport { markdownToTelegramHtml } from '../utils/telegramHtml.js';\nimport type { IncomingMessage } from '../types.js';\n\nexport const TELEGRAM_CHANNEL_TYPE = 'telegram';\nexport const TELEGRAM_MAX_MESSAGE_LENGTH = 4096;\nconst TELEGRAM_PARSE_MODE = 'HTML' as const;\n\nexport interface TelegramAdapterOptions {\n botToken: string;\n}\n\n/**\n * Telegram Bot API adapter using telegraf with long polling.\n */\nexport class TelegramAdapter implements ChannelAdapter {\n readonly type = TELEGRAM_CHANNEL_TYPE;\n\n private bot: Telegraf;\n private messageHandler: ((msg: IncomingMessage) => Promise<void>) | null = null;\n private running = false;\n\n constructor(options: TelegramAdapterOptions) {\n this.bot = new Telegraf(options.botToken);\n }\n\n async start(): Promise<void> {\n this.bot.on('text', async (ctx) => {\n if (!this.messageHandler) return;\n\n const msg: IncomingMessage = {\n channelType: TELEGRAM_CHANNEL_TYPE,\n chatId: String(ctx.message.chat.id),\n userId: String(ctx.message.from.id),\n text: ctx.message.text,\n timestamp: new Date(ctx.message.date * 1000),\n };\n\n try {\n await this.messageHandler(msg);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n await ctx.reply(`Error processing message: ${errorMessage}`);\n }\n });\n\n await this.bot.launch();\n this.running = true;\n }\n\n async stop(): Promise<void> {\n this.running = false;\n await this.bot.stop();\n }\n\n /**\n * Input is treated as markdown and rendered as Telegram-compatible HTML.\n * Long messages are chunked at paragraph boundaries when possible; very\n * long single blocks (e.g. a `<pre>` over 4096 chars) may still split\n * mid-tag and produce a partial render in the second chunk.\n */\n async sendMessage(chatId: string, text: string): Promise<void> {\n let html: string;\n try {\n html = markdownToTelegramHtml(text);\n } catch {\n for (const chunk of chunkMessage(text, TELEGRAM_MAX_MESSAGE_LENGTH)) {\n await this.bot.telegram.sendMessage(chatId, chunk);\n }\n return;\n }\n\n for (const chunk of chunkMessage(html, TELEGRAM_MAX_MESSAGE_LENGTH)) {\n try {\n await this.bot.telegram.sendMessage(chatId, chunk, { parse_mode: TELEGRAM_PARSE_MODE });\n } catch (error) {\n if (!isParseEntitiesError(error)) throw error;\n // Telegram rejected the rendered HTML โ€” fall back to plain text\n // so the user still gets the content (just unformatted).\n await this.bot.telegram.sendMessage(chatId, htmlToPlainText(chunk));\n }\n }\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.messageHandler = handler;\n }\n\n async isHealthy(): Promise<boolean> {\n return this.running;\n }\n}\n\nfunction isParseEntitiesError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n const description = (error as { description?: string }).description;\n const message = (error as { message?: string }).message;\n return ((description ?? '') + (message ?? '')).includes(\"can't parse entities\");\n}\n\nfunction htmlToPlainText(html: string): string {\n return html\n .replace(/<[^>]+>/g, '')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&amp;/g, '&');\n}\n\n/**\n * Split text into chunks of maxLen or fewer characters. Prefers paragraph\n * boundaries (\\n\\n), then single newlines (\\n), then hard-splits at maxLen.\n */\nfunction chunkMessage(text: string, maxLen: number): string[] {\n const chunks: string[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n if (remaining.length <= maxLen) {\n chunks.push(remaining);\n break;\n }\n\n const lastParagraph = remaining.lastIndexOf('\\n\\n', maxLen - 2);\n const lastNewline = remaining.lastIndexOf('\\n', maxLen - 1);\n\n let splitAt: number;\n if (lastParagraph > 0) {\n splitAt = lastParagraph + 2;\n } else if (lastNewline > 0) {\n splitAt = lastNewline + 1;\n } else {\n splitAt = maxLen;\n }\n\n chunks.push(remaining.slice(0, splitAt));\n remaining = remaining.slice(splitAt);\n }\n\n return chunks;\n}\n"],"names":["Telegraf","markdownToTelegramHtml","TELEGRAM_CHANNEL_TYPE","TELEGRAM_MAX_MESSAGE_LENGTH","TELEGRAM_PARSE_MODE","TelegramAdapter","type","bot","messageHandler","running","options","botToken","start","on","ctx","msg","channelType","chatId","String","message","chat","id","userId","from","text","timestamp","Date","date","error","errorMessage","Error","reply","launch","stop","sendMessage","html","chunk","chunkMessage","telegram","parse_mode","isParseEntitiesError","htmlToPlainText","onMessage","handler","isHealthy","description","includes","replace","maxLen","chunks","remaining","length","push","lastParagraph","lastIndexOf","lastNewline","splitAt","slice"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/adapters/TelegramAdapter.ts"],"sourcesContent":["import { Telegraf } from 'telegraf';\nimport { Marked, type Token, type Tokens } from 'marked';\nimport type { ChannelAdapter } from './ChannelAdapter.js';\nimport { markdownToTelegramHtml } from '../utils/telegramHtml.js';\nimport type { IncomingMessage } from '../types.js';\n\nexport const TELEGRAM_CHANNEL_TYPE = 'telegram';\nexport const TELEGRAM_MAX_MESSAGE_LENGTH = 4096;\nconst TELEGRAM_PARSE_MODE = 'HTML' as const;\nconst markdownLexer = new Marked();\n\ntype TelegramMessageChunk = {\n text: string;\n html: boolean;\n};\n\nexport interface TelegramAdapterOptions {\n botToken: string;\n}\n\n/**\n * Telegram Bot API adapter using telegraf with long polling.\n */\nexport class TelegramAdapter implements ChannelAdapter {\n readonly type = TELEGRAM_CHANNEL_TYPE;\n\n private bot: Telegraf;\n private messageHandler: ((msg: IncomingMessage) => Promise<void>) | null = null;\n private running = false;\n\n constructor(options: TelegramAdapterOptions) {\n this.bot = new Telegraf(options.botToken);\n }\n\n async start(): Promise<void> {\n this.bot.on('text', async (ctx) => {\n if (!this.messageHandler) return;\n\n const msg: IncomingMessage = {\n channelType: TELEGRAM_CHANNEL_TYPE,\n chatId: String(ctx.message.chat.id),\n userId: String(ctx.message.from.id),\n text: ctx.message.text,\n timestamp: new Date(ctx.message.date * 1000),\n };\n\n try {\n await this.messageHandler(msg);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n await ctx.reply(`Error processing message: ${errorMessage}`);\n }\n });\n\n await this.bot.launch();\n this.running = true;\n }\n\n async stop(): Promise<void> {\n this.running = false;\n await this.bot.stop();\n }\n\n /**\n * Input is treated as markdown and rendered as Telegram-compatible HTML.\n * Long messages are chunked as markdown source before rendering so each\n * Telegram HTML payload is independently valid.\n */\n async sendMessage(chatId: string, text: string): Promise<void> {\n let chunks: TelegramMessageChunk[];\n try {\n chunks = chunkMarkdownForTelegram(text, TELEGRAM_MAX_MESSAGE_LENGTH);\n } catch {\n for (const chunk of chunkMessage(text, TELEGRAM_MAX_MESSAGE_LENGTH)) {\n await this.bot.telegram.sendMessage(chatId, chunk);\n }\n return;\n }\n\n for (const chunk of chunks) {\n if (!chunk.html) {\n await this.bot.telegram.sendMessage(chatId, chunk.text);\n continue;\n }\n\n try {\n await this.bot.telegram.sendMessage(chatId, chunk.text, { parse_mode: TELEGRAM_PARSE_MODE });\n } catch (error) {\n if (!isParseEntitiesError(error)) throw error;\n // Telegram rejected the rendered HTML โ€” fall back to plain text\n // so the user still gets the content (just unformatted).\n await this.bot.telegram.sendMessage(chatId, htmlToPlainText(chunk.text));\n }\n }\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.messageHandler = handler;\n }\n\n async isHealthy(): Promise<boolean> {\n return this.running;\n }\n}\n\nfunction isParseEntitiesError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n const description = (error as { description?: string }).description;\n const message = (error as { message?: string }).message;\n return ((description ?? '') + (message ?? '')).includes(\"can't parse entities\");\n}\n\nfunction htmlToPlainText(html: string): string {\n return html\n .replace(/<[^>]+>/g, '')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&amp;/g, '&');\n}\n\nfunction chunkMarkdownForTelegram(markdown: string, maxLen: number): TelegramMessageChunk[] {\n const markdownChunks = splitMarkdownSource(markdown, maxLen);\n const chunks: TelegramMessageChunk[] = [];\n\n for (const markdownChunk of markdownChunks) {\n const html = markdownToTelegramHtml(markdownChunk);\n if (html.length <= maxLen) {\n if (html.length > 0) chunks.push({ text: html, html: true });\n continue;\n }\n\n for (const plainChunk of chunkMessage(markdownChunk, maxLen)) {\n if (plainChunk.length > 0) chunks.push({ text: plainChunk, html: false });\n }\n }\n\n return chunks;\n}\n\nfunction splitMarkdownSource(markdown: string, maxLen: number, depth = 0): string[] {\n if (markdown.length === 0) return [];\n if (renderedLengthFits(markdown, maxLen)) return [markdown];\n if (depth > 6) return splitPlainMarkdownText(markdown, maxLen);\n\n const chunks: string[] = [];\n let current = '';\n const tokens = markdownLexer.lexer(markdown);\n\n for (const token of tokens) {\n const raw = token.raw ?? '';\n if (raw.length === 0) continue;\n\n const candidate = current + raw;\n if (candidate.length > 0 && renderedLengthFits(candidate, maxLen)) {\n current = candidate;\n continue;\n }\n\n if (current.length > 0) {\n chunks.push(current);\n current = '';\n }\n\n if (renderedLengthFits(raw, maxLen)) {\n current = raw;\n } else {\n chunks.push(...splitOversizedToken(token, maxLen, depth + 1));\n }\n }\n\n if (current.length > 0) chunks.push(current);\n return chunks.flatMap((chunk) => renderedLengthFits(chunk, maxLen) ? [chunk] : splitPlainMarkdownText(chunk, maxLen));\n}\n\nfunction splitOversizedToken(token: Token, maxLen: number, depth: number): string[] {\n switch (token.type) {\n case 'code':\n return splitCodeToken(token as Tokens.Code, maxLen);\n case 'list':\n return splitListToken(token as Tokens.List, maxLen, depth);\n case 'paragraph':\n case 'text':\n return splitPlainMarkdownText(token.raw, maxLen);\n default:\n if ('tokens' in token && Array.isArray(token.tokens) && token.raw !== undefined) {\n return splitMarkdownSource(token.raw, maxLen, depth);\n }\n return splitPlainMarkdownText(token.raw ?? '', maxLen);\n }\n}\n\nfunction splitListToken(token: Tokens.List, maxLen: number, depth: number): string[] {\n const chunks: string[] = [];\n let current = '';\n\n for (const item of token.items) {\n const raw = item.raw;\n const candidate = current + raw;\n if (candidate.length > 0 && renderedLengthFits(candidate, maxLen)) {\n current = candidate;\n continue;\n }\n\n if (current.length > 0) {\n chunks.push(current);\n current = '';\n }\n\n if (renderedLengthFits(raw, maxLen)) {\n current = raw;\n } else {\n chunks.push(...splitMarkdownSource(raw, maxLen, depth + 1));\n }\n }\n\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\nfunction splitCodeToken(token: Tokens.Code, maxLen: number): string[] {\n const fence = token.raw.startsWith('~~~') ? '~~~' : '```';\n const lang = token.lang ? token.lang.split(/\\s/)[0] : '';\n const lines = token.text.split('\\n');\n const chunks: string[] = [];\n let currentLines: string[] = [];\n\n const renderFence = (codeLines: string[]): string =>\n `${fence}${lang}\\n${codeLines.join('\\n')}\\n${fence}\\n\\n`;\n\n for (const line of lines) {\n const candidateLines = [...currentLines, line];\n if (renderedLengthFits(renderFence(candidateLines), maxLen)) {\n currentLines = candidateLines;\n continue;\n }\n\n if (currentLines.length > 0) {\n chunks.push(renderFence(currentLines));\n currentLines = [];\n }\n\n if (renderedLengthFits(renderFence([line]), maxLen)) {\n currentLines = [line];\n } else {\n for (const segment of splitCodeLine(line, fence, lang, maxLen)) {\n chunks.push(renderFence([segment]));\n }\n }\n }\n\n if (currentLines.length > 0) chunks.push(renderFence(currentLines));\n return chunks;\n}\n\nfunction splitCodeLine(line: string, fence: string, lang: string, maxLen: number): string[] {\n const segments: string[] = [];\n let current = '';\n const renderFence = (value: string): string => `${fence}${lang}\\n${value}\\n${fence}\\n\\n`;\n\n for (const char of Array.from(line)) {\n const candidate = current + char;\n if (renderedLengthFits(renderFence(candidate), maxLen)) {\n current = candidate;\n continue;\n }\n\n if (current.length > 0) {\n segments.push(current);\n current = '';\n }\n\n if (renderedLengthFits(renderFence(char), maxLen)) {\n current = char;\n } else {\n segments.push(char);\n }\n }\n\n if (current.length > 0) segments.push(current);\n return segments;\n}\n\nfunction splitPlainMarkdownText(markdown: string, maxLen: number): string[] {\n if (markdown.length === 0) return [];\n if (renderedLengthFits(markdown, maxLen)) return [markdown];\n\n const newlineUnits = markdown.split(/(?<=\\n)/u);\n if (newlineUnits.length > 1) {\n return packMarkdownUnits(newlineUnits, maxLen, splitPlainMarkdownText);\n }\n\n const sentenceUnits = markdown.match(/[^.!?]+[.!?]+(?:\\s+|$)|[^.!?]+$/gu);\n if (sentenceUnits && sentenceUnits.length > 1) {\n return packMarkdownUnits(sentenceUnits, maxLen, splitPlainMarkdownText);\n }\n\n const wordUnits = markdown.match(/\\S+\\s*/gu);\n if (wordUnits && wordUnits.length > 1) {\n return packMarkdownUnits(wordUnits, maxLen, splitPlainMarkdownText);\n }\n\n return splitByCodePoint(markdown, maxLen);\n}\n\nfunction packMarkdownUnits(\n units: string[],\n maxLen: number,\n splitOversized: (unit: string, maxLen: number) => string[],\n): string[] {\n const chunks: string[] = [];\n let current = '';\n\n for (const unit of units) {\n const candidate = current + unit;\n if (renderedLengthFits(candidate, maxLen)) {\n current = candidate;\n continue;\n }\n\n if (current.length > 0) {\n chunks.push(current);\n current = '';\n }\n\n if (renderedLengthFits(unit, maxLen)) {\n current = unit;\n } else {\n chunks.push(...splitOversized(unit, maxLen));\n }\n }\n\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\nfunction splitByCodePoint(text: string, maxLen: number): string[] {\n const chunks: string[] = [];\n let current = '';\n\n for (const char of Array.from(text)) {\n const candidate = current + char;\n if (renderedLengthFits(candidate, maxLen)) {\n current = candidate;\n continue;\n }\n\n if (current.length > 0) {\n chunks.push(current);\n current = '';\n }\n\n if (renderedLengthFits(char, maxLen)) {\n current = char;\n } else {\n chunks.push(char);\n }\n }\n\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\nfunction renderedLengthFits(markdown: string, maxLen: number): boolean {\n return markdownToTelegramHtml(markdown).length <= maxLen;\n}\n\n/**\n * Split text into chunks of maxLen or fewer characters. Prefers paragraph\n * boundaries (\\n\\n), then single newlines (\\n), then hard-splits at maxLen.\n */\nfunction chunkMessage(text: string, maxLen: number): string[] {\n const chunks: string[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n if (remaining.length <= maxLen) {\n chunks.push(remaining);\n break;\n }\n\n const lastParagraph = remaining.lastIndexOf('\\n\\n', maxLen - 2);\n const lastNewline = remaining.lastIndexOf('\\n', maxLen - 1);\n\n let splitAt: number;\n if (lastParagraph > 0) {\n splitAt = lastParagraph + 2;\n } else if (lastNewline > 0) {\n splitAt = lastNewline + 1;\n } else {\n splitAt = maxLen;\n }\n\n chunks.push(remaining.slice(0, splitAt));\n remaining = remaining.slice(splitAt);\n }\n\n return chunks;\n}\n"],"names":["Telegraf","Marked","markdownToTelegramHtml","TELEGRAM_CHANNEL_TYPE","TELEGRAM_MAX_MESSAGE_LENGTH","TELEGRAM_PARSE_MODE","markdownLexer","TelegramAdapter","type","bot","messageHandler","running","options","botToken","start","on","ctx","msg","channelType","chatId","String","message","chat","id","userId","from","text","timestamp","Date","date","error","errorMessage","Error","reply","launch","stop","sendMessage","chunks","chunkMarkdownForTelegram","chunk","chunkMessage","telegram","html","parse_mode","isParseEntitiesError","htmlToPlainText","onMessage","handler","isHealthy","description","includes","replace","markdown","maxLen","markdownChunks","splitMarkdownSource","markdownChunk","length","push","plainChunk","depth","renderedLengthFits","splitPlainMarkdownText","current","tokens","lexer","token","raw","candidate","splitOversizedToken","flatMap","splitCodeToken","splitListToken","Array","isArray","undefined","item","items","fence","startsWith","lang","split","lines","currentLines","renderFence","codeLines","join","line","candidateLines","segment","splitCodeLine","segments","value","char","newlineUnits","packMarkdownUnits","sentenceUnits","match","wordUnits","splitByCodePoint","units","splitOversized","unit","remaining","lastParagraph","lastIndexOf","lastNewline","splitAt","slice"],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "nx": {
4
4
  "name": "channel-connector"
5
5
  },
6
- "version": "0.10.0",
6
+ "version": "0.10.1",
7
7
  "type": "module",
8
8
  "description": "Generic messaging bridge for connecting to external communication platforms",
9
9
  "main": "dist/index.js",