import discord import aiohttp import logging from redbot.core import app_commands, commands, Config from discord.ext import tasks log = logging.getLogger("red.bierbaron") API_URL = "https://live.der-bierbaron.de/api/vrchat.json" class Bierbaron(commands.Cog): """Bierbaron Commands and Stats.""" def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, identifier=9483726154, force_registration=True) self.config.register_guild(live_channel=None, live_message=None) self.live_update_task.start() def cog_unload(self): self.live_update_task.cancel() async def _fetch_api_data(self): """Fetches data from the Bierbaron API. Returns None on failure.""" try: async with aiohttp.ClientSession() as session: async with session.get(API_URL, timeout=aiohttp.ClientTimeout(total=10)) as response: if response.status != 200: log.warning(f"Bierbaron API returned {response.status}") return None return await response.json() except Exception as e: log.error(f"Error fetching Bierbaron API: {e}") return None def _build_live_embed(self, data: dict) -> discord.Embed: """Builds the live stats embed from API data.""" server_info = data.get("server", {}) aggregate = data.get("aggregate", {}) users = data.get("users", []) last_prost = data.get("lastProst", {}) online_players = server_info.get("onlinePlayers", 0) avg_vol = aggregate.get("averageVolume_ml", 0) avg_fill = aggregate.get("averageFillLevel", 0) embed = discord.Embed( title="🍺 der-Bierbaron – Live Server", description=( "Willkommen auf dem **Live-Server des BierbarΒ­ons**!\n" "Hier ΓΌbertragen alle aktiven Teilnehmer ihren FΓΌllstand\n" "fast in Echtzeit aus VRChat. Trinkt ihr mit?\n\n" "[🌐 Webseite](https://der-bierbaron.de) Β· " "[πŸ“‘ Live-Dashboard](https://live.der-bierbaron.de)" ), color=discord.Color.from_rgb(255, 140, 0), timestamp=discord.utils.utcnow() ) embed.set_footer(text="Aktualisiert alle 15 Sek. Β· der-Bierbaron Live") # Server Stats embed.add_field(name="πŸ‘₯ Spieler Online", value=f"**{online_players}**", inline=True) embed.add_field(name="πŸ§ͺ Ø Volumen", value=f"**{avg_vol:.1f} ml**", inline=True) embed.add_field(name="πŸ“Š Ø FΓΌllstand", value=f"**{avg_fill:.1f} %**", inline=True) # User table if users: lines = [] for idx, u in enumerate( sorted(users, key=lambda x: x.get("drinkedToday_ml", 0), reverse=True)[:10] ): name = u.get("username", "Unknown") bev = u.get("beverage", "?") drank = u.get("drinkedToday_ml", 0) fill = u.get("fillLevel", 0) # Simple fill bar (10 cells) fill_pct = max(0, min(fill, 100)) filled_cells = round(fill_pct / 10) bar = "🟧" * filled_cells + "⬛" * (10 - filled_cells) lines.append(f"**{idx + 1}. {name}** – {bev}\n{bar} `{fill:.1f}%` Β· {drank} ml heute") embed.add_field( name=f"πŸ† Trinker Online ({len(users)})", value="\n".join(lines) or "–", inline=False ) else: embed.add_field(name="πŸ† Trinker Online", value="Derzeit niemand aktiv.", inline=False) # Last Prost if last_prost: sender = last_prost.get("sender", "?") recipient = last_prost.get("recipient", "?") msg = last_prost.get("message", "Prost!") embed.add_field( name="🍻 Letztes Prost", value=f"**{sender}** trank auf **{recipient}**:\n*β€ž{msg}"*", inline=False ) return embed # ── Background task ────────────────────────────────────────────────────── @tasks.loop(seconds=15) async def live_update_task(self): """Updates all registered live embeds every 15 seconds.""" data = await self._fetch_api_data() if not data: return embed = self._build_live_embed(data) all_guilds = await self.config.all_guilds() for guild_id, cfg in all_guilds.items(): channel_id = cfg.get("live_channel") message_id = cfg.get("live_message") if not channel_id or not message_id: continue guild = self.bot.get_guild(guild_id) if not guild: continue channel = guild.get_channel(channel_id) if not channel: continue try: message = await channel.fetch_message(message_id) await message.edit(embed=embed) except discord.NotFound: # Message deleted – clear config await self.config.guild(guild).live_message.set(None) await self.config.guild(guild).live_channel.set(None) log.info(f"Live embed message not found in guild {guild_id}, cleared config.") except discord.Forbidden: log.warning(f"Missing permission to edit live embed in guild {guild_id}.") except Exception as e: log.error(f"Error updating live embed in guild {guild_id}: {e}") @live_update_task.before_loop async def before_live_update_task(self): await self.bot.wait_until_red_ready() # ── Slash Commands ──────────────────────────────────────────────────────── @app_commands.command(name="bierbaron") async def info(self, interaction: discord.Interaction): """Informationen ΓΌber den Bierbaron.""" embed = discord.Embed( title="der-Bierbaron", description=( "Willkommen beim offiziellen **Bierbaron** Discord-System.\n" "Hier dreht sich alles ums Trinken in VRChat – GlΓ€ser, Scales und Echtzeit-Stats." ), color=discord.Color.gold(), url="https://der-bierbaron.de" ) embed.add_field(name="🌐 Webseite", value="[der-bierbaron.de](https://der-bierbaron.de)", inline=True) embed.add_field(name="πŸ“‘ Live Stats", value="[live.der-bierbaron.de](https://live.der-bierbaron.de)", inline=True) embed.set_footer(text="Bierbaron VRChat Projekt") await interaction.response.send_message(embed=embed) @app_commands.command(name="bierbaron-live") async def live(self, interaction: discord.Interaction): """Zeige einen einmaligen Live-Snapshot des Bierbaron-Servers.""" await interaction.response.defer() data = await self._fetch_api_data() if data is None: return await interaction.followup.send( "❌ Fehler beim Abrufen der Bierbaron-API. Bitte spΓ€ter erneut versuchen." ) await interaction.followup.send(embed=self._build_live_embed(data)) # ── Bot-Owner-only management commands ─────────────────────────────────── @app_commands.command(name="bierbaron-setlive") @app_commands.describe(channel="Kanal fΓΌr das automatisch aktualisierende Live-Embed.") async def setlive(self, interaction: discord.Interaction, channel: discord.TextChannel): """[Bot-Owner] Startet das permanente Live-Embed in einem Kanal (alle 15 Sek.).""" if not await self.bot.is_owner(interaction.user): return await interaction.response.send_message( "β›” Nur der Bot-Owner kann diesen Befehl verwenden.", ephemeral=True ) await interaction.response.defer(ephemeral=True) # Remove old embed if present old_ch_id = await self.config.guild(interaction.guild).live_channel() old_msg_id = await self.config.guild(interaction.guild).live_message() if old_ch_id and old_msg_id: old_ch = interaction.guild.get_channel(old_ch_id) if old_ch: try: old_msg = await old_ch.fetch_message(old_msg_id) await old_msg.delete() except discord.NotFound: pass data = await self._fetch_api_data() if data is None: return await interaction.followup.send("❌ Konnte die API nicht erreichen.") try: msg = await channel.send(embed=self._build_live_embed(data)) await self.config.guild(interaction.guild).live_channel.set(channel.id) await self.config.guild(interaction.guild).live_message.set(msg.id) await interaction.followup.send( f"βœ… Live-Embed in {channel.mention} gestartet. Es wird alle **15 Sekunden** aktualisiert." ) except discord.Forbidden: await interaction.followup.send("❌ Ich habe keine Berechtigung, in diesem Kanal zu schreiben.") except Exception as e: await interaction.followup.send(f"❌ Fehler: {e}") @app_commands.command(name="bierbaron-stoplive") async def stoplive(self, interaction: discord.Interaction): """[Bot-Owner] Stoppt das permanente Live-Embed auf diesem Server.""" if not await self.bot.is_owner(interaction.user): return await interaction.response.send_message( "β›” Nur der Bot-Owner kann diesen Befehl verwenden.", ephemeral=True ) await interaction.response.defer(ephemeral=True) channel_id = await self.config.guild(interaction.guild).live_channel() message_id = await self.config.guild(interaction.guild).live_message() if not channel_id: return await interaction.followup.send("ℹ️ Es lΓ€uft kein Live-Embed auf diesem Server.") # Try to delete the embed message channel = interaction.guild.get_channel(channel_id) if channel and message_id: try: msg = await channel.fetch_message(message_id) await msg.delete() except discord.NotFound: pass except discord.Forbidden: await interaction.followup.send( "⚠️ Konnte die Live-Embed-Nachricht nicht lΓΆschen (fehlende Berechtigung), " "aber das Auto-Update wurde deaktiviert." ) await self.config.guild(interaction.guild).live_channel.set(None) await self.config.guild(interaction.guild).live_message.set(None) await interaction.followup.send("βœ… Live-Embed wurde gestoppt und die Nachricht gelΓΆscht.")