|
|
@@ -11,7 +11,6 @@ from pymongo.errors import DuplicateKeyError
|
|
|
from pyrogram.enums import ChatMemberStatus, ChatType, ParseMode
|
|
|
from pyrogram.errors import RPCError
|
|
|
from pyrogram.types import (
|
|
|
- ChatPrivileges,
|
|
|
InputMediaAnimation,
|
|
|
InputMediaDocument,
|
|
|
InputMediaPhoto,
|
|
|
@@ -63,6 +62,48 @@ def telegram_error_detail(exc: Exception) -> str:
|
|
|
return f"{type(exc).__name__}: {str(exc)[:180]}" if str(exc) else type(exc).__name__
|
|
|
|
|
|
|
|
|
+async def channel_bot_api(method: str, **params: Any) -> Any:
|
|
|
+ token = str(getattr(wbb, "BOT_TOKEN", "") or "")
|
|
|
+ if not token:
|
|
|
+ raise ChatManagementError("bot_token_missing", "机器人令牌不可用。", status=503)
|
|
|
+ data = {
|
|
|
+ key: ("true" if value else "false") if isinstance(value, bool) else str(value)
|
|
|
+ for key, value in params.items()
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ async with ClientSession(timeout=ClientTimeout(total=15)) as session:
|
|
|
+ async with session.post(f"https://api.telegram.org/bot{token}/{method}", data=data) as response:
|
|
|
+ payload = await response.json(content_type=None)
|
|
|
+ except Exception as exc:
|
|
|
+ raise ChatManagementError(
|
|
|
+ "channel_telegram_unavailable", "Telegram 管理员接口暂时不可用。", status=502
|
|
|
+ ) from exc
|
|
|
+ if not isinstance(payload, dict) or not payload.get("ok"):
|
|
|
+ detail = str(payload.get("description") or "请求失败")[:180] if isinstance(payload, dict) else "响应无效"
|
|
|
+ raise ChatManagementError(
|
|
|
+ "channel_telegram_failed", f"Telegram 拒绝管理员操作:{detail}", status=502
|
|
|
+ )
|
|
|
+ return payload.get("result")
|
|
|
+
|
|
|
+
|
|
|
+def serialize_bot_api_member(member: dict[str, Any]) -> dict[str, Any]:
|
|
|
+ user = member.get("user") or {}
|
|
|
+ return {
|
|
|
+ "user": {
|
|
|
+ "id": str(user.get("id") or ""),
|
|
|
+ "username": user.get("username"),
|
|
|
+ "first_name": user.get("first_name"),
|
|
|
+ "last_name": user.get("last_name"),
|
|
|
+ "is_bot": bool(user.get("is_bot")),
|
|
|
+ "is_deleted": bool(user.get("is_deleted")),
|
|
|
+ },
|
|
|
+ "status": "owner" if member.get("status") == "creator" else member.get("status"),
|
|
|
+ "custom_title": member.get("custom_title"),
|
|
|
+ "privileges": [name for name in sorted(CHANNEL_ADMIN_PRIVILEGES) if member.get(name)],
|
|
|
+ "until_date": member.get("until_date"),
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
def public_post(post: dict[str, Any]) -> dict[str, Any]:
|
|
|
return {key: value for key, value in post.items() if key not in {"_id", "telegram_key"}}
|
|
|
|
|
|
@@ -177,45 +218,28 @@ async def set_channel_admin(chat_id: int, user_id: int, privileges: list[str]) -
|
|
|
allowed = set(_privilege_list(bot))
|
|
|
if bot.status != ChatMemberStatus.OWNER and requested - allowed:
|
|
|
raise ChatManagementError("privilege_exceeds_bot", "不能授予超出机器人自身的权限。", status=403)
|
|
|
- try:
|
|
|
- target = await app.get_chat_member(chat_id, user_id)
|
|
|
- if target.status == ChatMemberStatus.OWNER:
|
|
|
- raise ChatManagementError("owner_protected", "不能修改频道所有者。", status=403)
|
|
|
- values = {key: key in requested for key in CHANNEL_ADMIN_PRIVILEGES}
|
|
|
- await app.promote_chat_member(chat_id, user_id, ChatPrivileges(**values))
|
|
|
- except ChatManagementError:
|
|
|
- raise
|
|
|
- except RPCError as exc:
|
|
|
- raise ChatManagementError(
|
|
|
- "admin_update_failed", f"Telegram 拒绝调整管理员:{telegram_error_detail(exc)}", status=502
|
|
|
- ) from exc
|
|
|
- except Exception as exc:
|
|
|
- raise ChatManagementError("admin_update_failed", "Telegram 未能调整管理员;请确认用户已订阅频道及机器人有权管理该用户。", status=502) from exc
|
|
|
- try:
|
|
|
- return serialize_member(await app.get_chat_member(chat_id, user_id))
|
|
|
- except Exception:
|
|
|
- return {"user_id": str(user_id), "updated": True}
|
|
|
+ target = await channel_bot_api("getChatMember", chat_id=chat_id, user_id=user_id)
|
|
|
+ if target.get("status") in {"creator", "owner"}:
|
|
|
+ raise ChatManagementError("owner_protected", "不能修改频道所有者。", status=403)
|
|
|
+ values = {key: key in requested for key in CHANNEL_ADMIN_PRIVILEGES}
|
|
|
+ await channel_bot_api("promoteChatMember", chat_id=chat_id, user_id=user_id, **values)
|
|
|
+ return serialize_bot_api_member(
|
|
|
+ await channel_bot_api("getChatMember", chat_id=chat_id, user_id=user_id)
|
|
|
+ )
|
|
|
|
|
|
|
|
|
async def remove_channel_admin(chat_id: int, user_id: int) -> dict:
|
|
|
await ensure_channel(chat_id)
|
|
|
await ensure_permission(chat_id, "can_promote_members")
|
|
|
- try:
|
|
|
- target = await app.get_chat_member(chat_id, user_id)
|
|
|
- except Exception as exc:
|
|
|
- raise ChatManagementError("admin_lookup_failed", "Telegram 未能读取该用户的频道身份。", status=502) from exc
|
|
|
- if target.status == ChatMemberStatus.OWNER:
|
|
|
+ target = await channel_bot_api("getChatMember", chat_id=chat_id, user_id=user_id)
|
|
|
+ if target.get("status") in {"creator", "owner"}:
|
|
|
raise ChatManagementError("owner_protected", "不能移除频道所有者。", status=403)
|
|
|
- if target.status != ChatMemberStatus.ADMINISTRATOR:
|
|
|
+ if target.get("status") != "administrator":
|
|
|
raise ChatManagementError("not_admin", "该用户不是频道管理员。", status=409)
|
|
|
- try:
|
|
|
- await app.promote_chat_member(chat_id, user_id, ChatPrivileges(can_manage_chat=False))
|
|
|
- except RPCError as exc:
|
|
|
- raise ChatManagementError(
|
|
|
- "admin_remove_failed", f"Telegram 拒绝移除管理员:{telegram_error_detail(exc)}", status=502
|
|
|
- ) from exc
|
|
|
- except Exception as exc:
|
|
|
- raise ChatManagementError("admin_remove_failed", "Telegram 未能移除管理员。", status=502) from exc
|
|
|
+ await channel_bot_api(
|
|
|
+ "promoteChatMember", chat_id=chat_id, user_id=user_id,
|
|
|
+ **{key: False for key in CHANNEL_ADMIN_PRIVILEGES},
|
|
|
+ )
|
|
|
return {"user_id": str(user_id), "removed": True}
|
|
|
|
|
|
|