- Migration now uses a _mention_migrated flag so it only forces mentions
off once. After that, admins can freely toggle per-network settings
with 'wh mentions set <network> <type> <true/false>'
- New mention_optout_users list per network: users can opt out of being
@pinged through the relay even when user mentions are enabled
network-wide. Their <@ID> mentions are stripped; everyone else's pass
through. Default: opted in.
- New commands:
• wh mentions optout <network> — stop receiving relay pings
• wh mentions optin <network> — start receiving relay pings again
• wh mentions mystatus <network> — check your current ping status
- apply_mention_policy() now accepts optout_users set and selectively
strips only opted-out users' mentions when user mentions are globally
allowed
- Updated 'wh mentions status' to show opt-out count and correct
defaults
- Default mention_policy now disables all mention types including user
mentions (previously allow_user_mentions defaulted to True)
- Blocked mentions are now removed entirely instead of replaced with
ZWSP placeholders — '@mention HI!' becomes 'HI!' on the other side
- Trailing whitespace from removed mentions is cleaned up
- Updated both apply_mention_policy() and sanitise_mentions()
Root cause: apply_mention_policy() in utils.py used raw strings for
re.sub replacement arguments:
re.sub(r'<@&(\d+)>', r'@\u200brole', content)
The r-prefix makes \u200b literal backslash-u instead of Unicode ZWSP.
Python's re.sub always validates the replacement template, so \u at
position 1 raises re.error on EVERY message — even without matches.
Since _DEFAULT_NETWORK sets mention_policy.allow_role_mentions=False,
this code path runs for all networks, causing _do_relay to throw on
every single message. The exception was caught by on_message's
try/except, silently killing relay with total_messages stuck at 0.
Fix: remove r-prefix from replacement strings so \u200b is correctly
interpreted as the Unicode zero-width space character.
Phase 5 — Security, Privacy & Power Update:
HYBRID COMMANDS
- Main command group converted to hybrid_group (slash + prefix)
- All commands work as both /wh and [p]wh
CONTEXT MENU ACTIONS (right-click)
- Report to Wormhole — opens modal for report reason
- Wormhole Bookmark — saves message to DM
- Wormhole Delete — staff can delete from entire network
- Wormhole Profile — view user stats across networks
GRANULAR MENTION POLICY
- Per-network control: allow/block @user, @role, @everyone, @here
- Per-server overrides (different rules per guild)
- Per-user exemptions (trusted users bypass policy)
- Replaces legacy strip-* with proper policy engine
- wh mentions set/server-set/exempt/unexempt/status
TERMS OF SERVICE / RULES ACCEPTANCE GATE
- wh tos enable — require acceptance before messaging
- Built-in legal template (6 sections, prosecution-ready)
- Consent tracking with timestamps
- wh tos set (custom text), template (reset), accepted (view), reset (force re-accept)
- wh accept / wh agree — user-facing acceptance flow
- Blocks relay for non-accepted users with helpful reminder
MOD EDIT/DELETE ACROSS NETWORK
- wh mod edit <name> <msg_id> <new_content> — edits all relayed copies
- wh mod nuke <name> <msg_id> — deletes from every server + original
- Both audit-logged
REPORT SYSTEM
- wh report message — report via reply or message ID
- Context menu report with modal (reason input)
- Anti-abuse cooldown (60s between reports)
- Content hashing for audit trails
- Reports sent to log channel + network owner DM
- wh report list/resolve/action (ban/mute/warn/dismiss)
- Capped at 200 reports per network
PRIVACY & SECURITY
- _ReportModal class for context menu reporting
- Content hash stored instead of plaintext in report records
- Report cooldowns prevent abuse
- ToS acceptance with ISO timestamps for legal compliance
4,196 lines in wormhole.py, 419 in utils.py — 85+ commands total