ForcePilot/backend/package/yuxi/channel/extensions/telegram/chat.py
Kris 8a3601250e feat(channel): 添加 Telegram 渠道扩展
新增 Telegram 渠道扩展,支持在 Yuxi 平台中集成 Telegram 即时通讯渠道。

包含以下功能模块:
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- polling: 长轮询模式
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- session: 会话管理
- actions: 动作处理
- inline_keyboard: 内联键盘
- native_commands: 原生指令
- chat: 聊天管理
- delivery: 消息送达确认
- media: 媒体资源处理
- profile: 用户资料
- reactions: 表情反应
- sticker: 贴纸处理
- types: 类型定义
2026-05-21 11:48:09 +08:00

183 lines
6.9 KiB
Python

from __future__ import annotations
import logging
from typing import Any
logger = logging.getLogger(__name__)
class TelegramChat:
def __init__(self, outbound):
self._outbound = outbound
async def _call(self, token: str, method: str, payload: dict) -> dict | list | int | None:
return await self._outbound._api_call(token, method, payload)
async def get_chat(self, account_id: str, chat_id: str) -> dict | None:
token = await self._resolve_token(account_id)
if not token:
return None
result = await self._call(token, "getChat", {"chat_id": chat_id})
return result if isinstance(result, dict) else None
async def get_chat_member(
self, account_id: str, chat_id: str, user_id: int,
) -> dict | None:
token = await self._resolve_token(account_id)
if not token:
return None
result = await self._call(
token, "getChatMember", {"chat_id": chat_id, "user_id": user_id},
)
return result if isinstance(result, dict) else None
async def get_chat_member_count(self, account_id: str, chat_id: str) -> int | None:
token = await self._resolve_token(account_id)
if not token:
return None
result = await self._call(token, "getChatMemberCount", {"chat_id": chat_id})
return result if isinstance(result, int) else None
async def get_chat_administrators(self, account_id: str, chat_id: str) -> list[dict] | None:
token = await self._resolve_token(account_id)
if not token:
return None
result = await self._call(token, "getChatAdministrators", {"chat_id": chat_id})
return result if isinstance(result, list) else None
async def get_user_profile_photos(
self, account_id: str, user_id: int, *, limit: int = 100,
) -> dict | None:
token = await self._resolve_token(account_id)
if not token:
return None
result = await self._call(
token, "getUserProfilePhotos",
{"user_id": user_id, "limit": limit},
)
return result if isinstance(result, dict) else None
async def pin_message(
self, account_id: str, chat_id: str, message_id: int, *,
disable_notification: bool = False,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
result = await self._call(
token, "pinChatMessage",
{"chat_id": chat_id, "message_id": message_id, "disable_notification": disable_notification},
)
return result is not None and not isinstance(result, dict)
async def unpin_message(
self, account_id: str, chat_id: str, message_id: int | None = None,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
payload: dict[str, Any] = {"chat_id": chat_id}
if message_id:
payload["message_id"] = message_id
result = await self._call(token, "unpinChatMessage", payload)
return result is not None and not isinstance(result, dict)
async def create_forum_topic(
self, account_id: str, chat_id: str, name: str, *,
icon_color: int | None = None, icon_custom_emoji_id: str | None = None,
) -> dict | None:
token = await self._resolve_token(account_id)
if not token:
return None
payload: dict[str, Any] = {"chat_id": chat_id, "name": name}
if icon_color is not None:
payload["icon_color"] = icon_color
if icon_custom_emoji_id:
payload["icon_custom_emoji_id"] = icon_custom_emoji_id
result = await self._call(token, "createForumTopic", payload)
return result if isinstance(result, dict) else None
async def close_forum_topic(
self, account_id: str, chat_id: str, message_thread_id: int,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
result = await self._call(
token, "closeForumTopic",
{"chat_id": chat_id, "message_thread_id": message_thread_id},
)
return result is not None and not isinstance(result, dict)
async def reopen_forum_topic(
self, account_id: str, chat_id: str, message_thread_id: int,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
result = await self._call(
token, "reopenForumTopic",
{"chat_id": chat_id, "message_thread_id": message_thread_id},
)
return result is not None and not isinstance(result, dict)
async def delete_forum_topic(
self, account_id: str, chat_id: str, message_thread_id: int,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
result = await self._call(
token, "deleteForumTopic",
{"chat_id": chat_id, "message_thread_id": message_thread_id},
)
return result is not None and not isinstance(result, dict)
async def edit_forum_topic(
self, account_id: str, chat_id: str, message_thread_id: int, *,
name: str | None = None, icon_custom_emoji_id: str | None = None,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
payload: dict[str, Any] = {"chat_id": chat_id, "message_thread_id": message_thread_id}
if name:
payload["name"] = name
if icon_custom_emoji_id:
payload["icon_custom_emoji_id"] = icon_custom_emoji_id
result = await self._call(token, "editForumTopic", payload)
return result is not None and not isinstance(result, dict)
async def edit_general_forum_topic(
self, account_id: str, chat_id: str, name: str,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
result = await self._call(
token, "editGeneralForumTopic",
{"chat_id": chat_id, "name": name},
)
return result is not None and not isinstance(result, dict)
async def set_chat_member_tag(
self, account_id: str, chat_id: str, user_id: int, tag: str,
) -> bool:
token = await self._resolve_token(account_id)
if not token:
return False
result = await self._call(
token, "setChatMemberTag",
{"chat_id": chat_id, "user_id": user_id, "tag": tag},
)
return result is not None and not isinstance(result, dict)
async def _resolve_token(self, account_id: str | None) -> str:
from yuxi.channel.extensions.telegram.config import TelegramConfigAdapter
adapter = TelegramConfigAdapter()
aid = account_id or "default"
account = await adapter.resolve_account(aid)
return account.get("token", "")