Fixed it so follow-up notes can be added to any incidents
This commit is contained in:
		| @@ -8,6 +8,16 @@ from utils.database import Database | |||||||
| db = 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() | ||||||
|  |  | ||||||
| import discord | import discord | ||||||
| from discord import app_commands | from discord import app_commands | ||||||
| from typing import Optional, List | from typing import Optional, List | ||||||
| @@ -123,7 +133,7 @@ class IncidentModal(discord.ui.Modal): | |||||||
|             embed = discord.Embed( |             embed = discord.Embed( | ||||||
|                 title="✅ Incident Logged", |                 title="✅ Incident Logged", | ||||||
|                 description=f"**ID:** `{incident_id}`\n**Mode:** {capture_mode.title()}", |                 description=f"**ID:** `{incident_id}`\n**Mode:** {capture_mode.title()}", | ||||||
|                 color=0xff0000 |                 color=0x00ff00 | ||||||
|             ) |             ) | ||||||
|             embed.add_field(name="Reason", value=self.reason.value[:500], inline=False) |             embed.add_field(name="Reason", value=self.reason.value[:500], inline=False) | ||||||
|              |              | ||||||
| @@ -145,6 +155,41 @@ class IncidentModal(discord.ui.Modal): | |||||||
|                 ephemeral=True |                 ephemeral=True | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  | class FollowupModal(discord.ui.Modal): | ||||||
|  |     def __init__(self, incident_id: str): | ||||||
|  |         super().__init__(title=f"Follow-up: {incident_id}") | ||||||
|  |         self.incident_id = incident_id | ||||||
|  |         self.notes = discord.ui.TextInput( | ||||||
|  |             label="Additional notes/actions", | ||||||
|  |             style=discord.TextStyle.long, | ||||||
|  |             required=True | ||||||
|  |         ) | ||||||
|  |         self.add_item(self.notes) | ||||||
|  |  | ||||||
|  |     async def on_submit(self, interaction: discord.Interaction): | ||||||
|  |         try: | ||||||
|  |             success = db.add_followup( | ||||||
|  |                 incident_id=self.incident_id, | ||||||
|  |                 moderator_id=interaction.user.id, | ||||||
|  |                 notes=self.notes.value | ||||||
|  |             ) | ||||||
|  |              | ||||||
|  |             if success: | ||||||
|  |                 await interaction.response.send_message( | ||||||
|  |                     f"✅ Follow-up added to **{self.incident_id}**", | ||||||
|  |                     ephemeral=True | ||||||
|  |                 ) | ||||||
|  |             else: | ||||||
|  |                 await interaction.response.send_message( | ||||||
|  |                     "❌ Failed to save follow-up", | ||||||
|  |                     ephemeral=True | ||||||
|  |                 ) | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Followup error: {str(e)}") | ||||||
|  |             await interaction.response.send_message( | ||||||
|  |                 "⚠️ Failed to add follow-up", | ||||||
|  |                 ephemeral=True | ||||||
|  |             ) | ||||||
|  |  | ||||||
| async def setup(client): | async def setup(client): | ||||||
|     # Context menu command |     # Context menu command | ||||||
| @@ -205,31 +250,30 @@ async def setup(client): | |||||||
|         name="review", |         name="review", | ||||||
|         description="Review a logged incident" |         description="Review a logged incident" | ||||||
|     ) |     ) | ||||||
|     @app_commands.describe( |     @app_commands.describe(incident_id="The incident ID to review") | ||||||
|         incident_id="The incident ID to review" |  | ||||||
|     ) |  | ||||||
|     @app_commands.checks.has_permissions(manage_messages=True) |     @app_commands.checks.has_permissions(manage_messages=True) | ||||||
|     async def review_incident( |     async def review_incident(interaction: discord.Interaction, incident_id: str): | ||||||
|         interaction: discord.Interaction, |  | ||||||
|         incident_id: str |  | ||||||
|     ): |  | ||||||
|         try: |         try: | ||||||
|             incident = db.get_incident(incident_id) |             incident = db.get_incident(incident_id) | ||||||
|             if not incident: |             if not incident: | ||||||
|                 await interaction.response.send_message( |                 await interaction.response.send_message("❌ Incident not found", ephemeral=True) | ||||||
|                     "❌ Incident not found", |  | ||||||
|                     ephemeral=True |  | ||||||
|                 ) |  | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|  |             # Format messages | ||||||
|             messages = "\n\n".join( |             messages = "\n\n".join( | ||||||
|                 f"**{msg['timestamp']}** <@{msg['author_id']}>:\n" |                 f"**<t:{int(msg['timestamp'].timestamp())}:F>** <@{msg['author_id']}>:\n{msg['content']}" | ||||||
|                 f"{msg['content']}" |  | ||||||
|                 for msg in incident['messages'] |                 for msg in incident['messages'] | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|             moderator = await interaction.guild.fetch_member(incident['details']['moderator_id']) |             # Format follow-ups | ||||||
|  |             followups = db.get_followups(incident_id) | ||||||
|  |             followup_text = "\n\n".join( | ||||||
|  |                 f"**<t:{int(f['timestamp'].timestamp())}:f>** <@{f['moderator_id']}>:\n{f['notes'][:200]}" | ||||||
|  |                 for f in followups | ||||||
|  |             ) if followups else "No follow-up reports yet" | ||||||
|  |  | ||||||
|  |             # Create embed | ||||||
|  |             moderator = await interaction.guild.fetch_member(incident['details']['moderator_id']) | ||||||
|             embed = discord.Embed( |             embed = discord.Embed( | ||||||
|                 title=f"Incident {incident_id}", |                 title=f"Incident {incident_id}", | ||||||
|                 description=( |                 description=( | ||||||
| @@ -239,39 +283,82 @@ async def setup(client): | |||||||
|                 ), |                 ), | ||||||
|                 color=0xff0000 |                 color=0xff0000 | ||||||
|             ) |             ) | ||||||
|  |             embed.add_field(name="Messages", value=messages[:1020] + "..." if len(messages) > 1024 else messages, inline=False) | ||||||
|  |             embed.add_field(name=f"Follow-ups ({len(followups)})", value=followup_text[:1020] + "..." if len(followup_text) > 1024 else followup_text, inline=False) | ||||||
|  |  | ||||||
|             embed.add_field( |             await interaction.response.send_message(embed=embed, ephemeral=True) | ||||||
|                 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: |         except Exception as e: | ||||||
|             logging.error(f"Incident review error: {e}") |             logging.error(f"Incident review error: {str(e)}", exc_info=True) | ||||||
|  |             await interaction.response.send_message("❌ Failed to retrieve incident", ephemeral=True) | ||||||
|  |  | ||||||
|  |     # Followup commands | ||||||
|  |     @moments_group.command( | ||||||
|  |         name="followup", | ||||||
|  |         description="Add follow-up to an incident" | ||||||
|  |     ) | ||||||
|  |     @app_commands.describe( | ||||||
|  |         incident_id="The incident ID to follow up on", | ||||||
|  |         notes="Quick note (optional)" | ||||||
|  |     ) | ||||||
|  |     @app_commands.checks.has_permissions(manage_messages=True) | ||||||
|  |     async def add_followup( | ||||||
|  |         interaction: discord.Interaction, | ||||||
|  |         incident_id: str, | ||||||
|  |         notes: Optional[str] = None | ||||||
|  |     ): | ||||||
|  |         try: | ||||||
|  |             if not db.get_incident(incident_id): | ||||||
|                 await interaction.response.send_message( |                 await interaction.response.send_message( | ||||||
|                 "❌ Failed to retrieve incident", |                     "❌ Incident not found", | ||||||
|  |                     ephemeral=True | ||||||
|  |                 ) | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             if notes: | ||||||
|  |                 success = db.add_followup( | ||||||
|  |                     incident_id=incident_id, | ||||||
|  |                     moderator_id=interaction.user.id, | ||||||
|  |                     notes=notes | ||||||
|  |                 ) | ||||||
|  |  | ||||||
|  |                 if success: | ||||||
|  |                     await interaction.response.send_message( | ||||||
|  |                         f"✅ Added quick follow-up to **{incident_id}**", | ||||||
|  |                         ephemeral=True | ||||||
|  |                     ) | ||||||
|  |                 else: | ||||||
|  |                     await interaction.response.send_message( | ||||||
|  |                         "❌ Failed to add follow-up", | ||||||
|  |                         ephemeral=True | ||||||
|  |                     ) | ||||||
|  |             else: | ||||||
|  |                 await interaction.response.send_modal(FollowupModal(incident_id)) | ||||||
|  |  | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Followup error: {e}") | ||||||
|  |             await interaction.response.send_message( | ||||||
|  |                 "⚠️ Failed to add follow-up", | ||||||
|                 ephemeral=True |                 ephemeral=True | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     # Autocomplete |     # Autocomplete | ||||||
|     @review_incident.autocomplete("incident_id") |     @review_incident.autocomplete("incident_id") | ||||||
|  |     @add_followup.autocomplete("incident_id") | ||||||
|     async def incident_autocomplete( |     async def incident_autocomplete( | ||||||
|         interaction: discord.Interaction, |         interaction: discord.Interaction, | ||||||
|         current: str |         current: str | ||||||
|     ) -> List[app_commands.Choice[str]]: |     ) -> List[app_commands.Choice[str]]: | ||||||
|         # Changed to get all incidents, not just current mod's |  | ||||||
|         incidents = db.get_recent_incidents(25) |         incidents = db.get_recent_incidents(25) | ||||||
|         return [ |         choices = [] | ||||||
|             app_commands.Choice( |         for inc in incidents: | ||||||
|                 name=f"{inc['id']} (by {await interaction.guild.fetch_member(inc['moderator_id'])})", |             try: | ||||||
|                 value=inc['id'] |                 mod = await interaction.guild.fetch_member(inc['moderator_id']) | ||||||
|             ) |                 name = f"{inc['id']} (by {mod.display_name})" | ||||||
|             for inc in incidents if current.lower() in inc['id'].lower() |             except: | ||||||
|         ][:25] |                 name = inc['id'] | ||||||
|  |             if current.lower() in name.lower(): | ||||||
|  |                 choices.append(app_commands.Choice(name=name, value=inc['id'])) | ||||||
|  |         return choices[:25] | ||||||
|  |  | ||||||
|     client.tree.add_command(moments_group) |     client.tree.add_command(moments_group) | ||||||
| @@ -55,6 +55,17 @@ class Database: | |||||||
|                     FOREIGN KEY (incident_id) REFERENCES incidents(id) |                     FOREIGN KEY (incident_id) REFERENCES incidents(id) | ||||||
|                 ) |                 ) | ||||||
|             """) |             """) | ||||||
|  |  | ||||||
|  |             conn.execute(""" | ||||||
|  |                 CREATE TABLE IF NOT EXISTS incident_followups ( | ||||||
|  |                     id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||||
|  |                     incident_id TEXT NOT NULL, | ||||||
|  |                     moderator_id INTEGER NOT NULL, | ||||||
|  |                     notes TEXT NOT NULL, | ||||||
|  |                     timestamp DATETIME NOT NULL, | ||||||
|  |                     FOREIGN KEY (incident_id) REFERENCES incidents(id) | ||||||
|  |                 ) | ||||||
|  |             """) | ||||||
|             conn.commit() |             conn.commit() | ||||||
|  |  | ||||||
|     def _get_connection(self): |     def _get_connection(self): | ||||||
| @@ -120,27 +131,25 @@ class Database: | |||||||
|             conn.row_factory = sqlite3.Row |             conn.row_factory = sqlite3.Row | ||||||
|             cursor = conn.cursor() |             cursor = conn.cursor() | ||||||
|  |  | ||||||
|             # Get incident details |             # Get incident details and parse timestamp | ||||||
|             cursor.execute(""" |             cursor.execute("SELECT * FROM incidents WHERE id = ?", (incident_id,)) | ||||||
|                 SELECT * FROM incidents |  | ||||||
|                 WHERE id = ? |  | ||||||
|             """, (incident_id,)) |  | ||||||
|             incident = cursor.fetchone() |             incident = cursor.fetchone() | ||||||
|  |  | ||||||
|             if not incident: |             if not incident: | ||||||
|                 return None |                 return None | ||||||
|  |  | ||||||
|             # Get related messages |             incident_details = dict(incident) | ||||||
|             cursor.execute(""" |             incident_details['timestamp'] = datetime.fromisoformat(incident_details['timestamp'])  # Convert string to datetime | ||||||
|                 SELECT * FROM incident_messages |  | ||||||
|                 WHERE incident_id = ? |             # Get messages with parsed timestamps | ||||||
|                 ORDER BY timestamp ASC |             cursor.execute("SELECT * FROM incident_messages WHERE incident_id = ?", (incident_id,)) | ||||||
|             """, (incident_id,)) |             messages = [ | ||||||
|             messages = cursor.fetchall() |                 {**dict(msg), 'timestamp': datetime.fromisoformat(msg['timestamp'])}  | ||||||
|  |                 for msg in cursor.fetchall() | ||||||
|  |             ] | ||||||
|  |  | ||||||
|             return { |             return { | ||||||
|                 "details": dict(incident), |                 "details": incident_details, | ||||||
|                 "messages": [dict(msg) for msg in messages] |                 "messages": messages | ||||||
|             } |             } | ||||||
|  |  | ||||||
|     def get_recent_incidents(self, limit: int = 25): |     def get_recent_incidents(self, limit: int = 25): | ||||||
| @@ -154,3 +163,29 @@ class Database: | |||||||
|                 LIMIT ? |                 LIMIT ? | ||||||
|             """, (limit,)) |             """, (limit,)) | ||||||
|             return [dict(row) for row in cursor.fetchall()] |             return [dict(row) for row in cursor.fetchall()] | ||||||
|  |  | ||||||
|  |     def add_followup(self, incident_id: str, moderator_id: int, notes: str) -> bool: | ||||||
|  |         """Add a follow-up report to an incident""" | ||||||
|  |         try: | ||||||
|  |             with self._get_connection() as conn: | ||||||
|  |                 conn.execute(""" | ||||||
|  |                     INSERT INTO incident_followups  | ||||||
|  |                     (incident_id, moderator_id, notes, timestamp) | ||||||
|  |                     VALUES (?, ?, ?, ?) | ||||||
|  |                 """, (incident_id, moderator_id, notes, datetime.now())) | ||||||
|  |                 conn.commit() | ||||||
|  |                 return True | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Failed to add followup: {str(e)}") | ||||||
|  |             return False | ||||||
|  |  | ||||||
|  |     def get_followups(self, incident_id: str) -> List[Dict]: | ||||||
|  |         """Get follow-ups with proper timestamps""" | ||||||
|  |         with self._get_connection() as conn: | ||||||
|  |             conn.row_factory = sqlite3.Row | ||||||
|  |             cursor = conn.cursor() | ||||||
|  |             cursor.execute("SELECT * FROM incident_followups WHERE incident_id = ?", (incident_id,)) | ||||||
|  |             return [ | ||||||
|  |                 {**dict(row), 'timestamp': datetime.fromisoformat(row['timestamp'])} | ||||||
|  |                 for row in cursor.fetchall() | ||||||
|  |             ] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user