bot_permissions.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. from __future__ import annotations
  2. from collections.abc import Iterable
  3. from dataclasses import asdict, dataclass
  4. @dataclass(frozen=True)
  5. class BotPermission:
  6. key: str
  7. name: str
  8. group: str
  9. description: str
  10. @dataclass(frozen=True)
  11. class BuiltinBotRole:
  12. role_id: str
  13. name: str
  14. description: str
  15. permissions: tuple[str, ...]
  16. PERMISSIONS = (
  17. BotPermission("chat.profile", "群资料", "群组管理", "修改群标题和描述。"),
  18. BotPermission("chat.members", "成员与管理员", "群组管理", "查询、处罚及调整管理员。"),
  19. BotPermission("chat.permissions", "群权限", "群组管理", "修改普通成员默认权限。"),
  20. BotPermission("chat.announcements", "公告", "群组管理", "发送并置顶群公告。"),
  21. BotPermission("chat.invites", "邀请链接", "群组管理", "创建和撤销邀请链接。"),
  22. BotPermission("chat.rules", "群规", "群组管理", "查看和维护群规。"),
  23. BotPermission("chat.notes", "群笔记", "群组管理", "维护群内笔记功能。"),
  24. BotPermission(
  25. "automation.manage",
  26. "自动化与风控",
  27. "自动化",
  28. "管理欢迎语、自动回复、验证码、聊天机器人、内容风控和资料监控。",
  29. ),
  30. BotPermission("points.manage", "积分", "社区运营", "运行积分规则、排行和人工调整。"),
  31. BotPermission("giveaways.manage", "抽奖", "社区运营", "创建、开奖、退款和管理参与者。"),
  32. BotPermission("karma.manage", "声望", "社区运营", "处理点赞、点踩和声望排行。"),
  33. BotPermission("media.upload", "媒体中转", "系统能力", "向媒体中转群上传文件。"),
  34. )
  35. ALL_BOT_PERMISSIONS = frozenset(item.key for item in PERMISSIONS)
  36. PRIVATE_MANAGEMENT_PERMISSIONS = frozenset(
  37. {
  38. "chat.announcements",
  39. "chat.members",
  40. "chat.permissions",
  41. "automation.manage",
  42. "points.manage",
  43. "giveaways.manage",
  44. }
  45. )
  46. BUILTIN_ROLES = (
  47. BuiltinBotRole(
  48. "full_access",
  49. "全部职责",
  50. "拥有当前系统的全部机器人职责。",
  51. tuple(item.key for item in PERMISSIONS),
  52. ),
  53. BuiltinBotRole(
  54. "group_manager",
  55. "群组管理员",
  56. "负责群资料、成员、权限、公告、邀请链接、群规和群笔记。",
  57. (
  58. "chat.profile",
  59. "chat.members",
  60. "chat.permissions",
  61. "chat.announcements",
  62. "chat.invites",
  63. "chat.rules",
  64. "chat.notes",
  65. ),
  66. ),
  67. BuiltinBotRole(
  68. "automation_manager",
  69. "自动化与风控专员",
  70. "负责自动回复、欢迎语、验证码、聊天机器人、风控和资料监控。",
  71. ("automation.manage", "media.upload"),
  72. ),
  73. BuiltinBotRole(
  74. "points_manager",
  75. "积分专员",
  76. "负责积分规则、签到、排行榜和积分调整。",
  77. ("points.manage",),
  78. ),
  79. BuiltinBotRole(
  80. "giveaway_manager",
  81. "抽奖专员",
  82. "负责抽奖全流程,并可处理抽奖关联积分。",
  83. ("giveaways.manage", "points.manage"),
  84. ),
  85. BuiltinBotRole(
  86. "karma_manager",
  87. "声望专员",
  88. "负责点赞、点踩和声望排行。",
  89. ("karma.manage",),
  90. ),
  91. )
  92. MODULE_PERMISSIONS: dict[str, frozenset[str]] = {
  93. "admin": frozenset({"chat.members", "chat.announcements", "chat.invites"}),
  94. "admin_misc": frozenset({"chat.profile", "chat.members"}),
  95. "admin_panel": PRIVATE_MANAGEMENT_PERMISSIONS,
  96. "antiservice": frozenset({"automation.manage"}),
  97. "blacklist": frozenset({"automation.manage"}),
  98. "blacklist_chat": frozenset({"automation.manage"}),
  99. "chatbot": frozenset({"automation.manage"}),
  100. "filters": frozenset({"automation.manage"}),
  101. "flood": frozenset({"automation.manage"}),
  102. "greetings": frozenset({"automation.manage"}),
  103. "giveaway": frozenset({"giveaways.manage"}),
  104. "karma": frozenset({"karma.manage"}),
  105. "locks": frozenset({"chat.permissions"}),
  106. "notes": frozenset({"chat.notes"}),
  107. "points": frozenset({"points.manage"}),
  108. "rules": frozenset({"chat.rules"}),
  109. }
  110. TELEGRAM_COMMAND_PERMISSIONS = {
  111. "purge": "chat.members",
  112. "kick": "chat.members",
  113. "dkick": "chat.members",
  114. "ban": "chat.members",
  115. "dban": "chat.members",
  116. "tban": "chat.members",
  117. "unban": "chat.members",
  118. "listban": "chat.members",
  119. "listunban": "chat.members",
  120. "del": "chat.members",
  121. "promote": "chat.members",
  122. "fullpromote": "chat.members",
  123. "demote": "chat.members",
  124. "mute": "chat.members",
  125. "tmute": "chat.members",
  126. "unmute": "chat.members",
  127. "ban_ghosts": "chat.members",
  128. "warn": "chat.members",
  129. "dwarn": "chat.members",
  130. "rmwarns": "chat.members",
  131. "warns": "chat.members",
  132. "report": "chat.members",
  133. "admins": "chat.members",
  134. "admin": "chat.members",
  135. "set_user_title": "chat.members",
  136. "pin": "chat.announcements",
  137. "unpin": "chat.announcements",
  138. "invite": "chat.invites",
  139. "set_chat_title": "chat.profile",
  140. "set_chat_photo": "chat.profile",
  141. }
  142. def permission_catalog() -> list[dict[str, str]]:
  143. return [asdict(item) for item in PERMISSIONS]
  144. def builtin_roles() -> list[dict[str, object]]:
  145. return [
  146. {
  147. "role_id": item.role_id,
  148. "name": item.name,
  149. "description": item.description,
  150. "permissions": list(item.permissions),
  151. "builtin": True,
  152. }
  153. for item in BUILTIN_ROLES
  154. ]
  155. def normalize_permissions(values: object) -> list[str]:
  156. if not isinstance(values, (list, tuple, set, frozenset)):
  157. values = str(values or "").replace(",", " ").split()
  158. normalized = list(dict.fromkeys(str(item).strip() for item in values if str(item).strip()))
  159. unknown = sorted(set(normalized) - ALL_BOT_PERMISSIONS)
  160. if unknown:
  161. raise ValueError(f"未知机器人权限:{', '.join(unknown)}")
  162. return normalized
  163. def has_permission(permission: str, permissions: Iterable[str] | None) -> bool:
  164. current = set(permissions or ())
  165. return "*" in current or permission in current
  166. def has_any_permission(
  167. required: Iterable[str],
  168. permissions: Iterable[str] | None,
  169. ) -> bool:
  170. current = set(permissions or ())
  171. return "*" in current or bool(set(required) & current)
  172. def module_allowed(module: str, permissions: Iterable[str] | None) -> bool:
  173. required = MODULE_PERMISSIONS.get(module)
  174. return not required or has_any_permission(required, permissions)
  175. def api_permission(method: str, path: str) -> str | None:
  176. if path.startswith("/api/admin/v1/media"):
  177. return "media.upload"
  178. if path.startswith("/api/admin/v1/points"):
  179. return "points.manage"
  180. if path.startswith("/api/admin/v1/giveaways"):
  181. return "giveaways.manage"
  182. if "/giveaway-bans" in path:
  183. return "giveaways.manage"
  184. if not path.startswith("/api/admin/v1/chats/"):
  185. return None
  186. if path.endswith("/profile"):
  187. return "chat.profile"
  188. if path.endswith("/permissions"):
  189. return "chat.permissions"
  190. if path.endswith("/announcements"):
  191. return "chat.announcements"
  192. if "/members" in path or path.endswith("/admins"):
  193. return "chat.members"
  194. if path.endswith("/invites"):
  195. return "chat.invites"
  196. if path.endswith("/rules"):
  197. return "chat.rules"
  198. if path.endswith("/automation"):
  199. return "automation.manage"
  200. if "/points/" in path:
  201. return "points.manage"
  202. return None