新增 IRC 渠道的完整扩展实现,包括客户端连接管理、消息收发与流式处理、安全策略与配对验证、消息去重与规范化、状态监控与健康探测、配置模型与账户管理、错误处理等模块。
74 lines
2.0 KiB
Python
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),
|
|
) |