Files
ASF-Control-Bot/main.py
T
2026-05-28 16:48:14 +02:00

1022 lines
45 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio, aiohttp, json, html, requests, re, os
from datetime import datetime, timezone
from aiogram import Bot, Dispatcher
from aiogram.filters import Command, BaseFilter
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, Message
from dotenv import load_dotenv
load_dotenv()
API_TOKEN = os.getenv("API_TOKEN")
ADMIN_ID = int(os.getenv("ADMIN_ID"))
ASF_URL = os.getenv("ASF_URL")
ASF_PASSWORD = os.getenv("ASF_PASSWORD")
PAGE_SIZE = 40
GAMES = {
730: "CS2",
440: "TF2",
570: "Dota 2"}
appid = 753
contextid = 6
console_users = set()
redeem_users = set()
bot_redeem_users = {}
bot_redeem_messages = {}
redeem_messages = {}
twofa_tasks = {}
inventory_cache = {}
bot = Bot(token=API_TOKEN)
dp = Dispatcher()
class AdminFilter(BaseFilter):
async def __call__(self, message: Message) -> bool:
return message.from_user.id == ADMIN_ID
class AdminCallbackFilter(BaseFilter):
async def __call__(self, callback: CallbackQuery) -> bool:
return callback.from_user.id == ADMIN_ID
def get_currency_name(currency_id: int) -> str:
if currency_id == 1:
return "USD"
elif currency_id == 3:
return "EUR"
elif currency_id == 5:
return "RUB"
elif currency_id == 18:
return "UAH"
elif currency_id == 6:
return "PLN"
elif currency_id == 0:
return ""
else:
return "???"
def get_inventory_icon(desc: dict) -> str:
tags = desc.get("tags", [])
for tag in tags:
category = tag.get("category", "")
name = tag.get("localized_tag_name", "")
if category == "item_class":
if "Trading Card" in name:
return "🃏"
if "Emoticon" in name:
return "😀"
if "Profile Background" in name:
return "🖼"
if "Booster Pack" in name:
return "📦"
if "Steam Gems" in name:
return "💎"
if "Gift" in name:
return "🎁"
item_type = desc.get("type", "").lower()
if "knife" in item_type:
return "🔪"
if "pistol" in item_type:
return "🔫"
if "rifle" in item_type:
return "🎯"
if "graffiti" in item_type:
return "🎨"
if "music kit" in item_type:
return "🎵"
return "📦"
def format_asf(data: dict) -> str:
result = data.get("Result", {})
config = result.get("GlobalConfig", {})
current_version = result.get("Version")
latest_version = result.get("LatestVersion", current_version)
lines = [f"Версия ASF: {current_version}"]
if current_version != 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_uptime(start_time_str: str) -> str:
start_time_str = start_time_str[:26] + "Z"
start_time = datetime.fromisoformat(start_time_str.replace("Z", "+00:00"))
now = datetime.now(timezone.utc)
delta = now - start_time
days = delta.days
hours, remainder = divmod(delta.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{days}д {hours}ч {minutes}м {seconds}с"
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")
if now_farming:
status_line = "Идет фарм карточек"
else:
status_line = "Карточки не фармится"
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 ""))
profile_url = f"https://steamcommunity.com/profiles/{steam_id}"
text = (
f"{online}"
f" {bot.get('BotName') or 'Загрузка...'}"
f"(<a href=\"{profile_url}\">{nickname}</a>)\n"
f"{status_line}\n")
idle_games = bot.get("BotConfig", {}).get("GamesPlayedWhileIdle", [])
if idle_games:
names = [GAMES.get(g, str(g)) for g in idle_games]
if bot.get("IsConnectedAndLoggedOn"):
idle_text = f"Idle: включен ({', '.join(names)})"
else:
idle_text = f"Idle: включен ({', '.join(names)}) применится после запуска"
text += f"{idle_text}\n"
else:
text += "Idle: выключен\n"
text += (
f"Steam id: <code>{steam_id}</code>\n"
f"2FA: {twofa}\n\n"
f"Баланс: {balance:.2f} {currency}\n")
if redeem > 0:
text += f"\nКлючей в очереди: {redeem}\n"
if now_farming:
text += f"\nОсталось: {time}\n"
if games_to_farm > 0:
text += f"В очереди игр: {games_to_farm}\n"
return text
def get_asf_status_text():
response = requests.get(ASF_URL, headers={"Authentication": ASF_PASSWORD})
if response.status_code != 200:
return f"Ошибка API: {response.status_code}"
data = response.json()
if not data.get("Success"):
return "ASF вернул ошибку"
status_text = format_asf(data)
return f"Панель управления ASF\n\n{status_text}"
async def get_2fa_code(bot_name: str) -> str:
async with aiohttp.ClientSession() as session:
async with session.get(f"http://127.0.0.1:1242/Api/Bot/{bot_name}/TwoFactorAuthentication/Token", headers={"Authentication": ASF_PASSWORD}) as response:
if response.status != 200:
return f"Ошибка HTTP {response.status}"
data = await response.json()
if not data.get("Success"):
return "Ошибка ASF"
try:
return data["Result"][bot_name]["Result"]
except:
return "Не удалось получить код"
def get_farm_summary(bots: dict) -> str:
total_games = 0
total_time_seconds = 0
for bot in bots.values():
farmer = bot.get("CardsFarmer", {})
if farmer.get("NowFarming"):
games = farmer.get("CurrentGamesFarming", [])
total_games += len(games)
time_str = farmer.get("TimeRemaining", "00:00:00")
h, m, s = map(int, time_str.split(":"))
total_time_seconds += h * 3600 + m * 60 + s
if total_games == 0:
return "На аккаунтах нечего не фармиться"
hours = total_time_seconds // 3600
minutes = (total_time_seconds % 3600) // 60
return (
f"Игр фармится: {total_games}\n"
f"Осталось времени: {hours}ч {minutes}м\n"
f"Карт осталось: ~{total_games * 3}")
async def get_confirmations(bot_name: str):
async with aiohttp.ClientSession() as session:
async with session.get(f"http://127.0.0.1:1242/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations", headers={"Authentication": ASF_PASSWORD}) as response:
if response.status != 200:
return []
data = await response.json()
if not data.get("Success"):
return []
try:
return data["Result"][bot_name]["Result"]
except:
return []
async def get_inventory(bot_name: str, appid: int, contextid: int = 2):
url = (f"http://127.0.0.1:1242/Api/Bot/{bot_name}/Inventory/{appid}/{contextid}")
async with aiohttp.ClientSession() as session:
async with session.get(url, headers={"Authentication": ASF_PASSWORD}) as response:
if response.status != 200:
return None
data = await response.json()
if not data.get("Success"):
return None
try:
return data["Result"]
except:
return None
async def render_inventory_page(callback: CallbackQuery, bot_name: str, inventory_type: str, appid: int, contextid: int, page: int):
await callback.message.edit_text("Загрузка inventory...")
inventory = await get_inventory(bot_name, appid, contextid)
if not inventory:
await callback.message.edit_text("Не удалось загрузить inventory")
return
bot_inventory = inventory.get(bot_name, {})
assets = bot_inventory.get("Assets", [])
descriptions = bot_inventory.get("Descriptions", [])
if not assets:
await callback.message.edit_text("Инвентарь пуст", reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data=f"inventory_{bot_name}")]]))
return
desc_map = {}
for desc in descriptions:
key = (str(desc.get("classid")), str(desc.get("instanceid")))
desc_map[key] = desc
total_pages = (len(assets) + PAGE_SIZE - 1) // PAGE_SIZE
start = page * PAGE_SIZE
end = start + PAGE_SIZE
page_assets = assets[start:end]
lines = []
marketable_count = 0
tradable_count = 0
cards_count = 0
emotes_count = 0
backgrounds_count = 0
gems_count = 0
for asset in page_assets:
key = (str(asset.get("classid")), str(asset.get("instanceid")))
desc = desc_map.get(key, {})
name = desc.get("market_name", "Unknown")
amount = asset.get("amount", 1)
tradable = ("🔄" if desc.get("tradable") else "")
marketable = ("💰" if desc.get("marketable") else "🔒")
icon = get_inventory_icon(desc)
if desc.get("marketable"):
marketable_count += 1
if desc.get("tradable"):
tradable_count += 1
if icon == "🃏":
cards_count += amount
elif icon == "😀":
emotes_count += amount
elif icon == "🖼":
backgrounds_count += amount
elif icon == "💎":
gems_count += amount
lines.append(f"{icon} {tradable}{marketable} {name} x{amount}")
inv_name = ("CS2" if appid == 730 else "Steam")
stats = (
f"📦 Items: {len(assets)}\n"
f"💰 Marketable: {marketable_count}\n"
f"🔄 Tradable: {tradable_count}")
if appid == 753:
stats += (
f"\n🃏 Cards: {cards_count}"
f"\n😀 Emotes: {emotes_count}"
f"\n🖼 Backgrounds: {backgrounds_count}"
f"\n💎 Gems: {gems_count}")
text = (
f"📦 {inv_name} Inventory {bot_name}\n"
f"📄 Страница {page + 1}/{total_pages}\n\n"
f"{stats}\n\n"
+ "\n".join(lines))
nav_buttons = []
if page > 0:
nav_buttons.append(InlineKeyboardButton(text="", callback_data=f"invpage|{inventory_type}|{bot_name}|{page - 1}"))
if page < total_pages - 1:
nav_buttons.append(InlineKeyboardButton(text="", callback_data=f"invpage|{inventory_type}|{bot_name}|{page + 1}"))
keyboard = []
if nav_buttons:
keyboard.append(nav_buttons)
keyboard.append([InlineKeyboardButton(text="Назад", callback_data=f"inventory_{bot_name}")])
await callback.message.edit_text(text[:4000], reply_markup=InlineKeyboardMarkup(inline_keyboard=keyboard))
def format_bot(bot_data: dict) -> str:
return json.dumps(bot_data, indent=2, ensure_ascii=False)
def main_keyboard():
return InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="Обновить", callback_data="refresh")],
[InlineKeyboardButton(text="Боты", callback_data="bots")],
[InlineKeyboardButton(text="Активация ключей на всех аккаунтах", callback_data="redeem_keys")],
[InlineKeyboardButton(text="Плагины", callback_data="plugins")],
[InlineKeyboardButton(text="Консоль", callback_data="console")],
[InlineKeyboardButton(text="♻ Restart ASF", callback_data="restart_asf_confirm")],
])
def back_keyboard():
return InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data="back")]])
def games_keyboard(bot_name, is_enabled):
if is_enabled:
text = "Остановить накрутку CS2"
else:
text = "Накрутить часы CS2"
return InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=text, callback_data=f"farm|{bot_name}|730")],
[InlineKeyboardButton(text="Назад", callback_data=f"bot_{bot_name}")]])
def bots_keyboard(bots: dict):
keyboard = []
for name, bot in bots.items():
status = get_bot_status_icon(bot)
loaded = (bool(bot.get("BotName")) and bool(bot.get("SteamID")))
if not loaded:
callback_data = "bot_loading"
else:
callback_data = f"bot_{name}"
keyboard.append([InlineKeyboardButton(text=f"{status} {name}", callback_data=callback_data)])
keyboard.append([InlineKeyboardButton(text="Назад", callback_data="back")])
return InlineKeyboardMarkup(inline_keyboard=keyboard)
@dp.callback_query(lambda c: c.data == "bot_loading", AdminCallbackFilter())
async def bot_loading_handler(callback: CallbackQuery):
await callback.answer("Аккаунт ещё загружается. Попробуйте через несколько секунд.", show_alert=True)
async def auto_update_2fa(message, bot_name):
last_code = None
message_id = message.message_id
try:
while True:
code = await get_2fa_code(bot_name)
if code != last_code:
confirmations = await get_confirmations(bot_name)
keyboard = []
if confirmations:
keyboard.append([InlineKeyboardButton(text="Подтверждения", callback_data=f"confirm_list_{bot_name}")])
keyboard.append([InlineKeyboardButton(text="Назад", callback_data=f"bot_{bot_name}")])
text = (
f"🔐 {bot_name}\n\n"
f"<code>{code}</code>")
try:
await message.edit_text(text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(inline_keyboard=keyboard))
last_code = code
except:
break
await asyncio.sleep(15)
except asyncio.CancelledError:
pass
except Exception as e:
print(f"2FA updater error: {e}")
finally:
twofa_tasks.pop(message_id, None)
async def stop_twofa_task(message_id: int):
task = twofa_tasks.pop(message_id, None)
if task:
task.cancel()
try:
await task
except:
pass
@dp.message(Command("start"), AdminFilter())
async def start_handler(message: Message):
try:
await message.delete()
except:
pass
try:
text = get_asf_status_text()
await message.answer(text, reply_markup=main_keyboard())
except Exception as e:
await message.answer(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data == "console", AdminCallbackFilter())
async def console_enter(callback: CallbackQuery):
await callback.answer()
console_users.add(callback.from_user.id)
await callback.message.edit_text("Консоль ASF\n\nОтправь команду (без !)", reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data="console_exit")]]))
@dp.callback_query(lambda c: c.data == "console_exit", AdminCallbackFilter())
async def console_exit(callback: CallbackQuery):
await callback.answer()
console_users.discard(callback.from_user.id)
text = get_asf_status_text()
await callback.message.edit_text(text, reply_markup=main_keyboard())
@dp.callback_query(lambda c: c.data == "delete_msg", AdminCallbackFilter())
async def delete_message(callback: CallbackQuery):
await callback.answer()
try:
await callback.message.delete()
except:
pass
@dp.callback_query(lambda c: c.data == "bots", AdminCallbackFilter())
async def bots_handler(callback: CallbackQuery):
await stop_twofa_task(callback.message.message_id)
await callback.answer()
try:
response = requests.get(
"http://127.0.0.1:1242/Api/Bot/ASF",
headers={"Authentication": ASF_PASSWORD}
)
if response.status_code != 200:
await callback.message.edit_text("Ошибка API")
return
data = response.json()
if not data.get("Success"):
await callback.message.edit_text("ASF вернул ошибку")
return
bots = data.get("Result", {})
count = len(bots)
summary = get_farm_summary(bots)
text = (
f"Ботов всего: {count}\n\n"
f"{summary}\n\n"
f"Выбери бота:"
)
await callback.message.edit_text(text, reply_markup=bots_keyboard(bots))
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data and c.data.startswith("bot_"), AdminCallbackFilter())
async def bot_selected(callback: CallbackQuery):
await stop_twofa_task(callback.message.message_id)
await callback.answer()
if not callback.data:
return
bot_name = callback.data.replace("bot_", "")
try:
response = requests.get("http://127.0.0.1:1242/Api/Bot/ASF", headers={"Authentication": ASF_PASSWORD})
data = response.json()
bots = data.get("Result", {})
bot = bots.get(bot_name)
if not bot or not bot.get("BotName") or not bot.get("SteamID"):
await callback.answer("Аккаунт ещё загружается. Попробуйте через несколько секунд.", show_alert=True)
return
if not bot:
await callback.message.edit_text("Бот не найден")
return
text = format_bot_ui(bot)
keyboard = []
if bot.get("HasMobileAuthenticator"):
keyboard.append([InlineKeyboardButton(text="2FA", callback_data=f"2fa_{bot_name}")])
keyboard.append([InlineKeyboardButton(text="Игры", callback_data=f"games_{bot_name}")])
keyboard.append([InlineKeyboardButton(text="Активировать ключ", callback_data=f"redeem_bot_{bot_name}")])
keyboard.append([InlineKeyboardButton(text="Инвентарь", callback_data=f"inventory_{bot_name}")])
keyboard.append([InlineKeyboardButton(text="Назад", callback_data="bots")])
await callback.message.edit_text(text, parse_mode="HTML", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup(inline_keyboard=keyboard))
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data and c.data.startswith("games_"), AdminCallbackFilter())
async def games(callback: CallbackQuery):
await callback.answer()
if not callback.data:
return
bot_name = callback.data.replace("games_", "")
idle_enabled = is_idle_enabled(bot_name, 730)
await callback.message.edit_text("Выберите игру для накрутки часов", reply_markup=games_keyboard(bot_name, idle_enabled))
@dp.callback_query(lambda c: c.data == "back", AdminCallbackFilter())
async def back_button(callback: CallbackQuery):
redeem_users.discard(callback.from_user.id)
redeem_messages.pop(callback.from_user.id, None)
console_users.discard(callback.from_user.id)
bot_redeem_users.pop(callback.from_user.id, None)
bot_redeem_messages.pop(callback.from_user.id, None)
await stop_twofa_task(callback.message.message_id)
await callback.answer()
try:
text = get_asf_status_text()
await callback.message.edit_text(text, reply_markup=main_keyboard())
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data == "refresh", AdminCallbackFilter())
async def refresh_handler(callback: CallbackQuery):
await callback.answer()
try:
text = get_asf_status_text()
await callback.message.edit_text(text, reply_markup=main_keyboard())
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data == "restart_asf_confirm", AdminCallbackFilter())
async def restart_asf_confirm(callback: CallbackQuery):
await callback.answer()
await callback.message.edit_text(
"♻ Точно перезапустить ASF?\n\n"
"Во время перезапуска IPC будет недоступен несколько секунд.",
reply_markup=InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="Да", callback_data="restart_asf")],
[InlineKeyboardButton(text="Нет", callback_data="back")]]))
@dp.callback_query(lambda c: c.data == "restart_asf", AdminCallbackFilter())
async def restart_asf(callback: CallbackQuery):
await callback.answer()
try:
await callback.message.edit_text("♻ ASF перезапускается...")
response = requests.post("http://127.0.0.1:1242/Api/Command", headers={"Authentication": ASF_PASSWORD}, json={"Command": "!restart"})
if response.status_code != 200:
await callback.message.edit_text(f"Ошибка HTTP {response.status_code}", reply_markup=main_keyboard())
return
await asyncio.sleep(6)
text = get_asf_status_text()
await callback.message.edit_text(f"ASF успешно перезапущен\n\n{text}", reply_markup=main_keyboard())
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}", reply_markup=main_keyboard())
@dp.callback_query(lambda c: c.data and c.data.startswith("2fa_") and not c.data.startswith("2fa_refresh_"), AdminCallbackFilter())
async def twofa_handler(callback: CallbackQuery):
await callback.answer()
if not callback.data:
return
bot_name = callback.data.replace("2fa_", "")
message_id = callback.message.message_id
# убиваем старую task
old_task = twofa_tasks.pop(message_id, None)
if old_task:
old_task.cancel()
try:
await old_task
except:
pass
# мгновенный экран загрузки
await callback.message.edit_text(
f"🔐 {bot_name}\n\nЗагрузка 2FA...",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data=f"bot_{bot_name}")]]))
# запускаем updater
task = asyncio.create_task(auto_update_2fa(callback.message, bot_name))
twofa_tasks[message_id] = task
@dp.callback_query(lambda c: c.data and c.data.startswith("confirm_") and not c.data.startswith("confirm_list_") and not c.data.startswith("confirm_all_"), AdminCallbackFilter())
async def confirm_trades(callback: CallbackQuery):
await callback.answer()
bot_name = callback.data.replace("confirm_", "")
try:
response = requests.post(f"http://127.0.0.1:1242/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations/Accept", headers={"Authentication": ASF_PASSWORD})
if response.status_code != 200:
await callback.answer("Ошибка HTTP", show_alert=True)
return
await callback.answer("Подтверждено", show_alert=True)
# обновляем экран
await twofa_handler(callback)
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data == "plugins", AdminCallbackFilter())
async def plugins_handler(callback: CallbackQuery):
await stop_twofa_task(callback.message.message_id)
await callback.answer()
try:
response = requests.get("http://127.0.0.1:1242/Api/Plugins", headers={"Authentication": ASF_PASSWORD})
if response.status_code != 200:
await callback.message.edit_text("Ошибка API")
return
data = response.json()
if not data.get("Success"):
await callback.message.edit_text("ASF вернул ошибку")
return
plugins = data.get("Result", [])
if not plugins:
text = "Плагины не установлены"
else:
text = "Плагины ASF:\n\n"
for plugin in plugins:
name = plugin.get("Name", "???")
version = plugin.get("Version", "???")
text += f"{name} ({version})\n"
await callback.message.edit_text(
text.strip(),
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data="back")]]))
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data and c.data.startswith("confirm_list_"), AdminCallbackFilter())
async def confirm_list(callback: CallbackQuery):
await stop_twofa_task(callback.message.message_id)
await callback.answer()
if not callback.data:
return
bot_name = callback.data.replace("confirm_list_", "")
try:
confirmations = await get_confirmations(bot_name)
if not confirmations:
text = "Подтверждений нет"
else:
text = f" {bot_name}\n\nПодтверждения:\n\n"
for conf in confirmations:
text += (
f"- {conf['type_name']}\n"
f"ID: <code>{conf['id']}</code>\n\n"
)
await callback.message.edit_text(text, parse_mode="HTML",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="Подтвердить всё", callback_data=f"confirm_all_{bot_name}")],
[InlineKeyboardButton(text="Назад", callback_data=f"2fa_{bot_name}")]]))
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
@dp.callback_query(lambda c: c.data and c.data.startswith("confirm_all_"), AdminCallbackFilter())
async def confirm_all(callback: CallbackQuery):
await stop_twofa_task(callback.message.message_id)
await callback.answer()
if not callback.data:
return
bot_name = callback.data.replace("confirm_all_", "")
try:
response = requests.post(f"http://127.0.0.1:1242/Api/Bot/{bot_name}/TwoFactorAuthentication/Confirmations/Accept", headers={"Authentication": ASF_PASSWORD})
if response.status_code != 200:
await callback.answer("Ошибка HTTP", show_alert=True)
return
# заново получаем список
confirmations = await get_confirmations(bot_name)
if not confirmations:
text = f" {bot_name}\n\nПодтверждений больше нет"
else:
text = f" {bot_name}\n\nПодтверждения:\n\n"
for conf in confirmations:
text += (
f"- {conf['type_name']}\n"
f"ID: <code>{conf['id']}</code>\n\n"
)
await callback.message.edit_text(
text,
parse_mode="HTML",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="Подтвердить всё", callback_data=f"confirm_all_{bot_name}")],
[InlineKeyboardButton(text="Назад", callback_data=f"2fa_{bot_name}")]]))
await callback.answer(" Все подтверждено", show_alert=True)
except Exception as e:
await callback.message.edit_text(f"Ошибка: {e}")
def is_bot_playing(bot_name: str) -> bool:
response = requests.get("http://127.0.0.1:1242/Api/Bot/ASF", headers={"Authentication": ASF_PASSWORD})
if response.status_code != 200:
return False
data = response.json()
bot = data.get("Result", {}).get(bot_name, {})
# правильный способ
return bool(bot.get("PlayingBlocked", False)) or bool(bot.get("CurrentGamesPlayed", []))
def is_idle_enabled(bot_name: str, app_id: int) -> bool:
response = requests.get(f"http://127.0.0.1:1242/Api/Bot/{bot_name}", headers={"Authentication": ASF_PASSWORD})
if response.status_code != 200:
return False
data = response.json()
bot = data.get("Result", {}).get(bot_name, {})
config = bot.get("BotConfig", {})
return app_id in config.get("GamesPlayedWhileIdle", [])
async def reload_bot(bot_name: str):
try:
requests.post("http://127.0.0.1:1242/Api/Command", headers={"Authentication": ASF_PASSWORD}, json={"Command": f"!reload {bot_name}"})
except:
pass
@dp.callback_query(lambda c: c.data and c.data.startswith("farm|"), AdminCallbackFilter())
async def farm_game(callback: CallbackQuery):
await callback.answer()
if not callback.data:
return
_, bot_name, app_id = callback.data.split("|")
app_id = int(app_id)
try:
# получаем текущий конфиг
response = requests.get(f"http://127.0.0.1:1242/Api/Bot/{bot_name}", headers={"Authentication": ASF_PASSWORD})
data = response.json()
bot_data = data.get("Result", {}).get(bot_name, {})
config = bot_data.get("BotConfig", {})
idle_games = config.get("GamesPlayedWhileIdle", [])
# переключение
if app_id in idle_games:
idle_games.remove(app_id)
action = "Idle выключен"
else:
idle_games.append(app_id)
action = "Idle включен"
# обновляем конфиг
config["GamesPlayedWhileIdle"] = idle_games
save = requests.post(
f"http://127.0.0.1:1242/Api/Bot/{bot_name}", headers={"Authentication": ASF_PASSWORD}, json={"BotConfig": config})
if save.status_code != 200:
await callback.answer("Ошибка сохранения", show_alert=True)
return
asyncio.create_task(reload_bot(bot_name))
await callback.answer(action, show_alert=True)
try:
await callback.message.edit_reply_markup(reply_markup=games_keyboard(bot_name, app_id in idle_games))
except:
pass
except Exception as e:
await callback.answer(f"Ошибка: {e}", show_alert=True)
@dp.callback_query(lambda c: c.data and c.data.startswith("inventory_"), AdminCallbackFilter())
async def inventory_menu(callback: CallbackQuery):
await callback.answer()
bot_name = callback.data.replace("inventory_", "")
await callback.message.edit_text("Проверка inventory...")
cs2_inventory = await get_inventory(bot_name, 730, 2)
steam_inventory = await get_inventory(bot_name, 753, 6)
cs2_assets = []
steam_assets = []
try:
cs2_assets = (cs2_inventory.get(bot_name, {}).get("Assets", []))
except:
pass
try:
steam_assets = (steam_inventory.get(bot_name, {}).get("Assets", []))
except:
pass
if not cs2_assets and not steam_assets:
await callback.message.edit_text(f"📦 Инвентарь {bot_name} пуст", reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data=f"bot_{bot_name}")]]))
return
keyboard = []
if cs2_assets:
keyboard.append([InlineKeyboardButton(text=f"CS2 ({len(cs2_assets)})", callback_data=f"inv_cs2_{bot_name}")])
if steam_assets:
keyboard.append([InlineKeyboardButton(text=f"Steam ({len(steam_assets)})", callback_data=f"inv_steam_{bot_name}")])
keyboard.append([InlineKeyboardButton(text="Назад", callback_data=f"bot_{bot_name}")])
await callback.message.edit_text(
(
f"📦 Инвентарь {bot_name}\n\n"
"🔄 — можно трейдить\n"
"💰 — можно продавать\n"
"🔒 — нельзя продавать\n"
"❌ — нельзя трейдить"
),
reply_markup=InlineKeyboardMarkup(inline_keyboard=keyboard)
)
@dp.callback_query(lambda c: c.data and c.data.startswith("inv_cs2_"), AdminCallbackFilter())
async def cs2_inventory(callback: CallbackQuery):
await callback.answer()
bot_name = callback.data.replace("inv_cs2_", "")
await render_inventory_page(callback, bot_name, "cs2", 730, 2, 0)
@dp.callback_query(lambda c: c.data and c.data.startswith("inv_steam_"), AdminCallbackFilter())
async def steam_inventory(callback: CallbackQuery):
await callback.answer()
bot_name = callback.data.replace("inv_steam_", "")
await render_inventory_page(callback, bot_name, "steam", 753, 6, 0)
@dp.callback_query(lambda c: c.data and c.data.startswith("invpage|"), AdminCallbackFilter())
async def inventory_page(callback: CallbackQuery):
await callback.answer()
_, inv_type, bot_name, page = callback.data.split("|")
page = int(page)
if inv_type == "cs2":
await render_inventory_page(callback, bot_name, "cs2", 730, 2, page)
else:
await render_inventory_page(callback, bot_name, "steam", 753, 6, page)
async def redeem_key(bot_name: str, key: str):
async with aiohttp.ClientSession() as session:
async with session.post("http://127.0.0.1:1242/Api/Command", headers={"Authentication": ASF_PASSWORD}, json={"Command": f"!redeem {bot_name} {key}"}) as response:
if response.status != 200:
return False, f"HTTP_ERROR_{response.status}"
data = await response.json()
if not data.get("Success"):
return False, "ASF_ERROR"
return True, str(data.get("Result", ""))
@dp.callback_query(lambda c: c.data == "redeem_keys", AdminCallbackFilter())
async def redeem_keys_menu(callback: CallbackQuery):
await callback.answer()
redeem_users.add(callback.from_user.id)
redeem_messages[callback.from_user.id] = callback.message
await callback.message.edit_text(
"Отправьте ключ Steam для активации\n\n"
"Можно отправить сразу несколько ключей.\n\n"
"Пример:\n"
"<code>AAAAA-BBBBB-CCCCC\nDDDDD-EEEEE-FFFFF</code>",
parse_mode="HTML",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data="back")]]))
@dp.callback_query(lambda c: c.data and c.data.startswith("redeem_bot_"), AdminCallbackFilter())
async def redeem_bot_menu(callback: CallbackQuery):
await callback.answer()
bot_name = callback.data.replace("redeem_bot_", "")
bot_redeem_users[callback.from_user.id] = bot_name
bot_redeem_messages[callback.from_user.id] = callback.message
await callback.message.edit_text(
f"Отправьте ключ Steam для активации на аккаунте:\n"
f"<code>{bot_name}</code>\n\n"
f"Можно отправить сразу несколько ключей.",
parse_mode="HTML",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data=f"bot_{bot_name}")]]))
@dp.message(AdminFilter())
async def text_router(message: Message):
# console mode
if message.from_user.id in console_users:
command = message.text.strip()
try:
await message.delete()
except:
pass
try:
response = requests.post("http://127.0.0.1:1242/Api/Command", headers={"Authentication": ASF_PASSWORD}, json={"Command": f"!{command}"})
if response.status_code != 200:
text = f"Ошибка HTTP {response.status_code}"
else:
data = response.json()
if not data.get("Success"):
text = "Ошибка ASF"
else:
result = html.escape(str(data.get("Result", "Нет ответа")))
text = (
f"<b>Команда:</b> <code>{command}</code>\n\n"
f"<b>Ответ:</b>\n<code>{result}</code>"
)
await message.answer(text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Удалить", callback_data="delete_msg")]]))
except Exception as e:
await message.answer(f"Ошибка: {e}")
return
# single bot redeem mode
if message.from_user.id in bot_redeem_users:
bot_name = bot_redeem_users.pop(message.from_user.id)
menu_message = bot_redeem_messages.pop(message.from_user.id, None)
if menu_message:
try:
response = requests.get("http://127.0.0.1:1242/Api/Bot/ASF",headers={"Authentication": ASF_PASSWORD})
data = response.json()
bot_data = data.get("Result", {}).get(bot_name)
if bot_data:
await menu_message.edit_text(
format_bot_ui(bot_data),
parse_mode="HTML",
disable_web_page_preview=True,
reply_markup=InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="2FA", callback_data=f"2fa_{bot_name}")]
if bot_data.get("HasMobileAuthenticator") else [],
[InlineKeyboardButton(text="Игры", callback_data=f"games_{bot_name}")],
[InlineKeyboardButton(text="Активировать ключ", callback_data=f"redeem_bot_{bot_name}")],
[InlineKeyboardButton(text="Инвентарь", callback_data=f"inventory_{bot_name}")],
[InlineKeyboardButton(text="Назад", callback_data="bots")]]))
except:
pass
try:
await bot.delete_message(chat_id=message.chat.id, message_id=message.message_id)
except:
pass
raw_text = message.text.upper()
keys = re.findall(r"[A-Z0-9]{5}(?:-[A-Z0-9]{5}){2}", raw_text)
keys = list(dict.fromkeys(keys))
if not keys:
await message.answer("Ключи не найдены")
return
checking = await message.answer(
f"Аккаунт: <code>{bot_name}</code>\n"
f"Найдено ключей: {len(keys)}\n"
f"Начинаю активацию...",
parse_mode="HTML")
success_count = 0
failed_count = 0
results = []
for key in keys:
success, result = await redeem_key(bot_name, key)
await asyncio.sleep(2)
if not success or not result:
failed_count += 1
results.append(f"{key}: ASF ERROR")
continue
result_lower = result.lower()
if "ok/nodetail" in result_lower or "ok/" in result_lower:
success_count += 1
results.append(f"{key}: активировано")
elif "ratelimited" in result_lower:
failed_count += 1
results.append(f"{key}: rate limit")
elif "alreadypurchased" in result_lower:
failed_count += 1
results.append(f"⚠️ {key}: игра уже есть")
elif "duplicateactivationcode" in result_lower:
failed_count += 1
results.append(f"{key}: ключ уже использован")
elif "regionlocked" in result_lower:
failed_count += 1
results.append(f"🌍 {key}: регион лок")
elif "invalidactivationcode" in result_lower or "invalid" in result_lower:
failed_count += 1
results.append(f"{key}: неверный ключ")
else:
failed_count += 1
results.append(f"{key}: неизвестный ответ")
text = (
f"Результат активации для <code>{bot_name}</code>\n\n"
f"✅ Успешно: {success_count}\n"
f"❌ Ошибок: {failed_count}\n\n"
+ "\n".join(results[:50]))
if len(text) > 4000:
text = text[:4000] + "\n\n...обрезано"
await checking.edit_text(text, parse_mode="HTML")
return
# redeem mode
if message.from_user.id in redeem_users:
redeem_users.discard(message.from_user.id)
menu_message = redeem_messages.pop(message.from_user.id, None)
if menu_message:
try:
await menu_message.edit_text(get_asf_status_text(), reply_markup=main_keyboard())
except:
pass
try:
await bot.delete_message(chat_id=message.chat.id, message_id=message.message_id)
except:
pass
raw_text = message.text.upper()
keys = re.findall(r"[A-Z0-9]{5}(?:-[A-Z0-9]{5}){2}", raw_text)
keys = list(dict.fromkeys(keys))
if not keys:
await message.answer("Ключи не найдены")
return
checking = await message.answer(f"Найдено ключей: {len(keys)}\nНачинаю активацию...")
try:
response = requests.get("http://127.0.0.1:1242/Api/Bot/ASF", headers={"Authentication": ASF_PASSWORD})
data = response.json()
bots = list(data.get("Result", {}).keys())
success_count = 0
failed_count = 0
results = []
for key in keys:
activated = False
key_report = []
for bot_name in bots:
success, result = await redeem_key(bot_name, key)
await asyncio.sleep(2)
if not success or not result:
key_report.append(f"{bot_name}: ASF ERROR")
continue
print(result)
result_lower = result.lower()
bot_line = ""
for line in result.splitlines():
if f"<{bot_name.lower()}>" in line.lower():
bot_line = line
break
if bot_line:
parsed = bot_line.lower()
else:
parsed = result_lower
if "ok/nodetail" in parsed or "ok/" in parsed:
key_report.append(f"{bot_name}: активировано")
success_count += 1
activated = True
break
elif "ratelimited" in parsed:
key_report.append(f"{bot_name}: rate limit")
continue
elif "alreadypurchased" in parsed or "already purchased" in parsed:
key_report.append(f"⚠️ {bot_name}: игра уже есть")
continue
elif "duplicateactivationcode" in parsed:
key_report.append(f"{bot_name}: ключ уже использован")
break
elif "regionlocked" in parsed:
key_report.append(f"🌍 {bot_name}: регион лок")
continue
elif "invalidactivationcode" in parsed or "invalid" in parsed:
key_report.append(f"{bot_name}: неверный ключ")
break
else:
key_report.append(f"{bot_name}: неизвестный ответ")
continue
if not activated:
failed_count += 1
results.append(f"\n🔑 {key}\n" + "\n".join(key_report))
text = (
f"Результат активации\n\n"
f"✅ Успешно: {success_count}\n"
f"❌ Ошибок: {failed_count}\n\n"
+ "\n".join(results[:50]))
if len(text) > 4000:
text = text[:4000] + "\n\n...обрезано"
await checking.edit_text(text)
except Exception as e:
await checking.edit_text(f"Ошибка: {e}")
return
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())