import test from 'node:test'; import assert from 'node:assert/strict'; import { TelegramAlerter } from '../lib/alerts/telegram.mjs'; import { TelegramChatAssistant, buildTelegramChatContext } from '../lib/llm/telegram-chat.mjs'; import { extractBotChannelMessages } from '../apis/sources/telegram.mjs'; test('Telegram AI chat uses bounded history and current intelligence context', async () => { const calls = []; const provider = { isConfigured: true, async complete(systemPrompt, userMessage, options) { calls.push({ systemPrompt, userMessage, options }); return { text: calls.length === 1 ? 'First answer' : 'Second answer' }; }, }; const assistant = new TelegramChatAssistant({ provider, getContext: () => '{"direction":"risk-off"}', historyMessages: 4, maxInputChars: 200, maxTokens: 1024, timeoutMs: 120000, }); assert.equal(await assistant.reply('What changed today?', { chatId: 42 }), 'First answer'); assert.equal(await assistant.reply('Explain the implications in detail', { chatId: 42 }), 'Second answer'); assert.match(calls[0].systemPrompt, /untrusted evidence/i); assert.match(calls[0].systemPrompt, /Your name is DAVE/); assert.match(calls[0].systemPrompt, /ADAPTIVE WRITING STYLE/); assert.match(calls[0].userMessage, /risk-off/); assert.deepEqual(calls[0].options, { maxTokens: 1024, timeout: 120000 }); assert.match(calls[1].userMessage, /User: What changed today\?/); assert.match(calls[1].userMessage, /Assistant: First answer/); assert.match(calls[1].userMessage, /NEW USER MESSAGE: Explain the implications in detail/); assert.equal(assistant.historySize(42), 4); assistant.reset(42); assert.equal(assistant.historySize(42), 0); }); test('Telegram AI chat reports missing LLM configuration', async () => { const assistant = new TelegramChatAssistant({ provider: null }); assert.match(await assistant.reply('hello', { chatId: 1 }), /unavailable/i); }); test('Telegram AI chat enforces the encrypted profile language in provider prompts', async () => { const calls = []; const assistant = new TelegramChatAssistant({ provider: { isConfigured: true, async complete(systemPrompt, userMessage) { calls.push({ systemPrompt, userMessage }); return { text: 'Antwort auf Deutsch.' }; }, }, getPreferredLanguage: () => 'de', }); assert.equal(await assistant.reply('Kurzer Lagecheck', { chatId: 42 }), 'Antwort auf Deutsch.'); assert.match(calls[0].systemPrompt, /REQUIRED RESPONSE LANGUAGE: German \(de\)/); assert.match(calls[0].userMessage, /All user-visible prose in the final answer must be German/); }); test('Telegram chat context is compact and operationally useful', () => { const context = JSON.parse(buildTelegramChatContext({ meta: { generatedAt: '2026-07-05T10:00:00Z' }, delta: { summary: { direction: 'risk-off', totalChanges: 3, criticalChanges: 1 } }, ideas: [{ title: 'Gold hedge', type: 'HEDGE', ticker: 'GLD', confidence: 'HIGH' }], news: [{ title: 'Headline', source: 'Feed', url: 'https://example.test/story' }], sourceHealth: [{ name: 'ACLED', status: 'degraded', error: 'missing credentials' }], }, { status: 'degraded', sourcesOk: 22, sourcesDegraded: 1 })); assert.equal(context.direction, 'risk-off'); assert.equal(context.ideas[0].ticker, 'GLD'); assert.equal(context.news[0].url, 'https://example.test/story'); assert.equal(context.degradedSources[0].name, 'ACLED'); }); test('Telegram transport routes authorized free text as plain-text AI reply', async () => { const alerter = new TelegramAlerter({ botToken: 'test-token', chatId: '42' }); let handled = 0; const requests = []; alerter.onMessage(async (text) => { handled++; return { text: `Answer: ${text}`, parseMode: null }; }); const originalFetch = globalThis.fetch; globalThis.fetch = async (url, options = {}) => { requests.push({ url, body: options.body ? JSON.parse(options.body) : null }); if (url.includes('/getUpdates')) { return { ok: true, json: async () => ({ ok: true, result: [ { update_id: 1, message: { message_id: 10, text: 'ignore me', chat: { id: 99 } } }, { update_id: 2, message: { message_id: 11, text: 'What changed?', chat: { id: 42 } } }, ], }), }; } return { ok: true, json: async () => ({ ok: true, result: { message_id: 12 } }) }; }; try { await alerter._pollUpdates(); } finally { globalThis.fetch = originalFetch; } assert.equal(handled, 1); const sent = requests.find(request => request.url.includes('/sendMessage')); assert.equal(sent.body.text, 'Answer: What changed?'); assert.equal('parse_mode' in sent.body, false); assert.equal(sent.body.reply_to_message_id, 11); }); test('Telegram conversation delivery strips visible markdown and splits large AI replies', async () => { const alerter = new TelegramAlerter({ botToken: 'test-token', chatId: '42' }); const requests = []; const originalFetch = globalThis.fetch; globalThis.fetch = async (url, options = {}) => { requests.push({ url, body: options.body ? JSON.parse(options.body) : null }); return { ok: true, json: async () => ({ ok: true, result: { message_id: requests.length } }) }; }; try { const text = '**Aktuelle Lage:**\n\n' + [ 'Erster kurzer Absatz mit einer direkten Einschaetzung.', 'Zweiter Absatz mit mehr Kontext und genug Laenge, damit er in einer eigenen Nachricht landet. '.repeat(6), 'Dritter Absatz mit einer Rueckfrage, auf die der Nutzer direkt antworten kann. '.repeat(5), ].join('\n\n'); const result = await alerter.sendConversation(text, { maxChunkChars: 300, delayMs: 0, replyToMessageId: 7 }); assert.equal(result.ok, true); } finally { globalThis.fetch = originalFetch; } const sent = requests.filter(request => request.url.includes('/sendMessage')); assert.ok(sent.length >= 2); assert.doesNotMatch(sent[0].body.text, /\*\*/); assert.equal('parse_mode' in sent[0].body, false); assert.equal(sent[0].body.reply_to_message_id, 7); assert.equal('reply_to_message_id' in sent[1].body, false); }); test('Telegram OSINT extraction excludes private AI chat messages', () => { const messages = extractBotChannelMessages([ { update_id: 1, message: { text: 'private question', chat: { id: 42, type: 'private' } } }, { update_id: 2, channel_post: { text: 'public channel report', chat: { title: 'OSINT', type: 'channel' } } }, ]); assert.equal(messages.length, 1); assert.equal(messages[0].text, 'public channel report'); assert.equal(messages[0].chat, 'OSINT'); });