EveCogs/wormhole/models/message_map.py
Viktor AI 0b028e7bb9 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

54 lines
2 KiB
Python

"""
In-memory bidirectional message map for tracking relayed messages.
Maps original message IDs ↔ relayed copies across channels within a network.
Capped at MAP_LIMIT entries per direction to bound memory usage.
"""
from __future__ import annotations
from collections import OrderedDict
from typing import Dict, List, Optional
from .config import MAP_LIMIT
class MessageMap:
"""Bidirectional map: original_id ↔ {channel_id: relayed_msg_id}.
``forward[net][original_id] = {ch_id: relayed_id, ...}``
``reverse[net][relayed_id] = original_id``
"""
def __init__(self) -> None:
self.forward: Dict[str, OrderedDict[int, Dict[int, int]]] = {}
self.reverse: Dict[str, OrderedDict[int, int]] = {}
def add(self, network: str, original_id: int, mapping: Dict[int, int]) -> None:
"""Record that *original_id* was relayed to *mapping* channels."""
fwd = self.forward.setdefault(network, OrderedDict())
rev = self.reverse.setdefault(network, OrderedDict())
fwd[original_id] = mapping
for relayed_id in mapping.values():
rev[relayed_id] = original_id
# Prune oldest entries
while len(fwd) > MAP_LIMIT:
_old_orig, old_map = fwd.popitem(last=False)
for rid in old_map.values():
rev.pop(rid, None)
while len(rev) > MAP_LIMIT * 2:
rev.popitem(last=False)
def get_relayed(self, network: str, original_id: int) -> Dict[int, int]:
"""Get ``{ch_id: relayed_id}`` for an original message."""
return self.forward.get(network, {}).get(original_id, {})
def get_original(self, network: str, relayed_id: int) -> Optional[int]:
"""Get the original message ID from a relayed copy ID."""
return self.reverse.get(network, {}).get(relayed_id)
def get_all_relayed_ids(self, network: str, original_id: int) -> List[int]:
"""Get all relayed message IDs for an original."""
return list(self.get_relayed(network, original_id).values())