diff --git a/.gitignore b/.gitignore index 0a19790..d153cf8 100644 --- a/.gitignore +++ b/.gitignore @@ -172,3 +172,4 @@ cython_debug/ # PyPI configuration file .pypirc +/data \ No newline at end of file diff --git a/commands/moments.py b/commands/moments.py index e1df310..c5e4c24 100644 --- a/commands/moments.py +++ b/commands/moments.py @@ -1,16 +1,14 @@ import discord from discord import app_commands -from typing import Optional, List +from typing import Optional from datetime import datetime import logging +from utils.database import Database -# Temporary storage for moments (consider database in future) -saved_moments = [] -incident_logs = {} +db = Database() async def setup(client): - # Create command group moments_group = app_commands.Group( name="moments", description="Capture and manage important moments" @@ -30,9 +28,7 @@ async def setup(client): message_link: str, description: Optional[str] = None ): - """Capture a funny moment with reaction tracking""" try: - # Parse message ID from link parts = message_link.split("/") channel_id = int(parts[-2]) message_id = int(parts[-1]) @@ -40,20 +36,18 @@ async def setup(client): channel = interaction.guild.get_channel(channel_id) message = await channel.fetch_message(message_id) - # Add tracking reactions await message.add_reaction("😂") await message.add_reaction("🎉") - # Store moment - saved_moments.append({ - "message": message.content, - "author": message.author.id, - "description": description, - "timestamp": datetime.now().isoformat() - }) + # Store in database + record_id = db.add_funny_moment( + message_link=message_link, + author_id=message.author.id, + description=description + ) await interaction.response.send_message( - "✅ Funny moment registered! Reactions added!", + f"✅ Funny moment registered! (ID: {record_id})", ephemeral=True ) @@ -64,7 +58,7 @@ async def setup(client): ephemeral=True ) - # Incident tracking command + # Incident command @moments_group.command( name="incident", description="Log an incident with recent messages" @@ -79,29 +73,33 @@ async def setup(client): message_count: app_commands.Range[int, 1, 50], reason: str ): - """Log messages for moderator review""" try: - # Fetch messages messages = [ msg async for msg in interaction.channel.history(limit=message_count) - ][::-1] # Reverse to get chronological order + ][::-1] - # Format log - log_content = "\n".join( - f"{msg.author.display_name} ({msg.author.id}) | {msg.created_at}:\n" - f"{msg.content}\n{'-'*40}" - for msg in messages + # Prepare messages for storage + formatted_messages = [{ + "id": msg.id, + "author_id": msg.author.id, + "content": msg.content, + "timestamp": msg.created_at + } for msg in messages] + + # Generate incident ID + incident_id = f"incident_{int(datetime.now().timestamp())}" + + # Store in database + success = db.add_incident( + incident_id=incident_id, + reason=reason, + moderator_id=interaction.user.id, + messages=formatted_messages ) - # Store log - incident_id = f"incident_{datetime.now().timestamp()}" - incident_logs[incident_id] = { - "reason": reason, - "messages": log_content, - "moderator": interaction.user.id - } + if not success: + raise Exception("Database storage failed") - # Send confirmation await interaction.response.send_message( f"✅ Incident logged with ID `{incident_id}`\n" f"**Reason:** {reason}\n" @@ -112,9 +110,60 @@ async def setup(client): except Exception as e: logging.error(f"Incident log error: {e}") await interaction.response.send_message( - "❌ Failed to log incident. Check permissions!", + "❌ Failed to log incident. Check permissions and message count!", + ephemeral=True + ) + + # Add review command + @moments_group.command( + name="review", + description="Review a logged incident" + ) + @app_commands.describe( + incident_id="The incident ID to review" + ) + @app_commands.checks.has_permissions(manage_messages=True) + async def review_incident( + interaction: discord.Interaction, + incident_id: str + ): + try: + incident = db.get_incident(incident_id) + if not incident: + await interaction.response.send_message( + "❌ Incident not found", + ephemeral=True + ) + return + + # Format response + messages = "\n\n".join( + f"**{msg['timestamp']}** <@{msg['author_id']}>:\n" + f"{msg['content']}" + for msg in incident['messages'] + ) + + embed = discord.Embed( + title=f"Incident {incident_id}", + description=f"**Reason:** {incident['details']['reason']}", + color=0xff0000 + ) + embed.add_field( + name="Messages", + value=messages[:1020] + "..." if len(messages) > 1024 else messages, + inline=False + ) + + await interaction.response.send_message( + embed=embed, + ephemeral=True + ) + + except Exception as e: + logging.error(f"Incident review error: {e}") + await interaction.response.send_message( + "❌ Failed to retrieve incident", ephemeral=True ) - # Add commands to client client.tree.add_command(moments_group) diff --git a/utils/database.py b/utils/database.py new file mode 100644 index 0000000..588f299 --- /dev/null +++ b/utils/database.py @@ -0,0 +1,121 @@ +import sqlite3 +import logging +from datetime import datetime +from typing import List, Dict, Optional + + +class Database: + def __init__(self, db_path: str = "data/moments.db"): + self.db_path = db_path + self._init_db() + + def _init_db(self): + """Initialize database tables""" + with self._get_connection() as conn: + conn.execute(""" + CREATE TABLE IF NOT EXISTS funny_moments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + message_link TEXT NOT NULL, + description TEXT, + author_id INTEGER NOT NULL, + timestamp DATETIME NOT NULL + ) + """) + + conn.execute(""" + CREATE TABLE IF NOT EXISTS incidents ( + id TEXT PRIMARY KEY, + reason TEXT NOT NULL, + moderator_id INTEGER NOT NULL, + timestamp DATETIME NOT NULL + ) + """) + + conn.execute(""" + CREATE TABLE IF NOT EXISTS incident_messages ( + incident_id TEXT, + message_id INTEGER, + author_id INTEGER, + content TEXT, + timestamp DATETIME, + PRIMARY KEY (incident_id, message_id), + FOREIGN KEY (incident_id) REFERENCES incidents(id) + ) + """) + + conn.commit() + + def _get_connection(self): + return sqlite3.connect(self.db_path) + + def add_funny_moment(self, message_link: str, author_id: int, description: str = None) -> int: + """Store a funny moment in database""" + with self._get_connection() as conn: + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO funny_moments + (message_link, description, author_id, timestamp) + VALUES (?, ?, ?, ?) + """, (message_link, description, author_id, datetime.now())) + conn.commit() + return cursor.lastrowid + + def add_incident(self, incident_id: str, reason: str, moderator_id: int, messages: List[Dict]) -> bool: + """Store an incident with related messages""" + try: + with self._get_connection() as conn: + # Add incident record + conn.execute(""" + INSERT INTO incidents + (id, reason, moderator_id, timestamp) + VALUES (?, ?, ?, ?) + """, (incident_id, reason, moderator_id, datetime.now())) + + # Add incident messages + for msg in messages: + conn.execute(""" + INSERT INTO incident_messages + (incident_id, message_id, author_id, content, timestamp) + VALUES (?, ?, ?, ?, ?) + """, ( + incident_id, + msg['id'], + msg['author_id'], + msg['content'], + msg['timestamp'] + )) + + conn.commit() + return True + except Exception as e: + logging.error(f"Failed to save incident: {e}") + return False + + def get_incident(self, incident_id: str) -> Optional[Dict]: + """Retrieve an incident with its messages""" + with self._get_connection() as conn: + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + + # Get incident details + cursor.execute(""" + SELECT * FROM incidents + WHERE id = ? + """, (incident_id,)) + incident = cursor.fetchone() + + if not incident: + return None + + # Get related messages + cursor.execute(""" + SELECT * FROM incident_messages + WHERE incident_id = ? + ORDER BY timestamp ASC + """, (incident_id,)) + messages = cursor.fetchall() + + return { + "details": dict(incident), + "messages": [dict(msg) for msg in messages] + }