diff --git a/commands/moments.py b/commands/moments.py index 8c0bd59..78f577d 100644 --- a/commands/moments.py +++ b/commands/moments.py @@ -8,6 +8,16 @@ from utils.database import Database db = Database() +import discord +from discord import app_commands +from typing import Optional, List +from datetime import datetime +import logging +import uuid +from utils.database import Database + +db = Database() + class IncidentModal(discord.ui.Modal): def __init__(self): super().__init__(title="Log Incident") @@ -17,24 +27,75 @@ class IncidentModal(discord.ui.Modal): required=True ) self.message_count = discord.ui.TextInput( - label="Messages to capture (1-50)", - default="10", - required=True + label="Recent messages to capture (1-50)", + placeholder="Leave blank to use timeframe", + default="", + required=False + ) + self.start_time = discord.ui.TextInput( + label="Start time (YYYY-MM-DD HH:MM)", + placeholder="Optional - Example: 2024-05-10 14:30", + required=False + ) + self.end_time = discord.ui.TextInput( + label="End time (YYYY-MM-DD HH:MM)", + placeholder="Optional - Example: 2024-05-10 15:00", + required=False ) self.add_item(self.reason) self.add_item(self.message_count) + self.add_item(self.start_time) + self.add_item(self.end_time) async def on_submit(self, interaction: discord.Interaction): try: - count = int(self.message_count.value) - if not 1 <= count <= 50: - raise ValueError + messages = [] + capture_mode = "count" + capture_param = "" + start_time = None + end_time = None - messages = [ - msg async for msg in interaction.channel.history(limit=count) - ][::-1] + # Determine capture mode + if self.start_time.value or self.end_time.value: + if not all([self.start_time.value, self.end_time.value]): + raise ValueError("Both start and end times required for timeframe") + start_time = datetime.strptime(self.start_time.value, "%Y-%m-%d %H:%M") + end_time = datetime.strptime(self.end_time.value, "%Y-%m-%d %H:%M") + + if start_time >= end_time: + raise ValueError("End time must be after start time") + + if (end_time - start_time).total_seconds() > 86400: + raise ValueError("Maximum timeframe duration is 24 hours") + + async for msg in interaction.channel.history( + limit=None, + after=start_time, + before=end_time + ): + messages.append(msg) + + messages = messages[::-1] # Oldest first + capture_mode = "timeframe" + capture_param = f"{start_time.strftime('%Y-%m-%d %H:%M')} to {end_time.strftime('%Y-%m-%d %H:%M')}" + + else: + if not self.message_count.value: + raise ValueError("Please provide either message count or timeframe") + + count = int(self.message_count.value) + if not 1 <= count <= 50: + raise ValueError("Message count must be between 1-50") + + messages = [ + msg async for msg in interaction.channel.history(limit=count) + ][::-1] + capture_mode = "count" + capture_param = str(count) + + # Format messages formatted_messages = [{ "id": msg.id, "author_id": msg.author.id, @@ -42,34 +103,45 @@ class IncidentModal(discord.ui.Modal): "timestamp": msg.created_at } for msg in messages] - incident_id = f"incident_{int(datetime.now().timestamp())}" + # Generate unique ID + incident_id = f"incident_{uuid.uuid4().hex[:8]}" success = db.add_incident( incident_id=incident_id, reason=self.reason.value, moderator_id=interaction.user.id, - messages=formatted_messages + messages=formatted_messages, + capture_mode=capture_mode, + capture_param=capture_param, + start_time=start_time, + end_time=end_time ) if not success: - raise Exception("Database storage failed") + raise Exception("Database storage failed - check server logs") embed = discord.Embed( - title="Incident Logged", - description=f"**ID:** `{incident_id}`\n**Reason:** {self.reason.value}", + title="✅ Incident Logged", + description=f"**ID:** `{incident_id}`\n**Mode:** {capture_mode.title()}", color=0xff0000 ) - preview = messages[0].content[:50] + "..." if messages else "No messages" - embed.add_field(name="First Message", value=preview, inline=False) + embed.add_field(name="Reason", value=self.reason.value[:500], inline=False) + + if messages: + preview = f"{messages[0].content[:100]}..." if len(messages[0].content) > 100 else messages[0].content + embed.add_field(name="First Message", value=preview, inline=False) + await interaction.response.send_message(embed=embed, ephemeral=True) + + except ValueError as e: await interaction.response.send_message( - embed=embed, + f"❌ Validation Error: {str(e)}", ephemeral=True ) - - except ValueError: + except Exception as e: + logging.error(f"Incident submission error: {str(e)}", exc_info=True) await interaction.response.send_message( - "❌ Please enter a number between 1-50", + "⚠️ Failed to log incident - please check input format", ephemeral=True ) @@ -158,9 +230,14 @@ async def setup(client): embed = discord.Embed( title=f"Incident {incident_id}", - description=f"**Reason:** {incident['details']['reason']}", - color=0xff0000 - ) + description=( + f"**Reason:** {incident['details']['reason']}\n" + f"**Capture Mode:** {incident['details']['capture_mode'].title()}\n" + f"**Params:** {incident['details']['capture_param']}" + ), + color=0xff0000 + ) + embed.add_field( name="Messages", value=messages[:1020] + "..." if len(messages) > 1024 else messages, diff --git a/utils/database.py b/utils/database.py index a92e8af..0760960 100644 --- a/utils/database.py +++ b/utils/database.py @@ -4,6 +4,11 @@ from datetime import datetime from typing import List, Dict, Optional +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 @@ -12,6 +17,10 @@ class Database: def _init_db(self): """Initialize database tables""" with self._get_connection() as conn: + # Drop tables if they exist (for development) + conn.execute("DROP TABLE IF EXISTS incidents") + conn.execute("DROP TABLE IF EXISTS incident_messages") + conn.execute(""" CREATE TABLE IF NOT EXISTS funny_moments ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -27,7 +36,11 @@ class Database: id TEXT PRIMARY KEY, reason TEXT NOT NULL, moderator_id INTEGER NOT NULL, - timestamp DATETIME NOT NULL + timestamp DATETIME NOT NULL, + capture_mode TEXT NOT NULL, + capture_param TEXT, + start_time DATETIME, + end_time DATETIME ) """) @@ -42,34 +55,32 @@ class Database: 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: + def add_incident(self, incident_id: str, reason: str, moderator_id: int, + messages: List[Dict], capture_mode: str, capture_param: str, + start_time: datetime = None, end_time: datetime = None) -> 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())) + (id, reason, moderator_id, timestamp, capture_mode, capture_param, start_time, end_time) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, ( + incident_id, + reason, + moderator_id, + datetime.now(), + capture_mode, + capture_param, + start_time, + end_time + )) # Add incident messages for msg in messages: @@ -88,9 +99,21 @@ class Database: conn.commit() return True except Exception as e: - logging.error(f"Failed to save incident: {e}") + logging.error(f"Failed to save incident: {str(e)}") return False + 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 get_incident(self, incident_id: str) -> Optional[Dict]: """Retrieve an incident with its messages""" with self._get_connection() as conn: