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",
|
"name": "telegram-shop-bot",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
|
|||||||
@@ -15,7 +15,53 @@ const initBot = async () => {
|
|||||||
}
|
}
|
||||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||||
try {
|
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) => {
|
await new Promise((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
instance.stopPolling();
|
instance.stopPolling();
|
||||||
|
|||||||
Reference in New Issue
Block a user