admin.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. """
  2. MIT License
  3. Copyright (c) 2024 TheHamkerCat
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.
  19. """
  20. import asyncio
  21. import re
  22. from contextlib import suppress
  23. from time import time
  24. from pyrogram import filters
  25. from pyrogram.enums import ChatMembersFilter, ChatMemberStatus, ChatType
  26. from pyrogram.errors import FloodWait
  27. from pyrogram.types import (
  28. CallbackQuery,
  29. ChatMemberUpdated,
  30. ChatPermissions,
  31. ChatPrivileges,
  32. Message,
  33. )
  34. from wbb import BOT_ID, BOT_PERMISSIONS, SUDOERS, app, log
  35. from wbb.core.decorators.errors import capture_err
  36. from wbb.core.keyboard import ikb
  37. from wbb.services.bot_permissions import has_permission
  38. from wbb.services.interaction_settings import get_interaction_settings
  39. from wbb.services.member_shortcuts import (
  40. member_action_shortcut_command,
  41. )
  42. from wbb.services.message_cleanup import schedule_group_cleanup
  43. from wbb.utils.dbadmin import record_audit
  44. from wbb.utils.dbfunctions import (
  45. add_warn,
  46. get_warn,
  47. int_to_alpha,
  48. remove_warns,
  49. save_filter,
  50. )
  51. from wbb.utils.functions import (
  52. extract_user,
  53. extract_user_and_reason,
  54. time_converter,
  55. )
  56. __MODULE__ = "成员管理"
  57. __HELP__ = """/ban - 封禁成员。
  58. /dban - 删除被回复消息并封禁发送者。
  59. /tban - 临时封禁成员。
  60. /unban - 解除封禁。
  61. /warn - 警告成员。
  62. /dwarn - 删除被回复消息并警告发送者。
  63. /rmwarns - 清除成员的全部警告。
  64. /warns - 查看成员警告次数。
  65. /kick - 踢出成员。
  66. /dkick - 删除被回复消息并踢出发送者。
  67. /purge [数量] - 从被回复消息开始批量删除。
  68. /del - 删除被回复消息。
  69. /promote - 提升成员为管理员。
  70. /fullpromote - 授予成员全部可用管理权限。
  71. /demote - 取消成员的管理员身份。
  72. /pin 或 /unpin - 置顶或取消置顶被回复消息。
  73. /mute - 永久禁言成员。
  74. /tmute - 临时禁言成员。
  75. /unmute - 解除禁言。
  76. /ban_ghosts - 封禁已注销账号。
  77. /report、@admins 或 @admin - 向管理员举报消息。
  78. /invite - 获取群邀请链接。
  79. 回复成员消息发送“封禁”“解封”“禁言”“禁言10分钟”“解除禁言”、
  80. “警告”“删除消息”“删除”“删”“踢出”“踢”或“飞”,
  81. 可快捷执行对应管理操作。"""
  82. MEMBER_ACTION_CLEANUP_SECONDS = 10
  83. async def member_permissions(chat_id: int, user_id: int):
  84. perms = []
  85. member = (await app.get_chat_member(chat_id, user_id)).privileges
  86. if not member:
  87. return []
  88. if member.can_post_messages:
  89. perms.append("can_post_messages")
  90. if member.can_edit_messages:
  91. perms.append("can_edit_messages")
  92. if member.can_delete_messages:
  93. perms.append("can_delete_messages")
  94. if member.can_restrict_members:
  95. perms.append("can_restrict_members")
  96. if member.can_promote_members:
  97. perms.append("can_promote_members")
  98. if member.can_change_info:
  99. perms.append("can_change_info")
  100. if member.can_invite_users:
  101. perms.append("can_invite_users")
  102. if member.can_pin_messages:
  103. perms.append("can_pin_messages")
  104. if member.can_manage_video_chats:
  105. perms.append("can_manage_video_chats")
  106. return perms
  107. from wbb.core.decorators.permissions import adminsOnly
  108. admins_in_chat = {}
  109. async def list_admins(chat_id: int):
  110. global admins_in_chat
  111. if chat_id in admins_in_chat:
  112. interval = time() - admins_in_chat[chat_id]["last_updated_at"]
  113. if interval < 3600:
  114. return admins_in_chat[chat_id]["data"]
  115. admins_in_chat[chat_id] = {
  116. "last_updated_at": time(),
  117. "data": [
  118. member.user.id
  119. async for member in app.get_chat_members(
  120. chat_id, filter=ChatMembersFilter.ADMINISTRATORS
  121. )
  122. ],
  123. }
  124. return admins_in_chat[chat_id]["data"]
  125. # Admin cache reload
  126. @app.on_chat_member_updated()
  127. async def admin_cache_func(_, cmu: ChatMemberUpdated):
  128. if cmu.old_chat_member and cmu.old_chat_member.promoted_by:
  129. admins_in_chat[cmu.chat.id] = {
  130. "last_updated_at": time(),
  131. "data": [
  132. member.user.id
  133. async for member in app.get_chat_members(
  134. cmu.chat.id, filter=ChatMembersFilter.ADMINISTRATORS
  135. )
  136. ],
  137. }
  138. log.info(f"Updated admin cache for {cmu.chat.id} [{cmu.chat.title}]")
  139. # Purge Messages
  140. @app.on_message(filters.command("purge") & ~filters.private)
  141. @adminsOnly("can_delete_messages")
  142. async def purgeFunc(_, message: Message):
  143. repliedmsg = message.reply_to_message
  144. await message.delete()
  145. if not repliedmsg:
  146. return await message.reply_text("请回复一条消息,机器人将从该消息开始删除。")
  147. cmd = message.command
  148. if len(cmd) > 1 and cmd[1].isdigit():
  149. purge_to = repliedmsg.id + int(cmd[1])
  150. if purge_to > message.id:
  151. purge_to = message.id
  152. else:
  153. purge_to = message.id
  154. chat_id = message.chat.id
  155. message_ids = []
  156. for message_id in range(
  157. repliedmsg.id,
  158. purge_to,
  159. ):
  160. message_ids.append(message_id)
  161. # Max message deletion limit is 100
  162. if len(message_ids) == 100:
  163. await app.delete_messages(
  164. chat_id=chat_id,
  165. message_ids=message_ids,
  166. revoke=True, # For both sides
  167. )
  168. # To delete more than 100 messages, start again
  169. message_ids = []
  170. # Delete if any messages left
  171. if len(message_ids) > 0:
  172. await app.delete_messages(
  173. chat_id=chat_id,
  174. message_ids=message_ids,
  175. revoke=True,
  176. )
  177. # Kick members
  178. @app.on_message(filters.command(["kick", "dkick"]) & ~filters.private)
  179. @adminsOnly("can_restrict_members")
  180. async def kickFunc(_, message: Message):
  181. user_id, reason = await extract_user_and_reason(message)
  182. if not user_id:
  183. return await message.reply_text("未找到该成员。")
  184. if user_id == BOT_ID:
  185. return await message.reply_text(
  186. "机器人不能将自己踢出群组。"
  187. )
  188. if user_id in SUDOERS:
  189. return await message.reply_text("不能踢出超级管理员。")
  190. if user_id in (await list_admins(message.chat.id)):
  191. return await message.reply_text(
  192. "不能踢出群管理员,请先取消其管理员身份。"
  193. )
  194. mention = (await app.get_users(user_id)).mention
  195. msg = f"""
  196. **已踢出:** {mention}
  197. **操作者:** {message.from_user.mention if message.from_user else '匿名管理员'}
  198. **原因:** {reason or '未填写原因'}"""
  199. if message.command[0][0] == "d":
  200. await message.reply_to_message.delete()
  201. await message.chat.ban_member(user_id)
  202. replied_message = message.reply_to_message
  203. if replied_message:
  204. message = replied_message
  205. await message.reply_text(msg)
  206. await asyncio.sleep(1)
  207. await message.chat.unban_member(user_id)
  208. # Ban members
  209. @app.on_message(filters.command(["ban", "dban", "tban"]) & ~filters.private)
  210. @adminsOnly("can_restrict_members")
  211. async def banFunc(_, message: Message):
  212. user_id, reason = await extract_user_and_reason(message, sender_chat=True)
  213. if not user_id:
  214. return await message.reply_text("未找到该成员。")
  215. if user_id == BOT_ID:
  216. return await message.reply_text(
  217. "机器人不能封禁自己。"
  218. )
  219. if user_id in SUDOERS:
  220. return await message.reply_text(
  221. "不能封禁超级管理员。"
  222. )
  223. if user_id in (await list_admins(message.chat.id)):
  224. return await message.reply_text(
  225. "不能封禁群管理员,请先取消其管理员身份。"
  226. )
  227. try:
  228. mention = (await app.get_users(user_id)).mention
  229. except IndexError:
  230. mention = (
  231. message.reply_to_message.sender_chat.title
  232. if message.reply_to_message
  233. else "匿名成员"
  234. )
  235. msg = (
  236. f"**已封禁:** {mention}\n"
  237. f"**操作者:** "
  238. f"{message.from_user.mention if message.from_user else '匿名管理员'}\n"
  239. )
  240. if message.command[0][0] == "d":
  241. await message.reply_to_message.delete()
  242. if message.command[0] == "tban":
  243. split = reason.split(None, 1)
  244. time_value = split[0]
  245. temp_reason = split[1] if len(split) > 1 else ""
  246. temp_ban = await time_converter(message, time_value)
  247. msg += f"**封禁时长:** {time_value}\n"
  248. if temp_reason:
  249. msg += f"**原因:** {temp_reason}"
  250. with suppress(AttributeError):
  251. if len(time_value[:-1]) < 3:
  252. await message.chat.ban_member(user_id, until_date=temp_ban)
  253. replied_message = message.reply_to_message
  254. if replied_message:
  255. message = replied_message
  256. await message.reply_text(msg)
  257. else:
  258. await message.reply_text("时间数值不能超过两位数。")
  259. return
  260. if reason:
  261. msg += f"**原因:** {reason}"
  262. await message.chat.ban_member(user_id)
  263. replied_message = message.reply_to_message
  264. if replied_message:
  265. message = replied_message
  266. await message.reply_text(msg)
  267. # Unban members
  268. @app.on_message(filters.command("unban") & ~filters.private)
  269. @adminsOnly("can_restrict_members")
  270. async def unban_func(_, message: Message):
  271. # we don't need reasons for unban, also, we
  272. # don't need to get "text_mention" entity, because
  273. # normal users won't get text_mention if the user
  274. # they want to unban is not in the group.
  275. reply = message.reply_to_message
  276. if reply and reply.sender_chat and reply.sender_chat != message.chat.id:
  277. return await message.reply_text("不能对频道身份执行解除封禁。")
  278. if len(message.command) == 2:
  279. user = message.text.split(None, 1)[1]
  280. elif len(message.command) == 1 and reply:
  281. user = message.reply_to_message.from_user.id
  282. else:
  283. return await message.reply_text(
  284. "请提供用户名,或回复该成员的消息。"
  285. )
  286. await message.chat.unban_member(user)
  287. umention = (await app.get_users(user)).mention
  288. replied_message = message.reply_to_message
  289. if replied_message:
  290. message = replied_message
  291. await message.reply_text(f"已解除封禁:{umention}")
  292. # Ban users listed in a message
  293. @app.on_message(SUDOERS & filters.command("listban") & ~filters.private)
  294. async def list_ban_(c, message: Message):
  295. userid, msglink_reason = await extract_user_and_reason(message)
  296. if not userid or not msglink_reason:
  297. return await message.reply_text(
  298. "请提供用户 ID 或用户名、群列表消息链接和封禁原因。"
  299. )
  300. if (
  301. len(msglink_reason.split(" ")) == 1
  302. ): # message link included with the reason
  303. return await message.reply_text(
  304. "必须填写批量封禁原因。"
  305. )
  306. # seperate messge link from reason
  307. lreason = msglink_reason.split()
  308. messagelink, reason = lreason[0], " ".join(lreason[1:])
  309. if not re.search(
  310. r"(https?://)?t(elegram)?\.me/\w+/\d+", messagelink
  311. ): # validate link
  312. return await message.reply_text("消息链接无效。")
  313. if userid == BOT_ID:
  314. return await message.reply_text("机器人不能封禁自己。")
  315. if userid in SUDOERS:
  316. return await message.reply_text(
  317. "不能封禁超级管理员。"
  318. )
  319. splitted = messagelink.split("/")
  320. uname, mid = splitted[-2], int(splitted[-1])
  321. m = await message.reply_text(
  322. "`正在从多个群组封禁该成员,请稍候。`"
  323. )
  324. try:
  325. msgtext = (await app.get_messages(uname, mid)).text
  326. gusernames = re.findall(r"@\\w+", msgtext)
  327. except:
  328. return await m.edit_text("无法从消息中读取群组用户名。")
  329. count = 0
  330. for username in gusernames:
  331. try:
  332. await app.ban_chat_member(username.strip("@"), userid)
  333. await asyncio.sleep(1)
  334. except FloodWait as e:
  335. await asyncio.sleep(e.x)
  336. except:
  337. continue
  338. count += 1
  339. mention = (await app.get_users(userid)).mention
  340. msg = f"""
  341. **已批量封禁:** {mention}
  342. **用户 ID:** `{userid}`
  343. **操作者:** {message.from_user.mention}
  344. **影响群数:** `{count}`
  345. **原因:** {reason}
  346. """
  347. await m.edit_text(msg)
  348. # Unban users listed in a message
  349. @app.on_message(SUDOERS & filters.command("listunban") & ~filters.private)
  350. async def list_unban_(c, message: Message):
  351. userid, msglink = await extract_user_and_reason(message)
  352. if not userid or not msglink:
  353. return await message.reply_text(
  354. "请提供用户 ID 或用户名,以及群列表消息链接。"
  355. )
  356. if not re.search(
  357. r"(https?://)?t(elegram)?\.me/\w+/\d+", msglink
  358. ): # validate link
  359. return await message.reply_text("消息链接无效。")
  360. splitted = msglink.split("/")
  361. uname, mid = splitted[-2], int(splitted[-1])
  362. m = await message.reply_text(
  363. "`正在从多个群组解除封禁,请稍候。`"
  364. )
  365. try:
  366. msgtext = (await app.get_messages(uname, mid)).text
  367. gusernames = re.findall(r"@\\w+", msgtext)
  368. except:
  369. return await m.edit_text("无法从消息中读取群组用户名。")
  370. count = 0
  371. for username in gusernames:
  372. try:
  373. await app.unban_chat_member(username.strip("@"), userid)
  374. await asyncio.sleep(1)
  375. except FloodWait as e:
  376. await asyncio.sleep(e.x)
  377. except:
  378. continue
  379. count += 1
  380. mention = (await app.get_users(userid)).mention
  381. msg = f"""
  382. **已批量解除封禁:** {mention}
  383. **用户 ID:** `{userid}`
  384. **操作者:** {message.from_user.mention}
  385. **影响群数:** `{count}`
  386. """
  387. await m.edit_text(msg)
  388. # Delete messages
  389. @app.on_message(filters.command("del") & ~filters.private)
  390. @adminsOnly("can_delete_messages")
  391. async def deleteFunc(_, message: Message):
  392. if not message.reply_to_message:
  393. return await message.reply_text("请回复要删除的消息。")
  394. await message.reply_to_message.delete()
  395. await message.delete()
  396. # Promote Members
  397. @app.on_message(filters.command(["promote", "fullpromote"]) & ~filters.private)
  398. @adminsOnly("can_promote_members")
  399. async def promoteFunc(_, message: Message):
  400. user_id = await extract_user(message)
  401. if not user_id:
  402. return await message.reply_text("未找到该成员。")
  403. bot = (await app.get_chat_member(message.chat.id, BOT_ID)).privileges
  404. if user_id == BOT_ID:
  405. return await message.reply_text("机器人不能提升自己。")
  406. if not bot:
  407. return await message.reply_text("机器人不是本群管理员。")
  408. if not bot.can_promote_members:
  409. return await message.reply_text("机器人缺少任命管理员权限。")
  410. umention = (await app.get_users(user_id)).mention
  411. if message.command[0][0] == "f":
  412. await message.chat.promote_member(
  413. user_id=user_id,
  414. privileges=ChatPrivileges(
  415. can_change_info=bot.can_change_info,
  416. can_invite_users=bot.can_invite_users,
  417. can_delete_messages=bot.can_delete_messages,
  418. can_restrict_members=bot.can_restrict_members,
  419. can_pin_messages=bot.can_pin_messages,
  420. can_promote_members=bot.can_promote_members,
  421. can_manage_chat=bot.can_manage_chat,
  422. can_manage_video_chats=bot.can_manage_video_chats,
  423. ),
  424. )
  425. return await message.reply_text(f"已授予完整管理权限:{umention}")
  426. await message.chat.promote_member(
  427. user_id=user_id,
  428. privileges=ChatPrivileges(
  429. can_change_info=False,
  430. can_invite_users=bot.can_invite_users,
  431. can_delete_messages=bot.can_delete_messages,
  432. can_restrict_members=False,
  433. can_pin_messages=False,
  434. can_promote_members=False,
  435. can_manage_chat=bot.can_manage_chat,
  436. can_manage_video_chats=bot.can_manage_video_chats,
  437. ),
  438. )
  439. await message.reply_text(f"已提升为管理员:{umention}")
  440. # Demote Member
  441. @app.on_message(filters.command("demote") & ~filters.private)
  442. @adminsOnly("can_promote_members")
  443. async def demote(_, message: Message):
  444. user_id = await extract_user(message)
  445. if not user_id:
  446. return await message.reply_text("未找到该成员。")
  447. if user_id == BOT_ID:
  448. return await message.reply_text("机器人不能取消自己的管理员身份。")
  449. if user_id in SUDOERS:
  450. return await message.reply_text(
  451. "不能取消超级管理员。"
  452. )
  453. try:
  454. member = await app.get_chat_member(message.chat.id, user_id)
  455. if member.status == ChatMemberStatus.ADMINISTRATOR:
  456. await message.chat.promote_member(
  457. user_id=user_id,
  458. privileges=ChatPrivileges(
  459. can_change_info=False,
  460. can_invite_users=False,
  461. can_delete_messages=False,
  462. can_restrict_members=False,
  463. can_pin_messages=False,
  464. can_promote_members=False,
  465. can_manage_chat=False,
  466. can_manage_video_chats=False,
  467. ),
  468. )
  469. umention = (await app.get_users(user_id)).mention
  470. await message.reply_text(f"已取消管理员身份:{umention}")
  471. else:
  472. await message.reply_text(
  473. "该成员不是管理员。"
  474. )
  475. except Exception:
  476. await message.reply_text("取消管理员身份失败,请检查机器人权限。")
  477. # Pin Messages
  478. @app.on_message(filters.command(["pin", "unpin"]) & ~filters.private)
  479. @adminsOnly("can_pin_messages")
  480. async def pin(_, message: Message):
  481. if not message.reply_to_message:
  482. return await message.reply_text("请回复要置顶或取消置顶的消息。")
  483. r = message.reply_to_message
  484. if message.command[0][0] == "u":
  485. await r.unpin()
  486. return await message.reply_text(
  487. f"**已取消置顶[该消息]({r.link})。**",
  488. disable_web_page_preview=True,
  489. )
  490. await r.pin(disable_notification=True)
  491. await message.reply(
  492. f"**已置顶[该消息]({r.link})。**",
  493. disable_web_page_preview=True,
  494. )
  495. msg = "请查看置顶消息:~ " + f"[查看, {r.link}]"
  496. filter_ = dict(type="text", data=msg)
  497. await save_filter(message.chat.id, "~pinned", filter_)
  498. # Mute members
  499. @app.on_message(filters.command(["mute", "tmute"]) & ~filters.private)
  500. @adminsOnly("can_restrict_members")
  501. async def mute(_, message: Message):
  502. user_id, reason = await extract_user_and_reason(message)
  503. if not user_id:
  504. return await message.reply_text("未找到该成员。")
  505. if user_id == BOT_ID:
  506. return await message.reply_text("机器人不能禁言自己。")
  507. if user_id in SUDOERS:
  508. return await message.reply_text(
  509. "不能禁言超级管理员。"
  510. )
  511. if user_id in (await list_admins(message.chat.id)):
  512. return await message.reply_text(
  513. "不能禁言群管理员,请先取消其管理员身份。"
  514. )
  515. mention = (await app.get_users(user_id)).mention
  516. keyboard = ikb({"解除禁言": f"unmute_{user_id}"})
  517. msg = (
  518. f"**已禁言:** {mention}\n"
  519. f"**操作者:** "
  520. f"{message.from_user.mention if message.from_user else '匿名管理员'}\n"
  521. )
  522. if message.command[0] == "tmute":
  523. split = reason.split(None, 1)
  524. time_value = split[0]
  525. temp_reason = split[1] if len(split) > 1 else ""
  526. temp_mute = await time_converter(message, time_value)
  527. msg += f"**禁言时长:** {time_value}\n"
  528. if temp_reason:
  529. msg += f"**原因:** {temp_reason}"
  530. try:
  531. if len(time_value[:-1]) < 3:
  532. await message.chat.restrict_member(
  533. user_id,
  534. permissions=ChatPermissions(),
  535. until_date=temp_mute,
  536. )
  537. replied_message = message.reply_to_message
  538. if replied_message:
  539. message = replied_message
  540. await message.reply_text(msg, reply_markup=keyboard)
  541. else:
  542. await message.reply_text("时间数值不能超过两位数。")
  543. except AttributeError:
  544. pass
  545. return
  546. if reason:
  547. msg += f"**原因:** {reason}"
  548. await message.chat.restrict_member(user_id, permissions=ChatPermissions())
  549. replied_message = message.reply_to_message
  550. if replied_message:
  551. message = replied_message
  552. await message.reply_text(msg, reply_markup=keyboard)
  553. async def _member_action_shortcut_filter(_, __, message: Message) -> bool:
  554. return member_action_shortcut_command(message.text or "") is not None
  555. member_action_shortcut_filter = filters.create(
  556. _member_action_shortcut_filter,
  557. "MemberActionShortcutFilter",
  558. )
  559. @adminsOnly("can_delete_messages")
  560. async def delete_shortcut(_, message: Message):
  561. await message.reply_text("已接收删除请求。")
  562. @app.on_message(
  563. filters.group & filters.reply & member_action_shortcut_filter,
  564. group=-999,
  565. )
  566. async def member_action_shortcut(client, message: Message):
  567. settings = await get_interaction_settings(message.chat.id)
  568. if not settings["commands"]["shortcuts"]:
  569. return None
  570. if not has_permission("chat.members", BOT_PERMISSIONS):
  571. return await message.reply_text(
  572. "当前机器人角色未启用成员管理权限。"
  573. )
  574. command = member_action_shortcut_command(message.text or "")
  575. if not command or not message.reply_to_message:
  576. return None
  577. original_text = message.text
  578. original_command = message.command
  579. replied_message = message.reply_to_message
  580. original_message_reply = message.reply_text
  581. original_target_reply = replied_message.reply_text
  582. def cleanup_reply(original_reply):
  583. async def wrapped_reply(text, *args, **kwargs):
  584. response = await original_reply(text, *args, **kwargs)
  585. schedule_group_cleanup(
  586. message,
  587. replied_message,
  588. response,
  589. delay_seconds=settings["cleanup_seconds"],
  590. )
  591. return response
  592. return wrapped_reply
  593. message.reply_text = cleanup_reply(original_message_reply)
  594. replied_message.reply_text = cleanup_reply(original_target_reply)
  595. message.text, message.command = command
  596. log.info(
  597. f"成员快捷操作:chat={message.chat.id} "
  598. f"actor={getattr(message.from_user, 'id', 0)} "
  599. f"action={message.command[0]}"
  600. )
  601. try:
  602. result = None
  603. if message.command[0] == "ban":
  604. result = await banFunc(client, message)
  605. elif message.command[0] in {"mute", "tmute"}:
  606. result = await mute(client, message)
  607. elif message.command[0] == "warn":
  608. result = await warn_user(client, message)
  609. elif message.command[0] == "unban":
  610. result = await unban_func(client, message)
  611. elif message.command[0] == "unmute":
  612. result = await unmute(client, message)
  613. elif message.command[0] == "del":
  614. result = await delete_shortcut(client, message)
  615. else:
  616. result = await kickFunc(client, message)
  617. with suppress(Exception):
  618. await record_audit(
  619. source="telegram", actor_id=getattr(message.from_user, "id", None),
  620. actor_name=getattr(message.from_user, "first_name", ""),
  621. action=f"chat.shortcut.{message.command[0]}", chat_id=message.chat.id,
  622. target_id=getattr(getattr(replied_message, "from_user", None), "id", None),
  623. summary=original_text,
  624. )
  625. return result
  626. finally:
  627. message.text = original_text
  628. message.command = original_command
  629. message.reply_text = original_message_reply
  630. replied_message.reply_text = original_target_reply
  631. # Unmute members
  632. @app.on_message(filters.command("unmute") & ~filters.private)
  633. @adminsOnly("can_restrict_members")
  634. async def unmute(_, message: Message):
  635. user_id = await extract_user(message)
  636. if not user_id:
  637. return await message.reply_text("未找到该成员。")
  638. await message.chat.unban_member(user_id)
  639. umention = (await app.get_users(user_id)).mention
  640. replied_message = message.reply_to_message
  641. if replied_message:
  642. message = replied_message
  643. await message.reply_text(f"已解除禁言:{umention}")
  644. # Ban deleted accounts
  645. @app.on_message(filters.command("ban_ghosts") & ~filters.private)
  646. @adminsOnly("can_restrict_members")
  647. async def ban_deleted_accounts(_, message: Message):
  648. chat_id = message.chat.id
  649. deleted_users = []
  650. banned_users = 0
  651. m = await message.reply("正在查找已注销账号...")
  652. async for i in app.get_chat_members(chat_id):
  653. if i.user.is_deleted:
  654. deleted_users.append(i.user.id)
  655. if len(deleted_users) > 0:
  656. for deleted_user in deleted_users:
  657. try:
  658. await message.chat.ban_member(deleted_user)
  659. except Exception:
  660. pass
  661. banned_users += 1
  662. await m.edit(f"已封禁 {banned_users} 个已注销账号。")
  663. else:
  664. await m.edit("本群没有已注销账号。")
  665. @app.on_message(filters.command(["warn", "dwarn"]) & ~filters.private)
  666. @adminsOnly("can_restrict_members")
  667. async def warn_user(_, message: Message):
  668. user_id, reason = await extract_user_and_reason(message)
  669. chat_id = message.chat.id
  670. if not user_id:
  671. return await message.reply_text("未找到该成员。")
  672. if user_id == BOT_ID:
  673. return await message.reply_text(
  674. "机器人不能警告自己。"
  675. )
  676. if user_id in SUDOERS:
  677. return await message.reply_text(
  678. "不能警告超级管理员。"
  679. )
  680. if user_id in (await list_admins(chat_id)):
  681. return await message.reply_text(
  682. "不能警告群管理员。"
  683. )
  684. user, warns = await asyncio.gather(
  685. app.get_users(user_id),
  686. get_warn(chat_id, await int_to_alpha(user_id)),
  687. )
  688. mention = user.mention
  689. keyboard = ikb({"撤销一次警告": f"unwarn_{user_id}"})
  690. if warns:
  691. warns = warns["warns"]
  692. else:
  693. warns = 0
  694. if message.command[0][0] == "d":
  695. await message.reply_to_message.delete()
  696. if warns >= 2:
  697. await message.chat.ban_member(user_id)
  698. await message.reply_text(
  699. f"{mention} 已累计 3 次警告,现已封禁。"
  700. )
  701. await remove_warns(chat_id, await int_to_alpha(user_id))
  702. else:
  703. warn = {"warns": warns + 1}
  704. msg = f"""
  705. **已警告:** {mention}
  706. **操作者:** {message.from_user.mention if message.from_user else '匿名管理员'}
  707. **原因:** {reason or '未填写原因'}
  708. **警告次数:** {warns + 1}/3"""
  709. replied_message = message.reply_to_message
  710. if replied_message:
  711. message = replied_message
  712. await message.reply_text(msg, reply_markup=keyboard)
  713. await add_warn(chat_id, await int_to_alpha(user_id), warn)
  714. @app.on_callback_query(filters.regex("unwarn_"))
  715. async def remove_warning(_, cq: CallbackQuery):
  716. from_user = cq.from_user
  717. chat_id = cq.message.chat.id
  718. permissions = await member_permissions(chat_id, from_user.id)
  719. permission = "can_restrict_members"
  720. if permission not in permissions:
  721. return await cq.answer(
  722. "你没有执行此操作所需的限制成员权限。",
  723. show_alert=True,
  724. )
  725. user_id = cq.data.split("_")[1]
  726. warns = await get_warn(chat_id, await int_to_alpha(user_id))
  727. if warns:
  728. warns = warns["warns"]
  729. if not warns or warns == 0:
  730. return await cq.answer("该成员没有警告记录。")
  731. warn = {"warns": warns - 1}
  732. await add_warn(chat_id, await int_to_alpha(user_id), warn)
  733. text = cq.message.text.markdown
  734. text = f"~~{text}~~\n\n"
  735. text += f"__{from_user.mention} 已撤销一次警告。__"
  736. await cq.message.edit(text)
  737. # Rmwarns
  738. @app.on_message(filters.command("rmwarns") & ~filters.private)
  739. @adminsOnly("can_restrict_members")
  740. async def remove_warnings(_, message: Message):
  741. if not message.reply_to_message:
  742. return await message.reply_text(
  743. "请回复该成员的消息以清除警告。"
  744. )
  745. user_id = message.reply_to_message.from_user.id
  746. mention = message.reply_to_message.from_user.mention
  747. chat_id = message.chat.id
  748. warns = await get_warn(chat_id, await int_to_alpha(user_id))
  749. if warns:
  750. warns = warns["warns"]
  751. if warns == 0 or not warns:
  752. await message.reply_text(f"{mention} 没有警告记录。")
  753. else:
  754. await remove_warns(chat_id, await int_to_alpha(user_id))
  755. await message.reply_text(f"已清除 {mention} 的全部警告。")
  756. # Warns
  757. @app.on_message(filters.command("warns") & ~filters.private)
  758. @capture_err
  759. async def check_warns(_, message: Message):
  760. user_id = await extract_user(message)
  761. if not user_id:
  762. return await message.reply_text("未找到该成员。")
  763. warns = await get_warn(message.chat.id, await int_to_alpha(user_id))
  764. mention = (await app.get_users(user_id)).mention
  765. if warns:
  766. warns = warns["warns"]
  767. else:
  768. return await message.reply_text(f"{mention} 没有警告记录。")
  769. return await message.reply_text(f"{mention} 当前有 {warns}/3 次警告。")
  770. # Report
  771. @app.on_message(
  772. (
  773. filters.command("report")
  774. | filters.command(["admins", "admin"], prefixes="@")
  775. )
  776. & ~filters.private
  777. )
  778. @capture_err
  779. async def report_user(_, message):
  780. if len(message.text.split()) <= 1 and not message.reply_to_message:
  781. return await message.reply_text(
  782. "请回复要举报的消息。"
  783. )
  784. reply = message.reply_to_message if message.reply_to_message else message
  785. reply_id = reply.from_user.id if reply.from_user else reply.sender_chat.id
  786. user_id = (
  787. message.from_user.id if message.from_user else message.sender_chat.id
  788. )
  789. list_of_admins = await list_admins(message.chat.id)
  790. linked_chat = (await app.get_chat(message.chat.id)).linked_chat
  791. if linked_chat is not None:
  792. if (
  793. reply_id in list_of_admins
  794. or reply_id == message.chat.id
  795. or reply_id == linked_chat.id
  796. ):
  797. return await message.reply_text(
  798. "不能举报群管理员或关联频道。"
  799. )
  800. else:
  801. if reply_id in list_of_admins or reply_id == message.chat.id:
  802. return await message.reply_text(
  803. "不能举报群管理员。"
  804. )
  805. user_mention = (
  806. reply.from_user.mention if reply.from_user else reply.sender_chat.title
  807. )
  808. text = f"已向管理员举报 {user_mention}。"
  809. admin_data = [
  810. i
  811. async for i in app.get_chat_members(
  812. chat_id=message.chat.id, filter=ChatMembersFilter.ADMINISTRATORS
  813. )
  814. ] # will it give floods ???
  815. for admin in admin_data:
  816. if admin.user.is_bot or admin.user.is_deleted:
  817. # return bots or deleted admins
  818. continue
  819. text += f"[\u2063](tg://user?id={admin.user.id})"
  820. await reply.reply_text(text)
  821. @app.on_message(filters.command("invite"))
  822. @adminsOnly("can_invite_users")
  823. async def invite(_, message):
  824. if message.chat.type in [ChatType.GROUP, ChatType.SUPERGROUP]:
  825. link = (await app.get_chat(message.chat.id)).invite_link
  826. if not link:
  827. link = await app.export_chat_invite_link(message.chat.id)
  828. text = f"本群邀请链接:\n\n{link}"
  829. if message.reply_to_message:
  830. await message.reply_to_message.reply_text(
  831. text, disable_web_page_preview=True
  832. )
  833. else:
  834. await message.reply_text(text, disable_web_page_preview=True)