Fixed it so follow-up notes can be added to any incidents
This commit is contained in:
parent
979e7c74d5
commit
02155f3e0f
@ -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()
|
||||||
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user