Files
dota-random-builds/dota-random-builds-bot/bot.py
2025-12-16 22:34:40 +03:00

289 lines
8.8 KiB
Python

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"📅 <b>Build of the Day</b> ({data.get('date', 'N/A')})")
else:
lines.append("🎲 <b>Random Build</b>")
lines.append("")
# Hero
hero = data["hero"]
attr_emoji = {"strength": "💪", "agility": "🏃", "intelligence": "🧠"}
emoji = attr_emoji.get(hero["primary"], "")
lines.append(f"🦸 <b>Hero:</b> {hero['name']} {emoji}")
# Items
items = [item["name"] for item in data["items"]]
lines.append(f"\n🎒 <b>Items:</b>")
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📊 <b>Skill Build:</b>")
lines.append(f"<code>{skills_str}</code>")
lines.append("<i>Levels: 1-16, 18, 20, 25</i>")
# Aspect
if "aspect" in data and data["aspect"]:
lines.append(f"\n✨ <b>Aspect:</b> {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(
"🎮 <b>Dota 2 Random Build Generator</b>\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(
"⚙️ <b>Settings</b>\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(
"⚙️ <b>Settings</b>\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())