ForcePilot/backend/package/yuxi/channel/extensions/tencent_im/group.py
Kris 331daa1216 feat(channel): 添加腾讯 IM 渠道扩展
新增腾讯 IM(Tencent IM)渠道扩展,支持在 Yuxi 平台中集成腾讯即时通讯 IM 渠道。

包含以下功能模块:
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- pairing: 用户配对与绑定
- security: 安全校验
- signature: 请求签名验证
- usersig: UserSig 生成
- dedupe: 消息去重
- status: 会话状态管理
- group: 群组管理
- types: 类型定义
2026-05-21 11:49:29 +08:00

215 lines
7.4 KiB
Python

import logging
from yuxi.channel.extensions.tencent_im.gateway import TencentIMError, TencentIMGateway
logger = logging.getLogger(__name__)
GROUP_TYPES = {
"public": "Public",
"private": "Private",
"chatroom": "ChatRoom",
"avchatroom": "AVChatRoom",
"topic": "Topic",
}
class TencentIMGroupManager:
def __init__(self, gateway: TencentIMGateway | None = None):
self._gateway = gateway
async def create_group(
self,
name: str,
group_type: str = "public",
owner_account: str = "",
member_list: list[str] | None = None,
) -> str:
if not self._gateway:
raise TencentIMError(-1, "gateway not started")
im_type = GROUP_TYPES.get(group_type, "Public")
body = {
"Owner_Account": owner_account or self._gateway._account.admin_userid,
"Type": im_type,
"Name": name,
}
if member_list:
body["MemberList"] = [{"Member_Account": uid} for uid in member_list]
result = await self._gateway.call_api("group_open_http_svc/create_group", body)
return result.get("GroupId", "")
async def destroy_group(self, group_id: str) -> bool:
if not self._gateway:
return False
await self._gateway.call_api(
"group_open_http_svc/destroy_group",
{
"GroupId": group_id,
},
)
return True
async def add_group_member(self, group_id: str, member_ids: list[str]) -> bool:
if not self._gateway:
return False
await self._gateway.call_api(
"group_open_http_svc/add_group_member",
{
"GroupId": group_id,
"MemberList": [{"Member_Account": uid} for uid in member_ids],
},
)
return True
async def delete_group_member(self, group_id: str, member_ids: list[str], reason: str = "") -> bool:
if not self._gateway:
return False
await self._gateway.call_api(
"group_open_http_svc/delete_group_member",
{
"GroupId": group_id,
"MemberToDel_Account": member_ids,
"Reason": reason,
},
)
return True
async def get_group_info(self, group_ids: list[str]) -> list[dict]:
if not self._gateway:
return []
result = await self._gateway.call_api(
"group_open_http_svc/get_group_info",
{"GroupIdList": [{"GroupId": gid} for gid in group_ids]},
)
return result.get("GroupInfo", [])
async def get_group_member_info(self, group_id: str, limit: int = 100, offset: int = 0) -> list[dict]:
if not self._gateway:
return []
result = await self._gateway.call_api(
"group_open_http_svc/get_group_member_info",
{"GroupId": group_id, "Limit": limit, "Offset": offset},
)
return result.get("MemberList", [])
async def forbid_send_msg(self, group_id: str, member_ids: list[str], shut_up_time: int = 60) -> bool:
if not self._gateway:
return False
await self._gateway.call_api(
"group_open_http_svc/forbid_send_msg",
{
"GroupId": group_id,
"Members_Account": member_ids,
"ShutUpTime": shut_up_time,
},
)
return True
async def get_group_muted_account(self, group_id: str) -> list[dict]:
if not self._gateway:
return []
result = await self._gateway.call_api(
"group_open_http_svc/get_group_muted_account",
{"GroupId": group_id},
)
return result.get("MutedAccountList", [])
async def modify_group_base_info(self, group_id: str, **kwargs) -> bool:
if not self._gateway:
return False
body = {"GroupId": group_id}
if "name" in kwargs:
body["Name"] = kwargs["name"]
if "introduction" in kwargs:
body["Introduction"] = kwargs["introduction"]
if "notification" in kwargs:
body["Notification"] = kwargs["notification"]
if "face_url" in kwargs:
body["FaceUrl"] = kwargs["face_url"]
if "max_member_num" in kwargs:
body["MaxMemberNum"] = kwargs["max_member_num"]
await self._gateway.call_api("group_open_http_svc/modify_group_base_info", body)
return True
async def get_appid_group_list(self, limit: int = 100, offset: int = 0, group_type: str = "") -> list[dict]:
if not self._gateway:
return []
body = {"Limit": limit, "Offset": offset}
if group_type:
body["GroupType"] = group_type
result = await self._gateway.call_api("group_open_http_svc/get_appid_group_list", body)
return result.get("GroupIdList", [])
async def get_joined_group_list(self, user_id: str, limit: int = 100, offset: int = 0) -> list[dict]:
if not self._gateway:
return []
result = await self._gateway.call_api(
"group_open_http_svc/get_joined_group_list",
{
"Member_Account": user_id,
"Limit": limit,
"Offset": offset,
},
)
return result.get("GroupIdList", [])
async def get_role_in_group(self, group_id: str, user_id: str) -> str:
if not self._gateway:
return ""
result = await self._gateway.call_api(
"group_open_http_svc/get_role_in_group",
{
"GroupId": group_id,
"User_Account": [user_id],
},
)
roles = result.get("UserIdList", [])
if roles:
return roles[0].get("Role", "")
return ""
async def modify_group_member_info(self, group_id: str, user_id: str, **kwargs) -> bool:
if not self._gateway:
return False
body = {"GroupId": group_id, "Member_Account": user_id}
if "name_card" in kwargs:
body["NameCard"] = kwargs["name_card"]
if "shut_up_time" in kwargs:
body["ShutUpUntil"] = kwargs["shut_up_time"]
await self._gateway.call_api("group_open_http_svc/modify_group_member_info", body)
return True
async def change_group_owner(self, group_id: str, new_owner_id: str) -> bool:
if not self._gateway:
return False
await self._gateway.call_api(
"group_open_http_svc/change_group_owner",
{"GroupId": group_id, "NewOwner_Account": new_owner_id},
)
return True
async def set_unread_msg_num(self, group_id: str, user_id: str, unread_num: int) -> bool:
if not self._gateway:
return False
await self._gateway.call_api(
"group_open_http_svc/set_unread_msg_num",
{
"GroupId": group_id,
"Member_Account": user_id,
"UnreadMsgNum": unread_num,
},
)
return True
async def group_msg_get_simple(self, group_id: str, req_msg_seq: int = 0, req_msg_number: int = 20) -> list[dict]:
if not self._gateway:
return []
result = await self._gateway.call_api(
"group_open_http_svc/group_msg_get_simple",
{
"GroupId": group_id,
"ReqMsgSeq": req_msg_seq,
"ReqMsgNumber": req_msg_number,
},
)
return result.get("RspMsgList", [])