__main__.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  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 importlib
  22. import os
  23. import re
  24. import time
  25. from contextlib import closing, suppress
  26. import psutil
  27. from pyrogram import StopPropagation, filters, idle
  28. from pyrogram.enums import ChatType, ParseMode
  29. from pyrogram.types import BotCommand, InlineKeyboardButton, InlineKeyboardMarkup
  30. from uvloop import install
  31. from wbb import (
  32. BOT_NAME,
  33. BOT_PERMISSIONS,
  34. BOT_USERNAME,
  35. LOG_GROUP_ID,
  36. USERBOT_CONNECTED,
  37. USERBOT_NAME,
  38. aiohttpsession,
  39. app,
  40. app2,
  41. bot_start_time,
  42. log,
  43. )
  44. from wbb.core.keyboard import ikb
  45. from wbb.modules import ALL_MODULES
  46. from wbb.services.bot_permissions import (
  47. GROUP_MANAGEMENT_PERMISSIONS,
  48. PRIVATE_MANAGEMENT_PERMISSIONS,
  49. TELEGRAM_COMMAND_PERMISSIONS,
  50. has_any_permission,
  51. has_permission,
  52. )
  53. from wbb.utils import paginate_modules
  54. from wbb.utils.constants import MARKDOWN
  55. from wbb.utils.dbfunctions import clean_restart_stage, get_rules
  56. from wbb.utils.functions import extract_text_and_keyb
  57. loop = asyncio.get_event_loop()
  58. HELPABLE = {}
  59. HELP_MODULE_KEYS = {
  60. "admin",
  61. "admin_misc",
  62. "admin_panel",
  63. "antiservice",
  64. "blacklist",
  65. "chatbot",
  66. "feds",
  67. "filters",
  68. "flood",
  69. "giveaway",
  70. "greetings",
  71. "karma",
  72. "locks",
  73. "notes",
  74. "points",
  75. "rules",
  76. }
  77. BOT_COMMANDS = [
  78. BotCommand("start", "打开机器人首页"),
  79. BotCommand("help", "查看中文功能帮助"),
  80. BotCommand("manage", "打开私聊群管理菜单"),
  81. BotCommand("cancel", "取消当前管理流程"),
  82. BotCommand("points", "查看积分余额"),
  83. BotCommand("checkin", "领取每日签到积分"),
  84. BotCommand("points_rank", "查看群积分排行榜"),
  85. BotCommand("points_history", "查看最近积分流水"),
  86. BotCommand("giveaway", "创建群抽奖"),
  87. BotCommand("gjoin", "报名参加抽奖"),
  88. BotCommand("glist", "查看进行中的抽奖"),
  89. BotCommand("gparticipants", "查看抽奖参与者"),
  90. BotCommand("gend", "立即开奖"),
  91. BotCommand("gcancel", "取消抽奖并退款"),
  92. BotCommand("greroll", "重新抽取中奖者"),
  93. BotCommand("rules", "查看本群群规"),
  94. BotCommand("directory", "打开技师咨询与评价"),
  95. BotCommand("find_technician", "浏览并咨询技师"),
  96. BotCommand("technician_profile", "管理技师服务资料"),
  97. BotCommand("review_teacher", "主动评价技师"),
  98. BotCommand("technician_top", "查看审核评价排行"),
  99. ]
  100. BOT_COMMAND_PERMISSIONS = {
  101. "manage": "__group_any__",
  102. "cancel": "__any__",
  103. "points": "points.manage",
  104. "checkin": "points.manage",
  105. "points_rank": "points.manage",
  106. "points_history": "points.manage",
  107. "giveaway": "giveaways.manage",
  108. "gjoin": "giveaways.manage",
  109. "glist": "giveaways.manage",
  110. "gparticipants": "giveaways.manage",
  111. "gend": "giveaways.manage",
  112. "gcancel": "giveaways.manage",
  113. "greroll": "giveaways.manage",
  114. "rules": "chat.rules",
  115. "directory": "teacher_directory.manage",
  116. "nearby": "teacher_directory.manage",
  117. "teacher_rank": "teacher_directory.manage",
  118. "find_technician": "teacher_directory.manage",
  119. "my_services": "teacher_directory.manage",
  120. "technician_profile": "teacher_directory.manage",
  121. "review_teacher": "teacher_directory.manage",
  122. "technician_top": "teacher_directory.manage",
  123. "review_qr": "teacher_directory.manage",
  124. }
  125. def _visible_bot_commands() -> list[BotCommand]:
  126. visible = []
  127. for command in BOT_COMMANDS:
  128. required = BOT_COMMAND_PERMISSIONS.get(command.command)
  129. if required == "__group_any__" and not has_any_permission(
  130. GROUP_MANAGEMENT_PERMISSIONS, BOT_PERMISSIONS
  131. ):
  132. continue
  133. if required == "__any__" and not has_any_permission(
  134. PRIVATE_MANAGEMENT_PERMISSIONS, BOT_PERMISSIONS
  135. ):
  136. continue
  137. if required and required not in {"__any__", "__group_any__"} and not has_permission(
  138. required, BOT_PERMISSIONS
  139. ):
  140. continue
  141. visible.append(command)
  142. return visible
  143. @app.on_message(
  144. filters.command(list(TELEGRAM_COMMAND_PERMISSIONS)),
  145. group=-1000,
  146. )
  147. async def bot_role_command_guard(_, message):
  148. command = (
  149. str(message.command[0]).lower().split("@", 1)[0]
  150. if message.command
  151. else ""
  152. )
  153. required = TELEGRAM_COMMAND_PERMISSIONS.get(command)
  154. if required and not has_permission(required, BOT_PERMISSIONS):
  155. raise StopPropagation
  156. @app.on_message(
  157. filters.command(["admins", "admin"], prefixes="@"),
  158. group=-1000,
  159. )
  160. async def bot_role_at_command_guard(_, __):
  161. if not has_permission("chat.members", BOT_PERMISSIONS):
  162. raise StopPropagation
  163. @app.on_callback_query(filters.regex(r"^unwarn_"), group=-1000)
  164. async def bot_role_callback_guard(_, __):
  165. if not has_permission("chat.members", BOT_PERMISSIONS):
  166. raise StopPropagation
  167. async def bot_sys_stats() -> str:
  168. uptime = max(0, int(time.time() - bot_start_time))
  169. days, remainder = divmod(uptime, 86400)
  170. hours, remainder = divmod(remainder, 3600)
  171. minutes, seconds = divmod(remainder, 60)
  172. duration = f"{days} 天 {hours} 小时 {minutes} 分 {seconds} 秒"
  173. process = psutil.Process(os.getpid())
  174. return (
  175. f"机器人:{BOT_NAME}\n"
  176. f"运行时长:{duration}\n"
  177. f"进程内存:{round(process.memory_info().rss / 1024 ** 2)} MB\n"
  178. f"处理器:{psutil.cpu_percent()}%\n"
  179. f"系统内存:{psutil.virtual_memory().percent}%\n"
  180. f"磁盘:{psutil.disk_usage('/').percent}%"
  181. )
  182. async def start_bot():
  183. global HELPABLE
  184. loaded_modules = {}
  185. for module in ALL_MODULES:
  186. imported_module = importlib.import_module("wbb.modules." + module)
  187. loaded_modules[module] = imported_module
  188. if (
  189. module in HELP_MODULE_KEYS
  190. and hasattr(imported_module, "__MODULE__")
  191. and imported_module.__MODULE__
  192. ):
  193. if (
  194. hasattr(imported_module, "__HELP__")
  195. and imported_module.__HELP__
  196. ):
  197. HELPABLE[module] = imported_module
  198. bot_modules = ""
  199. j = 1
  200. for i in ALL_MODULES:
  201. if j == 4:
  202. bot_modules += f"|{i:<15}|\n"
  203. j = 0
  204. else:
  205. bot_modules += f"|{i:<15}"
  206. j += 1
  207. print("+===============================================================+")
  208. print("| WBB |")
  209. print("+===============+===============+===============+===============+")
  210. print(bot_modules)
  211. print("+===============+===============+===============+===============+")
  212. log.info(f"机器人已启动:{BOT_NAME}")
  213. if USERBOT_CONNECTED:
  214. log.info(f"用户客户端已启动:{USERBOT_NAME}")
  215. try:
  216. await app.set_bot_commands(_visible_bot_commands())
  217. log.info("已注册中文 Telegram 命令菜单")
  218. except Exception as exc:
  219. log.error(f"注册 Telegram 命令菜单失败:{exc}")
  220. business_assistant_module = loaded_modules.get("business_assistant")
  221. if business_assistant_module and hasattr(
  222. business_assistant_module, "start_business_assistant_runtime"
  223. ):
  224. try:
  225. await business_assistant_module.start_business_assistant_runtime()
  226. except Exception as exc:
  227. log.error(f"启动 Telegram Business 智能接待失败:{exc}")
  228. restart_data = await clean_restart_stage()
  229. try:
  230. log.info("正在发送上线状态")
  231. if restart_data:
  232. await app.edit_message_text(
  233. restart_data["chat_id"],
  234. restart_data["message_id"],
  235. "**机器人已成功重启。**",
  236. )
  237. else:
  238. await app.send_message(LOG_GROUP_ID, "机器人已启动。")
  239. except Exception:
  240. pass
  241. await idle()
  242. if business_assistant_module and hasattr(
  243. business_assistant_module, "stop_business_assistant_runtime"
  244. ):
  245. with suppress(Exception):
  246. await business_assistant_module.stop_business_assistant_runtime()
  247. await aiohttpsession.close()
  248. log.info("正在停止客户端")
  249. await app.stop()
  250. if USERBOT_CONNECTED:
  251. await app2.stop()
  252. log.info("正在取消异步任务")
  253. for task in asyncio.all_tasks():
  254. task.cancel()
  255. log.info("机器人已停止")
  256. home_actions = [
  257. InlineKeyboardButton(text="功能帮助", callback_data="bot_commands"),
  258. ]
  259. if has_any_permission(GROUP_MANAGEMENT_PERMISSIONS, BOT_PERMISSIONS):
  260. home_actions.append(
  261. InlineKeyboardButton(text="群组管理", callback_data="manage_hint")
  262. )
  263. if has_permission("teacher_directory.manage", BOT_PERMISSIONS):
  264. home_actions.append(
  265. InlineKeyboardButton(
  266. text="技师咨询与评价",
  267. url=f"https://t.me/{BOT_USERNAME}?start=services",
  268. )
  269. )
  270. home_keyboard_pm = InlineKeyboardMarkup(
  271. [
  272. home_actions,
  273. [
  274. InlineKeyboardButton(
  275. text="运行状态",
  276. callback_data="stats_callback",
  277. )
  278. ],
  279. [
  280. InlineKeyboardButton(
  281. text="添加机器人到群组",
  282. url=f"http://t.me/{BOT_USERNAME}?startgroup=new",
  283. )
  284. ],
  285. ]
  286. )
  287. assigned_features = [
  288. label
  289. for permission, label in (
  290. ("chat.profile", "群资料"),
  291. ("chat.members", "成员管理"),
  292. ("chat.permissions", "群权限"),
  293. ("chat.announcements", "公告"),
  294. ("chat.invites", "邀请链接"),
  295. ("chat.rules", "群规"),
  296. ("chat.notes", "群笔记"),
  297. ("automation.manage", "自动化与风控"),
  298. ("points.manage", "积分"),
  299. ("giveaways.manage", "抽奖"),
  300. ("karma.manage", "声望"),
  301. ("teacher_directory.manage", "技师咨询与评价"),
  302. ("business_assistant.manage", "Telegram Business 智能接待"),
  303. )
  304. if has_permission(permission, BOT_PERMISSIONS)
  305. ]
  306. home_text_pm = (
  307. f"你好,我是 {BOT_NAME}。\n\n"
  308. + (
  309. f"我当前负责:{'、'.join(assigned_features)}。"
  310. if assigned_features
  311. else "我当前尚未分配管理职责。"
  312. )
  313. + (
  314. "群管理员可发送 /manage 打开私聊管理菜单。"
  315. if has_any_permission(GROUP_MANAGEMENT_PERMISSIONS, BOT_PERMISSIONS)
  316. else ""
  317. )
  318. )
  319. keyboard = InlineKeyboardMarkup(
  320. [
  321. [
  322. InlineKeyboardButton(
  323. text="查看帮助",
  324. url=f"t.me/{BOT_USERNAME}?start=help",
  325. ),
  326. InlineKeyboardButton(
  327. text="运行状态",
  328. callback_data="stats_callback",
  329. ),
  330. ],
  331. ]
  332. )
  333. FED_MARKUP = InlineKeyboardMarkup(
  334. [
  335. [
  336. InlineKeyboardButton(
  337. "联邦创建者命令", callback_data="fed_owner"
  338. ),
  339. InlineKeyboardButton(
  340. "联邦管理员命令", callback_data="fed_admin"
  341. ),
  342. ],
  343. [
  344. InlineKeyboardButton("普通成员命令", callback_data="fed_user"),
  345. ],
  346. [
  347. InlineKeyboardButton("返回", callback_data="help_back"),
  348. ],
  349. ]
  350. )
  351. @app.on_message(filters.command("start"))
  352. async def start(_, message):
  353. if message.chat.type != ChatType.PRIVATE:
  354. return await message.reply(
  355. "请私聊我查看详细功能。", reply_markup=keyboard
  356. )
  357. if len(message.text.split()) > 1:
  358. user = await app.get_users(message.from_user.id)
  359. start_parameter = message.text.split(None, 1)[1]
  360. if start_parameter.startswith("review_") and has_permission(
  361. "teacher_directory.manage", BOT_PERMISSIONS
  362. ):
  363. from wbb.modules.technician_services import open_review_qr
  364. return await open_review_qr(
  365. message,
  366. start_parameter.removeprefix("review_"),
  367. )
  368. name = start_parameter.lower()
  369. match = re.match(r"rules_(.*)", name)
  370. if match:
  371. chat_id = match.group(1)
  372. user_id = message.from_user.id
  373. chat = await app.get_chat(int(chat_id))
  374. text = f"**`{chat.title}` 的群规:\n\n**"
  375. rules = await get_rules(int(chat_id))
  376. if rules:
  377. text = text + rules
  378. if "{chat}" in text:
  379. text = text.replace("{chat}", chat.title)
  380. if "{name}" in text:
  381. text = text.replace("{name}", user.mention)
  382. keyb = None
  383. if re.findall(r"\[.+\,.+\]", text):
  384. text, keyb = extract_text_and_keyb(ikb, text)
  385. await app.send_message(user_id, text=text, reply_markup=keyb)
  386. else:
  387. await app.send_message(
  388. user_id,
  389. "该群管理员尚未设置群规。",
  390. )
  391. return
  392. directory_match = re.fullmatch(r"directory_(-?\d+)", name)
  393. if directory_match and has_permission(
  394. "teacher_directory.manage", BOT_PERMISSIONS
  395. ):
  396. from wbb.modules.teacher_directory import send_directory_menu
  397. return await send_directory_menu(
  398. message,
  399. source_chat_id=int(directory_match.group(1)),
  400. )
  401. if name == "directory" and has_permission(
  402. "teacher_directory.manage", BOT_PERMISSIONS
  403. ):
  404. from wbb.modules.teacher_directory import send_directory_menu
  405. return await send_directory_menu(message)
  406. if name == "services" and has_permission(
  407. "teacher_directory.manage", BOT_PERMISSIONS
  408. ):
  409. from wbb.modules.technician_services import send_service_home
  410. return await send_service_home(message)
  411. if name == "mkdwn_help":
  412. await message.reply(
  413. MARKDOWN,
  414. parse_mode=ParseMode.HTML,
  415. disable_web_page_preview=True,
  416. )
  417. elif "_" in name:
  418. module = name.split("_", 1)[1]
  419. if module not in HELPABLE:
  420. return await message.reply("未找到对应的中文帮助内容。")
  421. text = (
  422. f"以下是 **{HELPABLE[module].__MODULE__}** 的使用说明:\n"
  423. + HELPABLE[module].__HELP__
  424. )
  425. if module == "feds":
  426. return await message.reply(
  427. text=text,
  428. reply_markup=FED_MARKUP,
  429. disable_web_page_preview=True,
  430. )
  431. await message.reply(
  432. text,
  433. reply_markup=InlineKeyboardMarkup(
  434. [[InlineKeyboardButton("返回", callback_data="help_back")]]
  435. ),
  436. disable_web_page_preview=True,
  437. )
  438. elif name == "help":
  439. text, keyb = await help_parser(message.from_user.first_name)
  440. await message.reply(
  441. text,
  442. reply_markup=keyb,
  443. )
  444. else:
  445. await message.reply(
  446. home_text_pm,
  447. reply_markup=home_keyboard_pm,
  448. )
  449. if has_permission("teacher_directory.manage", BOT_PERMISSIONS):
  450. from wbb.modules.technician_services import send_service_home
  451. await send_service_home(message)
  452. return
  453. @app.on_message(filters.command("help"))
  454. async def help_command(_, message):
  455. if message.chat.type != ChatType.PRIVATE:
  456. if len(message.command) >= 2:
  457. name = (message.text.split(None, 1)[1]).replace(" ", "_").lower()
  458. if str(name) in HELPABLE:
  459. key = InlineKeyboardMarkup(
  460. [
  461. [
  462. InlineKeyboardButton(
  463. text="查看帮助",
  464. url=f"t.me/{BOT_USERNAME}?start=help_{name}",
  465. )
  466. ],
  467. ]
  468. )
  469. await message.reply(
  470. f"点击下方按钮查看“{HELPABLE[name].__MODULE__}”的帮助。",
  471. reply_markup=key,
  472. )
  473. else:
  474. await message.reply(
  475. "请私聊我查看详细功能。", reply_markup=keyboard
  476. )
  477. else:
  478. await message.reply(
  479. "请私聊我查看详细功能。", reply_markup=keyboard
  480. )
  481. else:
  482. if len(message.command) >= 2:
  483. name = (message.text.split(None, 1)[1]).replace(" ", "_").lower()
  484. if str(name) in HELPABLE:
  485. text = (
  486. f"以下是 **{HELPABLE[name].__MODULE__}** 的使用说明:\n"
  487. + HELPABLE[name].__HELP__
  488. )
  489. await message.reply(text, disable_web_page_preview=True)
  490. else:
  491. text, help_keyboard = await help_parser(
  492. message.from_user.first_name
  493. )
  494. await message.reply(
  495. text,
  496. reply_markup=help_keyboard,
  497. disable_web_page_preview=True,
  498. )
  499. else:
  500. text, help_keyboard = await help_parser(
  501. message.from_user.first_name
  502. )
  503. await message.reply(
  504. text, reply_markup=help_keyboard, disable_web_page_preview=True
  505. )
  506. return
  507. async def help_parser(name, keyboard=None):
  508. if not keyboard:
  509. keyboard = InlineKeyboardMarkup(paginate_modules(0, HELPABLE, "help"))
  510. return (
  511. f"""你好,{name},我是 {BOT_NAME}。
  512. 我可以管理群组、自动回复、积分和抽奖。
  513. 请选择下方功能分类查看中文命令说明。
  514. """,
  515. keyboard,
  516. )
  517. @app.on_callback_query(filters.regex("bot_commands"))
  518. async def commands_callbacc(_, CallbackQuery):
  519. text, keyboard = await help_parser(CallbackQuery.from_user.mention)
  520. await app.send_message(
  521. CallbackQuery.message.chat.id,
  522. text=text,
  523. reply_markup=keyboard,
  524. )
  525. await CallbackQuery.message.delete()
  526. @app.on_callback_query(filters.regex("^manage_hint$"))
  527. async def manage_hint_callback(_, query):
  528. if not has_any_permission(PRIVATE_MANAGEMENT_PERMISSIONS, BOT_PERMISSIONS):
  529. return await query.answer("当前机器人未分配私聊管理职责。", show_alert=True)
  530. await query.answer("请发送 /manage 打开群组管理菜单。", show_alert=True)
  531. @app.on_callback_query(filters.regex("stats_callback"))
  532. async def stats_callbacc(_, CallbackQuery):
  533. text = await bot_sys_stats()
  534. await app.answer_callback_query(CallbackQuery.id, text, show_alert=True)
  535. @app.on_callback_query(filters.regex(r"help_(.*?)"))
  536. async def help_button(client, query):
  537. home_match = re.match(r"help_home\((.+?)\)", query.data)
  538. mod_match = re.match(r"help_module\((.+?)\)", query.data)
  539. prev_match = re.match(r"help_prev\((.+?)\)", query.data)
  540. next_match = re.match(r"help_next\((.+?)\)", query.data)
  541. back_match = re.match(r"help_back", query.data)
  542. create_match = re.match(r"help_create", query.data)
  543. top_text = f"""
  544. 你好,{query.from_user.first_name},我是 {BOT_NAME}。
  545. 我可以管理群组、自动回复、积分和抽奖。
  546. 请选择下方功能分类查看中文命令说明。
  547. 常用命令:
  548. - /start:打开机器人首页
  549. - /help:查看功能帮助
  550. - /manage:打开私聊群管理菜单
  551. """
  552. if mod_match:
  553. module = (mod_match.group(1)).replace(" ", "_")
  554. text = (
  555. "{} **{}**:\n".format(
  556. "以下是使用说明", HELPABLE[module].__MODULE__
  557. )
  558. + HELPABLE[module].__HELP__
  559. )
  560. if module == "feds":
  561. return await query.message.edit(
  562. text=text,
  563. reply_markup=FED_MARKUP,
  564. disable_web_page_preview=True,
  565. )
  566. await query.message.edit(
  567. text=text,
  568. reply_markup=InlineKeyboardMarkup(
  569. [[InlineKeyboardButton("返回", callback_data="help_back")]]
  570. ),
  571. disable_web_page_preview=True,
  572. )
  573. elif home_match:
  574. await app.send_message(
  575. query.from_user.id,
  576. text=home_text_pm,
  577. reply_markup=home_keyboard_pm,
  578. )
  579. await query.message.delete()
  580. elif prev_match:
  581. curr_page = int(prev_match.group(1))
  582. await query.message.edit(
  583. text=top_text,
  584. reply_markup=InlineKeyboardMarkup(
  585. paginate_modules(curr_page - 1, HELPABLE, "help")
  586. ),
  587. disable_web_page_preview=True,
  588. )
  589. elif next_match:
  590. next_page = int(next_match.group(1))
  591. await query.message.edit(
  592. text=top_text,
  593. reply_markup=InlineKeyboardMarkup(
  594. paginate_modules(next_page + 1, HELPABLE, "help")
  595. ),
  596. disable_web_page_preview=True,
  597. )
  598. elif back_match:
  599. await query.message.edit(
  600. text=top_text,
  601. reply_markup=InlineKeyboardMarkup(
  602. paginate_modules(0, HELPABLE, "help")
  603. ),
  604. disable_web_page_preview=True,
  605. )
  606. elif create_match:
  607. text, keyboard = await help_parser(query)
  608. await query.message.edit(
  609. text=text,
  610. reply_markup=keyboard,
  611. disable_web_page_preview=True,
  612. )
  613. return await client.answer_callback_query(query.id)
  614. if __name__ == "__main__":
  615. install()
  616. with closing(loop):
  617. with suppress(asyncio.exceptions.CancelledError):
  618. loop.run_until_complete(start_bot())
  619. loop.run_until_complete(asyncio.sleep(3.0)) # task cancel wait