'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const { TelegramNotifier, formatLobbyMessage, parseRecipientIds, validateBotToken, } = require('../lib/telegram-notifier'); class MemoryStore { constructor(initial = null) { this.value = initial; } load() { return this.value; } save(value) { this.value = JSON.parse(JSON.stringify(value)); } } function fakeResponse(result) { return { ok: true, status: 200, json: async () => ({ ok: true, result }), }; } test('validates Telegram bot tokens and numeric recipient IDs without losing precision', () => { assert.equal(validateBotToken('123456:abcdefghijklmnopqrstuvwxyz_ABC'), '123456:abcdefghijklmnopqrstuvwxyz_ABC'); assert.deepEqual(parseRecipientIds('123456789, 4503599627370495; 123456789'), [ '123456789', '4503599627370495', ]); assert.throws(() => parseRecipientIds('alice'), /positive numeric/); assert.throws(() => parseRecipientIds('4503599627370496'), /positive numeric/); }); test('formats lobby status with real-player count and resolved member names', () => { const text = formatLobbyMessage( { lobbyName: 'Duos queue', customMapName: 'duos', customMaxPlayers: 12, notifyAtPlayers: 8, leaveAtPlayers: 9, serverRegion: 9, }, { phase: 'monitoring', lobbyId: '29963550000000000', humanCount: 2, practicePublished: true, members: [ { steamId: '76561198000000001', name: 'Alice' }, { steamId: '76561198000000002', name: 'Bob' }, ], }, ); assert.match(text, /Игроки: 2\/12/); assert.match(text, /1\. Alice \(76561198000000001\)/); assert.match(text, /2\. Bob \(76561198000000002\)/); assert.match(text, /Публикация: в списке лобби/); }); test('sends one tracker message and edits it only when lobby content changes', async () => { const calls = []; const fetchImpl = async (url, options) => { const method = url.split('/').at(-1); const payload = JSON.parse(options.body); calls.push({ method, payload }); if (method === 'getMe') return fakeResponse({ id: 99, username: 'lobby_bot' }); if (method === 'sendMessage') return fakeResponse({ message_id: 42 }); if (method === 'editMessageText') return fakeResponse({ message_id: 42 }); throw new Error(`Unexpected method ${method}`); }; const store = new MemoryStore(); const notifier = new TelegramNotifier({ store, fetchImpl, debounceMs: 1 }); await notifier.configure({ token: '123456:abcdefghijklmnopqrstuvwxyz_ABC', recipientIds: ['123456789'], }); assert.equal(notifier.publicState().configured, true); assert.equal(notifier.publicState().token, undefined); notifier.beginLobbySession(); const settings = { lobbyName: 'Duos', customMaxPlayers: 12 }; const first = { phase: 'monitoring', armed: true, lobbyId: '123', humanCount: 1, members: [{ steamId: '76561198000000001', name: 'Alice' }], }; await notifier.syncLobby(settings, first); await notifier.syncLobby(settings, first); const restoredNotifier = new TelegramNotifier({ store, fetchImpl, debounceMs: 1 }); await restoredNotifier.syncLobby(settings, { ...first, humanCount: 2, members: [...first.members, { steamId: '76561198000000002', name: 'Bob' }], }); assert.deepEqual(calls.map((call) => call.method), ['getMe', 'sendMessage', 'editMessageText']); assert.equal(calls[1].payload.chat_id, '123456789'); assert.equal(calls[2].payload.message_id, 42); assert.match(calls[2].payload.text, /Игроки: 2\/12/); assert.equal(store.value.token, '123456:abcdefghijklmnopqrstuvwxyz_ABC'); }); test('test delivery reports recipients that have not started the bot chat', async () => { const fetchImpl = async (url) => { const method = url.split('/').at(-1); if (method === 'getMe') return fakeResponse({ id: 99, username: 'lobby_bot' }); return { ok: false, status: 403, json: async () => ({ ok: false, error_code: 403, description: 'Forbidden: bot can\'t initiate conversation with a user' }), }; }; const notifier = new TelegramNotifier({ store: new MemoryStore(), fetchImpl }); await notifier.configure({ token: '123456:abcdefghijklmnopqrstuvwxyz_ABC', recipientIds: ['123456789'], }); const result = await notifier.sendTest(); assert.equal(result.delivered, 0); assert.equal(result.failed, 1); assert.match(result.errors[0], /can't initiate conversation/); });