import os from pathlib import Path import discord from redbot.core import commands from redbot.core.data_manager import bundled_data_path from .activity_logger import log_activity class FitnessCog(commands.Cog): def __init__(self, bot): self.bot = bot default_threads = "1457402451530350727,1328363409648910356" threads_raw = os.getenv("THREADS_ID", default_threads) self.threads_id = [] for x in threads_raw.split(","): x = x.strip() if x.isdigit(): self.threads_id.append(int(x)) self.confirm_reactions = os.getenv("CONFIRMATION_REACTIONS", "🐈💨") self.bundled_activities_dir: Path = bundled_data_path(self) / "activities" # ---- path helpers ---- def activity_units_path(self, activity: str) -> Path: return self.bundled_activities_dir / f"{activity}.json" def activity_log_path(self, activity: str) -> Path: return self.bundled_activities_dir / f"{activity}_log.json" @commands.Cog.listener() async def on_message(self, message: discord.Message): if message.author.bot: return if message.channel.id not in self.threads_id: return content = (message.content or "").strip() if not content.startswith("!"): return cmdline = content[1:].strip().lower() if not cmdline: return cmd, *rest = cmdline.split(" ", 1) arg = rest[0].strip() if rest else "" if cmd == "getunits": await self.get_units_file(message, arg) return if cmd == "getlog": await self.get_log_file(message, arg) return await self.log_fitness(message) async def get_units_file(self, message: discord.Message, activity: str): try: if not activity: await message.channel.send( "Usage: `!getunits ` (example: `!getunits pushups`)" ) return path = self.activity_units_path(activity) if not path.exists(): await message.channel.send(f"No units JSON found for `{activity}`.") return await message.channel.send(file=discord.File(fp=str(path))) except Exception as e: await message.channel.send(str(e)) async def get_log_file(self, message: discord.Message, activity: str): try: if not activity: await message.channel.send( "Usage: `!getlog ` (example: `!getlog pushups`)" ) return path = self.activity_log_path(activity) if not path.exists(): await message.channel.send(f"No log JSON found for `{activity}`.") return await message.channel.send(file=discord.File(fp=str(path))) except Exception as e: await message.channel.send(str(e)) async def log_fitness(self, message: discord.Message): try: parts = message.content[1:].strip().split() if len(parts) < 2: await message.reply( "Usage: `! [unit]` (example: `!pushups 20` or `!run 5 km`)" ) return activity = parts[0] try: value = float(parts[1]) except ValueError: await message.reply( "Invalid number. Usage: `! [unit]` (example: `!run 5 km`)" ) return unit = parts[2] if len(parts) > 2 else "m" timestamp = int(message.created_at.timestamp()) username = message.author.name log_activity(timestamp, username, activity, value, unit) for r in self.confirm_reactions: await message.add_reaction(r) except Exception as e: await message.reply(str(e)) def setup(bot): bot.add_cog(FitnessCog(bot))