新增 KakaoTalk 渠道扩展,支持在 Yuxi 平台中集成 KakaoTalk 即时通讯渠道。 包含以下功能模块: - bot: Bot 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - webhook: Webhook 事件处理 - outbound: 外发消息管理 - streaming: 流式消息处理 - pairing: 用户配对与绑定 - security: 安全校验 - dedupe: 消息去重 - monitor: 渠道状态监控 - status: 会话状态管理 - card_builder: KakaoTalk 卡片消息构建 - quick_reply: 快捷回复处理 - types: 类型定义
105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
|
|
from yuxi.channel.extensions.kakaotalk.bot import KakaoTalkBotClient
|
|
from yuxi.channel.extensions.kakaotalk.webhook import _handler as _webhook_handler
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class KakaoTalkGatewayAdapter:
|
|
|
|
def __init__(self):
|
|
self._webhook = _webhook_handler
|
|
self._running = False
|
|
self._account_cache: dict = {}
|
|
|
|
async def start(self, ctx) -> object:
|
|
account_id = getattr(ctx, "account_id", "default")
|
|
config = getattr(ctx, "config", {}) or {}
|
|
|
|
account = self._resolve_account(ctx)
|
|
self._account_cache[account_id] = account
|
|
|
|
admin_key = account.get("admin_key", "")
|
|
bot_id = account.get("bot_id", "")
|
|
|
|
if not admin_key or not bot_id:
|
|
logger.warning("KakaoTalk gateway start: admin_key or bot_id not configured for account %s", account_id)
|
|
self._running = True
|
|
queue = getattr(ctx, "queue", asyncio.Queue())
|
|
return queue
|
|
|
|
try:
|
|
bot = KakaoTalkBotClient(admin_key=admin_key)
|
|
alive = await bot.probe()
|
|
if alive:
|
|
logger.info("KakaoTalk bot probe successful for account %s (bot_id=%s)", account_id, bot_id)
|
|
else:
|
|
logger.warning("KakaoTalk bot probe failed for account %s", account_id)
|
|
except Exception:
|
|
logger.exception("KakaoTalk bot probe error for account %s", account_id)
|
|
|
|
self._webhook.start()
|
|
self._webhook.configure(config)
|
|
self._running = True
|
|
|
|
queue = getattr(ctx, "queue", asyncio.Queue())
|
|
return queue
|
|
|
|
async def stop(self, ctx) -> None:
|
|
self._running = False
|
|
self._webhook.stop()
|
|
self._account_cache.clear()
|
|
logger.info("KakaoTalk gateway stopped")
|
|
|
|
async def probe(self, account: dict) -> bool:
|
|
admin_key = account.get("admin_key", "")
|
|
if not admin_key:
|
|
return False
|
|
try:
|
|
bot = KakaoTalkBotClient(admin_key=admin_key)
|
|
return await bot.probe()
|
|
except Exception:
|
|
return False
|
|
|
|
@staticmethod
|
|
def _resolve_account(ctx) -> dict:
|
|
account_id = getattr(ctx, "account_id", "default")
|
|
config = getattr(ctx, "config", {}) or {}
|
|
|
|
kt_cfg = config.get("channels", {}).get("kakaotalk", {})
|
|
if not isinstance(kt_cfg, dict):
|
|
kt_cfg = {}
|
|
|
|
accounts = kt_cfg.get("accounts", {})
|
|
if not isinstance(accounts, dict):
|
|
accounts = {}
|
|
|
|
account_raw = accounts.get(account_id, {})
|
|
if not isinstance(account_raw, dict):
|
|
account_raw = {}
|
|
|
|
admin_key = account_raw.get("admin_key") or kt_cfg.get("admin_key", "")
|
|
rest_api_key = account_raw.get("rest_api_key") or kt_cfg.get("rest_api_key", "")
|
|
bot_id = account_raw.get("bot_id") or kt_cfg.get("bot_id", "")
|
|
|
|
return {
|
|
"account_id": account_id,
|
|
"admin_key": admin_key,
|
|
"rest_api_key": rest_api_key,
|
|
"bot_id": bot_id,
|
|
"channel_name": account_raw.get("channel_name", kt_cfg.get("channel_name", "")),
|
|
"name": account_raw.get("name", account_id),
|
|
"dm_policy": account_raw.get("dm_policy", kt_cfg.get("dm_policy", "pairing")),
|
|
"allow_from": account_raw.get("allow_from", kt_cfg.get("allow_from", [])),
|
|
"skill_server_path": account_raw.get(
|
|
"skill_server_path", kt_cfg.get("skill_server_path", "/kakaotalk/skill")
|
|
),
|
|
"text_chunk_limit": account_raw.get(
|
|
"text_chunk_limit", kt_cfg.get("text_chunk_limit", 1000)
|
|
),
|
|
}
|