#!/usr/bin/env python3 import asyncio import discord from discord import app_commands from discord.ext import commands from app_config import get_settings from movie_pipeline import get_upcoming_movie_records from embed_builder import build_movie_embed, make_links_view async def fetch_records(locale: str, tmdb_token: str, schedule_token: str) -> list[dict]: return await asyncio.to_thread( get_upcoming_movie_records, locale, None, schedule_token, tmdb_token, ) def create_bot() -> commands.Bot: settings = get_settings() intents = discord.Intents.default() bot = commands.Bot(command_prefix="!", intents=intents) guild_obj = discord.Object(id=int(settings.discord_guild_id)) if settings.discord_guild_id.isdigit() else None async def sync_commands() -> None: if guild_obj: try: # Remove stale global commands to avoid duplicate /anime entries. cleared_global = await bot.tree.sync() cleared_names = ", ".join(cmd.name for cmd in cleared_global) if cleared_global else "keine" print(f"Globale Slash Commands aktualisiert: {len(cleared_global)} ({cleared_names})") synced = await bot.tree.sync(guild=guild_obj) names = ", ".join(cmd.name for cmd in synced) if synced else "keine" print(f"Slash Commands fuer Guild synchronisiert: {len(synced)} ({names})") return except Exception as exc: print(f"Guild-Sync fehlgeschlagen, nutze globalen Sync: {exc}") synced = await bot.tree.sync() names = ", ".join(cmd.name for cmd in synced) if synced else "keine" print(f"Globale Slash Commands synchronisiert: {len(synced)} ({names})") print("Hinweis: Globale Slash Commands koennen bis zu 60 Minuten brauchen.") print("Setze DISCORD_GUILD_ID fuer sofortige Verfuegbarkeit in deinem Server.") @bot.event async def setup_hook() -> None: await sync_commands() @bot.event async def on_ready() -> None: print(f"Bot online als {bot.user}") async def anime_handler(interaction: discord.Interaction, limit: app_commands.Range[int, 1, 25] = 10) -> None: locale = settings.locale safe_limit = int(limit) await interaction.response.defer(thinking=True) try: records = await fetch_records(locale, settings.tmdb_read_access_token, settings.animeschedule_api_token) except Exception as exc: await interaction.followup.send(f"Fehler beim Abruf: {exc}") return if not records: await interaction.followup.send("Keine Anime Filme fuer diesen und naechsten Monat gefunden.") return selected = records[:safe_limit] await interaction.followup.send(f"Gefunden: {len(selected)} Anime-Filme") for idx, item in enumerate(selected, start=1): embed = build_movie_embed(item, idx) view = make_links_view(str(item.get("anilist_url", ""))) if view is None: await interaction.followup.send(embed=embed) else: await interaction.followup.send(embed=embed, view=view) anime_handler = app_commands.describe(limit="Anzahl Ergebnisse (1-25)")(anime_handler) if guild_obj: bot.tree.command( name="anime", description="Zeigt kommende Anime-Filme", guild=guild_obj, )(anime_handler) else: bot.tree.command( name="anime", description="Zeigt kommende Anime-Filme", )(anime_handler) return bot def main() -> int: settings = get_settings() token = settings.discord_bot_token if not token: print("Fehler: DISCORD_BOT_TOKEN ist nicht gesetzt.") return 1 bot = create_bot() bot.run(token) return 0 if __name__ == "__main__": raise SystemExit(main())