hikka_config.py 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  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. "_cls_doc": "Интерактивный конфигуратор Hikka",
  125. "args": "🚫 <b>Ты указал неверные аргументы</b>",
  126. "no_mod": "🚫 <b>Модуль не существует</b>",
  127. "no_option": "🚫 <b>У модуля нет такого значения конфига</b>",
  128. "validation_error": (
  129. "🚫 <b>Введено некорректное значение конфига. \nОшибка: {}</b>"
  130. ),
  131. "try_again": "🔁 Попробовать еще раз",
  132. "typehint": "🕵️ <b>Должно быть {}</b>",
  133. "set": "поставить",
  134. "set_default_btn": "♻️ Значение по умолчанию",
  135. "enter_value_btn": "✍️ Ввести значение",
  136. "enter_value_desc": "✍️ Введи новое значение этого параметра",
  137. "add_item_desc": "✍️ Введи элемент, который нужно добавить",
  138. "remove_item_desc": "✍️ Введи элемент, который нужно удалить",
  139. "back_btn": "👈 Назад",
  140. "close_btn": "🔻 Закрыть",
  141. "add_item_btn": "➕ Добавить элемент",
  142. "remove_item_btn": "➖ Удалить элемент",
  143. "show_hidden": "🚸 Показать значение",
  144. "hide_value": "🔒 Скрыть значение",
  145. "builtin": "🛰 Встроенные",
  146. "external": "🛸 Внешние",
  147. "libraries": "🪴 Библиотеки",
  148. }
  149. _row_size = 3
  150. _num_rows = 5
  151. @staticmethod
  152. def prep_value(value: Any) -> Any:
  153. if isinstance(value, str):
  154. return f"</b><code>{utils.escape_html(value.strip())}</code><b>"
  155. if isinstance(value, list) and value:
  156. return (
  157. "</b><code>[</code>\n "
  158. + "\n ".join(
  159. [f"<code>{utils.escape_html(str(item))}</code>" for item in value]
  160. )
  161. + "\n<code>]</code><b>"
  162. )
  163. return f"</b><code>{utils.escape_html(value)}</code><b>"
  164. def hide_value(self, value: Any) -> str:
  165. if isinstance(value, list) and value:
  166. return self.prep_value(["*" * len(str(i)) for i in value])
  167. return self.prep_value("*" * len(str(value)))
  168. async def inline__set_config(
  169. self,
  170. call: InlineCall,
  171. query: str,
  172. mod: str,
  173. option: str,
  174. inline_message_id: str,
  175. obj_type: Union[bool, str] = False,
  176. ):
  177. try:
  178. self.lookup(mod).config[option] = query
  179. except loader.validators.ValidationError as e:
  180. await call.edit(
  181. self.strings("validation_error").format(e.args[0]),
  182. reply_markup={
  183. "text": self.strings("try_again"),
  184. "callback": self.inline__configure_option,
  185. "args": (mod, option),
  186. "kwargs": {"obj_type": obj_type},
  187. },
  188. )
  189. return
  190. await call.edit(
  191. self.strings(
  192. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  193. ).format(
  194. utils.escape_html(option),
  195. utils.escape_html(mod),
  196. self.prep_value(self.lookup(mod).config[option])
  197. if not self.lookup(mod).config._config[option].validator
  198. or self.lookup(mod).config._config[option].validator.internal_id
  199. != "Hidden"
  200. else self.hide_value(self.lookup(mod).config[option]),
  201. ),
  202. reply_markup=[
  203. [
  204. {
  205. "text": self.strings("back_btn"),
  206. "callback": self.inline__configure,
  207. "args": (mod,),
  208. "kwargs": {"obj_type": obj_type},
  209. },
  210. {"text": self.strings("close_btn"), "action": "close"},
  211. ]
  212. ],
  213. inline_message_id=inline_message_id,
  214. )
  215. async def inline__reset_default(
  216. self,
  217. call: InlineCall,
  218. mod: str,
  219. option: str,
  220. obj_type: Union[bool, str] = False,
  221. ):
  222. mod_instance = self.lookup(mod)
  223. mod_instance.config[option] = mod_instance.config.getdef(option)
  224. await call.edit(
  225. self.strings(
  226. "option_reset" if isinstance(obj_type, bool) else "option_reset_lib"
  227. ).format(
  228. utils.escape_html(option),
  229. utils.escape_html(mod),
  230. self.prep_value(self.lookup(mod).config[option])
  231. if not self.lookup(mod).config._config[option].validator
  232. or self.lookup(mod).config._config[option].validator.internal_id
  233. != "Hidden"
  234. else self.hide_value(self.lookup(mod).config[option]),
  235. ),
  236. reply_markup=[
  237. [
  238. {
  239. "text": self.strings("back_btn"),
  240. "callback": self.inline__configure,
  241. "args": (mod,),
  242. "kwargs": {"obj_type": obj_type},
  243. },
  244. {"text": self.strings("close_btn"), "action": "close"},
  245. ]
  246. ],
  247. )
  248. async def inline__set_bool(
  249. self,
  250. call: InlineCall,
  251. mod: str,
  252. option: str,
  253. value: bool,
  254. obj_type: Union[bool, str] = False,
  255. ):
  256. try:
  257. self.lookup(mod).config[option] = value
  258. except loader.validators.ValidationError as e:
  259. await call.edit(
  260. self.strings("validation_error").format(e.args[0]),
  261. reply_markup={
  262. "text": self.strings("try_again"),
  263. "callback": self.inline__configure_option,
  264. "args": (mod, option),
  265. "kwargs": {"obj_type": obj_type},
  266. },
  267. )
  268. return
  269. validator = self.lookup(mod).config._config[option].validator
  270. doc = utils.escape_html(
  271. next(
  272. (
  273. validator.doc[lang]
  274. for lang in self._db.get(translations.__name__, "lang", "en").split(
  275. " "
  276. )
  277. if lang in validator.doc
  278. ),
  279. validator.doc["en"],
  280. )
  281. )
  282. await call.edit(
  283. self.strings(
  284. "configuring_option"
  285. if isinstance(obj_type, bool)
  286. else "configuring_option_lib"
  287. ).format(
  288. utils.escape_html(option),
  289. utils.escape_html(mod),
  290. utils.escape_html(self.lookup(mod).config.getdoc(option)),
  291. self.prep_value(self.lookup(mod).config.getdef(option)),
  292. self.prep_value(self.lookup(mod).config[option])
  293. if not validator or validator.internal_id != "Hidden"
  294. else self.hide_value(self.lookup(mod).config[option]),
  295. self.strings("typehint").format(
  296. doc,
  297. eng_art="n" if doc.lower().startswith(tuple("euioay")) else "",
  298. )
  299. if doc
  300. else "",
  301. ),
  302. reply_markup=self._generate_bool_markup(mod, option, obj_type),
  303. )
  304. await call.answer("✅")
  305. def _generate_bool_markup(
  306. self,
  307. mod: str,
  308. option: str,
  309. obj_type: Union[bool, str] = False,
  310. ) -> list:
  311. return [
  312. [
  313. *(
  314. [
  315. {
  316. "text": f"❌ {self.strings('set')} `False`",
  317. "callback": self.inline__set_bool,
  318. "args": (mod, option, False),
  319. "kwargs": {"obj_type": obj_type},
  320. }
  321. ]
  322. if self.lookup(mod).config[option]
  323. else [
  324. {
  325. "text": f"✅ {self.strings('set')} `True`",
  326. "callback": self.inline__set_bool,
  327. "args": (mod, option, True),
  328. "kwargs": {"obj_type": obj_type},
  329. }
  330. ]
  331. )
  332. ],
  333. [
  334. *(
  335. [
  336. {
  337. "text": self.strings("set_default_btn"),
  338. "callback": self.inline__reset_default,
  339. "args": (mod, option),
  340. "kwargs": {"obj_type": obj_type},
  341. }
  342. ]
  343. if self.lookup(mod).config[option]
  344. != self.lookup(mod).config.getdef(option)
  345. else []
  346. )
  347. ],
  348. [
  349. {
  350. "text": self.strings("back_btn"),
  351. "callback": self.inline__configure,
  352. "args": (mod,),
  353. "kwargs": {"obj_type": obj_type},
  354. },
  355. {"text": self.strings("close_btn"), "action": "close"},
  356. ],
  357. ]
  358. async def inline__add_item(
  359. self,
  360. call: InlineCall,
  361. query: str,
  362. mod: str,
  363. option: str,
  364. inline_message_id: str,
  365. obj_type: Union[bool, str] = False,
  366. ):
  367. try:
  368. try:
  369. query = ast.literal_eval(query)
  370. except Exception:
  371. pass
  372. if isinstance(query, (set, tuple)):
  373. query = list(query)
  374. if not isinstance(query, list):
  375. query = [query]
  376. self.lookup(mod).config[option] = self.lookup(mod).config[option] + query
  377. except loader.validators.ValidationError as e:
  378. await call.edit(
  379. self.strings("validation_error").format(e.args[0]),
  380. reply_markup={
  381. "text": self.strings("try_again"),
  382. "callback": self.inline__configure_option,
  383. "args": (mod, option),
  384. "kwargs": {"obj_type": obj_type},
  385. },
  386. )
  387. return
  388. await call.edit(
  389. self.strings(
  390. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  391. ).format(
  392. utils.escape_html(option),
  393. utils.escape_html(mod),
  394. self.prep_value(self.lookup(mod).config[option])
  395. if not self.lookup(mod).config._config[option].validator
  396. or self.lookup(mod).config._config[option].validator.internal_id
  397. != "Hidden"
  398. else self.hide_value(self.lookup(mod).config[option]),
  399. ),
  400. reply_markup=[
  401. [
  402. {
  403. "text": self.strings("back_btn"),
  404. "callback": self.inline__configure,
  405. "args": (mod,),
  406. "kwargs": {"obj_type": obj_type},
  407. },
  408. {"text": self.strings("close_btn"), "action": "close"},
  409. ]
  410. ],
  411. inline_message_id=inline_message_id,
  412. )
  413. async def inline__remove_item(
  414. self,
  415. call: InlineCall,
  416. query: str,
  417. mod: str,
  418. option: str,
  419. inline_message_id: str,
  420. obj_type: Union[bool, str] = False,
  421. ):
  422. try:
  423. try:
  424. query = ast.literal_eval(query)
  425. except Exception:
  426. pass
  427. if isinstance(query, (set, tuple)):
  428. query = list(query)
  429. if not isinstance(query, list):
  430. query = [query]
  431. query = list(map(str, query))
  432. old_config_len = len(self.lookup(mod).config[option])
  433. self.lookup(mod).config[option] = [
  434. i for i in self.lookup(mod).config[option] if str(i) not in query
  435. ]
  436. if old_config_len == len(self.lookup(mod).config[option]):
  437. raise loader.validators.ValidationError(
  438. f"Nothing from passed value ({self.prep_value(query)}) is not in"
  439. " target list"
  440. )
  441. except loader.validators.ValidationError as e:
  442. await call.edit(
  443. self.strings("validation_error").format(e.args[0]),
  444. reply_markup={
  445. "text": self.strings("try_again"),
  446. "callback": self.inline__configure_option,
  447. "args": (mod, option),
  448. "kwargs": {"obj_type": obj_type},
  449. },
  450. )
  451. return
  452. await call.edit(
  453. self.strings(
  454. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  455. ).format(
  456. utils.escape_html(option),
  457. utils.escape_html(mod),
  458. self.prep_value(self.lookup(mod).config[option])
  459. if not self.lookup(mod).config._config[option].validator
  460. or self.lookup(mod).config._config[option].validator.internal_id
  461. != "Hidden"
  462. else self.hide_value(self.lookup(mod).config[option]),
  463. ),
  464. reply_markup=[
  465. [
  466. {
  467. "text": self.strings("back_btn"),
  468. "callback": self.inline__configure,
  469. "args": (mod,),
  470. "kwargs": {"obj_type": obj_type},
  471. },
  472. {"text": self.strings("close_btn"), "action": "close"},
  473. ]
  474. ],
  475. inline_message_id=inline_message_id,
  476. )
  477. def _generate_series_markup(
  478. self,
  479. call: InlineCall,
  480. mod: str,
  481. option: str,
  482. obj_type: Union[bool, str] = False,
  483. ) -> list:
  484. return [
  485. [
  486. {
  487. "text": self.strings("enter_value_btn"),
  488. "input": self.strings("enter_value_desc"),
  489. "handler": self.inline__set_config,
  490. "args": (mod, option, call.inline_message_id),
  491. "kwargs": {"obj_type": obj_type},
  492. }
  493. ],
  494. [
  495. *(
  496. [
  497. {
  498. "text": self.strings("remove_item_btn"),
  499. "input": self.strings("remove_item_desc"),
  500. "handler": self.inline__remove_item,
  501. "args": (mod, option, call.inline_message_id),
  502. "kwargs": {"obj_type": obj_type},
  503. },
  504. {
  505. "text": self.strings("add_item_btn"),
  506. "input": self.strings("add_item_desc"),
  507. "handler": self.inline__add_item,
  508. "args": (mod, option, call.inline_message_id),
  509. "kwargs": {"obj_type": obj_type},
  510. },
  511. ]
  512. if self.lookup(mod).config[option]
  513. else []
  514. ),
  515. ],
  516. [
  517. *(
  518. [
  519. {
  520. "text": self.strings("set_default_btn"),
  521. "callback": self.inline__reset_default,
  522. "args": (mod, option),
  523. "kwargs": {"obj_type": obj_type},
  524. }
  525. ]
  526. if self.lookup(mod).config[option]
  527. != self.lookup(mod).config.getdef(option)
  528. else []
  529. )
  530. ],
  531. [
  532. {
  533. "text": self.strings("back_btn"),
  534. "callback": self.inline__configure,
  535. "args": (mod,),
  536. "kwargs": {"obj_type": obj_type},
  537. },
  538. {"text": self.strings("close_btn"), "action": "close"},
  539. ],
  540. ]
  541. async def _choice_set_value(
  542. self,
  543. call: InlineCall,
  544. mod: str,
  545. option: str,
  546. value: bool,
  547. obj_type: Union[bool, str] = False,
  548. ):
  549. try:
  550. self.lookup(mod).config[option] = value
  551. except loader.validators.ValidationError as e:
  552. await call.edit(
  553. self.strings("validation_error").format(e.args[0]),
  554. reply_markup={
  555. "text": self.strings("try_again"),
  556. "callback": self.inline__configure_option,
  557. "args": (mod, option),
  558. "kwargs": {"obj_type": obj_type},
  559. },
  560. )
  561. return
  562. validator = self.lookup(mod).config._config[option].validator
  563. doc = utils.escape_html(
  564. next(
  565. (
  566. validator.doc[lang]
  567. for lang in self._db.get(translations.__name__, "lang", "en").split(
  568. " "
  569. )
  570. if lang in validator.doc
  571. ),
  572. validator.doc["en"],
  573. )
  574. )
  575. await call.edit(
  576. self.strings(
  577. "option_saved" if isinstance(obj_type, bool) else "option_saved_lib"
  578. ).format(
  579. utils.escape_html(option),
  580. utils.escape_html(mod),
  581. self.prep_value(self.lookup(mod).config[option])
  582. if not validator.internal_id == "Hidden"
  583. else self.hide_value(self.lookup(mod).config[option]),
  584. ),
  585. reply_markup=[
  586. [
  587. {
  588. "text": self.strings("back_btn"),
  589. "callback": self.inline__configure,
  590. "args": (mod,),
  591. "kwargs": {"obj_type": obj_type},
  592. },
  593. {"text": self.strings("close_btn"), "action": "close"},
  594. ]
  595. ],
  596. )
  597. await call.answer("✅")
  598. def _generate_choice_markup(
  599. self,
  600. call: InlineCall,
  601. mod: str,
  602. option: str,
  603. obj_type: Union[bool, str] = False,
  604. ) -> list:
  605. possible_values = list(
  606. self.lookup(mod)
  607. .config._config[option]
  608. .validator.validate.keywords["possible_values"]
  609. )
  610. return [
  611. [
  612. {
  613. "text": self.strings("enter_value_btn"),
  614. "input": self.strings("enter_value_desc"),
  615. "handler": self.inline__set_config,
  616. "args": (mod, option, call.inline_message_id),
  617. "kwargs": {"obj_type": obj_type},
  618. }
  619. ],
  620. *utils.chunks(
  621. [
  622. {
  623. "text": (
  624. f"{'☑️' if self.lookup(mod).config[option] == value else '🔘'} "
  625. f"{value if len(str(value)) < 20 else str(value)[:20]}"
  626. ),
  627. "callback": self._choice_set_value,
  628. "args": (mod, option, value, obj_type),
  629. }
  630. for value in possible_values
  631. ],
  632. 2,
  633. )[:6],
  634. [
  635. *(
  636. [
  637. {
  638. "text": self.strings("set_default_btn"),
  639. "callback": self.inline__reset_default,
  640. "args": (mod, option),
  641. "kwargs": {"obj_type": obj_type},
  642. }
  643. ]
  644. if self.lookup(mod).config[option]
  645. != self.lookup(mod).config.getdef(option)
  646. else []
  647. )
  648. ],
  649. [
  650. {
  651. "text": self.strings("back_btn"),
  652. "callback": self.inline__configure,
  653. "args": (mod,),
  654. "kwargs": {"obj_type": obj_type},
  655. },
  656. {"text": self.strings("close_btn"), "action": "close"},
  657. ],
  658. ]
  659. async def inline__configure_option(
  660. self,
  661. call: InlineCall,
  662. mod: str,
  663. config_opt: str,
  664. force_hidden: Optional[bool] = False,
  665. obj_type: Union[bool, str] = False,
  666. ):
  667. module = self.lookup(mod)
  668. args = [
  669. utils.escape_html(config_opt),
  670. utils.escape_html(mod),
  671. utils.escape_html(module.config.getdoc(config_opt)),
  672. self.prep_value(module.config.getdef(config_opt)),
  673. self.prep_value(module.config[config_opt])
  674. if not module.config._config[config_opt].validator
  675. or module.config._config[config_opt].validator.internal_id != "Hidden"
  676. or force_hidden
  677. else self.hide_value(module.config[config_opt]),
  678. ]
  679. if (
  680. module.config._config[config_opt].validator
  681. and module.config._config[config_opt].validator.internal_id == "Hidden"
  682. ):
  683. additonal_button_row = (
  684. [
  685. [
  686. {
  687. "text": self.strings("hide_value"),
  688. "callback": self.inline__configure_option,
  689. "args": (mod, config_opt, False),
  690. "kwargs": {"obj_type": obj_type},
  691. }
  692. ]
  693. ]
  694. if force_hidden
  695. else [
  696. [
  697. {
  698. "text": self.strings("show_hidden"),
  699. "callback": self.inline__configure_option,
  700. "args": (mod, config_opt, True),
  701. "kwargs": {"obj_type": obj_type},
  702. }
  703. ]
  704. ]
  705. )
  706. else:
  707. additonal_button_row = []
  708. try:
  709. validator = module.config._config[config_opt].validator
  710. doc = utils.escape_html(
  711. next(
  712. (
  713. validator.doc[lang]
  714. for lang in self._db.get(
  715. translations.__name__, "lang", "en"
  716. ).split(" ")
  717. if lang in validator.doc
  718. ),
  719. validator.doc["en"],
  720. )
  721. )
  722. except Exception:
  723. doc = None
  724. validator = None
  725. args += [""]
  726. else:
  727. args += [
  728. self.strings("typehint").format(
  729. doc,
  730. eng_art="n" if doc.lower().startswith(tuple("euioay")) else "",
  731. )
  732. ]
  733. if validator.internal_id == "Boolean":
  734. await call.edit(
  735. self.strings(
  736. "configuring_option"
  737. if isinstance(obj_type, bool)
  738. else "configuring_option_lib"
  739. ).format(*args),
  740. reply_markup=additonal_button_row
  741. + self._generate_bool_markup(mod, config_opt, obj_type),
  742. )
  743. return
  744. if validator.internal_id == "Series":
  745. await call.edit(
  746. self.strings(
  747. "configuring_option"
  748. if isinstance(obj_type, bool)
  749. else "configuring_option_lib"
  750. ).format(*args),
  751. reply_markup=additonal_button_row
  752. + self._generate_series_markup(call, mod, config_opt, obj_type),
  753. )
  754. return
  755. if validator.internal_id == "Choice":
  756. await call.edit(
  757. self.strings(
  758. "configuring_option"
  759. if isinstance(obj_type, bool)
  760. else "configuring_option_lib"
  761. ).format(*args),
  762. reply_markup=additonal_button_row
  763. + self._generate_choice_markup(call, mod, config_opt, obj_type),
  764. )
  765. return
  766. await call.edit(
  767. self.strings(
  768. "configuring_option"
  769. if isinstance(obj_type, bool)
  770. else "configuring_option_lib"
  771. ).format(*args),
  772. reply_markup=additonal_button_row
  773. + [
  774. [
  775. {
  776. "text": self.strings("enter_value_btn"),
  777. "input": self.strings("enter_value_desc"),
  778. "handler": self.inline__set_config,
  779. "args": (mod, config_opt, call.inline_message_id),
  780. "kwargs": {"obj_type": obj_type},
  781. }
  782. ],
  783. [
  784. {
  785. "text": self.strings("set_default_btn"),
  786. "callback": self.inline__reset_default,
  787. "args": (mod, config_opt),
  788. "kwargs": {"obj_type": obj_type},
  789. }
  790. ],
  791. [
  792. {
  793. "text": self.strings("back_btn"),
  794. "callback": self.inline__configure,
  795. "args": (mod,),
  796. "kwargs": {"obj_type": obj_type},
  797. },
  798. {"text": self.strings("close_btn"), "action": "close"},
  799. ],
  800. ],
  801. )
  802. async def inline__configure(
  803. self,
  804. call: InlineCall,
  805. mod: str,
  806. obj_type: Union[bool, str] = False,
  807. ):
  808. btns = [
  809. {
  810. "text": param,
  811. "callback": self.inline__configure_option,
  812. "args": (mod, param),
  813. "kwargs": {"obj_type": obj_type},
  814. }
  815. for param in self.lookup(mod).config
  816. ]
  817. await call.edit(
  818. self.strings(
  819. "configuring_mod" if isinstance(obj_type, bool) else "configuring_lib"
  820. ).format(
  821. utils.escape_html(mod),
  822. "\n".join(
  823. [
  824. f"▫️ <code>{utils.escape_html(key)}</code>:"
  825. 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>"
  826. for key, value in self.lookup(mod).config.items()
  827. ]
  828. ),
  829. ),
  830. reply_markup=list(utils.chunks(btns, 2))
  831. + [
  832. [
  833. {
  834. "text": self.strings("back_btn"),
  835. "callback": self.inline__global_config,
  836. "kwargs": {"obj_type": obj_type},
  837. },
  838. {"text": self.strings("close_btn"), "action": "close"},
  839. ]
  840. ],
  841. )
  842. async def inline__choose_category(self, call: Union[Message, InlineCall]):
  843. await utils.answer(
  844. call,
  845. self.strings("choose_core"),
  846. reply_markup=[
  847. [
  848. {
  849. "text": self.strings("builtin"),
  850. "callback": self.inline__global_config,
  851. "kwargs": {"obj_type": True},
  852. },
  853. {
  854. "text": self.strings("external"),
  855. "callback": self.inline__global_config,
  856. },
  857. ],
  858. *(
  859. [
  860. [
  861. {
  862. "text": self.strings("libraries"),
  863. "callback": self.inline__global_config,
  864. "kwargs": {"obj_type": "library"},
  865. }
  866. ]
  867. ]
  868. if self.allmodules.libraries
  869. and any(hasattr(lib, "config") for lib in self.allmodules.libraries)
  870. else []
  871. ),
  872. [{"text": self.strings("close_btn"), "action": "close"}],
  873. ],
  874. )
  875. async def inline__global_config(
  876. self,
  877. call: InlineCall,
  878. page: int = 0,
  879. obj_type: Union[bool, str] = False,
  880. ):
  881. if isinstance(obj_type, bool):
  882. to_config = [
  883. mod.strings("name")
  884. for mod in self.allmodules.modules
  885. if hasattr(mod, "config")
  886. and callable(mod.strings)
  887. and (getattr(mod, "__origin__", None) == "<core>" or not obj_type)
  888. and (getattr(mod, "__origin__", None) != "<core>" or obj_type)
  889. ]
  890. else:
  891. to_config = [
  892. lib.name for lib in self.allmodules.libraries if hasattr(lib, "config")
  893. ]
  894. to_config.sort()
  895. kb = []
  896. for mod_row in utils.chunks(
  897. to_config[
  898. page
  899. * self._num_rows
  900. * self._row_size : (page + 1)
  901. * self._num_rows
  902. * self._row_size
  903. ],
  904. 3,
  905. ):
  906. row = [
  907. {
  908. "text": btn,
  909. "callback": self.inline__configure,
  910. "args": (btn,),
  911. "kwargs": {"obj_type": obj_type},
  912. }
  913. for btn in mod_row
  914. ]
  915. kb += [row]
  916. if len(to_config) > self._num_rows * self._row_size:
  917. kb += self.inline.build_pagination(
  918. callback=functools.partial(
  919. self.inline__global_config, obj_type=obj_type
  920. ),
  921. total_pages=ceil(len(to_config) / (self._num_rows * self._row_size)),
  922. current_page=page + 1,
  923. )
  924. kb += [
  925. [
  926. {
  927. "text": self.strings("back_btn"),
  928. "callback": self.inline__choose_category,
  929. },
  930. {"text": self.strings("close_btn"), "action": "close"},
  931. ]
  932. ]
  933. await call.edit(
  934. self.strings(
  935. "configure" if isinstance(obj_type, bool) else "configure_lib"
  936. ),
  937. reply_markup=kb,
  938. )
  939. @loader.command(ru_doc="Настроить модули")
  940. async def configcmd(self, message: Message):
  941. """Configure modules"""
  942. args = utils.get_args_raw(message)
  943. if self.lookup(args) and hasattr(self.lookup(args), "config"):
  944. form = await self.inline.form("🌘 <b>Loading configuration</b>", message)
  945. mod = self.lookup(args)
  946. if isinstance(mod, loader.Library):
  947. type_ = "library"
  948. else:
  949. type_ = getattr(mod, "__origin__", None) == "<core>"
  950. await self.inline__configure(form, args, obj_type=type_)
  951. return
  952. await self.inline__choose_category(message)
  953. @loader.command(
  954. ru_doc=(
  955. "<модуль> <настройка> <значениеЮ - установить значение конфига для модуля"
  956. )
  957. )
  958. async def fconfig(self, message: Message):
  959. """<module_name> <property_name> <config_value> - set the config value for the module"""
  960. args = utils.get_args_raw(message).split(maxsplit=2)
  961. if len(args) < 3:
  962. await utils.answer(message, self.strings("args"))
  963. return
  964. mod, option, value = args
  965. instance = self.lookup(mod)
  966. if not instance:
  967. await utils.answer(message, self.strings("no_mod"))
  968. return
  969. if option not in instance.config:
  970. await utils.answer(message, self.strings("no_option"))
  971. return
  972. instance.config[option] = value
  973. await utils.answer(
  974. message,
  975. self.strings(
  976. "option_saved"
  977. if isinstance(instance, loader.Module)
  978. else "option_saved_lib"
  979. ).format(
  980. utils.escape_html(option),
  981. utils.escape_html(mod),
  982. self.prep_value(instance.config[option])
  983. if not instance.config._config[option].validator
  984. or instance.config._config[option].validator.internal_id != "Hidden"
  985. else self.hide_value(instance.config[option]),
  986. ),
  987. )