Spaces:
Runtime error
Runtime error
| import os | |
| import json | |
| import logging | |
| import requests | |
| import random | |
| from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup | |
| from telegram.ext import ( | |
| Application, | |
| CommandHandler, | |
| CallbackQueryHandler, | |
| ContextTypes, | |
| MessageHandler, | |
| filters, | |
| JobQueue | |
| ) | |
| # Настройка логирования | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
| logger = logging.getLogger(__name__) | |
| # ====================================================================== | |
| # --- ⚠️ КОНФИГУРАЦИЯ --- | |
| # В коде bot.py: | |
| TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN") | |
| WORKER_API_URL = "https://sk0lovek-aternosbot.hf.space" | |
| # ====================================================================== | |
| # Проверка конфигурации | |
| if not all([TELEGRAM_TOKEN, WORKER_API_URL]): | |
| logger.error("ОШИБКА: Не заданы все необходимые переменные конфигурации.") | |
| exit(1) | |
| # --- Глобальное состояние --- | |
| USER_DATA = {} | |
| DATA_FILE = "data.json" | |
| tg_app = None | |
| # ---------------------------------------------------------------------- | |
| # ФУНКЦИИ УПРАВЛЕНИЯ ДАННЫМИ | |
| # ---------------------------------------------------------------------- | |
| def load_data(): | |
| """Загружает данные пользователей из файла.""" | |
| global USER_DATA | |
| if os.path.exists(DATA_FILE): | |
| try: | |
| with open(DATA_FILE, 'r') as f: | |
| USER_DATA = json.load(f) | |
| # Инициализация недостающих полей для старых пользователей | |
| for chat_id in list(USER_DATA.keys()): | |
| if 'version' not in USER_DATA[chat_id]: | |
| USER_DATA[chat_id]['version'] = "1.20.1" | |
| if 'server_type' not in USER_DATA[chat_id]: | |
| USER_DATA[chat_id]['server_type'] = "vanilla" | |
| if 'state' not in USER_DATA[chat_id]: | |
| USER_DATA[chat_id]['state'] = "menu" | |
| except json.JSONDecodeError: | |
| logger.error("Ошибка декодирования JSON. Файл данных поврежден.") | |
| USER_DATA = {} | |
| else: | |
| logger.warning("Файл данных не найден. Создаю новое пустое состояние.") | |
| USER_DATA = {} | |
| def save_data(): | |
| """Сохраняет данные пользователей в файл.""" | |
| try: | |
| with open(DATA_FILE, 'w') as f: | |
| json.dump(USER_DATA, f, indent=4) | |
| except IOError as e: | |
| logger.error(f"Ошибка сохранения данных: {e}") | |
| # ---------------------------------------------------------------------- | |
| # ФУНКЦИИ API WORKER | |
| # ---------------------------------------------------------------------- | |
| async def start_bot_in_worker(chat_id: str, host: str, port: str, username: str, version: str, | |
| server_type: str) -> bool: | |
| """Отправляет запрос на запуск бота в Worker Service.""" | |
| url = f"{WORKER_API_URL}/api/start" | |
| payload = { | |
| "chatId": chat_id, | |
| "host": host, | |
| "port": port, | |
| "username": username, | |
| "version": version, | |
| "serverType": server_type | |
| } | |
| try: | |
| response = requests.post(url, json=payload, timeout=10) | |
| response.raise_for_status() | |
| logger.info(f"Worker API: Бот запущен для {chat_id}. Статус: {response.status_code}") | |
| return True | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Ошибка при запросе к Worker API (START): {e}") | |
| return False | |
| async def stop_bot_in_worker(chat_id: str) -> bool: | |
| """Отправляет запрос на остановку бота в Worker Service.""" | |
| url = f"{WORKER_API_URL}/api/stop" | |
| payload = {"chatId": chat_id} | |
| try: | |
| response = requests.post(url, json=payload, timeout=5) | |
| response.raise_for_status() | |
| logger.info(f"Worker API: Бот остановлен для {chat_id}. Статус: {response.status_code}") | |
| return True | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Ошибка при запросе к Worker API (STOP): {e}") | |
| return False | |
| async def get_bot_status(chat_id: str) -> bool: | |
| """Получает статус бота из Worker Service.""" | |
| url = f"{WORKER_API_URL}/api/status/{chat_id}" | |
| try: | |
| response = requests.get(url, timeout=5) | |
| response.raise_for_status() | |
| return response.json().get('isRunning', False) | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Ошибка при запросе к Worker API (STATUS): {e}") | |
| return False | |
| async def send_command_to_bot(chat_id: str, command: str) -> bool: | |
| """Отправляет команду чата Mineflayer-боту.""" | |
| url = f"{WORKER_API_URL}/api/command" | |
| payload = {"chatId": chat_id, "command": command} | |
| try: | |
| response = requests.post(url, json=payload, timeout=5) | |
| response.raise_for_status() | |
| return True | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Ошибка при запросе к Worker API (COMMAND): {e}") | |
| return False | |
| # ---------------------------------------------------------------------- | |
| # 🕵️ ANTI-KICK (ANTI-AFK) JOB | |
| # ---------------------------------------------------------------------- | |
| async def anti_afk_job(context: ContextTypes.DEFAULT_TYPE): | |
| """ | |
| Эта функция запускается каждые 45 секунд. | |
| Она проверяет всех активных ботов и отправляет сообщение в чат, | |
| чтобы сервер не кикнул за AFK. | |
| """ | |
| global USER_DATA | |
| # Фразы для спама, чтобы админы не сразу спалили, что это бот (или наоборот) | |
| afk_phrases = [ | |
| "Anti-AFK", | |
| "Stay alive", | |
| "Working...", | |
| "Not AFK", | |
| "Server check", | |
| "Bot active" | |
| ] | |
| # Проходимся по всем пользователям | |
| for chat_id in list(USER_DATA.keys()): | |
| try: | |
| # Спрашиваем у воркера: "Эй, бот для этого чела жив?" | |
| is_running = await get_bot_status(chat_id) | |
| if is_running: | |
| # Если бот жив, кидаем рандомную фразу в чат майнкрафта | |
| msg = random.choice(afk_phrases) | |
| # Можно также отправлять команду прыжка, если воркер поддерживает, | |
| # но чат - самый надежный способ. | |
| await send_command_to_bot(chat_id, msg) | |
| logger.info(f"[Anti-AFK] Сообщение '{msg}' отправлено для {chat_id}") | |
| except Exception as e: | |
| logger.error(f"[Anti-AFK] Ошибка для {chat_id}: {e}") | |
| # ---------------------------------------------------------------------- | |
| # ФУНКЦИИ TELEGRAM | |
| # ---------------------------------------------------------------------- | |
| def escape_markdown_v2(text: str, ignore_in_code_block: bool = False) -> str: | |
| """Экранирует специальные символы для MarkdownV2.""" | |
| specials = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] | |
| escaped_text = "" | |
| in_code_block = False | |
| i = 0 | |
| while i < len(text): | |
| char = text[i] | |
| if char == '`': | |
| if text[i:i + 3] == '```': | |
| escaped_text += '```' | |
| i += 3 | |
| in_code_block = not in_code_block | |
| continue | |
| elif text[i:i + 1] == '`': | |
| escaped_text += '`' | |
| i += 1 | |
| in_code_block = not in_code_block | |
| continue | |
| if in_code_block and ignore_in_code_block: | |
| escaped_text += char | |
| elif char in specials: | |
| escaped_text += '\\' + char | |
| else: | |
| escaped_text += char | |
| i += 1 | |
| return escaped_text.replace(r'\\`', '`') | |
| def get_main_menu_keyboard(chat_id_str: str, is_running: bool, is_setting_server: bool = False, | |
| is_setting_version: bool = False, is_setting_type: bool = False) -> InlineKeyboardMarkup: | |
| """Генерирует основное меню.""" | |
| data = USER_DATA.get(chat_id_str, {}) | |
| host = data.get('host', 'Нет') | |
| port = data.get('port', '25565') | |
| host_port = f"{host}:{port}" | |
| username = data.get('username', 'MineflayerBot') | |
| version = data.get('version', '1.20.1') | |
| server_type = data.get('server_type', 'vanilla') | |
| status_text = "🟢 Бот Активен" if is_running else "🔴 Бот Неактивен" | |
| start_stop_text = "🛑 Остановить Бота" if is_running else "▶️ Запустить Бота" | |
| keyboard = [] | |
| keyboard.append([InlineKeyboardButton(status_text, callback_data='status')]) | |
| settings_buttons = [ | |
| InlineKeyboardButton(f"🔗 Сервер: {host_port}", | |
| callback_data='set_server' if not is_setting_server else 'back_to_menu'), | |
| InlineKeyboardButton(f"👤 Имя: {username}", callback_data='set_username'), | |
| ] | |
| keyboard.append(settings_buttons) | |
| core_buttons = [ | |
| InlineKeyboardButton(f"📦 Ядро: {server_type.upper()}", | |
| callback_data='set_server_type' if not is_setting_type else 'back_to_menu'), | |
| InlineKeyboardButton(f"🕹️ Версия: {version}", | |
| callback_data='set_version' if not is_setting_version else 'back_to_menu'), | |
| ] | |
| keyboard.append(core_buttons) | |
| keyboard.append([InlineKeyboardButton(start_stop_text, callback_data='toggle_bot')]) | |
| ai_buttons = [ | |
| InlineKeyboardButton("🤖 AI: !start", callback_data='ai_start'), | |
| InlineKeyboardButton("⚔️ AI: !stop", callback_data='ai_stop') | |
| ] | |
| keyboard.append(ai_buttons) | |
| if is_setting_server or is_setting_version or is_setting_type: | |
| keyboard.append([InlineKeyboardButton("🔙 Назад в меню", callback_data='back_to_menu')]) | |
| return InlineKeyboardMarkup(keyboard) | |
| async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| chat_id = str(update.effective_chat.id) | |
| if chat_id not in USER_DATA: | |
| USER_DATA[chat_id] = { | |
| "host": "localhost", | |
| "port": "25565", | |
| "username": f"MineflayerBot_{chat_id[:4]}", | |
| "version": "1.20.1", | |
| "server_type": "vanilla", | |
| "state": "menu" | |
| } | |
| save_data() | |
| is_running = await get_bot_status(chat_id) | |
| current_state = USER_DATA[chat_id]['state'] | |
| is_setting_server = current_state == 'setting_host_port' | |
| is_setting_version = current_state == 'setting_version' | |
| is_setting_type = current_state == 'setting_server_type' | |
| reply_markup = get_main_menu_keyboard(chat_id, is_running, is_setting_server, is_setting_version, is_setting_type) | |
| data = USER_DATA[chat_id] | |
| host_esc = escape_markdown_v2(data['host'], ignore_in_code_block=True) | |
| port_esc = escape_markdown_v2(data['port'], ignore_in_code_block=True) | |
| username_esc = escape_markdown_v2(data['username'], ignore_in_code_block=True) | |
| version_esc = escape_markdown_v2(data['version'], ignore_in_code_block=True) | |
| server_type_esc = escape_markdown_v2(data['server_type'].upper(), ignore_in_code_block=True) | |
| message = ( | |
| "⚙️ **Главное меню управления Mineflayer Bot**\n\n" | |
| f"🔗 **Сервер:** `{host_esc}:{port_esc}`\n" | |
| f"👤 **Имя:** `{username_esc}`\n" | |
| f"📦 **Ядро:** `{server_type_esc}`\n" | |
| f"🕹️ **Версия:** `{version_esc}`\n" | |
| f"🚦 **Статус:** {'🟢 Активен' if is_running else '🔴 Неактивен'}\n\n" | |
| f"_Автоматический Anti\\-Kick \\(спам в чат\\) включен\\._" | |
| ) | |
| if update.callback_query is None: | |
| await update.message.reply_text(message, reply_markup=reply_markup, parse_mode='MarkdownV2') | |
| else: | |
| try: | |
| await update.callback_query.edit_message_text(message, reply_markup=reply_markup, parse_mode='MarkdownV2') | |
| except Exception: | |
| pass | |
| async def set_server_type_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| chat_id = str(update.effective_chat.id) | |
| USER_DATA[chat_id]['state'] = 'setting_server_type' | |
| save_data() | |
| keyboard = [ | |
| [InlineKeyboardButton("Ванильный (Vanilla)", callback_data='type_vanilla')], | |
| [InlineKeyboardButton("Paper/Spigot (Плагины)", callback_data='type_spigot')], | |
| [InlineKeyboardButton("Forge (Моды)", callback_data='type_forge')], | |
| [InlineKeyboardButton("Fabric (Моды)", callback_data='type_fabric')], | |
| [InlineKeyboardButton("🔙 Назад в меню", callback_data='back_to_menu')] | |
| ] | |
| reply_markup = InlineKeyboardMarkup(keyboard) | |
| await update.callback_query.edit_message_text( | |
| "📦 **Выберите тип ядра сервера:**\n\n" | |
| "*\\(Это важно для правильного подключения к серверам с модами или плагинами\\.\\)*", | |
| reply_markup=reply_markup, | |
| parse_mode='MarkdownV2' | |
| ) | |
| async def setserver_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| chat_id = str(update.effective_chat.id) | |
| USER_DATA[chat_id]['state'] = 'setting_host_port' | |
| save_data() | |
| message_text = ( | |
| "🔗 **Введите адрес сервера \\(host:port\\)**:\n\n" | |
| "Пример: `my_server\\.aternos\\.me:25565`\n\n" | |
| "*\\(Вы можете просто отправить новый адрес в следующем сообщении\\.\\)*" | |
| ) | |
| if update.callback_query: | |
| await update.callback_query.edit_message_text(message_text, parse_mode='MarkdownV2') | |
| else: | |
| await update.message.reply_text(message_text, parse_mode='MarkdownV2') | |
| async def setversion_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| chat_id = str(update.effective_chat.id) | |
| USER_DATA[chat_id]['state'] = 'setting_version' | |
| save_data() | |
| keyboard = [ | |
| [InlineKeyboardButton("1.20.1", callback_data='v_1.20.1'), | |
| InlineKeyboardButton("1.19.4", callback_data='v_1.19.4')], | |
| [InlineKeyboardButton("1.18.2", callback_data='v_1.18.2'), | |
| InlineKeyboardButton("1.17.1", callback_data='v_1.17.1')], | |
| [InlineKeyboardButton("1.16.5", callback_data='v_1.16.5'), | |
| InlineKeyboardButton("1.12.2", callback_data='v_1.12.2')], | |
| [InlineKeyboardButton("🔙 Назад в меню", callback_data='back_to_menu')] | |
| ] | |
| reply_markup = InlineKeyboardMarkup(keyboard) | |
| await update.callback_query.edit_message_text( | |
| "🕹️ **Выберите версию Minecraft**:\n\n*\\(Это обязательно должно соответствовать версии сервера\\.\\)*", | |
| reply_markup=reply_markup, | |
| parse_mode='MarkdownV2' | |
| ) | |
| async def set_username_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| chat_id = str(update.effective_chat.id) | |
| USER_DATA[chat_id]['state'] = 'setting_username' | |
| save_data() | |
| await update.callback_query.edit_message_text( | |
| "👤 **Введите новое имя для бота:**\n\nПример: `MyFarmingBot`\n\n*\\(Имя должно соответствовать правилам Minecraft\\.\\)*", | |
| parse_mode='MarkdownV2' | |
| ) | |
| async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| query = update.callback_query | |
| await query.answer() | |
| chat_id = str(query.message.chat.id) | |
| data = USER_DATA.get(chat_id, {}) | |
| callback_data = query.data | |
| if callback_data == 'back_to_menu': | |
| USER_DATA[chat_id]['state'] = 'menu' | |
| save_data() | |
| await start_command(update, context) | |
| return | |
| if callback_data == 'set_server': | |
| await setserver_command(update, context) | |
| return | |
| if callback_data == 'set_version': | |
| await setversion_command(update, context) | |
| return | |
| if callback_data == 'set_username': | |
| await set_username_command(update, context) | |
| return | |
| if callback_data == 'set_server_type': | |
| await set_server_type_command(update, context) | |
| return | |
| if callback_data.startswith('type_'): | |
| server_type = callback_data.split('_')[1] | |
| if server_type == 'spigot': | |
| server_type = 'vanilla' | |
| USER_DATA[chat_id]['server_type'] = server_type | |
| USER_DATA[chat_id]['state'] = 'menu' | |
| save_data() | |
| await start_command(update, context) | |
| return | |
| if callback_data.startswith('v_'): | |
| version = callback_data.split('_')[1] | |
| USER_DATA[chat_id]['version'] = version | |
| USER_DATA[chat_id]['state'] = 'menu' | |
| save_data() | |
| await start_command(update, context) | |
| return | |
| if callback_data == 'ai_start': | |
| if await send_command_to_bot(chat_id, "!start"): | |
| await query.edit_message_text( | |
| "🤖 **AI Gunner запущен\\!** Он начнет поиск оружия и мобов\\. *\\(Через несколько секунд бот ответит в чате сервера\\)*", | |
| parse_mode='MarkdownV2') | |
| else: | |
| await query.edit_message_text( | |
| "❌ **Ошибка:** Не удалось отправить команду \\!start\\. Убедитесь, что бот запущен\\.", | |
| parse_mode='MarkdownV2') | |
| return | |
| if callback_data == 'ai_stop': | |
| if await send_command_to_bot(chat_id, "!stop"): | |
| await query.edit_message_text("⚔️ **AI Gunner остановлен\\!** Бот переходит в режим ожидания\\.", | |
| parse_mode='MarkdownV2') | |
| else: | |
| await query.edit_message_text( | |
| "❌ **Ошибка:** Не удалось отправить команду \\!stop\\. Убедитесь, что бот запущен\\.", | |
| parse_mode='MarkdownV2') | |
| return | |
| if callback_data == 'toggle_bot': | |
| is_running = await get_bot_status(chat_id) | |
| if is_running: | |
| if await stop_bot_in_worker(chat_id): | |
| message = "🛑 **Бот остановлен\\!**" | |
| else: | |
| message = "❌ **Ошибка:** Не удалось отправить команду остановки Worker Service\\." | |
| else: | |
| host = data.get('host') | |
| port = data.get('port') | |
| version = data.get('version') | |
| server_type = data.get('server_type') | |
| if not host or host == 'localhost': | |
| await query.edit_message_text("❌ **Ошибка:** Сначала укажите адрес сервера через '🔗 Сервер'\\.", | |
| parse_mode='MarkdownV2') | |
| return | |
| if await start_bot_in_worker(chat_id, host, port, data.get('username'), version, server_type): | |
| host_esc = escape_markdown_v2(host, ignore_in_code_block=True) | |
| port_esc = escape_markdown_v2(port, ignore_in_code_block=True) | |
| version_esc = escape_markdown_v2(version, ignore_in_code_block=True) | |
| server_type_esc = escape_markdown_v2(server_type.upper(), ignore_in_code_block=True) | |
| message = f"▶️ **Бот запущен\\!** Попытка подключения к `{host_esc}:{port_esc}` \\(Версия: `{version_esc}`, Ядро: `{server_type_esc}`\\)\\. Ожидайте уведомления о статусе\\." | |
| else: | |
| message = "❌ **Ошибка:** Не удалось отправить команду запуска Worker Service\\. Проверьте логи или статус Worker'а\\." | |
| USER_DATA[chat_id]['state'] = 'menu' | |
| save_data() | |
| await query.message.reply_text(message, parse_mode='MarkdownV2') | |
| await start_command(update, context) | |
| return | |
| if callback_data == 'status': | |
| await start_command(update, context) | |
| return | |
| async def text_message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | |
| chat_id = str(update.effective_chat.id) | |
| user_input = update.message.text.strip() | |
| data = USER_DATA.get(chat_id) | |
| if not data: | |
| await start_command(update, context) | |
| return | |
| state = data.get('state', 'menu') | |
| if state == 'setting_host_port': | |
| if ':' in user_input: | |
| parts = user_input.split(':') | |
| host = parts[0] | |
| port = parts[1] if len(parts) > 1 else "25565" | |
| if not port.isdigit(): | |
| await update.message.reply_text("❌ **Ошибка:** Порт должен быть числом\\.", parse_mode='MarkdownV2') | |
| return | |
| USER_DATA[chat_id]['host'] = host | |
| USER_DATA[chat_id]['port'] = port | |
| USER_DATA[chat_id]['state'] = 'menu' | |
| save_data() | |
| host_esc = escape_markdown_v2(host, ignore_in_code_block=True) | |
| port_esc = escape_markdown_v2(port, ignore_in_code_block=True) | |
| await update.message.reply_text(f"✅ Сервер установлен: `{host_esc}:{port_esc}`", parse_mode='MarkdownV2') | |
| await start_command(update, context) | |
| else: | |
| await update.message.reply_text("❌ **Ошибка:** Введите в формате `host:port`\\.", parse_mode='MarkdownV2') | |
| elif state == 'setting_username': | |
| if 3 <= len(user_input) <= 16 and all(c.isalnum() or c in '_-' for c in user_input): | |
| USER_DATA[chat_id]['username'] = user_input | |
| USER_DATA[chat_id]['state'] = 'menu' | |
| save_data() | |
| username_esc = escape_markdown_v2(user_input, ignore_in_code_block=True) | |
| await update.message.reply_text(f"✅ Имя установлено: `{username_esc}`", parse_mode='MarkdownV2') | |
| await start_command(update, context) | |
| else: | |
| await update.message.reply_text( | |
| "❌ **Ошибка:** Имя должно быть от 3 до 16 символов и содержать только латинские буквы, цифры, \\_ или \\-\\.", | |
| parse_mode='MarkdownV2') | |
| elif state == 'setting_version': | |
| await update.message.reply_text( | |
| "⚠️ **Используйте кнопки для выбора версии\\.** Нажмите /menu и выберите '🕹️ Версия'\\.", | |
| parse_mode='MarkdownV2') | |
| elif state == 'setting_server_type': | |
| await update.message.reply_text( | |
| "⚠️ **Используйте кнопки для выбора типа ядра\\.** Нажмите /menu и выберите '📦 Ядро'\\.", | |
| parse_mode='MarkdownV2') | |
| elif state == 'menu': | |
| is_running = await get_bot_status(chat_id) | |
| if is_running: | |
| if await send_command_to_bot(chat_id, user_input): | |
| user_input_esc = escape_markdown_v2(user_input, ignore_in_code_block=True) | |
| await update.message.reply_text(f"💬 Команда отправлена боту: `{user_input_esc}`", | |
| parse_mode='MarkdownV2') | |
| else: | |
| await update.message.reply_text( | |
| "❌ **Ошибка:** Не удалось отправить команду Worker Service\\. Проверьте логи Worker'а\\.", | |
| parse_mode='MarkdownV2') | |
| else: | |
| await update.message.reply_text("🤖 Бот не запущен\\. Запустите его через /menu\\.", parse_mode='MarkdownV2') | |
| # ---------------------------------------------------------------------- | |
| # ТОЧКА ВХОДА (POLLING) | |
| # ---------------------------------------------------------------------- | |
| def main(): | |
| global tg_app | |
| load_data() | |
| tg_app = Application.builder().token(TELEGRAM_TOKEN).build() | |
| # Регистрация команд и обработчиков | |
| tg_app.add_handler(CommandHandler(["start", "menu"], start_command)) | |
| tg_app.add_handler(CallbackQueryHandler(button_callback)) | |
| tg_app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, text_message_handler)) | |
| # --- ⚠️ РЕГИСТРАЦИЯ ANTI-AFK ЗАДАЧИ --- | |
| # Запускается каждые 45 секунд, первый запуск через 10 секунд | |
| tg_app.job_queue.run_repeating(anti_afk_job, interval=45, first=10) | |
| # -------------------------------------- | |
| logger.info("Бот запущен...") | |
| tg_app.run_polling(drop_pending_updates=True) | |
| if __name__ == '__main__': | |
| main() |