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');