from __future__ import annotations from typing import Any TWITCH_ROLES = frozenset({"moderator", "owner", "vip", "subscriber", "all"}) def normalize_twitch_channel(channel: str) -> str: return channel.lstrip("#@").strip().lower() def resolve_channel_name(channel_chat_id: str) -> str: return normalize_twitch_channel(channel_chat_id) def resolve_route( agent_id: str, channel_name: str, ) -> str: channel_name = normalize_twitch_channel(channel_name) return f"agent:{agent_id}:twitch:channel:{channel_name}" def resolve_agent_id_for_channel( config: dict[str, Any], channel_name: str, default_agent_id: str | None = None, ) -> str: channel_name = normalize_twitch_channel(channel_name) channels_config = config.get("channels_config", {}) channel_config = channels_config.get(channel_name, {}) return channel_config.get("agent_id") or default_agent_id or "" def check_require_mention( config: dict[str, Any], content: str, ) -> bool: bot_name = config.get("bot_username", "").lower() require_mention = config.get("require_mention", True) if not require_mention: return True return bool(bot_name) and bot_name in content.lower() def check_allowed_roles( config: dict[str, Any], channel: str, badges: dict[str, str], ) -> bool: allowed_roles: list[str] = config.get("allowedRoles", []) if "all" in allowed_roles: return True channels_config = config.get("channels_config", {}) channel_name = normalize_twitch_channel(channel) per_channel_roles: list[str] = channels_config.get(channel_name, {}).get("allowedRoles", []) if "all" in per_channel_roles: return True effective_roles = list(dict.fromkeys(allowed_roles + per_channel_roles)) if not effective_roles: return True for role in effective_roles: role_lower = role.lower() if role_lower not in TWITCH_ROLES: continue if role_lower == "all": return True if role_lower == "owner" and "broadcaster" in badges: return True if role_lower == "moderator" and "moderator" in badges: return True if role_lower == "vip" and "vip" in badges: return True if role_lower == "subscriber" and ("subscriber" in badges or "founder" in badges): return True return False def check_group_policy( config: dict[str, Any], channel: str, user_id: str, content: str, user_name: str = "", ) -> bool: channel_name = normalize_twitch_channel(channel) group_policy = config.get("group_policy", "open") bot_name = config.get("bot_username", "").lower() match group_policy: case "open": return True case "disabled": return False case "mention_only": require_mention = config.get("require_mention", True) if not require_mention: return True return bool(bot_name) and bot_name in content.lower() case "allowlist": allowlist = config.get("group_allow_from", []) channels_config = config.get("channels_config", {}) per_channel_allow = channels_config.get(channel_name, {}).get("allow_from", []) candidates = [ f"twitch:{user_id}", user_id, ] if user_name: candidates.append(user_name.lower()) return any(candidate in allowlist or candidate in per_channel_allow for candidate in candidates) case _: return False