コード例 #1
0
ファイル: setting_env.py プロジェクト: AmanoTeam/UserLixo
async def on_edit(c: Client, cq: CallbackQuery):
    lang = cq._lang
    key = cq.matches[0]["key"]
    value = (await Config.get_or_none(key=key)).value

    text = lang.edit_env_text(key=key, value=value)
    keyboard = ikb([[(lang.back, "setting_env")]])
    last_msg = await cq.edit(text, keyboard)

    env_requires_restart = ["PREFIXES", "DATABASE_URL", "BOT_TOKEN"]
    try:
        while True:
            msg = await cq.from_user.listen(filters.text, None)
            await last_msg.remove_keyboard()
            await Config.get(key=key).update(value=msg.text)
            if key in env_requires_restart:
                text = lang.edit_env_text_restart(key=key, value=msg.text)
                keyboard = ikb([[(lang.restart_now, "restart_now")],
                                [(lang.back, "setting_env")]])
            else:
                text = lang.edit_env_text(key=key, value=msg.text)
                keyboard = ikb([[(lang.back, "setting_env")]])
            last_msg = await msg.reply_text(text, reply_markup=keyboard)
    except errors.ListenerCanceled:
        pass
コード例 #2
0
async def on_about_userlixo(c: Client, cq: CallbackQuery):
    lang = cq._lang
    subject = cq.matches[0]["subject"]
    keyboard = ikb([[(lang.back, "help")]])
    text = {
        "userlixo": lang.about_userlixo_text,
        "plugins": lang.about_plugins_text,
        "commands": lang.about_commands_text,
    }
    text = text[subject]
    if subject == "commands":
        commands = [*cmds.keys()]
        total = len(commands)
        prefixes = os.getenv("PREFIXES")
        examples = []
        for n, p in enumerate([*prefixes]):
            if n > total - 1:  # if passed the end
                break
            examples.append("<code>" + p + commands[n] + "</code>")
        examples = ", ".join(examples)
        commands_list = [*map(lambda x: f"<code>{x}</code>", commands)]

        text.escape_html = False
        text = text(
            total=total,
            commands=", ".join(commands_list[:-1]),
            last_command=commands_list[-1],
            prefixes=prefixes,
            examples=examples,
        )
    await cq.edit(text, keyboard, disable_web_page_preview=True)
コード例 #3
0
ファイル: index.py プロジェクト: AmanoTeam/UserLixo
async def on_index(c: Client, iq: InlineQuery):
    index = int(iq.matches[0]["index"])
    message = await Message.get_or_none(key=index)
    if not message:
        results = [
            InlineQueryResultArticle(
                title="undefined index",
                input_message_content=InputTextMessageContent(
                    f"Undefined index {index}"
                ),
            )
        ]
        return await iq.answer(results, cache_time=0)

    keyboard = ikb(message.keyboard)
    text = message.text

    results = [
        InlineQueryResultArticle(
            title="index",
            input_message_content=InputTextMessageContent(
                text, disable_web_page_preview=True
            ),
            reply_markup=keyboard,
        )
    ]

    await iq.answer(results, cache_time=0)
    await (await Message.get(key=message.key)).delete()
コード例 #4
0
ファイル: info_plugin.py プロジェクト: AmanoTeam/UserLixo
async def on_info_plugin(c: Client, cq: CallbackQuery):
    lang = cq._lang
    basename = cq.matches[0]["basename"]
    plugin_type = cq.matches[0]["plugin_type"]
    pg = int(cq.matches[0]["pg"])

    if basename not in plugins[plugin_type]:
        return await cq.answer("UNKNOWN")

    plugin = plugins[plugin_type][basename]

    status = lang.active
    first_btn = (lang.deactivate,
                 f"deactivate_plugin {basename} {plugin_type} {pg}")

    inactive = await get_inactive_plugins(plugins)

    if plugin["notation"] in inactive:
        status = lang.inactive
        first_btn = (lang.activate,
                     f"activate_plugin {basename} {plugin_type} {pg}")
    status_line = "\n" + status

    lines = [[
        first_btn,
        (lang.remove, f"remove_plugin {basename} {plugin_type} {pg}")
    ]]
    if "settings" in plugin and plugin["settings"]:
        lines.append([(lang.settings,
                       f"plugin_settings {basename} {plugin_type} {pg}")])
    lines.append([(lang.back, f"{plugin_type}_plugins {pg}")])
    keyb = ikb(lines)

    text = write_plugin_info(plugins, lang, plugin, status_line=status_line)
    await cq.edit(text, keyb, disable_web_page_preview=True)
コード例 #5
0
ファイル: patches.py プロジェクト: AmanoTeam/UserLixo
async def edit_text(self, text: str, reply_markup=None, *args, **kwargs):
    if type(reply_markup) == list:
        reply_markup = ikb(reply_markup)
    return await self._client.edit_message_text(self.chat.id,
                                                self.id,
                                                text,
                                                reply_markup=reply_markup,
                                                **kwargs)
コード例 #6
0
async def on_help_u(c: Client, u: Union[Message, CallbackQuery]):
    is_query = hasattr(u, "data")
    lang = u._lang
    keyb = [
        [(lang.about_userlixo, "about_userlixo")],
        [(lang.commands, "about_commands"), (lang.plugins, "about_plugins")],
        [
            (lang.chat, "https://t.me/UserLixoChat", "url"),
            (lang.channel, "https://t.me/UserLixo", "url"),
        ],
    ]
    keyb = ikb(keyb)
    await (u.edit if is_query else u.reply)(lang.help_text, keyb)
コード例 #7
0
ファイル: start.py プロジェクト: AmanoTeam/UserLixo
async def on_start_u(c: Client, u: Union[Message, CallbackQuery]):
    is_query = hasattr(u, "data")
    lang = u._lang
    keyb = ikb([
        [(lang.upgrade, "upgrade"), [lang.restart, "restart"]],
        [(lang.commands, "list_commands 0"), (lang.plugins, "list_plugins")],
        [(lang.help, "help"), (lang.settings, "settings")],
    ])
    text = lang.start_text
    kwargs = {}
    if not is_query:
        kwargs["quote"] = True
    await (u.edit if is_query else u.reply)(text, keyb, **kwargs)
コード例 #8
0
async def on_setting_language(c: Client, cq: CallbackQuery):
    lang = cq._lang
    buttons = []
    for code, obj in lang.strings.items():
        text, data = (
            (f"✅ {obj['NAME']}", "noop")
            if obj["LANGUAGE_CODE"] == lang.code
            else (obj["NAME"], f"set_language {obj['LANGUAGE_CODE']}")
        )
        buttons.append((text, data))
    lines = array_chunk(buttons, 2)
    lines.append([(lang.back, "settings")])
    keyboard = ikb(lines)
    await cq.edit(lang.choose_language, keyboard)
コード例 #9
0
ファイル: setting_env.py プロジェクト: AmanoTeam/UserLixo
async def on_setting_env(c: Client, cq: CallbackQuery):
    if cq.message:
        cq.message.chat.cancel_listener()
    lang = cq._lang
    buttons = []
    async for row in Config.all():
        btn = (f"👁‍🗨 {row.key}", f"view_env {row.key}")
        if cq.message and cq.message.from_user.id == bot.me.id:
            btn = (f"📝 {row.key}", f"edit_env {row.key}")
        buttons.append(btn)
    lines = array_chunk(buttons, 2)
    lines.append([(lang.back, "settings")])
    keyboard = ikb(lines)
    await cq.edit(lang.settings_env_text, keyboard)
コード例 #10
0
ファイル: settings.py プロジェクト: AmanoTeam/UserLixo
async def on_settings_u(c: Client, u: Union[Message, CallbackQuery]):
    lang = u._lang
    is_query = hasattr(u, "data")

    lines = [
        [(lang.language, "setting_language")],
        [(lang.sudoers, "setting_sudoers")],
        [(lang.env_vars, "setting_env")],
    ]
    if is_query:
        lines.append([(lang.back, "start")])
    keyb = ikb(lines)

    await (u.edit if is_query else u.reply)(lang.settings_text, keyb)
コード例 #11
0
async def on_info_command(c: Client, cq: CallbackQuery):
    lang = cq._lang
    cmd = cq.matches[0]["cmd"]
    pg = int(cq.matches[0]["pg"])
    if cmd not in cmds:
        return await cq.answer(lang.unknown_command)

    info = lang.strings[lang.code].get(f"cmd_info_{cmd}", cmds[cmd])

    if len(info) < 100:
        return await cq.answer("ℹ️ " + info, show_alert=True)

    kb = ikb([[(lang.back, f"list_commands {pg}")]])
    text = lang.command_info
    text.escape_html = False
    await cq.edit(text(command=cmd, info=info), kb)
コード例 #12
0
async def on_set_language(c: Client, cq: CallbackQuery):
    lang = cq._lang
    match = cq.matches[0]
    lang = lang.get_language(match["code"])
    await Config.get(key="LANGUAGE").update(value=lang.code)
    os.environ["LANGUAGE"] = lang.code
    buttons = []
    for code, obj in lang.strings.items():
        text, data = (
            (f"✅ {obj['NAME']}", "noop")
            if obj["LANGUAGE_CODE"] == lang.code
            else (obj["NAME"], f"set_language {obj['LANGUAGE_CODE']}")
        )
        buttons.append((text, data))
    lines = array_chunk(buttons, 2)
    lines.append([(lang.back, "settings")])
    keyboard = ikb(lines)
    await cq.edit(lang.choose_language, keyboard, {"text": lang.language_chosen})
コード例 #13
0
async def on_add_plugin_u(c: Client, u: Union[Message, CallbackQuery]):
    is_query = hasattr(u, "data")
    if is_query:
        await u.message.delete()
    lang = u._lang

    loop_count = 0
    while True:
        loop_count += 1
        if not is_query and u.document:
            msg = u
            if loop_count > 1:
                break  # avoid infinite loop
        elif not is_query and u.reply_to_message and u.reply_to_message.document:
            msg = u.reply_to_message
            if loop_count > 1:
                break  # avoid infinite loop
        else:
            msg = await u.from_user.ask(lang.plugin_file_ask)
        if await filters.regex("/cancel")(c, msg):
            return await msg.reply(lang.command_cancelled)
        if not msg.document:
            await msg.reply(lang.plugin_waiting_file, quote=True)
            continue
        if not re.search("(py)$", msg.document.file_name):
            await msg.reply(lang.plugin_format_not_accepted, quote=True)
            continue
        if msg.document.file_size > (5 * 1024 * 1024):
            await msg.reply(lang.plugin_too_big, quote=True)
            continue
        break
    filename = await msg.download("cache/")
    filename = os.path.relpath(filename)
    plugin = read_plugin_info(filename)

    # Showing info
    text = write_plugin_info(plugins, lang, plugin, status_line="")
    lines = [[
        (lang.add, f"confirm_add_plugin {plugin['type']} {filename}"),
        (lang.cancel, "cancel_plugin"),
    ]]
    keyb = ikb(lines)
    await msg.reply(text, keyb, disable_web_page_preview=True, quote=True)
コード例 #14
0
async def on_list_commands_u(c: Client, u: Union[Message, CallbackQuery]):
    lang = u._lang
    is_query = hasattr(u, "data")
    page = int(u.matches[0]["page"])

    item_format = "info_command {} {}"
    page_format = "list_commands {}"

    layout = Pagination(
        [*cmds.items()],
        item_data=lambda i, pg: item_format.format(i[0], pg),
        item_title=lambda i, pg: i[0],
        page_data=lambda pg: page_format.format(pg),
    )

    lines = layout.create(page, columns=2, lines=3)
    if is_query:
        lines.append([(lang.back, "start")])

    keyb = ikb(lines)
    await (u.edit if is_query else u.reply)(lang.commands_text, keyb)
コード例 #15
0
async def sudoers_interface(cq: CallbackQuery):
    lang = cq._lang
    c = cq._client
    text = lang.setting_sudoers_text + "\n"
    buttons = []
    added = []
    for user_id in sudoers:
        try:
            user_obj = await c.get_users(user_id)
        except BaseException:
            import traceback

            traceback.print_exc()
            user_obj = None
        id = user_obj.id if user_obj else user_id
        if id in added:
            continue
        added.append(id)

        mention = user_id
        if user_obj:
            mention = (f"@{user_obj.username}"
                       if user_obj.username else user_obj.first_name)
        text += f"\n👤 {mention}"

        if id not in ["me", user.me.id, cq.from_user.id]:
            buttons.append((f"🗑 {mention}", f"remove_sudoer {user_id}"))

    lines = array_chunk(buttons, 2)
    if bot.me.username:
        lines.append([(
            lang.add_sudoer,
            f"https://t.me/{bot.me.username}?start=add_sudoer",
            "url",
        )])
    lines.append([(lang.back, "settings")])
    keyboard = ikb(lines)
    return text, keyboard
コード例 #16
0
async def main():
    await connect_database()
    await load_env()
    os.system("clear")

    @aiocron.crontab("*/1 * * * *")
    async def clean_cache():
        for file in glob.glob("cache/*"):
            if not os.path.isfile(file):
                continue
            creation_time = datetime.fromtimestamp(os.path.getctime(file))
            now_time = datetime.now()
            diff = now_time - creation_time
            minutes_passed = diff.total_seconds() / 60

            if minutes_passed >= 10:
                os.remove(file)

    if not os.path.exists("user.session"):
        from userlixo.login import main as login

        await login()
        os.system("clear")

    await user.start()
    await bot.start()
    await unload_inactive_plugins()

    user.me = await user.get_me()
    bot.me = await bot.get_me()
    user.assistant = bot

    if user.me.id not in sudoers:
        sudoers.append(user.me.id)

    # Editing restaring alert
    restarting_alert = await Config.filter(key="restarting_alert")
    if len(restarting_alert) > 1:
        await Config.filter(key="restarting_alert").delete()
        restarting_alert = []

    if restarting_alert:
        restarting_alert = restarting_alert[0]

        parts = restarting_alert.value.split("|")
        message_id, chat_id, cmd_timestamp, from_cmd = parts

        cmd_timestamp = float(cmd_timestamp)
        now_timestamp = datetime.now().timestamp()
        diff = round(now_timestamp - cmd_timestamp, 2)

        title, p = await shell_exec('git log --format="%B" -1')
        rev, p = await shell_exec("git rev-parse --short HEAD")
        date, p = await shell_exec(
            'git log -1 --format=%cd --date=format:"%d/%m %H:%M"')
        timezone, p = await shell_exec(
            'git log -1 --format=%cd --date=format:"%z"')
        local_version = int((await shell_exec("git rev-list --count HEAD"))[0])

        timezone = timezone_shortener(timezone)
        date += f" ({timezone})"

        kwargs = {}
        text = (langs.upgraded_alert
                if from_cmd.startswith("upgrade") else langs.restarted_alert)

        text = text(rev=rev,
                    date=date,
                    seconds=diff,
                    local_version=local_version)

        try:
            editor = bot if from_cmd.endswith("_bot") else user
            if editor == bot:
                keyb = ikb([[(langs.back, "start")]])
                kwargs.update(reply_markup=keyb)
            if chat_id == "inline":
                await bot.edit_inline_text(message_id, text, **kwargs)
            else:
                await editor.edit_message_text(tryint(chat_id),
                                               tryint(message_id), text,
                                               **kwargs)
        except BaseException as e:
            print(
                f"[yellow]Failed to edit the restarting alert. Maybe the message has been deleted or somehow it became inacessible.\n>> {e}[/yellow]"
            )
        try:
            await (await Config.get(id=restarting_alert.id)).delete()
        except OperationalError:
            pass

    # Showing alert in cli
    date, p = await shell_exec(
        'git log -1 --format=%cd --date=format:"%d/%m %H:%M"')
    timezone, p = await shell_exec('git log -1 --format=%cd --date=format:"%z"'
                                   )
    local_version = int((await shell_exec("git rev-list --count HEAD"))[0])

    timezone = timezone_shortener(timezone)
    date += f" ({timezone})"
    mention = "@" + user.me.username if user.me.username else user.me.id
    text = ":ok: [bold green]UserLixo is running[/bold green] :ok:"

    userlixo_info = {
        "Version": local_version,
        "Account": mention,
        "Bot": "@" + bot.me.username,
        "Prefixes": os.getenv("PREFIXES"),
        "Logs_chat": os.getenv("LOGS_CHAT"),
        "Sudoers": ", ".join([*set(map(str, sudoers))
                              ]),  # using set() to make the values unique
        "Commit_date": date,
    }
    for k, v in userlixo_info.items():
        text += f"\n[dim cyan]{k}:[/dim cyan] [dim]{v}[/dim]"

    print(Panel.fit(text, border_style="green", box=box.ASCII))

    # Sending alert via Telegram
    try:
        await alert_startup()
    except Exception as e:
        print(
            f"[bold yellow]Error while sending startup alert to LOGS_CHAT: {e}"
        )

    # Alert about unused requirements
    if unused_requirements:
        unused = ", ".join(unused_requirements)
        print(
            f"[yellow]The following packages are not required by plugins anymore: [/][cyan]{unused}"
        )
        try:
            await user.send_message(
                os.getenv("LOGS_CHAT"),
                f"The following packages are not required by plugins anymore: {unused}",
            )
        except Exception as e:
            print("Error while sending alert about unused_requirements:\n  > ",
                  e)
    await idle()
    await user.stop()
    await bot.stop()
コード例 #17
0
async def on_confirm_plugin(c: Client, cq: CallbackQuery):
    lang = cq._lang
    module = None

    plugin_type = cq.matches[0]["plugin_type"]
    client = (user, bot)[plugin_type == "bot"]

    cache_filename = cq.matches[0]["filename"]
    basename = os.path.basename(cache_filename)
    new_filename = "userlixo/handlers/" + plugin_type + "/plugins/" + basename

    plugin = read_plugin_info(cache_filename)
    new_notation = re.sub("\.py$", "",
                          os.path.relpath(new_filename)).replace("/", ".")

    requirements = plugin.get("requirements")
    if requirements:
        DGRAY = 'echo -e "\033[1;30m"'
        RESET = 'echo -e "\033[0m"'
        req_list = requirements.split()
        req_text = "".join(f" ├ <code>{r}</code>\n" for r in req_list[:-1])
        req_text += f" └ <code>{req_list[-1]}</code>"
        text = lang.installing_plugin_requirements
        text.escape_html = False
        await cq.edit(text(requirements=req_text))
        os.system(
            f"{DGRAY}; {sys.executable} -m pip install {requirements}; {RESET}"
        )

    # Safely unload the plugin if existent
    if os.path.exists(new_filename):
        try:
            module = importlib.import_module(new_notation)
        except Exception as e:
            return await cq.edit(
                lang.plugin_could_not_load_existent(name=basename, e=e))

        functions = [*filter(callable, module.__dict__.values())]
        functions = [*filter(lambda f: hasattr(f, "handlers"), functions)]

        for f in functions:
            for handler in f.handlers:
                client.remove_handler(*handler)

    os.renames(cache_filename, new_filename)
    plugin = read_plugin_info(new_filename)

    try:
        if module:
            importlib.reload(module)
        module = importlib.import_module(plugin["notation"])
    except Exception as e:
        os.remove(new_filename)
        await cq.edit(lang.plugin_could_not_load(e=e))
        raise e

    functions = [*filter(callable, module.__dict__.values())]
    functions = [*filter(lambda f: hasattr(f, "handlers"), functions)]

    # if not len(functions):
    #     os.remove(new_filename)
    #     return await cq.edit(lang.plugin_has_no_handlers)

    for f in functions:
        for handler in f.handlers:
            client.add_handler(*handler)

    if module:
        r = None
        functions = [*filter(callable, module.__dict__.values())]
        for f in functions:
            if hasattr(f, "__name__") and f.__name__ == "post_install_script":
                await cq.edit(lang.running_post_install_script)
                if inspect.iscoroutinefunction(f):
                    r = await f()
                else:
                    r = await asyncio.get_event_loop().run_in_executor(None, f)
                break

        if r is not None:
            unload = False
            if isinstance(r, (tuple, list)):
                if len(r) == 2:
                    if r[0] != 1:
                        await cq.edit(lang.plugin_could_not_load(e=r[1]))
                        unload = True
                else:
                    await cq.edit(
                        lang.plugin_could_not_load(
                            e="The return of post_install_script should be like this: (0, 'nodejs not found')"
                        ))
                    unload = True

            else:
                await cq.edit(
                    lang.plugin_could_not_load(
                        e="The return of post_install_script should be a list or tuple"
                    ))
                unload = True

            if unload:
                functions = [
                    *filter(lambda f: hasattr(f, "handlers"), functions)
                ]

                for f in functions:
                    for handler in f.handlers:
                        client.remove_handler(*handler)
                os.remove(new_filename)
                return

    plugins[plugin_type][basename] = plugin
    reload_plugins_requirements(plugins)

    inactive = await get_inactive_plugins(plugins)

    if plugin["notation"] in inactive:
        inactive = [x for x in inactive if x != plugin["notation"]]
        await Config.get(key="INACTIVE_PLUGINS"
                         ).update(value=json.dumps(inactive))

    # Discover which page is this plugin listed in
    quant_per_page = 4 * 2  # lines times columns
    page = math.ceil(len(plugins) / quant_per_page)

    keyb = ikb([[(lang.see_plugin_info,
                  f"info_plugin {basename} {plugin_type} {page}")]])
    text = lang.plugin_added(name=basename)

    await cq.edit(text, keyb)
コード例 #18
0
ファイル: upgrade.py プロジェクト: AmanoTeam/UserLixo
async def on_upgrade_u(c: Client, u: Union[Message, CallbackQuery]):
    lang = u._lang
    is_query = hasattr(u, "data")
    is_inline = is_query and not u.message
    from_where = "_bot" if is_query else ""
    act = u.edit if is_query else u.reply
    keyb = ikb([[(lang.back, "start")]])
    try:
        with open(".git/HEAD") as f:
            branch = f.read().split("/")[-1].rstrip()
    except FileNotFoundError:
        return await act(lang.upgrade_error_not_git, keyb)

    stdout, process = await shell_exec("git fetch && git status -uno")

    if process.returncode != 0:
        await act(
            lang.upgrade_failed(branch=branch,
                                code=process.returncode,
                                output=stdout),
            keyb,
        )
        return await shell_exec("git merge --abort")

    if "Your branch is up to date" in stdout:
        # title, p = await shell_exec('git log --format="%B" -1')
        rev, p = await shell_exec("git rev-parse --short HEAD")
        date, p = await shell_exec(
            'git log -1 --format=%cd --date=format:"%d/%m %H:%M"')
        timezone, p = await shell_exec(
            'git log -1 --format=%cd --date=format:"%z"')
        local_version = int((await shell_exec("git rev-list --count HEAD"))[0])

        timezone = timezone_shortener(timezone)
        date += f" ({timezone})"

        args = []
        if is_query:
            args.append(keyb)
        return await act(
            lang.upgrade_alert_already_uptodate(rev=rev,
                                                date=date,
                                                local_version=local_version),
            *args,
        )

    msg = await act(lang.upgrading_now_alert)

    stdout, process = await shell_exec(f"git pull --no-edit origin {branch}")
    if process.returncode != 0:
        await msg.edit(
            lang.upgrade_failed(branch=branch,
                                code=process.returncode,
                                output=stdout),
            keyb,
        )
        return await shell_exec("git merge --abort")

    await Config.filter(key="restarting_alert").delete()
    message_id = u.inline_message_id if is_inline else msg.message_id
    chat_id = "inline" if is_inline else msg.chat.username or msg.chat.id
    await Config.create(
        **{
            "key":
            "restarting_alert",
            "value":
            f"{message_id}|{chat_id}|{datetime.now().timestamp()}|upgrade{from_where}",
        })

    args = [sys.executable, "-m", "userlixo"]
    if "--no-update" in sys.argv:
        args.append("--no-update")
    os.execv(sys.executable, args)
コード例 #19
0
async def ytdlcmd(c: Client, m: Message):
    user = m.from_user.id

    if m.reply_to_message and m.reply_to_message.text:
        url = m.reply_to_message.text
    elif len(m.command) > 1:
        url = m.text.split(None, 1)[1]
    else:
        await m.reply_text(await tld(m.chat.id, "ytdl_missing_argument"))
        return

    ydl = yt_dlp.YoutubeDL({
        "outtmpl": "dls/%(title)s-%(id)s.%(ext)s",
        "format": "mp4",
        "noplaylist": True
    })
    rege = re.match(
        r"http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?‌​[\w\?‌​=]*)?",
        url,
        re.M,
    )

    if "t=" in url:
        temp = url.split("t=")[1].split("&")[0]
    else:
        temp = 0

    if not rege:
        yt = await extract_info(ydl, "ytsearch:" + url, download=False)
        yt = yt["entries"][0]
    else:
        yt = await extract_info(ydl, rege.group(), download=False)

    for f in yt["formats"]:
        if f["format_id"] == "140":
            afsize = f["filesize"] or 0
        if f["ext"] == "mp4" and f["filesize"] is not None:
            vfsize = f["filesize"] or 0
            vformat = f["format_id"]

    keyboard = [[
        (
            await tld(m.chat.id, "ytdl_audio_button"),
            f'_aud.{yt["id"]}|{afsize}|{temp}|{vformat}|{m.chat.id}|{user}|{m.message_id}',
        ),
        (
            await tld(m.chat.id, "ytdl_video_button"),
            f'_vid.{yt["id"]}|{vfsize}|{temp}|{vformat}|{m.chat.id}|{user}|{m.message_id}',
        ),
    ]]

    if " - " in yt["title"]:
        performer, title = yt["title"].rsplit(" - ", 1)
    else:
        performer = yt.get("creator") or yt.get("uploader")
        title = yt["title"]

    text = f"🎧 <b>{performer}</b> - <i>{title}</i>\n"
    text += f"💾 <code>{pretty_size(afsize)}</code> (audio) / <code>{pretty_size(int(vfsize))}</code> (video)\n"
    text += f"⏳ <code>{datetime.timedelta(seconds=yt.get('duration'))}</code>"

    await m.reply_text(text, reply_markup=ikb(keyboard))