fix(bot): проверка дубликата BOT_TOKEN при старте (409 Conflict → exit 1)
All checks were successful
Release: multi-arch Docker images / build-push (push) Successful in 5m53s
All checks were successful
Release: multi-arch Docker images / build-push (push) Successful in 5m53s
Перед запуском polling делаем пробный getUpdates(timeout:0). Если Telegram
возвращает 409 'Conflict: terminated by other getUpdates request' (токен уже
используется другой нодой/процессом) — печатаем '🚫 ОБНАРУЖЕН ДУБЛИКАТ ТОКЕНА
ДЛЯ ЗАПУСКА' и завершаемся с кодом 1: контейнер не стартует, рестарт-цикл
останавливается (вместо молчаливого конфликта long poll между нодами).
Версия v1.0.1.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "telegram-shop-bot",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
|
||||
@@ -15,7 +15,53 @@ const initBot = async () => {
|
||||
}
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
const instance = new TelegramBot(config.BOT_TOKEN, {polling: true});
|
||||
const instance = new TelegramBot(config.BOT_TOKEN, {polling: false});
|
||||
// ── Проверка на ДУБЛИКАТ токена ─────────────────────────────
|
||||
// Перед запуском polling делаем разовый getUpdates. Если токен уже
|
||||
// используется другой нодой/процессом — Telegram возвращает 409
|
||||
// "Conflict: terminated by other getUpdates request" (или webhook).
|
||||
// Такой бот НЕ должен стартовать: он будет конфликтовать, терять
|
||||
// обновления и «прыгать» между серверами (issue: дубликат BotFather
|
||||
// токена на нескольких нодах).
|
||||
const dupCheck = await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => resolve(null), 10000);
|
||||
instance
|
||||
.getUpdates({ timeout: 0, allowed_updates: [] })
|
||||
.then((updates) => {
|
||||
clearTimeout(timeout);
|
||||
// допустимые значения — любой успешный ответ ([] или список)
|
||||
resolve({ ok: true, updates });
|
||||
})
|
||||
.catch((error) => {
|
||||
clearTimeout(timeout);
|
||||
const status = error?.response?.statusCode ?? error?.response?.status;
|
||||
const description = error?.response?.body?.description ?? error?.message ?? String(error);
|
||||
const is409 = status === 409 || /conflict/i.test(description);
|
||||
resolve({ ok: false, status, description, is409 });
|
||||
});
|
||||
});
|
||||
if (!dupCheck?.ok) {
|
||||
if (dupCheck?.is409) {
|
||||
console.error(
|
||||
'\n====================================================\n' +
|
||||
'🚫 ОБНАРУЖЕН ДУБЛИКАТ ТОКЕНА ДЛЯ ЗАПУСКА\n' +
|
||||
' Telegram вернул 409 Conflict — этот BOT_TOKEN уже\n' +
|
||||
' используется другим экземпляром/нодой.\n' +
|
||||
` (${dupCheck.description})\n` +
|
||||
' Запуск бота остановлен. Создайте новый бот в @BotFather\n' +
|
||||
' или замените BOT_TOKEN в настройках сервиса.\n' +
|
||||
'====================================================\n'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (dupCheck?.status === 404 || (dupCheck?.description || '').includes('Not Found')) {
|
||||
// неверный токен — пробуем дальше; ниже полноценная валидация
|
||||
logger.warn('validate: getUpdates 404 (invalid token?)');
|
||||
}
|
||||
}
|
||||
|
||||
// Реальная инициализация: включаем polling и проверяем getMe
|
||||
await instance.startPolling();
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
instance.stopPolling();
|
||||
|
||||
Reference in New Issue
Block a user