hikka_settings.py 41 KB

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