hikka_settings.py 37 KB

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