import asyncio import os from typing import Any import aiohttp from aiogram import Bot, Dispatcher, F from aiogram.filters import Command from aiogram.types import ( Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, ) API_URL = os.getenv("API_URL", "http://backend:8000") BOT_TOKEN = os.getenv("BOT_TOKEN") if not BOT_TOKEN: raise ValueError("BOT_TOKEN environment variable is required") bot = Bot(token=BOT_TOKEN) dp = Dispatcher() # User preferences storage (in-memory, resets on restart) user_prefs: dict[int, dict[str, Any]] = {} def get_user_prefs(user_id: int) -> dict[str, Any]: if user_id not in user_prefs: user_prefs[user_id] = { "include_skills": True, "include_aspect": True, "items_count": 6, } return user_prefs[user_id] def format_skill(skill: str) -> str: skill_map = { "q": "Q", "w": "W", "e": "E", "r": "R", "left_talent": "L", "right_talent": "R", } return skill_map.get(skill, skill) def format_build(data: dict, is_daily: bool = False) -> str: lines = [] if is_daily: lines.append(f"šŸ“… Build of the Day ({data.get('date', 'N/A')})") else: lines.append("šŸŽ² Random Build") lines.append("") # Hero hero = data["hero"] attr_emoji = {"strength": "šŸ’Ŗ", "agility": "šŸƒ", "intelligence": "🧠"} emoji = attr_emoji.get(hero["primary"], "") lines.append(f"🦸 Hero: {hero['name']} {emoji}") # Items items = [item["name"] for item in data["items"]] lines.append(f"\nšŸŽ’ Items:") for item in items: lines.append(f" • {item}") # Skill build if "skillBuild" in data and data["skillBuild"]: skill_build = data["skillBuild"] levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 25] skills_str = " ".join( format_skill(skill_build.get(str(lvl), "-")) for lvl in levels ) lines.append(f"\nšŸ“Š Skill Build:") lines.append(f"{skills_str}") lines.append("Levels: 1-16, 18, 20, 25") # Aspect if "aspect" in data and data["aspect"]: lines.append(f"\n✨ Aspect: {data['aspect']}") return "\n".join(lines) def get_settings_keyboard(user_id: int) -> InlineKeyboardMarkup: prefs = get_user_prefs(user_id) skills_text = "āœ… Skills" if prefs["include_skills"] else "āŒ Skills" aspect_text = "āœ… Aspect" if prefs["include_aspect"] else "āŒ Aspect" return InlineKeyboardMarkup( inline_keyboard=[ [ InlineKeyboardButton(text=skills_text, callback_data="toggle_skills"), InlineKeyboardButton(text=aspect_text, callback_data="toggle_aspect"), ], [ InlineKeyboardButton( text=f"Items: {prefs['items_count']}", callback_data="items_count" ), ], [ InlineKeyboardButton(text="šŸŽ² Generate", callback_data="generate"), ], ] ) def get_main_keyboard() -> InlineKeyboardMarkup: return InlineKeyboardMarkup( inline_keyboard=[ [ InlineKeyboardButton(text="šŸŽ² Random Build", callback_data="random"), InlineKeyboardButton(text="šŸ“… Build of Day", callback_data="daily"), ], [ InlineKeyboardButton(text="āš™ļø Settings", callback_data="settings"), ], ] ) @dp.message(Command("start")) async def cmd_start(message: Message): await message.answer( "šŸŽ® Dota 2 Random Build Generator\n\n" "Generate random builds for your Dota 2 challenges!\n\n" "Commands:\n" "/random - Generate random build\n" "/daily - Get build of the day\n" "/settings - Configure options", parse_mode="HTML", reply_markup=get_main_keyboard(), ) @dp.message(Command("random")) async def cmd_random(message: Message): await generate_random_build(message) @dp.message(Command("daily")) async def cmd_daily(message: Message): await get_daily_build(message) @dp.message(Command("settings")) async def cmd_settings(message: Message): await message.answer( "āš™ļø Settings\n\nConfigure your random build options:", parse_mode="HTML", reply_markup=get_settings_keyboard(message.from_user.id), ) @dp.callback_query(F.data == "random") async def callback_random(callback: CallbackQuery): await callback.answer() await generate_random_build(callback.message, callback.from_user.id) @dp.callback_query(F.data == "daily") async def callback_daily(callback: CallbackQuery): await callback.answer() await get_daily_build(callback.message) @dp.callback_query(F.data == "settings") async def callback_settings(callback: CallbackQuery): await callback.answer() await callback.message.edit_text( "āš™ļø Settings\n\nConfigure your random build options:", parse_mode="HTML", reply_markup=get_settings_keyboard(callback.from_user.id), ) @dp.callback_query(F.data == "toggle_skills") async def callback_toggle_skills(callback: CallbackQuery): prefs = get_user_prefs(callback.from_user.id) prefs["include_skills"] = not prefs["include_skills"] await callback.answer( f"Skills: {'enabled' if prefs['include_skills'] else 'disabled'}" ) await callback.message.edit_reply_markup( reply_markup=get_settings_keyboard(callback.from_user.id) ) @dp.callback_query(F.data == "toggle_aspect") async def callback_toggle_aspect(callback: CallbackQuery): prefs = get_user_prefs(callback.from_user.id) prefs["include_aspect"] = not prefs["include_aspect"] await callback.answer( f"Aspect: {'enabled' if prefs['include_aspect'] else 'disabled'}" ) await callback.message.edit_reply_markup( reply_markup=get_settings_keyboard(callback.from_user.id) ) @dp.callback_query(F.data == "items_count") async def callback_items_count(callback: CallbackQuery): prefs = get_user_prefs(callback.from_user.id) # Cycle through 3, 4, 5, 6 prefs["items_count"] = (prefs["items_count"] % 4) + 3 await callback.answer(f"Items count: {prefs['items_count']}") await callback.message.edit_reply_markup( reply_markup=get_settings_keyboard(callback.from_user.id) ) @dp.callback_query(F.data == "generate") async def callback_generate(callback: CallbackQuery): await callback.answer() await generate_random_build(callback.message, callback.from_user.id) async def generate_random_build(message: Message, user_id: int = None): if user_id is None: user_id = message.from_user.id prefs = get_user_prefs(user_id) payload = { "includeSkills": prefs["include_skills"], "includeAspect": prefs["include_aspect"], "itemsCount": prefs["items_count"], } try: async with aiohttp.ClientSession() as session: async with session.post( f"{API_URL}/api/randomize", json=payload ) as response: if response.status == 200: data = await response.json() await message.answer( format_build(data), parse_mode="HTML", reply_markup=get_main_keyboard(), ) else: await message.answer( "āŒ Failed to generate build. Try again later.", reply_markup=get_main_keyboard(), ) except Exception as e: await message.answer( f"āŒ Error connecting to server: {e}", reply_markup=get_main_keyboard(), ) async def get_daily_build(message: Message): try: async with aiohttp.ClientSession() as session: async with session.get(f"{API_URL}/api/build-of-day") as response: if response.status == 200: data = await response.json() await message.answer( format_build(data, is_daily=True), parse_mode="HTML", reply_markup=get_main_keyboard(), ) else: await message.answer( "āŒ Failed to get daily build. Try again later.", reply_markup=get_main_keyboard(), ) except Exception as e: await message.answer( f"āŒ Error connecting to server: {e}", reply_markup=get_main_keyboard(), ) async def main(): print("Bot started!") await dp.start_polling(bot) if __name__ == "__main__": asyncio.run(main())