hikka_settings.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. # ©️ Dan Gazizullin, 2021-2023
  2. # This file is a part of Hikka Userbot
  3. # 🌐 https://github.com/hikariatama/Hikka
  4. # You can redistribute it and/or modify it under the terms of the GNU AGPLv3
  5. # 🔑 https://www.gnu.org/licenses/agpl-3.0.html
  6. import logging
  7. import os
  8. import random
  9. import hikkatl
  10. from hikkatl.tl.functions.channels import JoinChannelRequest
  11. from hikkatl.tl.functions.messages import (
  12. GetDialogFiltersRequest,
  13. UpdateDialogFilterRequest,
  14. )
  15. from hikkatl.tl.types import Message
  16. from hikkatl.utils import get_display_name
  17. from .. import loader, log, main, utils
  18. from .._internal import fw_protect, restart
  19. from ..inline.types import InlineCall
  20. from ..web import core
  21. logger = logging.getLogger(__name__)
  22. ALL_INVOKES = [
  23. "flush_entity_cache",
  24. "flush_fulluser_cache",
  25. "flush_fullchannel_cache",
  26. "flush_perms_cache",
  27. "flush_loader_cache",
  28. "flush_cache",
  29. "reload_core",
  30. "inspect_cache",
  31. "inspect_modules",
  32. ]
  33. @loader.tds
  34. class HikkaSettingsMod(loader.Module):
  35. """Advanced settings for Hikka Userbot"""
  36. strings = {"name": "HikkaSettings"}
  37. def get_watchers(self) -> tuple:
  38. return [
  39. str(watcher.__self__.__class__.strings["name"])
  40. for watcher in self.allmodules.watchers
  41. if watcher.__self__.__class__.strings is not None
  42. ], self._db.get(main.__name__, "disabled_watchers", {})
  43. async def _uninstall(self, call: InlineCall):
  44. await call.edit(self.strings("uninstall"))
  45. async with self._client.conversation("@BotFather") as conv:
  46. for msg in [
  47. "/deletebot",
  48. f"@{self.inline.bot_username}",
  49. "Yes, I am totally sure.",
  50. ]:
  51. await fw_protect()
  52. m = await conv.send_message(msg)
  53. r = await conv.get_response()
  54. logger.debug(">> %s", m.raw_text)
  55. logger.debug("<< %s", r.raw_text)
  56. await fw_protect()
  57. await m.delete()
  58. await r.delete()
  59. async for dialog in self._client.iter_dialogs(
  60. None,
  61. ignore_migrated=True,
  62. ):
  63. if (
  64. dialog.name
  65. in {
  66. "hikka-logs",
  67. "hikka-onload",
  68. "hikka-assets",
  69. "hikka-backups",
  70. "hikka-acc-switcher",
  71. "silent-tags",
  72. }
  73. and dialog.is_channel
  74. and (
  75. dialog.entity.participants_count == 1
  76. or dialog.entity.participants_count == 2
  77. and dialog.name in {"hikka-logs", "silent-tags"}
  78. )
  79. or (
  80. self._client.loader.inline.init_complete
  81. and dialog.entity.id == self._client.loader.inline.bot_id
  82. )
  83. ):
  84. await fw_protect()
  85. await self._client.delete_dialog(dialog.entity)
  86. await fw_protect()
  87. folders = await self._client(GetDialogFiltersRequest())
  88. if any(folder.title == "hikka" for folder in folders):
  89. folder_id = max(
  90. folders,
  91. key=lambda x: x.id,
  92. ).id
  93. await fw_protect()
  94. await self._client(UpdateDialogFilterRequest(id=folder_id))
  95. for handler in logging.getLogger().handlers:
  96. handler.setLevel(logging.CRITICAL)
  97. await fw_protect()
  98. await self._client.log_out()
  99. restart()
  100. async def _uninstall_confirm_step_2(self, call: InlineCall):
  101. await call.edit(
  102. self.strings("deauth_confirm_step2"),
  103. utils.chunks(
  104. list(
  105. sorted(
  106. [
  107. {
  108. "text": self.strings("deauth_yes"),
  109. "callback": self._uninstall,
  110. },
  111. *[
  112. {
  113. "text": self.strings(f"deauth_no_{i}"),
  114. "action": "close",
  115. }
  116. for i in range(1, 4)
  117. ],
  118. ],
  119. key=lambda _: random.random(),
  120. )
  121. ),
  122. 2,
  123. )
  124. + [
  125. [
  126. {
  127. "text": self.strings("deauth_cancel"),
  128. "action": "close",
  129. }
  130. ]
  131. ],
  132. )
  133. @loader.command()
  134. async def uninstall_hikka(self, message: Message):
  135. await self.inline.form(
  136. self.strings("deauth_confirm"),
  137. message,
  138. [
  139. {
  140. "text": self.strings("deauth_confirm_btn"),
  141. "callback": self._uninstall_confirm_step_2,
  142. },
  143. {"text": self.strings("deauth_cancel"), "action": "close"},
  144. ],
  145. )
  146. @loader.command()
  147. async def watchers(self, message: Message):
  148. watchers, disabled_watchers = self.get_watchers()
  149. watchers = [
  150. f"♻️ {watcher}"
  151. for watcher in watchers
  152. if watcher not in list(disabled_watchers.keys())
  153. ]
  154. watchers += [f"💢 {k} {v}" for k, v in disabled_watchers.items()]
  155. await utils.answer(
  156. message, self.strings("watchers").format("\n".join(watchers))
  157. )
  158. @loader.command()
  159. async def watcherbl(self, message: Message):
  160. if not (args := utils.get_args_raw(message)):
  161. await utils.answer(message, self.strings("args"))
  162. return
  163. watchers, disabled_watchers = self.get_watchers()
  164. if args.lower() not in map(lambda x: x.lower(), watchers):
  165. await utils.answer(message, self.strings("mod404").format(args))
  166. return
  167. args = next((x.lower() == args.lower() for x in watchers), False)
  168. current_bl = [
  169. v for k, v in disabled_watchers.items() if k.lower() == args.lower()
  170. ]
  171. current_bl = current_bl[0] if current_bl else []
  172. chat = utils.get_chat_id(message)
  173. if chat not in current_bl:
  174. if args in disabled_watchers:
  175. for k in disabled_watchers:
  176. if k.lower() == args.lower():
  177. disabled_watchers[k].append(chat)
  178. break
  179. else:
  180. disabled_watchers[args] = [chat]
  181. await utils.answer(
  182. message,
  183. self.strings("disabled").format(args) + " <b>in current chat</b>",
  184. )
  185. else:
  186. for k in disabled_watchers.copy():
  187. if k.lower() == args.lower():
  188. disabled_watchers[k].remove(chat)
  189. if not disabled_watchers[k]:
  190. del disabled_watchers[k]
  191. break
  192. await utils.answer(
  193. message,
  194. self.strings("enabled").format(args) + " <b>in current chat</b>",
  195. )
  196. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  197. @loader.command()
  198. async def watchercmd(self, message: Message):
  199. if not (args := utils.get_args_raw(message)):
  200. return await utils.answer(message, self.strings("args"))
  201. chats, pm, out, incoming = False, False, False, False
  202. if "-c" in args:
  203. args = args.replace("-c", "").replace(" ", " ").strip()
  204. chats = True
  205. if "-p" in args:
  206. args = args.replace("-p", "").replace(" ", " ").strip()
  207. pm = True
  208. if "-o" in args:
  209. args = args.replace("-o", "").replace(" ", " ").strip()
  210. out = True
  211. if "-i" in args:
  212. args = args.replace("-i", "").replace(" ", " ").strip()
  213. incoming = True
  214. if chats and pm:
  215. pm = False
  216. if out and incoming:
  217. incoming = False
  218. watchers, disabled_watchers = self.get_watchers()
  219. if args.lower() not in [watcher.lower() for watcher in watchers]:
  220. return await utils.answer(message, self.strings("mod404").format(args))
  221. args = [watcher for watcher in watchers if watcher.lower() == args.lower()][0]
  222. if chats or pm or out or incoming:
  223. disabled_watchers[args] = [
  224. *(["only_chats"] if chats else []),
  225. *(["only_pm"] if pm else []),
  226. *(["out"] if out else []),
  227. *(["in"] if incoming else []),
  228. ]
  229. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  230. await utils.answer(
  231. message,
  232. self.strings("enabled").format(args)
  233. + f" (<code>{disabled_watchers[args]}</code>)",
  234. )
  235. return
  236. if args in disabled_watchers and "*" in disabled_watchers[args]:
  237. await utils.answer(message, self.strings("enabled").format(args))
  238. del disabled_watchers[args]
  239. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  240. return
  241. disabled_watchers[args] = ["*"]
  242. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  243. await utils.answer(message, self.strings("disabled").format(args))
  244. @loader.command()
  245. async def nonickuser(self, message: Message):
  246. if not (reply := await message.get_reply_message()):
  247. await utils.answer(message, self.strings("reply_required"))
  248. return
  249. u = reply.sender_id
  250. if not isinstance(u, int):
  251. u = u.user_id
  252. nn = self._db.get(main.__name__, "nonickusers", [])
  253. if u not in nn:
  254. nn += [u]
  255. nn = list(set(nn)) # skipcq: PTC-W0018
  256. await utils.answer(message, self.strings("user_nn").format("on"))
  257. else:
  258. nn = list(set(nn) - {u})
  259. await utils.answer(message, self.strings("user_nn").format("off"))
  260. self._db.set(main.__name__, "nonickusers", nn)
  261. @loader.command()
  262. async def nonickchat(self, message: Message):
  263. if message.is_private:
  264. await utils.answer(message, self.strings("private_not_allowed"))
  265. return
  266. chat = utils.get_chat_id(message)
  267. nn = self._db.get(main.__name__, "nonickchats", [])
  268. if chat not in nn:
  269. nn += [chat]
  270. nn = list(set(nn)) # skipcq: PTC-W0018
  271. await utils.answer(
  272. message,
  273. self.strings("cmd_nn").format(
  274. utils.escape_html((await message.get_chat()).title),
  275. "on",
  276. ),
  277. )
  278. else:
  279. nn = list(set(nn) - {chat})
  280. await utils.answer(
  281. message,
  282. self.strings("cmd_nn").format(
  283. utils.escape_html((await message.get_chat()).title),
  284. "off",
  285. ),
  286. )
  287. self._db.set(main.__name__, "nonickchats", nn)
  288. @loader.command()
  289. async def nonickcmdcmd(self, message: Message):
  290. if not (args := utils.get_args_raw(message)):
  291. await utils.answer(message, self.strings("no_cmd"))
  292. return
  293. if args not in self.allmodules.commands:
  294. await utils.answer(message, self.strings("cmd404"))
  295. return
  296. nn = self._db.get(main.__name__, "nonickcmds", [])
  297. if args not in nn:
  298. nn += [args]
  299. nn = list(set(nn))
  300. await utils.answer(
  301. message,
  302. self.strings("cmd_nn").format(
  303. utils.escape_html(self.get_prefix() + args),
  304. "on",
  305. ),
  306. )
  307. else:
  308. nn = list(set(nn) - {args})
  309. await utils.answer(
  310. message,
  311. self.strings("cmd_nn").format(
  312. utils.escape_html(self.get_prefix() + args),
  313. "off",
  314. ),
  315. )
  316. self._db.set(main.__name__, "nonickcmds", nn)
  317. @loader.command()
  318. async def nonickcmds(self, message: Message):
  319. if not self._db.get(main.__name__, "nonickcmds", []):
  320. await utils.answer(message, self.strings("nothing"))
  321. return
  322. await utils.answer(
  323. message,
  324. self.strings("cmd_nn_list").format(
  325. "\n".join(
  326. [
  327. f"▫️ <code>{utils.escape_html(self.get_prefix() + cmd)}</code>"
  328. for cmd in self._db.get(main.__name__, "nonickcmds", [])
  329. ]
  330. )
  331. ),
  332. )
  333. @loader.command()
  334. async def nonickusers(self, message: Message):
  335. users = []
  336. for user_id in self._db.get(main.__name__, "nonickusers", []).copy():
  337. try:
  338. user = await self._client.get_entity(user_id)
  339. except Exception:
  340. self._db.set(
  341. main.__name__,
  342. "nonickusers",
  343. list(
  344. (
  345. set(self._db.get(main.__name__, "nonickusers", []))
  346. - {user_id}
  347. )
  348. ),
  349. )
  350. logger.warning("User %s removed from nonickusers list", user_id)
  351. continue
  352. users += [
  353. '▫️ <b><a href="tg://user?id={}">{}</a></b>'.format(
  354. user_id,
  355. utils.escape_html(get_display_name(user)),
  356. )
  357. ]
  358. if not users:
  359. await utils.answer(message, self.strings("nothing"))
  360. return
  361. await utils.answer(
  362. message,
  363. self.strings("user_nn_list").format("\n".join(users)),
  364. )
  365. @loader.command()
  366. async def nonickchats(self, message: Message):
  367. chats = []
  368. for chat in self._db.get(main.__name__, "nonickchats", []):
  369. try:
  370. chat_entity = await self._client.get_entity(int(chat))
  371. except Exception:
  372. self._db.set(
  373. main.__name__,
  374. "nonickchats",
  375. list(
  376. (set(self._db.get(main.__name__, "nonickchats", [])) - {chat})
  377. ),
  378. )
  379. logger.warning("Chat %s removed from nonickchats list", chat)
  380. continue
  381. chats += [
  382. '▫️ <b><a href="{}">{}</a></b>'.format(
  383. utils.get_entity_url(chat_entity),
  384. utils.escape_html(get_display_name(chat_entity)),
  385. )
  386. ]
  387. if not chats:
  388. await utils.answer(message, self.strings("nothing"))
  389. return
  390. await utils.answer(
  391. message,
  392. self.strings("user_nn_list").format("\n".join(chats)),
  393. )
  394. async def inline__setting(self, call: InlineCall, key: str, state: bool = False):
  395. if callable(key):
  396. key()
  397. hikkatl.extensions.html.CUSTOM_EMOJIS = not main.get_config_key(
  398. "disable_custom_emojis"
  399. )
  400. else:
  401. self._db.set(main.__name__, key, state)
  402. if key == "no_nickname" and state and self.get_prefix() == ".":
  403. await call.answer(
  404. self.strings("nonick_warning"),
  405. show_alert=True,
  406. )
  407. else:
  408. await call.answer("Configuration value saved!")
  409. await call.edit(
  410. self.strings("inline_settings"),
  411. reply_markup=self._get_settings_markup(),
  412. )
  413. async def inline__update(
  414. self,
  415. call: InlineCall,
  416. confirm_required: bool = False,
  417. ):
  418. if confirm_required:
  419. await call.edit(
  420. self.strings("confirm_update"),
  421. reply_markup=[
  422. {"text": "🪂 Update", "callback": self.inline__update},
  423. {"text": "🚫 Cancel", "action": "close"},
  424. ],
  425. )
  426. return
  427. await call.answer("You userbot is being updated...", show_alert=True)
  428. await call.delete()
  429. await self.invoke("update", "-f", peer="me")
  430. async def _remove_core_protection(self, call: InlineCall):
  431. self._db.set(main.__name__, "remove_core_protection", True)
  432. await call.edit(self.strings("core_protection_removed"))
  433. @loader.command()
  434. async def remove_core_protection(self, message: Message):
  435. if self._db.get(main.__name__, "remove_core_protection", False):
  436. await utils.answer(message, self.strings("core_protection_already_removed"))
  437. return
  438. await self.inline.form(
  439. message=message,
  440. text=self.strings("core_protection_confirm"),
  441. reply_markup=[
  442. {
  443. "text": self.strings("core_protection_btn"),
  444. "callback": self._remove_core_protection,
  445. },
  446. {
  447. "text": self.strings("btn_no"),
  448. "action": "close",
  449. },
  450. ],
  451. )
  452. async def inline__restart(
  453. self,
  454. call: InlineCall,
  455. confirm_required: bool = False,
  456. ):
  457. if confirm_required:
  458. await call.edit(
  459. self.strings("confirm_restart"),
  460. reply_markup=[
  461. {"text": "🔄 Restart", "callback": self.inline__restart},
  462. {"text": "🚫 Cancel", "action": "close"},
  463. ],
  464. )
  465. return
  466. await call.answer("You userbot is being restarted...", show_alert=True)
  467. await call.delete()
  468. await self.invoke("restart", "-f", peer="me")
  469. def _get_settings_markup(self) -> list:
  470. return [
  471. [
  472. (
  473. {
  474. "text": "✅ NoNick",
  475. "callback": self.inline__setting,
  476. "args": (
  477. "no_nickname",
  478. False,
  479. ),
  480. }
  481. if self._db.get(main.__name__, "no_nickname", False)
  482. else {
  483. "text": "🚫 NoNick",
  484. "callback": self.inline__setting,
  485. "args": (
  486. "no_nickname",
  487. True,
  488. ),
  489. }
  490. ),
  491. (
  492. {
  493. "text": "✅ Grep",
  494. "callback": self.inline__setting,
  495. "args": (
  496. "grep",
  497. False,
  498. ),
  499. }
  500. if self._db.get(main.__name__, "grep", False)
  501. else {
  502. "text": "🚫 Grep",
  503. "callback": self.inline__setting,
  504. "args": (
  505. "grep",
  506. True,
  507. ),
  508. }
  509. ),
  510. (
  511. {
  512. "text": "✅ InlineLogs",
  513. "callback": self.inline__setting,
  514. "args": (
  515. "inlinelogs",
  516. False,
  517. ),
  518. }
  519. if self._db.get(main.__name__, "inlinelogs", True)
  520. else {
  521. "text": "🚫 InlineLogs",
  522. "callback": self.inline__setting,
  523. "args": (
  524. "inlinelogs",
  525. True,
  526. ),
  527. }
  528. ),
  529. ],
  530. [
  531. (
  532. {
  533. "text": self.strings("do_not_suggest_fs"),
  534. "callback": self.inline__setting,
  535. "args": (
  536. "disable_modules_fs",
  537. False,
  538. ),
  539. }
  540. if self._db.get(main.__name__, "disable_modules_fs", False)
  541. else {
  542. "text": self.strings("suggest_fs"),
  543. "callback": self.inline__setting,
  544. "args": (
  545. "disable_modules_fs",
  546. True,
  547. ),
  548. }
  549. )
  550. ],
  551. [
  552. (
  553. {
  554. "text": self.strings("use_fs"),
  555. "callback": self.inline__setting,
  556. "args": (
  557. "permanent_modules_fs",
  558. False,
  559. ),
  560. }
  561. if self._db.get(main.__name__, "permanent_modules_fs", False)
  562. else {
  563. "text": self.strings("do_not_use_fs"),
  564. "callback": self.inline__setting,
  565. "args": (
  566. "permanent_modules_fs",
  567. True,
  568. ),
  569. }
  570. ),
  571. ],
  572. [
  573. (
  574. {
  575. "text": self.strings("suggest_subscribe"),
  576. "callback": self.inline__setting,
  577. "args": (
  578. "suggest_subscribe",
  579. False,
  580. ),
  581. }
  582. if self._db.get(main.__name__, "suggest_subscribe", True)
  583. else {
  584. "text": self.strings("do_not_suggest_subscribe"),
  585. "callback": self.inline__setting,
  586. "args": (
  587. "suggest_subscribe",
  588. True,
  589. ),
  590. }
  591. ),
  592. ],
  593. [
  594. (
  595. {
  596. "text": self.strings("no_custom_emojis"),
  597. "callback": self.inline__setting,
  598. "args": (
  599. lambda: main.save_config_key(
  600. "disable_custom_emojis", False
  601. ),
  602. ),
  603. }
  604. if main.get_config_key("disable_custom_emojis")
  605. else {
  606. "text": self.strings("custom_emojis"),
  607. "callback": self.inline__setting,
  608. "args": (
  609. lambda: main.save_config_key("disable_custom_emojis", True),
  610. ),
  611. }
  612. ),
  613. ],
  614. [
  615. (
  616. {
  617. "text": self.strings("disable_debugger"),
  618. "callback": self.inline__setting,
  619. "args": lambda: self._db.set(log.__name__, "debugger", False),
  620. }
  621. if self._db.get(log.__name__, "debugger", False)
  622. else {
  623. "text": self.strings("enable_debugger"),
  624. "callback": self.inline__setting,
  625. "args": (lambda: self._db.set(log.__name__, "debugger", True),),
  626. }
  627. ),
  628. ],
  629. [
  630. {
  631. "text": self.strings("btn_restart"),
  632. "callback": self.inline__restart,
  633. "args": (True,),
  634. },
  635. {
  636. "text": self.strings("btn_update"),
  637. "callback": self.inline__update,
  638. "args": (True,),
  639. },
  640. ],
  641. [{"text": self.strings("close_menu"), "action": "close"}],
  642. ]
  643. @loader.command()
  644. async def settings(self, message: Message):
  645. await self.inline.form(
  646. self.strings("inline_settings"),
  647. message=message,
  648. reply_markup=self._get_settings_markup(),
  649. )
  650. @loader.command()
  651. async def weburl(self, message: Message, force: bool = False):
  652. if "LAVHOST" in os.environ:
  653. form = await self.inline.form(
  654. self.strings("lavhost_web"),
  655. message=message,
  656. reply_markup={
  657. "text": self.strings("web_btn"),
  658. "url": await main.hikka.web.get_url(proxy_pass=False),
  659. },
  660. gif="https://t.me/hikari_assets/28",
  661. )
  662. return
  663. if (
  664. not force
  665. and not message.is_private
  666. and "force_insecure" not in message.raw_text.lower()
  667. ):
  668. try:
  669. if not await self.inline.form(
  670. self.strings("privacy_leak_nowarn").format(self._client.tg_id),
  671. message=message,
  672. reply_markup=[
  673. {
  674. "text": self.strings("btn_yes"),
  675. "callback": self.weburl,
  676. "args": (True,),
  677. },
  678. {"text": self.strings("btn_no"), "action": "close"},
  679. ],
  680. gif="https://i.gifer.com/embedded/download/Z5tS.gif",
  681. ):
  682. raise Exception
  683. except Exception:
  684. await utils.answer(
  685. message,
  686. self.strings("privacy_leak").format(
  687. self._client.tg_id,
  688. utils.escape_html(self.get_prefix()),
  689. ),
  690. )
  691. return
  692. if not main.hikka.web:
  693. main.hikka.web = core.Web(
  694. data_root=main.BASE_DIR,
  695. api_token=main.hikka.api_token,
  696. proxy=main.hikka.proxy,
  697. connection=main.hikka.conn,
  698. )
  699. await main.hikka.web.add_loader(self._client, self.allmodules, self._db)
  700. await main.hikka.web.start_if_ready(
  701. len(self.allclients),
  702. main.hikka.arguments.port,
  703. proxy_pass=main.hikka.arguments.proxy_pass,
  704. )
  705. if force:
  706. form = message
  707. await form.edit(
  708. self.strings("opening_tunnel"),
  709. reply_markup={"text": "🕔 Wait...", "data": "empty"},
  710. gif=(
  711. "https://i.gifer.com/origin/e4/e43e1b221fd960003dc27d2f2f1b8ce1.gif"
  712. ),
  713. )
  714. else:
  715. form = await self.inline.form(
  716. self.strings("opening_tunnel"),
  717. message=message,
  718. reply_markup={"text": "🕔 Wait...", "data": "empty"},
  719. gif=(
  720. "https://i.gifer.com/origin/e4/e43e1b221fd960003dc27d2f2f1b8ce1.gif"
  721. ),
  722. )
  723. url = await main.hikka.web.get_url(proxy_pass=True)
  724. await form.edit(
  725. self.strings("tunnel_opened"),
  726. reply_markup={"text": self.strings("web_btn"), "url": url},
  727. gif="https://t.me/hikari_assets/48",
  728. )
  729. @loader.loop(interval=1, autostart=True)
  730. async def loop(self):
  731. if not (obj := self.allmodules.get_approved_channel):
  732. return
  733. channel, event = obj
  734. try:
  735. await self._client(JoinChannelRequest(channel))
  736. except Exception:
  737. logger.exception("Failed to join channel")
  738. event.status = False
  739. event.set()
  740. else:
  741. event.status = True
  742. event.set()
  743. def _get_all_IDM(self, module: str):
  744. return {
  745. getattr(getattr(self.lookup(module), name), "name", name): getattr(
  746. self.lookup(module), name
  747. )
  748. for name in dir(self.lookup(module))
  749. if getattr(getattr(self.lookup(module), name), "is_debug_method", False)
  750. }
  751. @loader.command()
  752. async def invokecmd(self, message: Message):
  753. if not (args := utils.get_args_raw(message)) or len(args.split()) < 2:
  754. await utils.answer(message, self.strings("no_args"))
  755. return
  756. module = args.split()[0]
  757. method = args.split(maxsplit=1)[1]
  758. if module != "core" and not self.lookup(module):
  759. await utils.answer(message, self.strings("module404").format(module))
  760. return
  761. if (
  762. module == "core"
  763. and method not in ALL_INVOKES
  764. or module != "core"
  765. and method not in self._get_all_IDM(module)
  766. ):
  767. await utils.answer(message, self.strings("invoke404").format(method))
  768. return
  769. message = await utils.answer(
  770. message, self.strings("invoking").format(method, module)
  771. )
  772. result = ""
  773. if module == "core":
  774. if method == "flush_entity_cache":
  775. result = (
  776. f"Dropped {len(self._client._hikka_entity_cache)} cache records"
  777. )
  778. self._client._hikka_entity_cache = {}
  779. elif method == "flush_fulluser_cache":
  780. result = (
  781. f"Dropped {len(self._client._hikka_fulluser_cache)} cache records"
  782. )
  783. self._client._hikka_fulluser_cache = {}
  784. elif method == "flush_fullchannel_cache":
  785. result = (
  786. f"Dropped {len(self._client._hikka_fullchannel_cache)} cache"
  787. " records"
  788. )
  789. self._client._hikka_fullchannel_cache = {}
  790. elif method == "flush_perms_cache":
  791. result = f"Dropped {len(self._client._hikka_perms_cache)} cache records"
  792. self._client._hikka_perms_cache = {}
  793. elif method == "flush_loader_cache":
  794. result = (
  795. f"Dropped {await self.lookup('loader').flush_cache()} cache records"
  796. )
  797. elif method == "flush_cache":
  798. count = self.lookup("loader").flush_cache()
  799. result = (
  800. f"Dropped {len(self._client._hikka_entity_cache)} entity cache"
  801. " records\nDropped"
  802. f" {len(self._client._hikka_fulluser_cache)} fulluser cache"
  803. " records\nDropped"
  804. f" {len(self._client._hikka_fullchannel_cache)} fullchannel cache"
  805. " records\nDropped"
  806. f" {count} loader links cache records"
  807. )
  808. self._client._hikka_entity_cache = {}
  809. self._client._hikka_fulluser_cache = {}
  810. self._client._hikka_fullchannel_cache = {}
  811. self._client.hikka_me = await self._client.get_me()
  812. elif method == "reload_core":
  813. core_quantity = await self.lookup("loader").reload_core()
  814. result = f"Reloaded {core_quantity} core modules"
  815. elif method == "inspect_cache":
  816. result = (
  817. "Entity cache:"
  818. f" {len(self._client._hikka_entity_cache)} records\nFulluser cache:"
  819. f" {len(self._client._hikka_fulluser_cache)} records\nFullchannel"
  820. " cache:"
  821. f" {len(self._client._hikka_fullchannel_cache)} records\nLoader"
  822. f" links cache: {self.lookup('loader').inspect_cache()} records"
  823. )
  824. elif method == "inspect_modules":
  825. result = (
  826. "Loaded modules: {}\nLoaded core modules: {}\nLoaded user"
  827. " modules: {}"
  828. ).format(
  829. len(self.allmodules.modules),
  830. sum(
  831. module.__origin__.startswith("<core")
  832. for module in self.allmodules.modules
  833. ),
  834. sum(
  835. not module.__origin__.startswith("<core")
  836. for module in self.allmodules.modules
  837. ),
  838. )
  839. else:
  840. result = await self._get_all_IDM(module)[method](message)
  841. await utils.answer(
  842. message,
  843. self.strings("invoke").format(method, utils.escape_html(result)),
  844. )