from __future__ import annotations from collections import Counter from typing import Any def build_reaction(emoji: str, target_event_id: str) -> dict[str, Any]: return { "m.relates_to": { "rel_type": "m.annotation", "event_id": target_event_id, "key": emoji, }, } def extract_reaction(event: Any) -> dict[str, str] | None: content = event.source.get("content", {}) if hasattr(event, "source") else {} relates_to = content.get("m.relates_to", {}) if relates_to.get("rel_type") != "m.annotation": return None return { "target_event_id": relates_to.get("event_id", ""), "key": relates_to.get("key", ""), } def summarize_reactions(events: list[dict[str, Any]]) -> dict[str, dict]: key_counter: Counter[str] = Counter() user_keys: dict[str, set[str]] = {} last_sender: dict[str, str] = {} for evt in events: sender = evt.get("sender", "") key = evt.get("key", "") target = evt.get("target_event_id", "") if not key or not target: continue key_counter[key] += 1 if target not in user_keys: user_keys[target] = set() user_keys[target].add(sender) last_sender[key] = sender result: dict[str, dict] = {} for target, users in user_keys.items(): result[target] = { "total_reactions": sum( count for k, count in key_counter.items() if any(e.get("target_event_id") == target and e.get("key") == k for e in events) ), "unique_reactors": len(users), "keys": [ {"key": k, "count": count} for k, count in key_counter.items() if any(e.get("target_event_id") == target and e.get("key") == k for e in events) ], } return result _COMMON_EMOJI_MAP = { "👍": "+1", "👎": "-1", "❤️": "heart", "😄": "smile", "🎉": "party", "🚀": "rocket", "👀": "eyes", "✅": "check", } def select_reaction(available: list[dict[str, Any]], query: str) -> dict[str, Any] | None: normalized = _COMMON_EMOJI_MAP.get(query, query).lower() for entry in available: key = entry.get("key", "") if key == query or key.lower() == normalized: return entry return available[0] if available else None def build_batch_remove( client_reactions: list[dict[str, Any]], target_event_id: str, ) -> list[dict[str, Any]]: return [ build_reaction(r.get("key", ""), target_event_id) for r in client_reactions if r.get("target_event_id") == target_event_id ]