Heroku/hikka/modules/help.py

387 lines
12 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# ©️ Dan Gazizullin, 2021-2023
# This file is a part of Hikka Userbot
# 🌐 https://github.com/hikariatama/Hikka
# You can redistribute it and/or modify it under the terms of the GNU AGPLv3
# 🔑 https://www.gnu.org/licenses/agpl-3.0.html
import difflib
import inspect
import logging
from hikkatl.extensions.html import CUSTOM_EMOJIS
from hikkatl.tl.types import Message
from .. import loader, utils
from ..compat.dragon import DRAGON_EMOJI
from ..types import DragonModule
logger = logging.getLogger(__name__)
@loader.tds
class HelpMod(loader.Module):
"""Shows help for modules and commands"""
strings = {"name": "Help"}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"core_emoji",
"▪️",
lambda: "Core module bullet",
validator=loader.validators.Emoji(length=1),
),
loader.ConfigValue(
"plain_emoji",
"▫️",
lambda: "Plain module bullet",
validator=loader.validators.Emoji(length=1),
),
loader.ConfigValue(
"empty_emoji",
"🙈",
lambda: "Empty modules bullet",
validator=loader.validators.Emoji(length=1),
),
)
@loader.command()
async def helphide(self, message: Message):
if not (modules := utils.get_args(message)):
await utils.answer(message, self.strings("no_mod"))
return
currently_hidden = self.get("hide", [])
hidden, shown = [], []
for module in filter(
lambda module: self.lookup(module, include_dragon=True), modules
):
module = self.lookup(module, include_dragon=True)
module = (
module.name
if isinstance(module, DragonModule)
else module.__class__.__name__
)
if module in currently_hidden:
currently_hidden.remove(module)
shown += [module]
else:
currently_hidden += [module]
hidden += [module]
self.set("hide", currently_hidden)
await utils.answer(
message,
self.strings("hidden_shown").format(
len(hidden),
len(shown),
"\n".join([f"👁‍🗨 <i>{m}</i>" for m in hidden]),
"\n".join([f"👁 <i>{m}</i>" for m in shown]),
),
)
def find_aliases(self, command: str) -> list:
"""Find aliases for command"""
aliases = []
_command = self.allmodules.commands[command]
if getattr(_command, "alias", None) and not (
aliases := getattr(_command, "aliases", None)
):
aliases = [_command.alias]
return aliases or []
async def modhelp(self, message: Message, args: str):
exact = True
if not (module := self.lookup(args, include_dragon=True)):
if method := self.allmodules.dispatch(
args.lower().strip(self.get_prefix())
)[1]:
module = method.__self__
else:
module = self.lookup(
next(
(
reversed(
sorted(
[
module.strings["name"]
for module in self.allmodules.modules
],
key=lambda x: difflib.SequenceMatcher(
None,
args.lower(),
x,
).ratio(),
)
)
),
None,
)
)
exact = False
is_dragon = isinstance(module, DragonModule)
try:
name = module.strings("name")
except (KeyError, AttributeError):
name = getattr(module, "name", "ERROR")
_name = (
"{} (v{}.{}.{})".format(
utils.escape_html(name),
module.__version__[0],
module.__version__[1],
module.__version__[2],
)
if hasattr(module, "__version__")
else utils.escape_html(name)
)
reply = "{} <b>{}</b>:".format(
(
DRAGON_EMOJI
if is_dragon
else "<emoji document_id=5188377234380954537>🌘</emoji>"
),
_name,
)
if module.__doc__:
reply += (
"<i>\n<emoji document_id=5787544344906959608></emoji> "
+ utils.escape_html(inspect.getdoc(module))
+ "\n</i>"
)
commands = (
module.commands
if is_dragon
else {
name: func
for name, func in module.commands.items()
if await self.allmodules.check_security(message, func)
}
)
if hasattr(module, "inline_handlers") and not is_dragon:
for name, fun in module.inline_handlers.items():
reply += (
"\n<emoji document_id=5372981976804366741>🤖</emoji>"
" <code>{}</code> {}".format(
f"@{self.inline.bot_username} {name}",
(
utils.escape_html(inspect.getdoc(fun))
if fun.__doc__
else self.strings("undoc")
),
)
)
for name, fun in commands.items():
reply += (
"\n<emoji document_id=4971987363145188045>▫️</emoji>"
" <code>{}{}</code>{} {}".format(
utils.escape_html(self.get_prefix("dragon" if is_dragon else None)),
name,
(
" ({})".format(
", ".join(
"<code>{}{}</code>".format(
utils.escape_html(
self.get_prefix("dragon" if is_dragon else None)
),
alias,
)
for alias in self.find_aliases(name)
)
)
if self.find_aliases(name)
else ""
),
(
utils.escape_html(fun)
if is_dragon
else (
utils.escape_html(inspect.getdoc(fun))
if fun.__doc__
else self.strings("undoc")
)
),
)
)
await utils.answer(
message,
reply
+ (f"\n\n{self.strings('not_exact')}" if not exact else "")
+ (
f"\n\n{self.strings('core_notice')}"
if module.__origin__.startswith("<core")
else ""
),
)
@loader.command()
async def help(self, message: Message):
args = utils.get_args_raw(message)
force = False
if "-f" in args:
args = args.replace(" -f", "").replace("-f", "")
force = True
if args:
await self.modhelp(message, args)
return
hidden = self.get("hide", [])
reply = self.strings("all_header").format(
len(self.allmodules.modules) + len(self.allmodules.dragon_modules),
(
0
if force
else sum(
module.__class__.__name__ in hidden
for module in self.allmodules.modules
)
+ sum(
module.name in hidden for module in self.allmodules.dragon_modules
)
),
)
shown_warn = False
plain_ = []
core_ = []
no_commands_ = []
dragon_ = []
for mod in self.allmodules.dragon_modules:
if mod.name in self.get("hide", []) and not force:
continue
tmp = "\n{} <code>{}</code>".format(DRAGON_EMOJI, mod.name)
first = True
for cmd in mod.commands:
cmd = cmd.split()[0]
if first:
tmp += f": ( {cmd}"
first = False
else:
tmp += f" | {cmd}"
dragon_ += [tmp + " )"]
for mod in self.allmodules.modules:
if not hasattr(mod, "commands"):
logger.debug("Module %s is not inited yet", mod.__class__.__name__)
continue
if mod.__class__.__name__ in self.get("hide", []) and not force:
continue
tmp = ""
try:
name = mod.strings["name"]
except KeyError:
name = getattr(mod, "name", "ERROR")
if (
not getattr(mod, "commands", None)
and not getattr(mod, "inline_handlers", None)
and not getattr(mod, "callback_handlers", None)
):
no_commands_ += [
"\n{} <code>{}</code>".format(self.config["empty_emoji"], name)
]
continue
core = mod.__origin__.startswith("<core")
tmp += "\n{} <code>{}</code>".format(
self.config["core_emoji"] if core else self.config["plain_emoji"], name
)
first = True
commands = [
name
for name, func in mod.commands.items()
if await self.allmodules.check_security(message, func) or force
]
for cmd in commands:
if first:
tmp += f": ( {cmd}"
first = False
else:
tmp += f" | {cmd}"
icommands = [
name
for name, func in mod.inline_handlers.items()
if await self.inline.check_inline_security(
func=func,
user=message.sender_id,
)
or force
]
for cmd in icommands:
if first:
tmp += f": ( 🤖 {cmd}"
first = False
else:
tmp += f" | 🤖 {cmd}"
if commands or icommands:
tmp += " )"
if core:
core_ += [tmp]
else:
plain_ += [tmp]
elif not shown_warn and (mod.commands or mod.inline_handlers):
reply = (
"<i>You have permissions to execute only these"
f" commands</i>\n{reply}"
)
shown_warn = True
plain_.sort(key=lambda x: x.split()[1])
core_.sort(key=lambda x: x.split()[1])
no_commands_.sort(key=lambda x: x.split()[1])
dragon_.sort()
await utils.answer(
message,
"{}\n{}{}".format(
reply,
"".join(core_ + plain_ + dragon_ + (no_commands_ if force else [])),
(
""
if self.lookup("Loader").fully_loaded
else f"\n\n{self.strings('partial_load')}"
),
),
)
@loader.command()
async def support(self, message):
if message.out:
await self.request_join("@hikka_talks", self.strings("request_join"))
await utils.answer(
message,
self.strings("support").format(
(
utils.get_platform_emoji()
if self._client.hikka_me.premium and CUSTOM_EMOJIS
else "🌘"
)
),
)