ForcePilot/backend/package/yuxi/channels/adapters/irc/protocol.py
Kris 2dd1a075f2 feat(irc): 实现完整的IRC适配器基础功能模块
新增IRC协议相关的全套工具模块,包括:
- 核心协议解析与CTCP处理
- 消息发送缓存与文本 sanitize
- 账号配置管理与运行时状态
- 命令处理与权限控制
- 服务发现与诊断工具
- 多账号网关与配置加载
2026-05-12 00:45:10 +08:00

105 lines
2.5 KiB
Python

from __future__ import annotations
import re
from typing import Any
_IRC_LINE_PATTERN = re.compile(
r"^(?:@(?P<tags>[^ ]+) )?(?::(?P<prefix>[^ ]+) )?(?P<command>[a-zA-Z]+|\d{3})"
r"(?: (?P<params>.+?))?(?: :(?P<trailing>.*))?$"
)
_IRC_PREFIX_PATTERN = re.compile(r"^([^!@]+)(?:!([^@]+))?(?:@(.+))?$")
def parse_irc_line(line: str) -> tuple[str | None, str, list[str]]:
match = _IRC_LINE_PATTERN.match(line)
if match is None:
return None, line, []
command = match.group("command").upper()
prefix = match.group("prefix") or None
args: list[str] = []
params = match.group("params") or ""
if params:
args.extend(params.split())
trailing = match.group("trailing")
if trailing is not None:
args.append(trailing)
return prefix, command, args
def parse_irc_prefix(prefix: str | None) -> dict[str, str | None]:
if not prefix:
return {"nick": None, "user": None, "host": None}
m = _IRC_PREFIX_PATTERN.match(prefix)
if m is None:
return {"nick": prefix, "user": None, "host": None}
return {
"nick": m.group(1) or None,
"user": m.group(2) or None,
"host": m.group(3) or None,
}
def extract_nick(prefix: str | None) -> str:
if not prefix:
return "unknown"
return prefix.split("!")[0]
def parse_irc_tags(line: str) -> dict[str, Any] | None:
if not line.startswith("@"):
return None
space_idx = line.find(" ")
if space_idx == -1:
return None
tag_str = line[1:space_idx]
tags: dict[str, Any] = {}
for tag in tag_str.split(";"):
if "=" in tag:
key, value = tag.split("=", 1)
tags[key] = value
else:
tags[tag] = True
return tags
def parse_irc_tags_dict(line: str) -> dict[str, Any]:
return parse_irc_tags(line) or {}
def is_ctcp_message(text: str) -> bool:
return text.startswith("\x01") and text.endswith("\x01")
def parse_ctcp(text: str) -> tuple[str, str]:
inner = text[1:-1]
parts = inner.split(" ", 1)
command = parts[0].upper()
args = parts[1] if len(parts) > 1 else ""
return command, args
def format_ctcp(command: str, args: str = "") -> str:
if args:
return f"\x01{command} {args}\x01"
return f"\x01{command}\x01"
def extract_account_from_tags(tags: dict[str, Any]) -> str | None:
return tags.get("account") or None
def is_identified(tags: dict[str, Any]) -> bool:
account = extract_account_from_tags(tags)
return account is not None and account != "*"