from __future__ import annotations from fnmatch import fnmatchcase def build_allowlist_candidates( sender_nick: str, sender_user: str | None = None, sender_host: str | None = None, ) -> list[str]: candidates = [sender_nick] if sender_user: candidates.append(f"{sender_nick}!{sender_user}") if sender_host: candidates.append(f"{sender_nick}@{sender_host}") if sender_user and sender_host: candidates.append(f"{sender_nick}!{sender_user}@{sender_host}") return candidates def resolve_allowlist_match( candidates: list[str], allowlist: list[str], ) -> str | None: for candidate in candidates: if candidate in allowlist: return candidate for candidate in candidates: for entry in allowlist: if fnmatchcase(candidate, entry): return entry if "*" in allowlist: return "*" return None def normalize_allow_entry(entry: str) -> str: for prefix in ("irc:", "irc:user:", "user:"): if entry.startswith(prefix): entry = entry[len(prefix) :] return entry def looks_like_irc_target_id(target: str) -> bool: if target.startswith("irc:channel:") or target.startswith("irc:user:"): return True if "!" in target and "@" in target: return True return False def normalize_irc_messaging_target(target: str) -> tuple[str, str]: if target.startswith("irc:channel:"): return "group", target.removeprefix("irc:channel:") if target.startswith("irc:user:"): return "direct", target.removeprefix("irc:user:") if target.startswith("#") or target.startswith("&"): return "group", target return "direct", normalize_allow_entry(target) def resolve_targets(target: str) -> dict: kind, resolved = normalize_irc_messaging_target(target) return {"kind": kind, "id": resolved, "display_name": resolved} def format_irc_sender_id(nick: str, user: str | None = None, host: str | None = None) -> str: if user and host: return f"{nick}!{user}@{host}" if user: return f"{nick}!{user}" if host: return f"{nick}@{host}" return nick