ForcePilot/backend/package/yuxi/channel/extensions/irc/probe.py
Kris 061cc7076b feat(channel): 添加 IRC 渠道扩展插件
新增 IRC 渠道的完整扩展实现,包括客户端连接管理、消息收发与流式处理、安全策略与配对验证、消息去重与规范化、状态监控与健康探测、配置模型与账户管理、错误处理等模块。
2026-05-21 10:59:12 +08:00

74 lines
2.0 KiB
Python

import asyncio
import logging
import ssl
import time
from yuxi.channel.extensions.irc.client import send_line
from yuxi.channel.extensions.irc.protocol import parse_irc_line
from yuxi.channel.extensions.irc.types import IrcProbe
logger = logging.getLogger(__name__)
async def probe_irc(
host: str,
port: int,
tls: bool,
nick: str,
timeout: float = 8.0,
) -> IrcProbe:
try:
ssl_context = ssl.create_default_context() if tls else None
start = time.monotonic()
reader, writer = await asyncio.wait_for(
asyncio.open_connection(host, port, ssl=ssl_context),
timeout=timeout,
)
send_line(writer, f"NICK {nick}")
send_line(writer, f"USER probe 0 * :Probe")
await writer.drain()
got_001 = False
while not got_001:
try:
line_bytes = await asyncio.wait_for(reader.readline(), timeout=timeout)
except asyncio.TimeoutError:
break
if not line_bytes:
break
line = line_bytes.decode("utf-8", errors="replace").rstrip("\r\n")
parsed = parse_irc_line(line)
if parsed.command == "001":
got_001 = True
break
if parsed.command in ("432", "433", "464", "465"):
break
send_line(writer, "QUIT :probe")
await writer.drain()
writer.close()
await writer.wait_closed()
end = time.monotonic()
latency_ms = (end - start) * 1000
return IrcProbe(
ok=got_001,
host=host,
port=port,
tls=tls,
nick=nick,
latency_ms=latency_ms,
)
except Exception as e:
logger.warning("IRC probe failed for %s:%d: %s", host, port, e)
return IrcProbe(
ok=False,
host=host,
port=port,
tls=tls,
nick=nick,
error=str(e),
)