From 4f0f3742065c590c2e353449e437fbd96e8f3727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= <78488229+viktor138irk@users.noreply.github.com> Date: Fri, 8 May 2026 19:40:55 +0900 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81=20Telegram?= =?UTF-8?q?=20bridge=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/telegram.js | 47 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/backend/src/telegram.js b/backend/src/telegram.js index 9451bac..2832d35 100644 --- a/backend/src/telegram.js +++ b/backend/src/telegram.js @@ -33,8 +33,6 @@ function buildProxyAgent(proxy) { ? `${encodeURIComponent(proxy.username)}:${encodeURIComponent(proxy.password || '')}@` : ''; - // socks5h forces DNS resolution through the proxy. This is required when Telegram - // is not reachable/resolvable directly from the VPS network. return new SocksProxyAgent(`socks5h://${auth}${proxy.host}:${proxy.port}`); } @@ -61,7 +59,7 @@ export async function stopTelegramBridge(reason = 'restart') { try { await bot.stop(reason); } catch { - // Telegraf can throw if polling was never started. Safe to ignore on restart. + // Safe to ignore when polling was not started yet. } } @@ -97,9 +95,9 @@ export async function startTelegramBridge({ logger } = {}) { } try { - bot = new Telegraf(token, buildTelegramOptions(settings)); + const nextBot = new Telegraf(token, buildTelegramOptions(settings)); - bot.start(async (ctx) => { + nextBot.start(async (ctx) => { const from = ctx.from || {}; const operator = upsertTelegramOperator({ telegramUserId: from.id, @@ -108,11 +106,11 @@ export async function startTelegramBridge({ logger } = {}) { }); await ctx.reply( - `✅ WSChat оператор подключён.\n\nID: ${operator.id}\nTelegram: ${from.username ? '@' + from.username : from.id}\n\nТеперь новые сообщения сайта будут приходить сюда.` + `WSChat operator connected.\n\nID: ${operator.id}\nTelegram: ${from.username ? '@' + from.username : from.id}\n\nNew site messages will come here.` ); }); - bot.command('status', async (ctx) => { + nextBot.command('status', async (ctx) => { const status = getTelegramBridgeStatus(); await ctx.reply( `WSChat bridge\n` + @@ -122,13 +120,13 @@ export async function startTelegramBridge({ logger } = {}) { ); }); - bot.on('text', async (ctx) => { + nextBot.on('text', async (ctx) => { const text = ctx.message?.text || ''; if (!text || text.startsWith('/')) return; const operator = getOperatorByTelegramUserId(ctx.from.id); if (!operator) { - await ctx.reply('Сначала отправьте /start, чтобы зарегистрироваться оператором.'); + await ctx.reply('Send /start first to register as operator.'); return; } @@ -137,14 +135,14 @@ export async function startTelegramBridge({ logger } = {}) { const match = sourceText.match(/Conversation:\s*(conv_[a-zA-Z0-9_-]+)/); if (!match) { - await ctx.reply('Ответьте реплаем на сообщение WSChat, чтобы отправить ответ посетителю.'); + await ctx.reply('Reply to a WSChat notification message to answer a visitor.'); return; } const conversationId = match[1]; const conversation = getConversationWithVisitor(conversationId); if (!conversation) { - await ctx.reply('Диалог не найден. Возможно, он был удалён.'); + await ctx.reply('Conversation not found.'); return; } @@ -156,12 +154,12 @@ export async function startTelegramBridge({ logger } = {}) { telegramMessageId: String(ctx.message.message_id) }); - await ctx.reply(`✅ Ответ сохранён для диалога ${conversationId}. Доставка в виджет будет включена на следующем шаге.`); + await ctx.reply(`Answer saved for conversation ${conversationId}.`); }); - const me = await bot.telegram.getMe(); - await bot.launch({ dropPendingUpdates: true }); + const me = await nextBot.telegram.getMe(); + bot = nextBot; botStatus = { enabled: true, running: true, @@ -171,6 +169,15 @@ export async function startTelegramBridge({ logger } = {}) { startedAt: new Date().toISOString() }; + nextBot.launch({ dropPendingUpdates: true }).catch((error) => { + botStatus = { + ...botStatus, + running: false, + error: error.message + }; + logger?.error?.(error, 'Telegram bridge polling failed'); + }); + logger?.info?.({ username: botStatus.username, proxyEnabled: botStatus.proxyEnabled }, 'Telegram bridge started'); return botStatus; @@ -197,21 +204,21 @@ export async function notifyOperatorsAboutVisitorMessage({ site, visitor, conver if (operators.length === 0) return { ok: true, sent: 0, error: 'no active telegram operators' }; const text = [ - '💬 Новое сообщение WSChat', + 'New WSChat message', '', - `Сайт: ${escapeHtml(site.domain || site.name || site.id)}`, - `Visitor: ${escapeHtml(visitor.visitor_key || visitor.id)}`, - `Conversation: ${escapeHtml(conversation.id)}`, + `Site: ${escapeHtml(site.domain || site.name || site.id)}`, + `Visitor: ${escapeHtml(visitor.visitor_key || visitor.id)}`, + `Conversation: ${escapeHtml(conversation.id)}`, '', escapeHtml(message.body), '', - 'Ответьте реплаем на это сообщение, чтобы сохранить ответ оператором.' + 'Reply to this message to save an operator answer.' ].join('\n'); let sent = 0; for (const operator of operators) { try { - await bot.telegram.sendMessage(operator.telegram_user_id, text, { parse_mode: 'HTML' }); + await bot.telegram.sendMessage(operator.telegram_user_id, text); sent += 1; } catch (error) { logger?.warn?.({ operatorId: operator.id, error: error.message }, 'Failed to notify Telegram operator');