diff --git a/.env.example b/.env.example index 97353bb..392d072 100644 --- a/.env.example +++ b/.env.example @@ -5,5 +5,5 @@ BOT_TOKEN= ADMIN_ID= # конфиг ASF -ASF_URL="http://127.0.0.1:1242/Api/ASF" -ASF_PASSWORD="0000" +ASF_URL=http://127.0.0.1:1242 +ASF_PASSWORD=0000 diff --git a/bot/api/asf.py b/bot/api/asf.py index deefc1c..cc42472 100644 --- a/bot/api/asf.py +++ b/bot/api/asf.py @@ -4,7 +4,6 @@ import requests from bot.config import ASF_PASSWORD, ASF_URL from bot.logging_utils import get_logger -IPC_BASE_URL = "http://127.0.0.1:1242" logger = get_logger(__name__) @@ -14,7 +13,7 @@ def _headers() -> dict[str, str | None]: def asf_get(path: str) -> requests.Response: logger.info("Отправка GET-запроса в ASF: path=%s", path) - response = requests.get(f"{IPC_BASE_URL}{path}", headers=_headers()) + response = requests.get(f"{ASF_URL}{path}", headers=_headers()) logger.info( "Получен ответ ASF на GET-запрос: path=%s status=%s", path, @@ -29,7 +28,7 @@ def asf_post(path: str, payload: dict | None = None) -> requests.Response: path, sorted((payload or {}).keys()), ) - response = requests.post(f"{IPC_BASE_URL}{path}", headers=_headers(), json=payload) + response = requests.post(f"{ASF_URL}{path}", headers=_headers(), json=payload) logger.info( "Получен ответ ASF на POST-запрос: path=%s status=%s", path, @@ -40,7 +39,7 @@ def asf_post(path: str, payload: dict | None = None) -> requests.Response: def get_asf_status() -> requests.Response: logger.info("Запрос статуса ASF") - response = requests.get(ASF_URL, headers=_headers()) # type: ignore[arg-type] + response = requests.get(f"{ASF_URL}/Api/ASF", headers=_headers()) # type: ignore[arg-type] logger.info("Получен статус ASF: status=%s", response.status_code) return response @@ -74,7 +73,7 @@ async def get_bot_inventory(bot_name: str, appid: int, contextid: int) -> dict | ) async with aiohttp.ClientSession() as session: async with session.get( - f"{IPC_BASE_URL}/Api/Bot/{bot_name}/Inventory/{appid}/{contextid}", + f"{ASF_URL}/Api/Bot/{bot_name}/Inventory/{appid}/{contextid}", headers=_headers(), ) as response: if response.status != 200: @@ -108,7 +107,7 @@ async def get_2fa_token(bot_name: str) -> str: logger.info("Запрос 2FA-кода: bot=%s", bot_name) async with aiohttp.ClientSession() as session: async with session.get( - f"{IPC_BASE_URL}/Api/Bot/{bot_name}/TwoFactorAuthentication/Token", + f"{ASF_URL}/Api/Bot/{bot_name}/TwoFactorAuthentication/Token", headers=_headers(), ) as response: if response.status != 200: @@ -120,13 +119,17 @@ async def get_2fa_token(bot_name: str) -> str: return f"Ошибка HTTP {response.status}" data = await response.json() if not data.get("Success"): - logger.warning("ASF вернул ошибку при запросе 2FA-кода: bot=%s", bot_name) + logger.warning( + "ASF вернул ошибку при запросе 2FA-кода: bot=%s", bot_name + ) return "Ошибка ASF" try: logger.info("2FA-код успешно получен: bot=%s", bot_name) return data["Result"][bot_name]["Result"] except Exception: - logger.exception("Не удалось извлечь 2FA-код из ответа ASF: bot=%s", bot_name) + logger.exception( + "Не удалось извлечь 2FA-код из ответа ASF: bot=%s", bot_name + ) return "Не удалось получить код" @@ -134,7 +137,7 @@ async def get_confirmations(bot_name: str) -> list: logger.info("Запрос подтверждений 2FA: bot=%s", bot_name) async with aiohttp.ClientSession() as session: async with session.get( - f"{IPC_BASE_URL}/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations", + f"{ASF_URL}/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations", headers=_headers(), ) as response: if response.status != 200: @@ -171,7 +174,7 @@ async def accept_confirmations(bot_name: str) -> int: logger.info("Отправка запроса на подтверждение всех 2FA-операций: bot=%s", bot_name) async with aiohttp.ClientSession() as session: async with session.post( - f"{IPC_BASE_URL}/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations/Accept", + f"{ASF_URL}/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations/Accept", headers=_headers(), ) as response: logger.info( @@ -191,7 +194,7 @@ async def redeem_key(bot_name: str, key: str) -> tuple[bool, str]: logger.info("Запрос активации ключа: bot=%s", bot_name) async with aiohttp.ClientSession() as session: async with session.post( - f"{IPC_BASE_URL}/Api/Command", + f"{ASF_URL}/Api/Command", headers=_headers(), json={"Command": f"!redeem {bot_name} {key}"}, ) as response: @@ -204,7 +207,9 @@ async def redeem_key(bot_name: str, key: str) -> tuple[bool, str]: return False, f"HTTP_ERROR_{response.status}" data = await response.json() if not data.get("Success"): - logger.warning("ASF вернул ошибку при активации ключа: bot=%s", bot_name) + logger.warning( + "ASF вернул ошибку при активации ключа: bot=%s", bot_name + ) return False, "ASF_ERROR" logger.info("ASF вернул результат активации ключа: bot=%s", bot_name) return True, str(data.get("Result", "")) diff --git a/bot/handlers/navigation.py b/bot/handlers/navigation.py index 4974af5..bc5fb63 100644 --- a/bot/handlers/navigation.py +++ b/bot/handlers/navigation.py @@ -14,7 +14,7 @@ from bot.state import ( redeem_messages, redeem_users, ) -from bot.ui.formatters import get_asf_status_text +from bot.ui.formatters import get_asf_status_text, get_asf_status from bot.ui.keyboards import back_keyboard, confirm_action_keyboard, main_keyboard router = Router() @@ -88,6 +88,7 @@ async def restart_asf(callback: CallbackQuery) -> None: await callback.answer() try: await callback.message.edit_text("♻ ASF перезапускается...") # type: ignore[union-attr] + response = restart_asf_request() if response.status_code != 200: logger.warning( @@ -100,14 +101,28 @@ async def restart_asf(callback: CallbackQuery) -> None: reply_markup=main_keyboard(), ) return - await asyncio.sleep(6) + + # ! Меняется подход к перезапуску ASF: вместо того чтобы ждать + # ! 6 секунд, лучше проверять статус ASF до тех пор он не запустится + # await asyncio.sleep(6) + + status = get_asf_status() + await asyncio.sleep(1) + + while status.status_code != 200: + status = get_asf_status() + await asyncio.sleep(1) + await callback.message.edit_text( # type: ignore[union-attr] f"ASF успешно перезапущен\n\n{get_asf_status_text()}", reply_markup=main_keyboard(), ) + logger.info("ASF успешно перезапущен: %s", user) + except Exception as error: logger.exception("Не удалось перезапустить ASF: %s", user) + await callback.message.edit_text( # type: ignore[union-attr] f"Ошибка: {error}", reply_markup=main_keyboard(), @@ -123,7 +138,11 @@ async def plugins_handler(callback: CallbackQuery) -> None: try: response = get_plugins() if response.status_code != 200: - logger.warning("Не удалось получить список плагинов: %s status=%s", user, response.status_code) + logger.warning( + "Не удалось получить список плагинов: %s status=%s", + user, + response.status_code, + ) await callback.message.edit_text("Ошибка API") # type: ignore[union-attr] return data = response.json() diff --git a/bot/ui/formatters.py b/bot/ui/formatters.py index 0dfe095..65705e0 100644 --- a/bot/ui/formatters.py +++ b/bot/ui/formatters.py @@ -68,50 +68,63 @@ def get_uptime(start_time_str: str) -> str: def format_asf(data: dict) -> str: result = data.get("Result", {}) + current_version = result.get("Version") latest_version = result.get("LatestVersion", current_version) - lines = [f"Версия ASF: {current_version}"] + + lines = [f"Версия ASF: `{current_version}`"] if current_version != latest_version: - lines.append(f"Доступно обновление: {latest_version}") + lines.append(f"Доступно обновление: `{latest_version}`") + memory_kb = result.get("MemoryUsage", 0) memory_mb = memory_kb / 1024 + lines.append(f"Используется памяти: {memory_mb:.2f} MB") lines.append(f"Работает: {get_uptime(result.get('ProcessStartTime'))}") + return "\n".join(lines) def get_bot_status_icon(bot: dict) -> str: online = bot.get("IsConnectedAndLoggedOn", False) farming = bot.get("CardsFarmer", {}).get("NowFarming", False) + idle_games = bot.get("BotConfig", {}).get("GamesPlayedWhileIdle", []) loaded = bool(bot.get("BotName")) and bool(bot.get("SteamID")) + if not loaded: return "🔄" + if farming: return "🎴" + if idle_games and online: return "⌚" + if online: return "🟢" + return "🔴" def format_bot_ui(bot: dict) -> str: online = get_bot_status_icon(bot) + now_farming = bot.get("CardsFarmer", {}).get("NowFarming") - status_line = ( - "🎴 Идет фарм карточек" - if now_farming - else "🎴 Карточки не фармятся" - ) + status_line = "🎴 Идет фарм карточек" if now_farming else "🎴 Карточки не фармятся" + time = bot.get("CardsFarmer", {}).get("TimeRemaining", "00:00:00") games_to_farm = len(bot.get("CardsFarmer", {}).get("GamesToFarm", [])) + twofa = "есть" if bot.get("HasMobileAuthenticator") else "нет" + balance = bot.get("WalletBalance", 0) / 100 currency = get_currency_name(bot.get("WalletCurrency", 0)) - redeem = bot.get("GamesToRedeemInBackgroundCount", 0) + steam_id = bot.get("SteamID") nickname = html.escape(str(bot.get("Nickname") or "")) + redeem = bot.get("GamesToRedeemInBackgroundCount", 0) + profile_url = f"https://steamcommunity.com/profiles/{steam_id}" text = ( f"{online} {bot.get('BotName') or 'Загрузка...'}" @@ -122,6 +135,7 @@ def format_bot_ui(bot: dict) -> str: idle_games = bot.get("BotConfig", {}).get("GamesPlayedWhileIdle", []) if idle_games: names = [GAMES.get(game, str(game)) for game in idle_games] + if bot.get("IsConnectedAndLoggedOn"): idle_text = f"⌚ Накрутка часов: включена ({', '.join(names)})" else: @@ -130,6 +144,7 @@ def format_bot_ui(bot: dict) -> str: "применится после запуска" ) text += f"{idle_text}\n" + else: text += "⌚ Накрутка часов: выключена\n" @@ -138,6 +153,7 @@ def format_bot_ui(bot: dict) -> str: f"🔐 2FA: {twofa}\n\n" f"💰 Баланс: {balance:.2f} {currency}\n" ) + if redeem > 0: text += f"\nКлючей в очереди: {redeem}\n" if now_farming: @@ -149,11 +165,14 @@ def format_bot_ui(bot: dict) -> str: def get_asf_status_text() -> str: response = get_asf_status() + if response.status_code != 200: return f"Ошибка API: {response.status_code}" + data = response.json() if not data.get("Success"): return "ASF вернул ошибку" + return f"💻 Панель управления ASF\n\n{format_asf(data)}" diff --git a/bot/ui/keyboards.py b/bot/ui/keyboards.py index 58325eb..c4f60c7 100644 --- a/bot/ui/keyboards.py +++ b/bot/ui/keyboards.py @@ -10,7 +10,7 @@ def main_keyboard() -> InlineKeyboardMarkup: [InlineKeyboardButton(text="🤖 Боты", callback_data="bots")], [ InlineKeyboardButton( - text="🔑 Активация ключей на всех аккаунтах", + text="🔑 Активация ключей", callback_data="redeem_keys", ) ], @@ -35,11 +35,7 @@ def back_keyboard(callback_data: str = "back") -> InlineKeyboardMarkup: def games_keyboard(bot_name: str, is_enabled: bool) -> InlineKeyboardMarkup: - text = ( - "Остановить накрутку CS2" - if is_enabled - else "⌚ Накрутить часы CS2" - ) + text = "Остановить накрутку CS2" if is_enabled else "⌚ Накрутить часы CS2" return InlineKeyboardMarkup( inline_keyboard=[ [InlineKeyboardButton(text=text, callback_data=f"farm|{bot_name}|730")], diff --git a/main.py b/main.py index c11adc6..3334329 100644 --- a/main.py +++ b/main.py @@ -31,7 +31,6 @@ dp.include_router(messages_router) async def main() -> None: - logger.info("Запуск Telegram-бота ASF Control Bot") await dp.start_polling(bot)