hikka_settings.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. # █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
  2. # █▀█ █ █ █ █▀█ █▀▄ █
  3. # © Copyright 2022
  4. # https://t.me/hikariatama
  5. #
  6. # 🔒 Licensed under the GNU AGPLv3
  7. # 🌐 https://www.gnu.org/licenses/agpl-3.0.html
  8. # scope: inline
  9. import logging
  10. import atexit
  11. import random
  12. import sys
  13. import os
  14. from telethon.tl.types import Message
  15. from telethon.tl.functions.messages import (
  16. GetDialogFiltersRequest,
  17. UpdateDialogFilterRequest,
  18. )
  19. from telethon.utils import get_display_name
  20. from .. import loader, main, utils
  21. from ..inline.types import InlineCall
  22. logger = logging.getLogger(__name__)
  23. def restart(*argv):
  24. os.execl(
  25. sys.executable,
  26. sys.executable,
  27. "-m",
  28. os.path.relpath(utils.get_base_dir()),
  29. *argv,
  30. )
  31. @loader.tds
  32. class HikkaSettingsMod(loader.Module):
  33. """Advanced settings for Hikka Userbot"""
  34. strings = {
  35. "name": "HikkaSettings",
  36. "watchers": "👀 <b>Watchers:</b>\n\n<b>{}</b>",
  37. "mod404": "🚫 <b>Watcher {} not found</b>",
  38. "disabled": "👀 <b>Watcher {} is now <u>disabled</u></b>",
  39. "enabled": "👀 <b>Watcher {} is now <u>enabled</u></b>",
  40. "args": "🚫 <b>You need to specify watcher name</b>",
  41. "user_nn": "🔰 <b>NoNick for this user is now {}</b>",
  42. "no_cmd": "🔰 <b>Please, specify command to toggle NoNick for</b>",
  43. "cmd_nn": "🔰 <b>NoNick for </b><code>{}</code><b> is now {}</b>",
  44. "cmd404": "🔰 <b>Command not found</b>",
  45. "inline_settings": "⚙️ <b>Here you can configure your Hikka settings</b>",
  46. "confirm_update": (
  47. "🧭 <b>Please, confirm that you want to update. Your userbot will be"
  48. " restarted</b>"
  49. ),
  50. "confirm_restart": "🔄 <b>Please, confirm that you want to restart</b>",
  51. "suggest_fs": "✅ Suggest FS for modules",
  52. "do_not_suggest_fs": "🚫 Suggest FS for modules",
  53. "use_fs": "✅ Always use FS for modules",
  54. "do_not_use_fs": "🚫 Always use FS for modules",
  55. "btn_restart": "🔄 Restart",
  56. "btn_update": "🧭 Update",
  57. "close_menu": "😌 Close menu",
  58. "download_btn": "✅ Download via button",
  59. "no_download_btn": "🚫 Download via button",
  60. "suggest_subscribe": "✅ Suggest subscribe to channel",
  61. "do_not_suggest_subscribe": "🚫 Suggest subscribe to channel",
  62. "private_not_allowed": "🚫 <b>This command must be executed in chat</b>",
  63. "nonick_warning": (
  64. "Warning! You enabled NoNick with default prefix! "
  65. "You may get muted in Hikka chats. Change prefix or "
  66. "disable NoNick!"
  67. ),
  68. "reply_required": (
  69. "🚫 <b>Reply to a message of user, which needs to be added to NoNick</b>"
  70. ),
  71. "deauth_confirm": (
  72. "⚠️ <b>This action will fully remove Hikka from this account and can't be"
  73. " reverted!</b>\n\n<i>- Hikka chats will be removed\n- Session will be"
  74. " terminated and removed\n- Hikka inline bot will be removed</i>"
  75. ),
  76. "deauth_confirm_step2": (
  77. "⚠️ <b>Are you really sure you want to delete Hikka?</b>"
  78. ),
  79. "deauth_yes": "I'm sure",
  80. "deauth_no_1": "I'm not sure",
  81. "deauth_no_2": "I'm uncertain",
  82. "deauth_no_3": "I'm struggling to answer",
  83. "deauth_cancel": "🚫 Cancel",
  84. "deauth_confirm_btn": "😢 Delete",
  85. "uninstall": "😢 <b>Uninstalling Hikka...</b>",
  86. "uninstalled": (
  87. "😢 <b>Hikka uninstalled. Web interface is still active, you can add another"
  88. " account</b>"
  89. ),
  90. "logs_cleared": "🗑 <b>Logs cleared</b>",
  91. "cmd_nn_list": "🔰 <b>NoNick is enabled for these commands:</b>\n\n{}",
  92. "user_nn_list": "🔰 <b>NoNick is enabled for these users:</b>\n\n{}",
  93. "chat_nn_list": "🔰 <b>NoNick is enabled for these chats:</b>\n\n{}",
  94. "nothing": "🔰 <b>Nothing to show...</b>",
  95. "privacy_leak": (
  96. "⚠️ <b>This command gives access to your Hikka web interface. It's not"
  97. " recommended to run it in public group chats. Consider using it in <a"
  98. " href='tg://openmessage?user_id={}'>Saved messages</a>. Type"
  99. " </b><code>{}proxypass force_insecure</code><b> to ignore this warning</b>"
  100. ),
  101. "privacy_leak_nowarn": (
  102. "⚠️ <b>This command gives access to your Hikka web interface. It's not"
  103. " recommended to run it in public group chats. Consider using it in <a"
  104. " href='tg://openmessage?user_id={}'>Saved messages</a>.</b>"
  105. ),
  106. "opening_tunnel": "🔁 <b>Opening tunnel to Hikka web interface...</b>",
  107. "tunnel_opened": "🎉 <b>Tunnel opened. This link is valid for about 1 hour</b>",
  108. "web_btn": "🌍 Web interface",
  109. "btn_yes": "🚸 Open anyway",
  110. "btn_no": "🔻 Cancel",
  111. "lavhost_web": (
  112. "✌️ <b>This link leads to your Hikka web interface on lavHost</b>\n\n<i>💡"
  113. " You'll need to authorize using lavHost credentials, specified on"
  114. " registration</i>"
  115. ),
  116. "disable_stats": "✅ Anonymous stats allowed",
  117. "enable_stats": "🚫 Anonymous stats disabled",
  118. }
  119. strings_ru = {
  120. "watchers": "👀 <b>Смотрители:</b>\n\n<b>{}</b>",
  121. "mod404": "🚫 <b>Смотритель {} не найден</b>",
  122. "disabled": "👀 <b>Смотритель {} теперь <u>выключен</u></b>",
  123. "enabled": "👀 <b>Смотритель {} теперь <u>включен</u></b>",
  124. "args": "🚫 <b>Укажи имя смотрителя</b>",
  125. "user_nn": "🔰 <b>Состояние NoNick для этого пользователя: {}</b>",
  126. "no_cmd": "🔰 <b>Укажи команду, для которой надо включить\\выключить NoNick</b>",
  127. "cmd_nn": "🔰 <b>Состояние NoNick для </b><code>{}</code><b>: {}</b>",
  128. "cmd404": "🔰 <b>Команда не найдена</b>",
  129. "inline_settings": "⚙️ <b>Здесь можно управлять настройками Hikka</b>",
  130. "confirm_update": "🧭 <b>Подтвердите обновление. Юзербот будет перезагружен</b>",
  131. "confirm_restart": "🔄 <b>Подтвердите перезагрузку</b>",
  132. "suggest_fs": "✅ Предлагать сохранение модулей",
  133. "do_not_suggest_fs": "🚫 Предлагать сохранение модулей",
  134. "use_fs": "✅ Всегда сохранять модули",
  135. "do_not_use_fs": "🚫 Всегда сохранять модули",
  136. "btn_restart": "🔄 Перезагрузка",
  137. "btn_update": "🧭 Обновление",
  138. "close_menu": "😌 Закрыть меню",
  139. "download_btn": "✅ Скачивать кнопкой",
  140. "no_download_btn": "🚫 Скачивать кнопкой",
  141. "suggest_subscribe": "✅ Предлагать подписку на канал",
  142. "do_not_suggest_subscribe": "🚫 Предлагать подписку на канал",
  143. "private_not_allowed": "🚫 <b>Эту команду нужно выполнять в чате</b>",
  144. "_cmd_doc_watchers": "Показать список смотрителей",
  145. "_cmd_doc_watcherbl": "<модуль> - Включить\\выключить смотритель в чате",
  146. "_cmd_doc_watcher": (
  147. "<модуль> - Управление глобальными правилами смотрителя\n"
  148. "Аргументы:\n"
  149. "[-c - только в чатах]\n"
  150. "[-p - только в лс]\n"
  151. "[-o - только исходящие]\n"
  152. "[-i - только входящие]"
  153. ),
  154. "_cmd_doc_nonickuser": (
  155. "Разрешить пользователю выполнять какую-то команду без ника"
  156. ),
  157. "_cmd_doc_nonickcmd": "Разрешить выполнять определенную команду без ника",
  158. "_cls_doc": "Дополнительные настройки Hikka",
  159. "nonick_warning": (
  160. "Внимание! Ты включил NoNick со стандартным префиксом! "
  161. "Тебя могут замьютить в чатах Hikka. Измени префикс или "
  162. "отключи глобальный NoNick!"
  163. ),
  164. "reply_required": (
  165. "🚫 <b>Ответь на сообщение пользователя, для которого нужно включить"
  166. " NoNick</b>"
  167. ),
  168. "deauth_confirm": (
  169. "⚠️ <b>Это действие полностью удалит Hikka с этого аккаунта! Его нельзя"
  170. " отменить</b>\n\n<i>- Все чаты, связанные с Hikka будут удалены\n- Сессия"
  171. " Hikka будет сброшена\n- Инлайн бот Hikka будет удален</i>"
  172. ),
  173. "deauth_confirm_step2": "⚠️ <b>Ты точно уверен, что хочешь удалить Hikka?</b>",
  174. "deauth_yes": "Я уверен",
  175. "deauth_no_1": "Я не уверен",
  176. "deauth_no_2": "Не точно",
  177. "deauth_no_3": "Нет",
  178. "deauth_cancel": "🚫 Отмена",
  179. "deauth_confirm_btn": "😢 Удалить",
  180. "uninstall": "😢 <b>Удаляю Hikka...</b>",
  181. "uninstalled": (
  182. "😢 <b>Hikka удалена. Веб-интерфейс все еще активен, можно добавить другие"
  183. " аккаунты!</b>"
  184. ),
  185. "logs_cleared": "🗑 <b>Логи очищены</b>",
  186. "cmd_nn_list": "🔰 <b>NoNick включен для этих команд:</b>\n\n{}",
  187. "user_nn_list": "🔰 <b>NoNick включен для этих пользователей:</b>\n\n{}",
  188. "chat_nn_list": "🔰 <b>NoNick включен для этих чатов:</b>\n\n{}",
  189. "nothing": "🔰 <b>Нечего показывать...</b>",
  190. "privacy_leak": (
  191. "⚠️ <b>Эта команда дает доступ к веб-интерфейсу Hikka. Ее выполнение в"
  192. " публичных чатах является угрозой безопасности. Предпочтительно выполнять"
  193. " ее в <a href='tg://openmessage?user_id={}'>Избранных сообщениях</a>."
  194. " Выполни </b><code>{}proxypass force_insecure</code><b> чтобы отключить"
  195. " это предупреждение</b>"
  196. ),
  197. "privacy_leak_nowarn": (
  198. "⚠️ <b>Эта команда дает доступ к веб-интерфейсу Hikka. Ее выполнение в"
  199. " публичных чатах является угрозой безопасности. Предпочтительно выполнять"
  200. " ее в <a href='tg://openmessage?user_id={}'>Избранных сообщениях</a>.</b>"
  201. ),
  202. "opening_tunnel": "🔁 <b>Открываю тоннель к веб-интерфейсу Hikka...</b>",
  203. "tunnel_opened": (
  204. "🎉 <b>Тоннель открыт. Эта ссылка будет активна не более часа</b>"
  205. ),
  206. "web_btn": "🌍 Веб-интерфейс",
  207. "btn_yes": "🚸 Все равно открыть",
  208. "btn_no": "🔻 Закрыть",
  209. "lavhost_web": (
  210. "✌️ <b>По этой ссылке ты попадешь в веб-интерфейс Hikka на"
  211. " lavHost</b>\n\n<i>💡 Тебе нужно будет авторизоваться, используя данные,"
  212. " указанные при настройке lavHost</i>"
  213. ),
  214. "disable_stats": "✅ Анонимная стата разрешена",
  215. "enable_stats": "🚫 Анонимная стата запрещена",
  216. }
  217. def get_watchers(self) -> tuple:
  218. return [
  219. str(watcher.__self__.__class__.strings["name"])
  220. for watcher in self.allmodules.watchers
  221. if watcher.__self__.__class__.strings is not None
  222. ], self._db.get(main.__name__, "disabled_watchers", {})
  223. async def _uninstall(self, call: InlineCall):
  224. await call.edit(self.strings("uninstall"))
  225. async with self._client.conversation("@BotFather") as conv:
  226. for msg in [
  227. "/deletebot",
  228. self.inline.bot_username,
  229. "Yes, I am totally sure.",
  230. ]:
  231. m = await conv.send_message(msg)
  232. r = await conv.get_response()
  233. logger.debug(f">> {m.raw_text}")
  234. logger.debug(f"<< {r.raw_text}")
  235. await m.delete()
  236. await r.delete()
  237. async for dialog in self._client.iter_dialogs(
  238. None,
  239. ignore_migrated=True,
  240. ):
  241. if (
  242. dialog.name
  243. in {
  244. "hikka-logs",
  245. "hikka-onload",
  246. "hikka-assets",
  247. "hikka-backups",
  248. "hikka-acc-switcher",
  249. "silent-tags",
  250. }
  251. and dialog.is_channel
  252. and (
  253. dialog.entity.participants_count == 1
  254. or dialog.entity.participants_count == 2
  255. and dialog.name in {"hikka-logs", "silent-tags"}
  256. )
  257. or (
  258. self._client.loader.inline.init_complete
  259. and dialog.entity.id == self._client.loader.inline.bot_id
  260. )
  261. ):
  262. await self._client.delete_dialog(dialog.entity)
  263. folders = await self._client(GetDialogFiltersRequest())
  264. if any(folder.title == "hikka" for folder in folders):
  265. folder_id = max(
  266. folders,
  267. key=lambda x: x.id,
  268. ).id
  269. await self._client(UpdateDialogFilterRequest(id=folder_id))
  270. for handler in logging.getLogger().handlers:
  271. handler.setLevel(logging.CRITICAL)
  272. await self._client.log_out()
  273. await call.edit(self.strings("uninstalled"))
  274. if "LAVHOST" in os.environ:
  275. os.system("lavhost restart")
  276. return
  277. atexit.register(restart, *sys.argv[1:])
  278. sys.exit(0)
  279. async def _uninstall_confirm_step_2(self, call: InlineCall):
  280. await call.edit(
  281. self.strings("deauth_confirm_step2"),
  282. utils.chunks(
  283. list(
  284. sorted(
  285. [
  286. {
  287. "text": self.strings("deauth_yes"),
  288. "callback": self._uninstall,
  289. },
  290. *[
  291. {
  292. "text": self.strings(f"deauth_no_{i}"),
  293. "action": "close",
  294. }
  295. for i in range(1, 4)
  296. ],
  297. ],
  298. key=lambda _: random.random(),
  299. )
  300. ),
  301. 2,
  302. )
  303. + [
  304. [
  305. {
  306. "text": self.strings("deauth_cancel"),
  307. "action": "close",
  308. }
  309. ]
  310. ],
  311. )
  312. async def uninstall_hikkacmd(self, message: Message):
  313. """Uninstall Hikka"""
  314. await self.inline.form(
  315. self.strings("deauth_confirm"),
  316. message,
  317. [
  318. {
  319. "text": self.strings("deauth_confirm_btn"),
  320. "callback": self._uninstall_confirm_step_2,
  321. },
  322. {"text": self.strings("deauth_cancel"), "action": "close"},
  323. ],
  324. )
  325. async def clearlogscmd(self, message: Message):
  326. """Clear logs"""
  327. for handler in logging.getLogger().handlers:
  328. handler.buffer = []
  329. handler.handledbuffer = []
  330. handler.tg_buff = ""
  331. await utils.answer(message, self.strings("logs_cleared"))
  332. async def watcherscmd(self, message: Message):
  333. """List current watchers"""
  334. watchers, disabled_watchers = self.get_watchers()
  335. watchers = [
  336. f"♻️ {watcher}"
  337. for watcher in watchers
  338. if watcher not in list(disabled_watchers.keys())
  339. ]
  340. watchers += [f"💢 {k} {v}" for k, v in disabled_watchers.items()]
  341. await utils.answer(
  342. message, self.strings("watchers").format("\n".join(watchers))
  343. )
  344. async def watcherblcmd(self, message: Message):
  345. """<module> - Toggle watcher in current chat"""
  346. args = utils.get_args_raw(message)
  347. if not args:
  348. await utils.answer(message, self.strings("args"))
  349. return
  350. watchers, disabled_watchers = self.get_watchers()
  351. if args.lower() not in map(lambda x: x.lower(), watchers):
  352. await utils.answer(message, self.strings("mod404").format(args))
  353. return
  354. args = next((x.lower() == args.lower() for x in watchers), False)
  355. current_bl = [
  356. v for k, v in disabled_watchers.items() if k.lower() == args.lower()
  357. ]
  358. current_bl = current_bl[0] if current_bl else []
  359. chat = utils.get_chat_id(message)
  360. if chat not in current_bl:
  361. if args in disabled_watchers:
  362. for k in disabled_watchers:
  363. if k.lower() == args.lower():
  364. disabled_watchers[k].append(chat)
  365. break
  366. else:
  367. disabled_watchers[args] = [chat]
  368. await utils.answer(
  369. message,
  370. self.strings("disabled").format(args) + " <b>in current chat</b>",
  371. )
  372. else:
  373. for k in disabled_watchers.copy():
  374. if k.lower() == args.lower():
  375. disabled_watchers[k].remove(chat)
  376. if not disabled_watchers[k]:
  377. del disabled_watchers[k]
  378. break
  379. await utils.answer(
  380. message,
  381. self.strings("enabled").format(args) + " <b>in current chat</b>",
  382. )
  383. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  384. async def watchercmd(self, message: Message):
  385. """<module> - Toggle global watcher rules
  386. Args:
  387. [-c - only in chats]
  388. [-p - only in pm]
  389. [-o - only out]
  390. [-i - only incoming]"""
  391. args = utils.get_args_raw(message)
  392. if not args:
  393. return await utils.answer(message, self.strings("args"))
  394. chats, pm, out, incoming = False, False, False, False
  395. if "-c" in args:
  396. args = args.replace("-c", "").replace(" ", " ").strip()
  397. chats = True
  398. if "-p" in args:
  399. args = args.replace("-p", "").replace(" ", " ").strip()
  400. pm = True
  401. if "-o" in args:
  402. args = args.replace("-o", "").replace(" ", " ").strip()
  403. out = True
  404. if "-i" in args:
  405. args = args.replace("-i", "").replace(" ", " ").strip()
  406. incoming = True
  407. if chats and pm:
  408. pm = False
  409. if out and incoming:
  410. incoming = False
  411. watchers, disabled_watchers = self.get_watchers()
  412. if args.lower() not in [watcher.lower() for watcher in watchers]:
  413. return await utils.answer(message, self.strings("mod404").format(args))
  414. args = [watcher for watcher in watchers if watcher.lower() == args.lower()][0]
  415. if chats or pm or out or incoming:
  416. disabled_watchers[args] = [
  417. *(["only_chats"] if chats else []),
  418. *(["only_pm"] if pm else []),
  419. *(["out"] if out else []),
  420. *(["in"] if incoming else []),
  421. ]
  422. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  423. await utils.answer(
  424. message,
  425. self.strings("enabled").format(args)
  426. + f" (<code>{disabled_watchers[args]}</code>)",
  427. )
  428. return
  429. if args in disabled_watchers and "*" in disabled_watchers[args]:
  430. await utils.answer(message, self.strings("enabled").format(args))
  431. del disabled_watchers[args]
  432. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  433. return
  434. disabled_watchers[args] = ["*"]
  435. self._db.set(main.__name__, "disabled_watchers", disabled_watchers)
  436. await utils.answer(message, self.strings("disabled").format(args))
  437. async def nonickusercmd(self, message: Message):
  438. """Allow no nickname for certain user"""
  439. reply = await message.get_reply_message()
  440. if not reply:
  441. await utils.answer(message, self.strings("reply_required"))
  442. return
  443. u = reply.sender_id
  444. if not isinstance(u, int):
  445. u = u.user_id
  446. nn = self._db.get(main.__name__, "nonickusers", [])
  447. if u not in nn:
  448. nn += [u]
  449. nn = list(set(nn)) # skipcq: PTC-W0018
  450. await utils.answer(message, self.strings("user_nn").format("on"))
  451. else:
  452. nn = list(set(nn) - {u})
  453. await utils.answer(message, self.strings("user_nn").format("off"))
  454. self._db.set(main.__name__, "nonickusers", nn)
  455. async def nonickchatcmd(self, message: Message):
  456. """Allow no nickname in certain chat"""
  457. if message.is_private:
  458. await utils.answer(message, self.strings("private_not_allowed"))
  459. return
  460. chat = utils.get_chat_id(message)
  461. nn = self._db.get(main.__name__, "nonickchats", [])
  462. if chat not in nn:
  463. nn += [chat]
  464. nn = list(set(nn)) # skipcq: PTC-W0018
  465. await utils.answer(
  466. message,
  467. self.strings("cmd_nn").format(
  468. utils.escape_html((await message.get_chat()).title),
  469. "on",
  470. ),
  471. )
  472. else:
  473. nn = list(set(nn) - {chat})
  474. await utils.answer(
  475. message,
  476. self.strings("cmd_nn").format(
  477. utils.escape_html((await message.get_chat()).title),
  478. "off",
  479. ),
  480. )
  481. self._db.set(main.__name__, "nonickchats", nn)
  482. async def nonickcmdcmd(self, message: Message):
  483. """Allow certain command to be executed without nickname"""
  484. args = utils.get_args_raw(message)
  485. if not args:
  486. await utils.answer(message, self.strings("no_cmd"))
  487. return
  488. if args not in self.allmodules.commands:
  489. await utils.answer(message, self.strings("cmd404"))
  490. return
  491. nn = self._db.get(main.__name__, "nonickcmds", [])
  492. if args not in nn:
  493. nn += [args]
  494. nn = list(set(nn))
  495. await utils.answer(
  496. message,
  497. self.strings("cmd_nn").format(
  498. self.get_prefix() + args,
  499. "on",
  500. ),
  501. )
  502. else:
  503. nn = list(set(nn) - {args})
  504. await utils.answer(
  505. message,
  506. self.strings("cmd_nn").format(
  507. self.get_prefix() + args,
  508. "off",
  509. ),
  510. )
  511. self._db.set(main.__name__, "nonickcmds", nn)
  512. async def nonickcmdscmd(self, message: Message):
  513. """Returns the list of NoNick commands"""
  514. if not self._db.get(main.__name__, "nonickcmds", []):
  515. await utils.answer(message, self.strings("nothing"))
  516. return
  517. await utils.answer(
  518. message,
  519. self.strings("cmd_nn_list").format(
  520. "\n".join(
  521. [
  522. f"▫️ <code>{self.get_prefix()}{cmd}</code>"
  523. for cmd in self._db.get(main.__name__, "nonickcmds", [])
  524. ]
  525. )
  526. ),
  527. )
  528. async def nonickuserscmd(self, message: Message):
  529. """Returns the list of NoNick users"""
  530. users = []
  531. for user_id in self._db.get(main.__name__, "nonickusers", []).copy():
  532. try:
  533. user = await self._client.get_entity(user_id)
  534. except Exception:
  535. self._db.set(
  536. main.__name__,
  537. "nonickusers",
  538. list(
  539. (
  540. set(self._db.get(main.__name__, "nonickusers", []))
  541. - {user_id}
  542. )
  543. ),
  544. )
  545. logger.warning(
  546. f"User {user_id} removed from nonickusers list", exc_info=True
  547. )
  548. continue
  549. users += [
  550. "▫️ <b><a"
  551. f' href="tg://user?id={user_id}">{utils.escape_html(get_display_name(user))}</a></b>'
  552. ]
  553. if not users:
  554. await utils.answer(message, self.strings("nothing"))
  555. return
  556. await utils.answer(
  557. message,
  558. self.strings("user_nn_list").format("\n".join(users)),
  559. )
  560. async def nonickchatscmd(self, message: Message):
  561. """Returns the list of NoNick chats"""
  562. chats = []
  563. for chat in self._db.get(main.__name__, "nonickchats", []):
  564. try:
  565. chat_entity = await self._client.get_entity(int(chat))
  566. except Exception:
  567. self._db.set(
  568. main.__name__,
  569. "nonickchats",
  570. list(
  571. (set(self._db.get(main.__name__, "nonickchats", [])) - {chat})
  572. ),
  573. )
  574. logger.warning(f"Chat {chat} removed from nonickchats list")
  575. continue
  576. chats += [
  577. "▫️ <b><a"
  578. f' href="{utils.get_entity_url(chat_entity)}">{utils.escape_html(get_display_name(chat_entity))}</a></b>'
  579. ]
  580. if not chats:
  581. await utils.answer(message, self.strings("nothing"))
  582. return
  583. await utils.answer(
  584. message,
  585. self.strings("user_nn_list").format("\n".join(chats)),
  586. )
  587. async def inline__setting(self, call: InlineCall, key: str, state: bool):
  588. self._db.set(main.__name__, key, state)
  589. if key == "no_nickname" and state and self.get_prefix() == ".":
  590. await call.answer(
  591. self.strings("nonick_warning"),
  592. show_alert=True,
  593. )
  594. else:
  595. await call.answer("Configuration value saved!")
  596. await call.edit(
  597. self.strings("inline_settings"),
  598. reply_markup=self._get_settings_markup(),
  599. )
  600. async def inline__update(
  601. self,
  602. call: InlineCall,
  603. confirm_required: bool = False,
  604. ):
  605. if confirm_required:
  606. await call.edit(
  607. self.strings("confirm_update"),
  608. reply_markup=[
  609. {"text": "🪂 Update", "callback": self.inline__update},
  610. {"text": "🚫 Cancel", "action": "close"},
  611. ],
  612. )
  613. return
  614. await call.answer("You userbot is being updated...", show_alert=True)
  615. await call.delete()
  616. m = await self._client.send_message("me", f"{self.get_prefix()}update --force")
  617. await self.allmodules.commands["update"](m)
  618. async def inline__restart(
  619. self,
  620. call: InlineCall,
  621. confirm_required: bool = False,
  622. ):
  623. if confirm_required:
  624. await call.edit(
  625. self.strings("confirm_restart"),
  626. reply_markup=[
  627. {"text": "🔄 Restart", "callback": self.inline__restart},
  628. {"text": "🚫 Cancel", "action": "close"},
  629. ],
  630. )
  631. return
  632. await call.answer("You userbot is being restarted...", show_alert=True)
  633. await call.delete()
  634. await self.allmodules.commands["restart"](
  635. await self._client.send_message("me", f"{self.get_prefix()}restart --force")
  636. )
  637. def _get_settings_markup(self) -> list:
  638. return [
  639. [
  640. (
  641. {
  642. "text": "✅ NoNick",
  643. "callback": self.inline__setting,
  644. "args": (
  645. "no_nickname",
  646. False,
  647. ),
  648. }
  649. if self._db.get(main.__name__, "no_nickname", False)
  650. else {
  651. "text": "🚫 NoNick",
  652. "callback": self.inline__setting,
  653. "args": (
  654. "no_nickname",
  655. True,
  656. ),
  657. }
  658. ),
  659. (
  660. {
  661. "text": "✅ Grep",
  662. "callback": self.inline__setting,
  663. "args": (
  664. "grep",
  665. False,
  666. ),
  667. }
  668. if self._db.get(main.__name__, "grep", False)
  669. else {
  670. "text": "🚫 Grep",
  671. "callback": self.inline__setting,
  672. "args": (
  673. "grep",
  674. True,
  675. ),
  676. }
  677. ),
  678. (
  679. {
  680. "text": "✅ InlineLogs",
  681. "callback": self.inline__setting,
  682. "args": (
  683. "inlinelogs",
  684. False,
  685. ),
  686. }
  687. if self._db.get(main.__name__, "inlinelogs", True)
  688. else {
  689. "text": "🚫 InlineLogs",
  690. "callback": self.inline__setting,
  691. "args": (
  692. "inlinelogs",
  693. True,
  694. ),
  695. }
  696. ),
  697. ],
  698. [
  699. {
  700. "text": self.strings("do_not_suggest_fs"),
  701. "callback": self.inline__setting,
  702. "args": (
  703. "disable_modules_fs",
  704. False,
  705. ),
  706. }
  707. if self._db.get(main.__name__, "disable_modules_fs", False)
  708. else {
  709. "text": self.strings("suggest_fs"),
  710. "callback": self.inline__setting,
  711. "args": (
  712. "disable_modules_fs",
  713. True,
  714. ),
  715. }
  716. ],
  717. [
  718. (
  719. {
  720. "text": self.strings("use_fs"),
  721. "callback": self.inline__setting,
  722. "args": (
  723. "permanent_modules_fs",
  724. False,
  725. ),
  726. }
  727. if self._db.get(main.__name__, "permanent_modules_fs", False)
  728. else {
  729. "text": self.strings("do_not_use_fs"),
  730. "callback": self.inline__setting,
  731. "args": (
  732. "permanent_modules_fs",
  733. True,
  734. ),
  735. }
  736. ),
  737. ],
  738. [
  739. (
  740. {
  741. "text": self.strings("suggest_subscribe"),
  742. "callback": self.inline__setting,
  743. "args": (
  744. "suggest_subscribe",
  745. False,
  746. ),
  747. }
  748. if self._db.get(main.__name__, "suggest_subscribe", True)
  749. else {
  750. "text": self.strings("do_not_suggest_subscribe"),
  751. "callback": self.inline__setting,
  752. "args": (
  753. "suggest_subscribe",
  754. True,
  755. ),
  756. }
  757. ),
  758. ],
  759. [
  760. (
  761. {
  762. "text": self.strings("disable_stats"),
  763. "callback": self.inline__setting,
  764. "args": ("stats", False),
  765. }
  766. if self._db.get(main.__name__, "stats", True)
  767. else {
  768. "text": self.strings("enable_stats"),
  769. "callback": self.inline__setting,
  770. "args": (
  771. "stats",
  772. True,
  773. ),
  774. }
  775. ),
  776. ],
  777. [
  778. {
  779. "text": self.strings("btn_restart"),
  780. "callback": self.inline__restart,
  781. "args": (True,),
  782. },
  783. {
  784. "text": self.strings("btn_update"),
  785. "callback": self.inline__update,
  786. "args": (True,),
  787. },
  788. ],
  789. [{"text": self.strings("close_menu"), "action": "close"}],
  790. ]
  791. @loader.owner
  792. async def settingscmd(self, message: Message):
  793. """Show settings menu"""
  794. await self.inline.form(
  795. self.strings("inline_settings"),
  796. message=message,
  797. reply_markup=self._get_settings_markup(),
  798. )
  799. @loader.owner
  800. async def weburlcmd(self, message: Message, force: bool = False):
  801. """Opens web tunnel to your Hikka web interface"""
  802. if "LAVHOST" in os.environ:
  803. form = await self.inline.form(
  804. self.strings("lavhost_web"),
  805. message=message,
  806. reply_markup={
  807. "text": self.strings("web_btn"),
  808. "url": await main.hikka.web.get_url(proxy_pass=False),
  809. },
  810. gif="https://t.me/hikari_assets/28",
  811. )
  812. return
  813. if (
  814. not force
  815. and not message.is_private
  816. and "force_insecure" not in message.raw_text.lower()
  817. ):
  818. try:
  819. if not await self.inline.form(
  820. self.strings("privacy_leak_nowarn").format(self._client.tg_id),
  821. message=message,
  822. reply_markup=[
  823. {
  824. "text": self.strings("btn_yes"),
  825. "callback": self.weburlcmd,
  826. "args": (True,),
  827. },
  828. {"text": self.strings("btn_no"), "action": "close"},
  829. ],
  830. gif="https://i.gifer.com/embedded/download/Z5tS.gif",
  831. ):
  832. raise Exception
  833. except Exception:
  834. await utils.answer(
  835. message,
  836. self.strings("privacy_leak").format(
  837. self._client.tg_id,
  838. self.get_prefix(),
  839. ),
  840. )
  841. return
  842. if force:
  843. form = message
  844. await form.edit(
  845. self.strings("opening_tunnel"),
  846. reply_markup={"text": "🕔 Wait...", "data": "empty"},
  847. gif=(
  848. "https://i.gifer.com/origin/e4/e43e1b221fd960003dc27d2f2f1b8ce1.gif"
  849. ),
  850. )
  851. else:
  852. form = await self.inline.form(
  853. self.strings("opening_tunnel"),
  854. message=message,
  855. reply_markup={"text": "🕔 Wait...", "data": "empty"},
  856. gif=(
  857. "https://i.gifer.com/origin/e4/e43e1b221fd960003dc27d2f2f1b8ce1.gif"
  858. ),
  859. )
  860. url = await main.hikka.web.get_url(proxy_pass=True)
  861. await form.edit(
  862. self.strings("tunnel_opened"),
  863. reply_markup={"text": self.strings("web_btn"), "url": url},
  864. gif="https://t.me/hikari_assets/28",
  865. )