hikka_settings.py 39 KB

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