| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- from __future__ import annotations
- from typing import Any
- from pyrogram.enums import ChatMemberStatus
- from wbb import BOT_ID, app
- class EligibilityUnavailable(RuntimeError):
- """Telegram cannot give a trustworthy membership decision."""
- def is_member(member: Any) -> bool:
- if member.status in {ChatMemberStatus.LEFT, ChatMemberStatus.BANNED}:
- return False
- if member.status == ChatMemberStatus.RESTRICTED:
- return bool(getattr(member, "is_member", False))
- return member.status in {
- ChatMemberStatus.OWNER,
- ChatMemberStatus.ADMINISTRATOR,
- ChatMemberStatus.MEMBER,
- }
- async def validate_targets(targets: list[dict[str, Any]]) -> list[dict[str, Any]]:
- if len(targets) > 10:
- raise ValueError("最多设置 10 个资格频道或群。")
- normalized: list[dict[str, Any]] = []
- seen: set[int] = set()
- for target in targets:
- try:
- chat_id = int(target["chat_id"])
- except (KeyError, TypeError, ValueError) as exc:
- raise ValueError("资格目标必须填写有效的 Telegram ID。") from exc
- if chat_id in seen:
- continue
- seen.add(chat_id)
- try:
- chat = await app.get_chat(chat_id)
- bot = await app.get_chat_member(chat_id, BOT_ID)
- except Exception as exc:
- raise ValueError(f"机器人无法访问资格目标 {chat_id}。") from exc
- kind = str(getattr(chat.type, "value", chat.type)).lower()
- if kind not in {"channel", "group", "supergroup"}:
- raise ValueError("资格目标只能是频道或群。")
- if bot.status not in {ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR}:
- raise ValueError(f"机器人必须是资格目标 {chat_id} 的管理员。")
- normalized.append(
- {
- "chat_id": chat_id, "type": kind,
- "title": str(chat.title or chat_id)[:200],
- "username": str(getattr(chat, "username", None) or "")[:100],
- }
- )
- return normalized
- async def check_eligibility(
- giveaway: dict[str, Any], user_id: int
- ) -> tuple[bool, list[int]]:
- targets = list(giveaway.get("eligibility_targets") or [])
- chat_ids = [int(giveaway["chat_id"]), *[int(item["chat_id"]) for item in targets]]
- outcomes: list[bool] = []
- failed: list[int] = []
- unknown: list[int] = []
- for chat_id in dict.fromkeys(chat_ids):
- try:
- if chat_id != int(giveaway["chat_id"]):
- bot = await app.get_chat_member(chat_id, BOT_ID)
- if bot.status not in {ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR}:
- raise EligibilityUnavailable(f"机器人不再是 {chat_id} 的管理员")
- member = await app.get_chat_member(chat_id, user_id)
- except EligibilityUnavailable:
- unknown.append(chat_id)
- continue
- except Exception:
- unknown.append(chat_id)
- continue
- present = is_member(member)
- if chat_id == int(giveaway["chat_id"]):
- if not present:
- failed.append(chat_id)
- else:
- outcomes.append(present)
- if not present:
- failed.append(chat_id)
- mode = giveaway.get("eligibility_mode", "all")
- if int(giveaway["chat_id"]) in failed:
- return False, failed
- if int(giveaway["chat_id"]) in unknown:
- raise EligibilityUnavailable("无法核验报名群的成员状态")
- if any(int(item["chat_id"]) == int(giveaway["chat_id"]) for item in targets):
- outcomes.append(True)
- if mode == "all" and failed:
- return False, failed
- if mode == "any" and any(outcomes):
- return True, []
- if unknown:
- raise EligibilityUnavailable(
- "无法核验以下频道或群的成员状态:" + ", ".join(map(str, unknown))
- )
- eligible = not targets or (all(outcomes) if mode == "all" else any(outcomes))
- return eligible, ([] if eligible else failed)
|