hikka_config.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  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 ast
  10. import functools
  11. import logging
  12. from math import ceil
  13. from typing import Optional, Union, Any
  14. from telethon.tl.types import Message
  15. from .. import loader, utils, translations
  16. from ..inline.types import InlineCall
  17. logger = logging.getLogger(__name__)
  18. # Everywhere in this module, we use the following naming convention:
  19. # `obj_type` of non-core module = False
  20. # `obj_type` of core module = True
  21. # `obj_type` of library = "library"
  22. @loader.tds
  23. class HikkaConfigMod(loader.Module):
  24. """Interactive configurator for Hikka Userbot"""
  25. strings = {
  26. "name": "HikkaConfig",
  27. "choose_core": "🎚 <b>Choose a category</b>",
  28. "configure": "🎚 <b>Choose a module to configure</b>",
  29. "configure_lib": "🪴 <b>Choose a library to configure</b>",
  30. "configuring_mod": (
  31. "🎚 <b>Choose config option for mod</b> <code>{}</code>\n\n<b>Current"
  32. " options:</b>\n\n{}"
  33. ),
  34. "configuring_lib": (
  35. "🪴 <b>Choose config option for library</b> <code>{}</code>\n\n<b>Current"
  36. " options:</b>\n\n{}"
  37. ),
  38. "configuring_option": (
  39. "🎚 <b>Configuring option </b><code>{}</code><b> of mod"
  40. " </b><code>{}</code>\n<i>ℹ️ {}</i>\n\n<b>Default: {}</b>\n\n<b>Current:"
  41. " {}</b>\n\n{}"
  42. ),
  43. "configuring_option_lib": (
  44. "🪴 <b>Configuring option </b><code>{}</code><b> of library"
  45. " </b><code>{}</code>\n<i>ℹ️ {}</i>\n\n<b>Default: {}</b>\n\n<b>Current:"
  46. " {}</b>\n\n{}"
  47. ),
  48. "option_saved": (
  49. "🎚 <b>Option </b><code>{}</code><b> of module </b><code>{}</code><b>"
  50. " saved!</b>\n<b>Current: {}</b>"
  51. ),
  52. "option_saved_lib": (
  53. "🪴 <b>Option </b><code>{}</code><b> of library </b><code>{}</code><b>"
  54. " saved!</b>\n<b>Current: {}</b>"
  55. ),
  56. "option_reset": (
  57. "♻️ <b>Option </b><code>{}</code><b> of module </b><code>{}</code><b> has"
  58. " been reset to default</b>\n<b>Current: {}</b>"
  59. ),
  60. "option_reset_lib": (
  61. "♻️ <b>Option </b><code>{}</code><b> of library </b><code>{}</code><b> has"
  62. " been reset to default</b>\n<b>Current: {}</b>"
  63. ),
  64. "args": "🚫 <b>You specified incorrect args</b>",
  65. "no_mod": "🚫 <b>Module doesn't exist</b>",
  66. "no_option": "🚫 <b>Configuration option doesn't exist</b>",
  67. "validation_error": "🚫 <b>You entered incorrect config value. \nError: {}</b>",
  68. "try_again": "🔁 Try again",
  69. "typehint": "🕵️ <b>Must be a{eng_art} {}</b>",
  70. "set": "set",
  71. "set_default_btn": "♻️ Reset default",
  72. "enter_value_btn": "✍️ Enter value",
  73. "enter_value_desc": "✍️ Enter new configuration value for this option",
  74. "add_item_desc": "✍️ Enter item to add",
  75. "remove_item_desc": "✍️ Enter item to remove",
  76. "back_btn": "👈 Back",
  77. "close_btn": "🔻 Close",
  78. "add_item_btn": "➕ Add item",
  79. "remove_item_btn": "➖ Remove item",
  80. "show_hidden": "🚸 Show value",
  81. "hide_value": "🔒 Hide value",
  82. "builtin": "🛰 Built-in",
  83. "external": "🛸 External",
  84. "libraries": "🪴 Libraries",
  85. }
  86. strings_ru = {
  87. "choose_core": "🎚 <b>Выбери категорию</b>",
  88. "configure": "🎚 <b>Выбери модуль для настройки</b>",
  89. "configure_lib": "🪴 <b>Выбери библиотеку для настройки</b>",
  90. "configuring_mod": (
  91. "🎚 <b>Выбери параметр для модуля</b> <code>{}</code>\n\n<b>Текущие"
  92. " настройки:</b>\n\n{}"
  93. ),
  94. "configuring_lib": (
  95. "🪴 <b>Выбери параметр для библиотеки</b> <code>{}</code>\n\n<b>Текущие"
  96. " настройки:</b>\n\n{}"
  97. ),
  98. "configuring_option": (
  99. "🎚 <b>Управление параметром </b><code>{}</code><b> модуля"
  100. " </b><code>{}</code>\n<i>ℹ️ {}</i>\n\n<b>Стандартное:"
  101. " {}</b>\n\n<b>Текущее: {}</b>\n\n{}"
  102. ),
  103. "configuring_option_lib": (
  104. "🪴 <b>Управление параметром </b><code>{}</code><b> библиотеки"
  105. " </b><code>{}</code>\n<i>ℹ️ {}</i>\n\n<b>Стандартное:"
  106. " {}</b>\n\n<b>Текущее: {}</b>\n\n{}"
  107. ),
  108. "option_saved": (
  109. "🎚 <b>Параметр </b><code>{}</code><b> модуля </b><code>{}</code><b>"
  110. " сохранен!</b>\n<b>Текущее: {}</b>"
  111. ),
  112. "option_saved_lib": (
  113. "🪴 <b>Параметр </b><code>{}</code><b> библиотеки </b><code>{}</code><b>"
  114. " сохранен!</b>\n<b>Текущее: {}</b>"
  115. ),
  116. "option_reset": (
  117. "♻️ <b>Параметр </b><code>{}</code><b> модуля </b><code>{}</code><b>"
  118. " сброшен до значения по умолчанию</b>\n<b>Текущее: {}</b>"
  119. ),
  120. "option_reset_lib": (
  121. "♻️ <b>Параметр </b><code>{}</code><b> библиотеки </b><code>{}</code><b>"
  122. " сброшен до значения по умолчанию</b>\n<b>Текущее: {}</b>"
  123. ),
  124. "_cmd_doc_config": "Настройки модулей",
  125. "_cmd_doc_fconfig": (
  126. "<имя модуля> <имя конфига> <значение> - Расшифровывается как ForceConfig -"
  127. " Принудительно устанавливает значение в конфиге, если это не удалось"
  128. " сделать через inline бота"
  129. ),
  130. "_cls_doc": "Интерактивный конфигуратор Hikka",
  131. "args": "🚫 <b>Ты указал неверные аргументы</b>",
  132. "no_mod": "🚫 <b>Модуль не существует</b>",
  133. "no_option": "🚫 <b>У модуля нет такого значения конфига</b>",
  134. "validation_error": (
  135. "🚫 <b>Введено некорректное значение конфига. \nОшибка: {}</b>"
  136. ),
  137. "try_again": "🔁 Попробовать еще раз",
  138. "typehint": "🕵️ <b>Должно быть {}</b>",
  139. "set": "поставить",
  140. "set_default_btn": "♻️ Значение по умолчанию",
  141. "enter_value_btn": "✍️ Ввести значение",
  142. "enter_value_desc": "✍️ Введи новое значение этого параметра",
  143. "add_item_desc": "✍️ Введи элемент, который нужно добавить",
  144. "remove_item_desc": "✍️ Введи элемент, который нужно удалить",
  145. "back_btn": "👈 Назад",
  146. "close_btn": "🔻 Закрыть",
  147. "add_item_btn": "➕ Добавить элемент",
  148. "remove_item_btn": "➖ Удалить элемент",
  149. "show_hidden": "🚸 Показать значение",
  150. "hide_value": "🔒 Скрыть значение",
  151. "builtin": "🛰 Встроенные",
  152. "external": "🛸 Внешние",
  153. "libraries": "🪴 Библиотеки",
  154. }
  155. _row_size = 3
  156. _num_rows = 5
  157. @staticmethod
  158. def prep_value(value: Any) -> Any:
  159. if isinstance(value, str):
  160. return f"</b><code>{utils.escape_html(value.strip())}</code><b>"
  161. if isinstance(value, list) and value:
  162. return (
  163. "</b><code>[</code>\n "
  164. + "\n ".join(
  165. [f"<code>{utils.escape_html(str(item))}</code>" for item in value]
  166. )
  167. + "\n<code>]</code><b>"
  168. )
  169. return f"</b><code>{utils.escape_html(value)}</code><b>"
  170. def hide_value(self, value: Any) -> str:
  171. if isinstance(value, list) and value:
  172. return self.prep_value(["*" * len(str(i)) for i in value])
  173. return self.prep_value("*" * len(str(value)))
  174. async def inline__set_config(
  175. self,
  176. call: InlineCall,
  177. query: str,
  178. mod: str,
  179. option: str,
  180. inline_message_id: str,
  181. obj_type: Union[bool, str] = False,
  182. ):
  183. try:
  184. self.lookup(mod).config[option] = query
  185. except loader.validators.ValidationError as e:
  186. await call.edit(
  187. self.strings("validation_error").format(e.args[0]),
  188. reply_markup={
  189. "text": self.strings("try_again"),
  190. "callback": self.inline__configure_option,
  191. "args": (mod, option),
  192. "kwargs": {"obj_type": obj_type},
  193. },
  194. )
  195. return
  196. await call.edit(
  197. self.strings(
  198. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  199. ).format(
  200. utils.escape_html(option),
  201. utils.escape_html(mod),
  202. self.prep_value(self.lookup(mod).config[option])
  203. if not self.lookup(mod).config._config[option].validator
  204. or self.lookup(mod).config._config[option].validator.internal_id
  205. != "Hidden"
  206. else self.hide_value(self.lookup(mod).config[option]),
  207. ),
  208. reply_markup=[
  209. [
  210. {
  211. "text": self.strings("back_btn"),
  212. "callback": self.inline__configure,
  213. "args": (mod,),
  214. "kwargs": {"obj_type": obj_type},
  215. },
  216. {"text": self.strings("close_btn"), "action": "close"},
  217. ]
  218. ],
  219. inline_message_id=inline_message_id,
  220. )
  221. async def inline__reset_default(
  222. self,
  223. call: InlineCall,
  224. mod: str,
  225. option: str,
  226. obj_type: Union[bool, str] = False,
  227. ):
  228. mod_instance = self.lookup(mod)
  229. mod_instance.config[option] = mod_instance.config.getdef(option)
  230. await call.edit(
  231. self.strings(
  232. "option_reset" if isinstance(obj_type, bool) else "option_reset_lib"
  233. ).format(
  234. utils.escape_html(option),
  235. utils.escape_html(mod),
  236. self.prep_value(self.lookup(mod).config[option])
  237. if not self.lookup(mod).config._config[option].validator
  238. or self.lookup(mod).config._config[option].validator.internal_id
  239. != "Hidden"
  240. else self.hide_value(self.lookup(mod).config[option]),
  241. ),
  242. reply_markup=[
  243. [
  244. {
  245. "text": self.strings("back_btn"),
  246. "callback": self.inline__configure,
  247. "args": (mod,),
  248. "kwargs": {"obj_type": obj_type},
  249. },
  250. {"text": self.strings("close_btn"), "action": "close"},
  251. ]
  252. ],
  253. )
  254. async def inline__set_bool(
  255. self,
  256. call: InlineCall,
  257. mod: str,
  258. option: str,
  259. value: bool,
  260. obj_type: Union[bool, str] = False,
  261. ):
  262. try:
  263. self.lookup(mod).config[option] = value
  264. except loader.validators.ValidationError as e:
  265. await call.edit(
  266. self.strings("validation_error").format(e.args[0]),
  267. reply_markup={
  268. "text": self.strings("try_again"),
  269. "callback": self.inline__configure_option,
  270. "args": (mod, option),
  271. "kwargs": {"obj_type": obj_type},
  272. },
  273. )
  274. return
  275. validator = self.lookup(mod).config._config[option].validator
  276. doc = utils.escape_html(
  277. next(
  278. (
  279. validator.doc[lang]
  280. for lang in self._db.get(translations.__name__, "lang", "en").split(
  281. " "
  282. )
  283. if lang in validator.doc
  284. ),
  285. validator.doc["en"],
  286. )
  287. )
  288. await call.edit(
  289. self.strings(
  290. "configuring_option"
  291. if isinstance(obj_type, bool)
  292. else "configuring_option_lib"
  293. ).format(
  294. utils.escape_html(option),
  295. utils.escape_html(mod),
  296. utils.escape_html(self.lookup(mod).config.getdoc(option)),
  297. self.prep_value(self.lookup(mod).config.getdef(option)),
  298. self.prep_value(self.lookup(mod).config[option])
  299. if not validator or validator.internal_id != "Hidden"
  300. else self.hide_value(self.lookup(mod).config[option]),
  301. self.strings("typehint").format(
  302. doc,
  303. eng_art="n" if doc.lower().startswith(tuple("euioay")) else "",
  304. )
  305. if doc
  306. else "",
  307. ),
  308. reply_markup=self._generate_bool_markup(mod, option, obj_type),
  309. )
  310. await call.answer("✅")
  311. def _generate_bool_markup(
  312. self,
  313. mod: str,
  314. option: str,
  315. obj_type: Union[bool, str] = False,
  316. ) -> list:
  317. return [
  318. [
  319. *(
  320. [
  321. {
  322. "text": f"❌ {self.strings('set')} `False`",
  323. "callback": self.inline__set_bool,
  324. "args": (mod, option, False),
  325. "kwargs": {"obj_type": obj_type},
  326. }
  327. ]
  328. if self.lookup(mod).config[option]
  329. else [
  330. {
  331. "text": f"✅ {self.strings('set')} `True`",
  332. "callback": self.inline__set_bool,
  333. "args": (mod, option, True),
  334. "kwargs": {"obj_type": obj_type},
  335. }
  336. ]
  337. )
  338. ],
  339. [
  340. *(
  341. [
  342. {
  343. "text": self.strings("set_default_btn"),
  344. "callback": self.inline__reset_default,
  345. "args": (mod, option),
  346. "kwargs": {"obj_type": obj_type},
  347. }
  348. ]
  349. if self.lookup(mod).config[option]
  350. != self.lookup(mod).config.getdef(option)
  351. else []
  352. )
  353. ],
  354. [
  355. {
  356. "text": self.strings("back_btn"),
  357. "callback": self.inline__configure,
  358. "args": (mod,),
  359. "kwargs": {"obj_type": obj_type},
  360. },
  361. {"text": self.strings("close_btn"), "action": "close"},
  362. ],
  363. ]
  364. async def inline__add_item(
  365. self,
  366. call: InlineCall,
  367. query: str,
  368. mod: str,
  369. option: str,
  370. inline_message_id: str,
  371. obj_type: Union[bool, str] = False,
  372. ):
  373. try:
  374. try:
  375. query = ast.literal_eval(query)
  376. except Exception:
  377. pass
  378. if isinstance(query, (set, tuple)):
  379. query = list(query)
  380. if not isinstance(query, list):
  381. query = [query]
  382. self.lookup(mod).config[option] = self.lookup(mod).config[option] + query
  383. except loader.validators.ValidationError as e:
  384. await call.edit(
  385. self.strings("validation_error").format(e.args[0]),
  386. reply_markup={
  387. "text": self.strings("try_again"),
  388. "callback": self.inline__configure_option,
  389. "args": (mod, option),
  390. "kwargs": {"obj_type": obj_type},
  391. },
  392. )
  393. return
  394. await call.edit(
  395. self.strings(
  396. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  397. ).format(
  398. utils.escape_html(option),
  399. utils.escape_html(mod),
  400. self.prep_value(self.lookup(mod).config[option])
  401. if not self.lookup(mod).config._config[option].validator
  402. or self.lookup(mod).config._config[option].validator.internal_id
  403. != "Hidden"
  404. else self.hide_value(self.lookup(mod).config[option]),
  405. ),
  406. reply_markup=[
  407. [
  408. {
  409. "text": self.strings("back_btn"),
  410. "callback": self.inline__configure,
  411. "args": (mod,),
  412. "kwargs": {"obj_type": obj_type},
  413. },
  414. {"text": self.strings("close_btn"), "action": "close"},
  415. ]
  416. ],
  417. inline_message_id=inline_message_id,
  418. )
  419. async def inline__remove_item(
  420. self,
  421. call: InlineCall,
  422. query: str,
  423. mod: str,
  424. option: str,
  425. inline_message_id: str,
  426. obj_type: Union[bool, str] = False,
  427. ):
  428. try:
  429. try:
  430. query = ast.literal_eval(query)
  431. except Exception:
  432. pass
  433. if isinstance(query, (set, tuple)):
  434. query = list(query)
  435. if not isinstance(query, list):
  436. query = [query]
  437. query = list(map(str, query))
  438. old_config_len = len(self.lookup(mod).config[option])
  439. self.lookup(mod).config[option] = [
  440. i for i in self.lookup(mod).config[option] if str(i) not in query
  441. ]
  442. if old_config_len == len(self.lookup(mod).config[option]):
  443. raise loader.validators.ValidationError(
  444. f"Nothing from passed value ({self.prep_value(query)}) is not in"
  445. " target list"
  446. )
  447. except loader.validators.ValidationError as e:
  448. await call.edit(
  449. self.strings("validation_error").format(e.args[0]),
  450. reply_markup={
  451. "text": self.strings("try_again"),
  452. "callback": self.inline__configure_option,
  453. "args": (mod, option),
  454. "kwargs": {"obj_type": obj_type},
  455. },
  456. )
  457. return
  458. await call.edit(
  459. self.strings(
  460. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  461. ).format(
  462. utils.escape_html(option),
  463. utils.escape_html(mod),
  464. self.prep_value(self.lookup(mod).config[option])
  465. if not self.lookup(mod).config._config[option].validator
  466. or self.lookup(mod).config._config[option].validator.internal_id
  467. != "Hidden"
  468. else self.hide_value(self.lookup(mod).config[option]),
  469. ),
  470. reply_markup=[
  471. [
  472. {
  473. "text": self.strings("back_btn"),
  474. "callback": self.inline__configure,
  475. "args": (mod,),
  476. "kwargs": {"obj_type": obj_type},
  477. },
  478. {"text": self.strings("close_btn"), "action": "close"},
  479. ]
  480. ],
  481. inline_message_id=inline_message_id,
  482. )
  483. def _generate_series_markup(
  484. self,
  485. call: InlineCall,
  486. mod: str,
  487. option: str,
  488. obj_type: Union[bool, str] = False,
  489. ) -> list:
  490. return [
  491. [
  492. {
  493. "text": self.strings("enter_value_btn"),
  494. "input": self.strings("enter_value_desc"),
  495. "handler": self.inline__set_config,
  496. "args": (mod, option, call.inline_message_id),
  497. "kwargs": {"obj_type": obj_type},
  498. }
  499. ],
  500. [
  501. *(
  502. [
  503. {
  504. "text": self.strings("remove_item_btn"),
  505. "input": self.strings("remove_item_desc"),
  506. "handler": self.inline__remove_item,
  507. "args": (mod, option, call.inline_message_id),
  508. "kwargs": {"obj_type": obj_type},
  509. },
  510. {
  511. "text": self.strings("add_item_btn"),
  512. "input": self.strings("add_item_desc"),
  513. "handler": self.inline__add_item,
  514. "args": (mod, option, call.inline_message_id),
  515. "kwargs": {"obj_type": obj_type},
  516. },
  517. ]
  518. if self.lookup(mod).config[option]
  519. else []
  520. ),
  521. ],
  522. [
  523. *(
  524. [
  525. {
  526. "text": self.strings("set_default_btn"),
  527. "callback": self.inline__reset_default,
  528. "args": (mod, option),
  529. "kwargs": {"obj_type": obj_type},
  530. }
  531. ]
  532. if self.lookup(mod).config[option]
  533. != self.lookup(mod).config.getdef(option)
  534. else []
  535. )
  536. ],
  537. [
  538. {
  539. "text": self.strings("back_btn"),
  540. "callback": self.inline__configure,
  541. "args": (mod,),
  542. "kwargs": {"obj_type": obj_type},
  543. },
  544. {"text": self.strings("close_btn"), "action": "close"},
  545. ],
  546. ]
  547. async def inline__configure_option(
  548. self,
  549. call: InlineCall,
  550. mod: str,
  551. config_opt: str,
  552. force_hidden: Optional[bool] = False,
  553. obj_type: Union[bool, str] = False,
  554. ):
  555. module = self.lookup(mod)
  556. args = [
  557. utils.escape_html(config_opt),
  558. utils.escape_html(mod),
  559. utils.escape_html(module.config.getdoc(config_opt)),
  560. self.prep_value(module.config.getdef(config_opt)),
  561. self.prep_value(module.config[config_opt])
  562. if not module.config._config[config_opt].validator
  563. or module.config._config[config_opt].validator.internal_id != "Hidden"
  564. or force_hidden
  565. else self.hide_value(module.config[config_opt]),
  566. ]
  567. if (
  568. module.config._config[config_opt].validator
  569. and module.config._config[config_opt].validator.internal_id == "Hidden"
  570. ):
  571. additonal_button_row = (
  572. [
  573. [
  574. {
  575. "text": self.strings("hide_value"),
  576. "callback": self.inline__configure_option,
  577. "args": (mod, config_opt, False),
  578. "kwargs": {"obj_type": obj_type},
  579. }
  580. ]
  581. ]
  582. if force_hidden
  583. else [
  584. [
  585. {
  586. "text": self.strings("show_hidden"),
  587. "callback": self.inline__configure_option,
  588. "args": (mod, config_opt, True),
  589. "kwargs": {"obj_type": obj_type},
  590. }
  591. ]
  592. ]
  593. )
  594. else:
  595. additonal_button_row = []
  596. try:
  597. validator = module.config._config[config_opt].validator
  598. doc = utils.escape_html(
  599. next(
  600. (
  601. validator.doc[lang]
  602. for lang in self._db.get(
  603. translations.__name__, "lang", "en"
  604. ).split(" ")
  605. if lang in validator.doc
  606. ),
  607. validator.doc["en"],
  608. )
  609. )
  610. except Exception:
  611. doc = None
  612. validator = None
  613. args += [""]
  614. else:
  615. args += [
  616. self.strings("typehint").format(
  617. doc,
  618. eng_art="n" if doc.lower().startswith(tuple("euioay")) else "",
  619. )
  620. ]
  621. if validator.internal_id == "Boolean":
  622. await call.edit(
  623. self.strings(
  624. "configuring_option"
  625. if isinstance(obj_type, bool)
  626. else "configuring_option_lib"
  627. ).format(*args),
  628. reply_markup=additonal_button_row
  629. + self._generate_bool_markup(mod, config_opt, obj_type),
  630. )
  631. return
  632. if validator.internal_id == "Series":
  633. await call.edit(
  634. self.strings(
  635. "configuring_option"
  636. if isinstance(obj_type, bool)
  637. else "configuring_option_lib"
  638. ).format(*args),
  639. reply_markup=additonal_button_row
  640. + self._generate_series_markup(call, mod, config_opt, obj_type),
  641. )
  642. return
  643. await call.edit(
  644. self.strings(
  645. "configuring_option"
  646. if isinstance(obj_type, bool)
  647. else "configuring_option_lib"
  648. ).format(*args),
  649. reply_markup=additonal_button_row
  650. + [
  651. [
  652. {
  653. "text": self.strings("enter_value_btn"),
  654. "input": self.strings("enter_value_desc"),
  655. "handler": self.inline__set_config,
  656. "args": (mod, config_opt, call.inline_message_id),
  657. "kwargs": {"obj_type": obj_type},
  658. }
  659. ],
  660. [
  661. {
  662. "text": self.strings("set_default_btn"),
  663. "callback": self.inline__reset_default,
  664. "args": (mod, config_opt),
  665. "kwargs": {"obj_type": obj_type},
  666. }
  667. ],
  668. [
  669. {
  670. "text": self.strings("back_btn"),
  671. "callback": self.inline__configure,
  672. "args": (mod,),
  673. "kwargs": {"obj_type": obj_type},
  674. },
  675. {"text": self.strings("close_btn"), "action": "close"},
  676. ],
  677. ],
  678. )
  679. async def inline__configure(
  680. self,
  681. call: InlineCall,
  682. mod: str,
  683. obj_type: Union[bool, str] = False,
  684. ):
  685. btns = [
  686. {
  687. "text": param,
  688. "callback": self.inline__configure_option,
  689. "args": (mod, param),
  690. "kwargs": {"obj_type": obj_type},
  691. }
  692. for param in self.lookup(mod).config
  693. ]
  694. await call.edit(
  695. self.strings(
  696. "configuring_mod" if isinstance(obj_type, bool) else "configuring_lib"
  697. ).format(
  698. utils.escape_html(mod),
  699. "\n".join(
  700. [
  701. f"▫️ <code>{utils.escape_html(key)}</code>:"
  702. f" <b>{self.prep_value(value) if not self.lookup(mod).config._config[key].validator or self.lookup(mod).config._config[key].validator.internal_id != 'Hidden' else self.hide_value(value)}</b>"
  703. for key, value in self.lookup(mod).config.items()
  704. ]
  705. ),
  706. ),
  707. reply_markup=list(utils.chunks(btns, 2))
  708. + [
  709. [
  710. {
  711. "text": self.strings("back_btn"),
  712. "callback": self.inline__global_config,
  713. "kwargs": {"obj_type": obj_type},
  714. },
  715. {"text": self.strings("close_btn"), "action": "close"},
  716. ]
  717. ],
  718. )
  719. async def inline__choose_category(self, call: Union[Message, InlineCall]):
  720. await utils.answer(
  721. call,
  722. self.strings("choose_core"),
  723. reply_markup=[
  724. [
  725. {
  726. "text": self.strings("builtin"),
  727. "callback": self.inline__global_config,
  728. "kwargs": {"obj_type": True},
  729. },
  730. {
  731. "text": self.strings("external"),
  732. "callback": self.inline__global_config,
  733. },
  734. ],
  735. *(
  736. [
  737. [
  738. {
  739. "text": self.strings("libraries"),
  740. "callback": self.inline__global_config,
  741. "kwargs": {"obj_type": "library"},
  742. }
  743. ]
  744. ]
  745. if self.allmodules.libraries
  746. and any(hasattr(lib, "config") for lib in self.allmodules.libraries)
  747. else []
  748. ),
  749. [{"text": self.strings("close_btn"), "action": "close"}],
  750. ],
  751. )
  752. async def inline__global_config(
  753. self,
  754. call: InlineCall,
  755. page: int = 0,
  756. obj_type: Union[bool, str] = False,
  757. ):
  758. if isinstance(obj_type, bool):
  759. to_config = [
  760. mod.strings("name")
  761. for mod in self.allmodules.modules
  762. if hasattr(mod, "config")
  763. and callable(mod.strings)
  764. and (getattr(mod, "__origin__", None) == "<core>" or not obj_type)
  765. and (getattr(mod, "__origin__", None) != "<core>" or obj_type)
  766. ]
  767. else:
  768. to_config = [
  769. lib.name for lib in self.allmodules.libraries if hasattr(lib, "config")
  770. ]
  771. to_config.sort()
  772. kb = []
  773. for mod_row in utils.chunks(
  774. to_config[
  775. page
  776. * self._num_rows
  777. * self._row_size : (page + 1)
  778. * self._num_rows
  779. * self._row_size
  780. ],
  781. 3,
  782. ):
  783. row = [
  784. {
  785. "text": btn,
  786. "callback": self.inline__configure,
  787. "args": (btn,),
  788. "kwargs": {"obj_type": obj_type},
  789. }
  790. for btn in mod_row
  791. ]
  792. kb += [row]
  793. if len(to_config) > self._num_rows * self._row_size:
  794. kb += self.inline.build_pagination(
  795. callback=functools.partial(
  796. self.inline__global_config, obj_type=obj_type
  797. ),
  798. total_pages=ceil(len(to_config) / (self._num_rows * self._row_size)),
  799. current_page=page + 1,
  800. )
  801. kb += [
  802. [
  803. {
  804. "text": self.strings("back_btn"),
  805. "callback": self.inline__choose_category,
  806. },
  807. {"text": self.strings("close_btn"), "action": "close"},
  808. ]
  809. ]
  810. await call.edit(
  811. self.strings(
  812. "configure" if isinstance(obj_type, bool) else "configure_lib"
  813. ),
  814. reply_markup=kb,
  815. )
  816. async def configcmd(self, message: Message):
  817. """Configure modules"""
  818. args = utils.get_args_raw(message)
  819. if self.lookup(args) and hasattr(self.lookup(args), "config"):
  820. form = await self.inline.form("🌘 <b>Loading configuration</b>", message)
  821. mod = self.lookup(args)
  822. if isinstance(mod, loader.Library):
  823. type_ = "library"
  824. else:
  825. type_ = getattr(mod, "__origin__", None) == "<core>"
  826. await self.inline__configure(form, args, obj_type=type_)
  827. return
  828. await self.inline__choose_category(message)
  829. async def fconfigcmd(self, message: Message):
  830. """<module_name> <property_name> <config_value> - Stands for ForceConfig - Set the config value if it is not possible using default method"""
  831. args = utils.get_args_raw(message).split(maxsplit=2)
  832. if len(args) < 3:
  833. await utils.answer(message, self.strings("args"))
  834. return
  835. mod, option, value = args
  836. instance = self.lookup(mod)
  837. if not instance:
  838. await utils.answer(message, self.strings("no_mod"))
  839. return
  840. if option not in instance.config:
  841. await utils.answer(message, self.strings("no_option"))
  842. return
  843. instance.config[option] = value
  844. await utils.answer(
  845. message,
  846. self.strings(
  847. "option_saved"
  848. if isinstance(instance, loader.Module)
  849. else "option_saved_lib"
  850. ).format(
  851. utils.escape_html(option),
  852. utils.escape_html(mod),
  853. self.prep_value(instance.config[option])
  854. if not instance.config._config[option].validator
  855. or instance.config._config[option].validator.internal_id != "Hidden"
  856. else self.hide_value(instance.config[option]),
  857. ),
  858. )