EveCogs/wormhole/commands/reports.py

161 lines
7.8 KiB
Python
Raw Normal View History

refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
"""Report system commands — create, view, resolve, dismiss reports."""
from __future__ import annotations
from datetime import datetime, timezone
import discord
from redbot.core import commands
from ..models.permissions import Role, requires_role, has_role
from ..utils import ok_embed, err_embed, info_embed, warn_embed, truncate, COLOUR_INFO
from ._base import WormholeBase
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
class ReportCommands(WormholeBase):
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
"""Mixin — user report system (per-network)."""
@WormholeBase.wh_report.command(name="msg")
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
async def wh_report_msg(self, ctx: commands.Context, message_id: int, *, reason: str = "No reason provided") -> None:
"""Report a message by ID."""
net_name = await self._net_for_ch(ctx.channel.id)
if not net_name:
return await ctx.send(embed=err_embed("This channel isn't in a network."))
Re-enable Report/Delete/Profile context menus, remove Bookmark, fix reports Re-enabled the "Report to Wormhole", "Delete from Network", and "Wormhole Profile" context menus (previously disabled entirely). Removed "Bookmark (Wormhole)" per request — the prefix `wh bm list`/`clear` commands are untouched, but note this was the only way to *add* a bookmark, so that feature is now list/clear-only for existing entries. Wormhole Profile is now considerably richer: account creation date, total cross-network message count, global-ban status, and per-network karma/ server-count/muted/banned status, sorted busiest-network-first. Fixed both report-creation paths (the "Report to Wormhole" context menu's modal, and `wh report msg`): report_counter was read before the config lock and written after, so concurrent reports could collide on the same id with the counter never advancing. Now computed and saved atomically inside the lock. Also gave both paths full parity — message id, jump link, and a real content preview (the context-menu path previously stored `hash()` of the content, which is randomized per-process in Python 3 and useless for anything; the prefix-command path never even recorded who wrote the message). `wh report view` now surfaces all of it. Also added the "Find Origin" context menu (right-click a message -> find its true source server/channel/author across the network) as a separate, complementary feature to Profile — not a replacement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 00:02:49 +01:00
# Fetch the message so the report actually records who wrote it and
# what it said, instead of just a bare, unverified id.
target_message = None
try:
target_message = await ctx.channel.fetch_message(message_id)
except (discord.NotFound, discord.Forbidden, discord.HTTPException):
pass
# report_id must be computed and written inside the same locked
# section — reading report_counter beforehand let two concurrent
# reports land on the same id and left the counter not advanced.
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
async with self.config.networks() as ns:
Re-enable Report/Delete/Profile context menus, remove Bookmark, fix reports Re-enabled the "Report to Wormhole", "Delete from Network", and "Wormhole Profile" context menus (previously disabled entirely). Removed "Bookmark (Wormhole)" per request — the prefix `wh bm list`/`clear` commands are untouched, but note this was the only way to *add* a bookmark, so that feature is now list/clear-only for existing entries. Wormhole Profile is now considerably richer: account creation date, total cross-network message count, global-ban status, and per-network karma/ server-count/muted/banned status, sorted busiest-network-first. Fixed both report-creation paths (the "Report to Wormhole" context menu's modal, and `wh report msg`): report_counter was read before the config lock and written after, so concurrent reports could collide on the same id with the counter never advancing. Now computed and saved atomically inside the lock. Also gave both paths full parity — message id, jump link, and a real content preview (the context-menu path previously stored `hash()` of the content, which is randomized per-process in Python 3 and useless for anything; the prefix-command path never even recorded who wrote the message). `wh report view` now surfaces all of it. Also added the "Find Origin" context menu (right-click a message -> find its true source server/channel/author across the network) as a separate, complementary feature to Profile — not a replacement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 00:02:49 +01:00
nd = ns.get(net_name)
if not nd:
return await ctx.send(embed=err_embed(f"Network `{net_name}` not found."))
report_id = nd.get("report_counter", 0) + 1
report = {
"id": report_id,
"reporter_id": ctx.author.id,
"author_id": target_message.author.id if target_message else None,
"message_id": message_id,
"channel_id": ctx.channel.id,
"guild_id": ctx.guild.id if ctx.guild else None,
"content_preview": truncate(target_message.content or "*[no text]*", 300) if target_message else None,
"jump_url": target_message.jump_url if target_message else None,
"reason": reason,
"timestamp": datetime.now(timezone.utc).isoformat(),
"resolved": False,
"resolved_by": None,
"resolution": None,
}
nd.setdefault("reports", []).append(report)
nd["report_counter"] = report_id
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
await ctx.send(embed=ok_embed(f"Report #{report_id} submitted. Staff will review it."), delete_after=10)
await self._log(nd, warn_embed(
f"🚩 **Report #{report_id}**\n"
f"Reporter: {ctx.author} (`{ctx.author.id}`)\n"
f"Message ID: `{message_id}`\n"
f"Reason: {reason}",
title="New Report",
))
@WormholeBase.wh_report.command(name="list")
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
@requires_role(Role.HELPER)
async def wh_report_list(self, ctx: commands.Context, name: str, show_resolved: bool = False) -> None:
"""List reports for a network."""
nd = await self._net(name)
if not nd:
return await ctx.send(embed=err_embed(f"Network `{name}` not found."))
reports = nd.get("reports", [])
if not show_resolved:
reports = [r for r in reports if not r.get("resolved")]
if not reports:
return await ctx.send(embed=info_embed("No open reports."))
lines = []
for r in reports[-20:]:
status = "✅" if r.get("resolved") else "🔴"
reporter = self.bot.get_user(r.get("reporter_id"))
lines.append(
f"{status} **#{r['id']}** — by {reporter or r.get('reporter_id')}\n"
f" Reason: {truncate(r.get('reason', ''), 80)}"
)
em = discord.Embed(title=f"🚩 Reports — {name}", description="\n".join(lines), colour=COLOUR_INFO)
await ctx.send(embed=em)
@WormholeBase.wh_report.command(name="view")
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
@requires_role(Role.HELPER)
async def wh_report_view(self, ctx: commands.Context, name: str, report_id: int) -> None:
"""View details of a specific report."""
nd = await self._net(name)
if not nd:
return await ctx.send(embed=err_embed(f"Network `{name}` not found."))
report = None
for r in nd.get("reports", []):
if r.get("id") == report_id:
report = r
break
if not report:
return await ctx.send(embed=err_embed(f"Report #{report_id} not found."))
reporter = self.bot.get_user(report.get("reporter_id"))
em = discord.Embed(title=f"🚩 Report #{report_id}", colour=COLOUR_INFO)
em.add_field(name="Reporter", value=str(reporter or report.get("reporter_id")), inline=True)
Re-enable Report/Delete/Profile context menus, remove Bookmark, fix reports Re-enabled the "Report to Wormhole", "Delete from Network", and "Wormhole Profile" context menus (previously disabled entirely). Removed "Bookmark (Wormhole)" per request — the prefix `wh bm list`/`clear` commands are untouched, but note this was the only way to *add* a bookmark, so that feature is now list/clear-only for existing entries. Wormhole Profile is now considerably richer: account creation date, total cross-network message count, global-ban status, and per-network karma/ server-count/muted/banned status, sorted busiest-network-first. Fixed both report-creation paths (the "Report to Wormhole" context menu's modal, and `wh report msg`): report_counter was read before the config lock and written after, so concurrent reports could collide on the same id with the counter never advancing. Now computed and saved atomically inside the lock. Also gave both paths full parity — message id, jump link, and a real content preview (the context-menu path previously stored `hash()` of the content, which is randomized per-process in Python 3 and useless for anything; the prefix-command path never even recorded who wrote the message). `wh report view` now surfaces all of it. Also added the "Find Origin" context menu (right-click a message -> find its true source server/channel/author across the network) as a separate, complementary feature to Profile — not a replacement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 00:02:49 +01:00
if report.get("author_id"):
author = self.bot.get_user(report["author_id"])
em.add_field(name="Message Author", value=str(author or report["author_id"]), inline=True)
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
em.add_field(name="Status", value="Resolved" if report.get("resolved") else "Open", inline=True)
em.add_field(name="Reason", value=report.get("reason", "N/A"), inline=False)
Re-enable Report/Delete/Profile context menus, remove Bookmark, fix reports Re-enabled the "Report to Wormhole", "Delete from Network", and "Wormhole Profile" context menus (previously disabled entirely). Removed "Bookmark (Wormhole)" per request — the prefix `wh bm list`/`clear` commands are untouched, but note this was the only way to *add* a bookmark, so that feature is now list/clear-only for existing entries. Wormhole Profile is now considerably richer: account creation date, total cross-network message count, global-ban status, and per-network karma/ server-count/muted/banned status, sorted busiest-network-first. Fixed both report-creation paths (the "Report to Wormhole" context menu's modal, and `wh report msg`): report_counter was read before the config lock and written after, so concurrent reports could collide on the same id with the counter never advancing. Now computed and saved atomically inside the lock. Also gave both paths full parity — message id, jump link, and a real content preview (the context-menu path previously stored `hash()` of the content, which is randomized per-process in Python 3 and useless for anything; the prefix-command path never even recorded who wrote the message). `wh report view` now surfaces all of it. Also added the "Find Origin" context menu (right-click a message -> find its true source server/channel/author across the network) as a separate, complementary feature to Profile — not a replacement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 00:02:49 +01:00
if report.get("content_preview"):
em.add_field(name="Message Content", value=truncate(report["content_preview"], 1024), inline=False)
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
em.add_field(name="Timestamp", value=report.get("timestamp", "?")[:16], inline=True)
if report.get("message_id"):
Re-enable Report/Delete/Profile context menus, remove Bookmark, fix reports Re-enabled the "Report to Wormhole", "Delete from Network", and "Wormhole Profile" context menus (previously disabled entirely). Removed "Bookmark (Wormhole)" per request — the prefix `wh bm list`/`clear` commands are untouched, but note this was the only way to *add* a bookmark, so that feature is now list/clear-only for existing entries. Wormhole Profile is now considerably richer: account creation date, total cross-network message count, global-ban status, and per-network karma/ server-count/muted/banned status, sorted busiest-network-first. Fixed both report-creation paths (the "Report to Wormhole" context menu's modal, and `wh report msg`): report_counter was read before the config lock and written after, so concurrent reports could collide on the same id with the counter never advancing. Now computed and saved atomically inside the lock. Also gave both paths full parity — message id, jump link, and a real content preview (the context-menu path previously stored `hash()` of the content, which is randomized per-process in Python 3 and useless for anything; the prefix-command path never even recorded who wrote the message). `wh report view` now surfaces all of it. Also added the "Find Origin" context menu (right-click a message -> find its true source server/channel/author across the network) as a separate, complementary feature to Profile — not a replacement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 00:02:49 +01:00
id_value = f"`{report['message_id']}`"
if report.get("jump_url"):
id_value += f" — [Jump]({report['jump_url']})"
em.add_field(name="Message ID", value=id_value, inline=True)
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
if report.get("resolved_by"):
resolver = self.bot.get_user(report["resolved_by"])
em.add_field(name="Resolved By", value=str(resolver or report["resolved_by"]), inline=True)
if report.get("resolution"):
em.add_field(name="Resolution", value=report["resolution"], inline=False)
await ctx.send(embed=em)
@WormholeBase.wh_report.command(name="resolve")
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
@requires_role(Role.MODERATOR)
async def wh_report_resolve(self, ctx: commands.Context, name: str, report_id: int, *, resolution: str = "Resolved") -> None:
"""Resolve a report."""
async with self.config.networks() as ns:
for r in ns[name].get("reports", []):
if r.get("id") == report_id:
r["resolved"] = True
r["resolved_by"] = ctx.author.id
r["resolution"] = resolution
r["resolved_at"] = datetime.now(timezone.utc).isoformat()
break
else:
return await ctx.send(embed=err_embed(f"Report #{report_id} not found."))
await ctx.send(embed=ok_embed(f"Report #{report_id} resolved."))
await self._audit(name, "report_resolve", str(ctx.author), str(report_id), resolution)
@WormholeBase.wh_report.command(name="dismiss")
refactor: Wormhole v4.0.0 — full modular architecture with hierarchical staff BREAKING: Complete cog restructure from monolithic wormhole.py into modular architecture. ## Architecture - models/permissions.py — Role IntEnum (MEMBER→OWNER), requires_role() decorator - models/config.py — DEFAULT_NETWORK/DEFAULT_GLOBAL with 80+ config keys - models/message_map.py — MessageMap class (extracted from _MessageMap) - services/emoji.py — Foreign emoji resolution and relay - ui/views.py — Reply jump button view - ui/modals.py — Report modal - core.py — Main Wormhole class, helpers, background loops, context menus - commands/ — 13 command modules as mixin classes - listeners/ — relay pipeline, edit/delete/reaction sync, typing/pin sync ## Staff System (new) Per-network hierarchical roles replacing flat owner/staff: Bot Owner (Red built-in) — global override └─ Network Owner → Admin → Moderator → Helper → Member Each role assigned per-network. Higher roles can assign lower. Legacy staff_ids auto-migrated to ADMIN role. ## Enhanced Moderation - Server-level ban/mute (not just user-level) - Allowlist mode (only whitelisted servers can send) - Mod edit and nuke (delete N messages by user) - Anti-raid detector with auto-freeze - Expanded automod: anti-zalgo, anti-emote-spam, anti-newline-spam ## Commands (100+) network, settings, staff, moderation, filters, social, dm, mentions, tos, reports, bridge, advanced (polls/afk/auto-responses/scheduled/ analytics/quiet-hours/colours/invites/announcements/blackout/portal), debug (health/trace/testsend/relaydebug/backup/restore/version) ## Files - 30 Python files, 5,274 lines total (was 4,782 in monolith) - Original wormhole.py preserved for reference - utils.py unchanged (all helpers reused)
2026-05-14 19:07:49 +00:00
@requires_role(Role.MODERATOR)
async def wh_report_dismiss(self, ctx: commands.Context, name: str, report_id: int, *, reason: str = "Dismissed") -> None:
"""Dismiss a report."""
async with self.config.networks() as ns:
for r in ns[name].get("reports", []):
if r.get("id") == report_id:
r["resolved"] = True
r["resolved_by"] = ctx.author.id
r["resolution"] = f"Dismissed: {reason}"
r["resolved_at"] = datetime.now(timezone.utc).isoformat()
break
else:
return await ctx.send(embed=err_embed(f"Report #{report_id} not found."))
await ctx.send(embed=ok_embed(f"Report #{report_id} dismissed."))
await self._audit(name, "report_dismiss", str(ctx.author), str(report_id), reason)