Example #1
0
async def who_cmd(client, message):
    try:
        peer = None
        if len(message.command) > 1 and message.command[1] != "-no":
            arg = message.command[1]
            if arg.isnumeric():
                peer = await client.get_users(int(arg))
            else:
                peer = await client.get_users(arg)
        elif message.reply_to_message is not None \
        and message.reply_to_message.from_user is not None:
            peer = message.reply_to_message.from_user
        else:
            peer = await client.get_me()
        logger.info("Getting info of user")
        if is_me(message):
            await message.edit(message.text.markdown +
                               f"\n` → ` Getting data of user `{peer.id}`")
        if len(message.command) < 3 or message.command[2] != "-no":
            out = io.BytesIO((str(peer)).encode('utf-8'))
            out.name = f"user-{peer.id}.json"
            await client.send_document(message.chat.id, out)
    except Exception as e:
        traceback.print_exc()
        await edit_or_reply(message, "`[!] → ` " + str(e))
    await client.set_offline()
Example #2
0
async def randomcase(client, message):
    logger.info(f"Making message randomly capitalized")
    text = message.command["arg"]
    if text == "":
        return
    msg = ""  # omg this part is done so badly
    val = 0  # but I want a kinda imbalanced random
    upper = False
    for c in text:
        last = val
        val = secrets.randbelow(4)
        if val > 2:
            msg += c.upper()
            upper = True
        elif val < 1:
            msg += c
            upper = False
        else:
            if upper:
                msg += c
                upper = False
            else:
                msg += c.upper()
                upper = True
    if is_me(message):
        await message.edit(msg)
    else:
        await message.reply(msg)
    await client.set_offline()
Example #3
0
async def meme_cmd(client, message):
	"""get a meme from collection

	If a name was specified, get meme matching requested name (regex). Otherwise, get random meme.
	Use flag `-list` to get all meme names and flag `-stats` to get count and disk usage.
	You can send a bunch of random memes together by specifying how many in the `-b` (batch) option \
	(only photos will be sent if a batch is requested).
	Memes can be any filetype.
	"""
	batchsize = max(min(int(message.command["batch"] or 10), 10), 2)
	reply_to = message.message_id
	if is_me(message) and message.reply_to_message is not None:
		reply_to = message.reply_to_message.message_id
	if message.command["-stats"]:
		memenumber = len(os.listdir("plugins/alemibot-tricks/data/meme"))
		proc_meme = await asyncio.create_subprocess_exec( # ewww this is not cross platform but will do for now
			"du", "-b", "plugins/alemibot-tricks/data/meme/",
			stdout=asyncio.subprocess.PIPE,
			stderr=asyncio.subprocess.STDOUT)
		stdout, _stderr = await proc_meme.communicate()
		memesize = float(stdout.decode('utf-8').split("\t")[0])
		await edit_or_reply(message, f"` → ` **{memenumber}** memes collected\n`  → ` folder size **{order_suffix(memesize)}**")
	elif message.command["-list"]:
		memes = os.listdir("plugins/alemibot-tricks/data/meme")
		memes.sort()
		out = f"` → ` **Meme list** ({len(memes)} total) :\n[ "
		out += ", ".join(memes)
		out += "]"
		await edit_or_reply(message, out)
	elif len(message.command) > 0 and (len(message.command) > 1 or message.command[0] != "-delme"):
		search = re.compile(message.command[0])
		found = []
		for meme in os.listdir("plugins/alemibot-tricks/data/meme"):
			if search.match(meme):
				found.append(meme)
		if len(found) > 1:
			await edit_or_reply(message, "`[!] → ` multiple memes match query\n" + "\n".join(f"`  → ` {meme}" for meme in found))
		elif len(found) == 1:
			meme = found[0]
			await send_media(client, message.chat.id, 'plugins/alemibot-tricks/data/meme/' + meme, reply_to_message_id=reply_to,
					caption=f"` → ` **{meme}**")
		elif len(found) < 1:
			await edit_or_reply(message, f"`[!] → ` no meme matching `{message.command[0]}`")
	else: 
		if "batch" in message.command:
			with ProgressChatAction(client, message.chat.id, action="upload_photo") as prog:
				pool = [ x for x in filter(lambda x: x.endswith((".jpg", ".jpeg", ".png")), os.listdir("plugins/alemibot-tricks/data/meme")) ]
				def pick(pool):
					pick = secrets.choice(pool)
					pool.remove(pick)
					return pick
				memes = [InputMediaPhoto("plugins/alemibot-tricks/data/meme/" + pick(pool)) for _ in range(batchsize)]
				await client.send_media_group(message.chat.id, memes)
		else:
			fname = secrets.choice(os.listdir("plugins/alemibot-tricks/data/meme"))
			await send_media(client, message.chat.id, 'plugins/alemibot-tricks/data/meme/' + fname, reply_to_message_id=reply_to,
					caption=f"` → ` [--random--] **{fname}**")
Example #4
0
async def voice_cmd(client, message):
    """convert text to voice

	Create a voice message using Google Text to Speech.
	By default, english will be	used as lang, but another one can be specified with `-l`.
	You can add `-slow` flag to make the generated speech slower.
	TTS result will be converted to `.ogg`. You can skip this step and send as mp3 by adding the `-mp3` flag.
	You can add the `-file` flag to make tts of a replied to or attached text file.
	"""
    text = ""
    opts = {}
    from_file = bool(message.command["-file"])
    if message.reply_to_message is not None:
        if from_file and message.reply_to_message.media:
            fpath = await client.download_media(message.reply_to_message)
            with open(fpath) as f:
                text = f.read()
            os.remove(fpath)
        else:
            text = get_text(message.reply_to_message)
    elif from_file and message.media:
        fpath = await client.download_media(message)
        with open(fpath) as f:
            text = f.read()
        os.remove(fpath)
    elif len(message.command) > 0:
        text = re.sub(r"-delme(?: |)(?:[0-9]+|)", "", message.command.text)
    else:
        return await edit_or_reply(message, "`[!] → ` No text given")
    prog = ProgressChatAction(client, message.chat.id, action="record_audio")
    lang = message.command["lang"] or "en"
    slow = bool(message.command["-slow"])
    if message.reply_to_message is not None:
        opts["reply_to_message_id"] = message.reply_to_message.message_id
    elif not is_me(message):
        opts["reply_to_message_id"] = message.message_id
    await prog.tick()
    gTTS(text=text, lang=lang, slow=slow).save("data/tts.mp3")
    if message.command["-mp3"]:
        await client.send_audio(message.chat.id,
                                "data/tts.mp3",
                                progress=prog.tick,
                                **opts)
    else:
        AudioSegment.from_mp3("data/tts.mp3").export("data/tts.ogg",
                                                     format="ogg",
                                                     codec="libopus")
        await client.send_voice(message.chat.id,
                                "data/tts.ogg",
                                progress=prog.tick,
                                **opts)
Example #5
0
async def search_triggers(client, message):
    if is_me(
            message
    ) or message.edit_date is not None:  # TODO allow triggers for self?
        return  # pyrogram gets edit events as message events!
    if message.chat is None:
        return  # wtf messages with no chat???
    if message.chat.type != "private" and not message.mentioned:
        return  # in groups only get triggered in mentions
    msg_txt = get_text(message).lower()
    if msg_txt == "":
        return
    for trg in triggers:
        if trg.lower() in msg_txt:
            await message.reply(triggers[trg])
            await client.set_offline()
            logger.info("T R I G G E R E D")
Example #6
0
async def bully(client, message):
    if message.edit_date is not None:
        return  # pyrogram gets edit events as message events!
    if message.chat is None or is_me(message):
        return  # can't censor messages outside of chats or from self
    if message.from_user is None:
        return  # Don't censory anonymous msgs
    if message.chat.id in censoring["MASS"] \
    and message.from_user.id not in censoring["FREE"]:
        await message.delete()
        logger.info("Get bullied")
    else:
        if message.chat.id not in censoring["SPEC"] \
        or message.from_user.id not in censoring["SPEC"][message.chat.id]:
            return  # Don't censor innocents!
        await message.delete()
        logger.info("Get bullied noob")
    await client.set_offline()
Example #7
0
async def deleted_cmd(client, message):  # This is a mess omg
    args = message.command
    show_time = "-t" in args["flags"]
    target_group = message.chat
    include_system = "-sys" in args["flags"]
    offset = int(args["offset"]) if "offset" in args else 0
    if is_me(message):
        if "-all" in args["flags"]:
            target_group = None
        elif "group" in args:
            target_group = await client.get_chat(int(args["group"]))
    limit = 1
    if "arg" in args:
        limit = int(args["arg"])
    lgr.info(f"Peeking {limit} messages")
    asyncio.get_event_loop(
    ).create_task(  # launch the task async because it may take some time
        lookup_deleted_messages(client, message, target_group, limit,
                                show_time, include_system, offset))
Example #8
0
async def getmeme(client, message):
    args = message.command
    try:
        await client.send_chat_action(message.chat.id, "upload_photo")
        reply_to = message.message_id
        if is_me(message) and message.reply_to_message is not None:
            reply_to = message.reply_to_message.message_id
        if "-list" in args["flags"]:
            logger.info("Getting meme list")
            memes = os.listdir("data/memes")
            memes.sort()
            out = f"` → ` **Meme list** ({len(memes)} total) :\n[ "
            out += ", ".join(memes)
            out += "]"
            await edit_or_reply(message, out)
        elif "cmd" in args:
            memes = [
                s for s in os.listdir("data/memes")  # I can't decide if this
                if s.lower().startswith(args["cmd"][0])
            ]  #  is nice or horrible
            if len(memes) > 0:
                fname = memes[0]
                logger.info(f"Getting specific meme : \"{fname}\"")
                await send_media_appropriately(client, message, fname,
                                               reply_to)
            else:
                await edit_or_reply(
                    message, f"`[!] → ` no meme named {args['cmd'][0]}")
        else:
            fname = secrets.choice(os.listdir("data/memes"))
            logger.info(f"Getting random meme : \"{fname}\"")
            await send_media_appropriately(client,
                                           message,
                                           fname,
                                           reply_to,
                                           extra_text="Random meme : ")
    except Exception as e:
        traceback.print_exc()
        await edit_or_reply(message, "`[!] → ` " + str(e))
    await client.send_chat_action(message.chat.id, "cancel")
    await client.set_offline()
Example #9
0
async def where_cmd(client, message):
    try:
        tgt = message.chat
        if len(message.command) > 1 and message.command[1] != "-no":
            arg = message.command[1]
            if arg.isnumeric():
                tgt = await client.get_chat(int(arg))
            else:
                tgt = await client.get_chat(arg)
        logger.info(f"Getting info of chat")
        if is_me(message):
            await message.edit(message.text.markdown +
                               f"\n` → ` Getting data of chat `{tgt.id}`")
        if len(message.command) < 3 or message.command[2] != "-no":
            out = io.BytesIO((str(tgt)).encode('utf-8'))
            out.name = f"chat-{message.chat.id}.json"
            await client.send_document(message.chat.id, out)
    except Exception as e:
        traceback.print_exc()
        await edit_or_reply(message, "`[!] → ` " + str(e))
    await client.set_offline()
Example #10
0
async def countdown(client, message):
    if is_me(message):
        tgt_msg = message
    else:
        tgt_msg = await message.reply("` → `")
    end = time.time() + 5
    if len(message.command) > 1:
        try:
            end = time.time() + float(message.command[1])
        except ValueError:
            return await tgt_msg.edit("`[!] → ` argument must be a float")
    msg = tgt_msg.text + "\n` → Countdown ` **{:.1f}**"
    last = ""
    logger.info(f"Countdown")
    while time.time() < end:
        curr = msg.format(time.time() - end)
        if curr != last:  # with fast counting down at the end it may try to edit with same value
            await tgt_msg.edit(msg.format(time.time() - end))
            last = curr
        await asyncio.sleep(interval(end - time.time()))
    await tgt_msg.edit(msg.format(0))
    await client.set_offline()
Example #11
0
async def what_cmd(client, message):
    msg = message
    try:
        if message.reply_to_message is not None:
            msg = await client.get_messages(
                message.chat.id, message.reply_to_message.message_id)
        elif len(message.command) > 1 and message.command[1].isnumeric():
            chat_id = message.chat.id
            if len(message.command) > 2 and message.command[2].isnumeric():
                chat_id = int(message.command[2])
            msg = await client.get_messages(chat_id, int(message.command[1]))
        logger.info("Getting info of msg")
        if is_me(message):
            await message.edit(
                message.text.markdown +
                f"\n` → ` Getting data of msg `{msg.message_id}`")
        if message.command[len(message.command) - 1] != "-no":
            out = io.BytesIO((str(msg)).encode('utf-8'))
            out.name = f"msg-{msg.message_id}.json"
            await client.send_document(message.chat.id, out)
    except Exception as e:
        traceback.print_exc()
        await edit_or_reply(message, "`[!] → ` " + str(e))
    await client.set_offline()
Example #12
0
async def plugin_remove_cmd(client, message):
    """remove an installed plugin.

	alemiBot plugins are git repos, cloned into the `plugins` folder as git submodules.
	This will call `git submodule deinit -f`, then remove the related folder in `.git/modules` and last remove \
	plugin folder and all its content.
	If flag `-lib` is added, libraries installed with pip will be removed too (may break dependancies of other plugins!)
	"""
    if not alemiBot.allow_plugin_install:
        return await edit_or_reply(message,
                                   "`[!] → ` Plugin management is disabled")
    out = message.text.markdown if is_me(
        message
    ) else f"`→ ` {get_username(message.from_user)} requested plugin removal"
    msg = message if is_me(message) else await message.reply(out)

    try:
        if len(message.command) < 1:
            out += "\n`[!] → ` No input"
            return await msg.edit(out)
        plugin = message.command[0]

        out += f"\n`→ ` Uninstalling `{plugin}`"

        if "/" in plugin:  # If user passes <user>/<repo> here too, get just repo name
            plugin = plugin.split("/")[1]

        logger.info(f"Removing plugin \"{plugin}\"")
        if message.command["-lib"]:
            out += "\n` → ` Removing libraries"
            await msg.edit(out)
            if os.path.isfile(f"plugins/{plugin}/requirements.txt"):
                proc = await asyncio.create_subprocess_exec(
                    "pip",
                    "uninstall",
                    "-y",
                    "-r",
                    f"plugins/{plugin}/requirements.txt",
                    stdout=asyncio.subprocess.PIPE,
                    stderr=asyncio.subprocess.STDOUT)
                stdout, _stderr = await proc.communicate()
                logger.info(stdout.decode())
                if b"ERROR" in stdout:
                    out += " [`WARN`]"
                else:
                    out += f" [`{stdout.count(b'Uninstalling')} del`]"
        out += "\n` → ` Removing source code"
        await msg.edit(out)
        proc = await asyncio.create_subprocess_shell(
            f"git submodule deinit -f plugins/{plugin} && rm -rf .git/modules/plugins/{plugin} && git rm -f plugins/{plugin}",
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT)

        stdout, _stderr = await proc.communicate()
        res = cleartermcolor(stdout.decode())
        logger.info(res)
        if not res.startswith("Cleared"):
            logger.error(res)
            out += f" [`FAIL`]\n`[!] → ` Could not deinit `{plugin}`"
            return await msg.edit(out)
        if f"rm 'plugins/{plugin}'" not in res:
            logger.error(res)
            out += f" [`FAIL`]\n`[!] → ` Could not delete `{plugin}`"
            return await msg.edit(out)
        out += f" [`OK`]\n` → ` Restarting process"
        await msg.edit(out)
        with open("data/lastmsg.json", "w") as f:
            json.dump({
                "message_id": msg.message_id,
                "chat_id": msg.chat.id
            }, f)
        asyncio.get_event_loop().create_task(client.restart())
    except Exception as e:
        logger.exception("Error while installing plugin")
        out += " [`FAIL`]\n`[!] → ` " + str(e)
        await msg.edit(out)
Example #13
0
async def plugin_add_cmd(client, message):
    """install a plugin

	alemiBot plugins are git repos, cloned into the `plugins` folder as git submodules.
	You can specify which extension to install by giving `user/repo` (will default to github.com), or specify the entire url.
	For example,
		`alemidev/alemibot-tricks`
	is the same as
		`https://github.com/alemidev/alemibot-tricks.git`
	By default, https will be used (meaning that if you try to clone a private repo, it will just fail).
	You can make it clone using ssh	with `-ssh` flag, or by adding `useSsh = True` to your config (under `[customization]`).
	You can also include your GitHub credentials in the clone url itself:
		`https://username:[email protected]/author/repo.git`
	Your github credentials will be stored in plain text inside project folder. \
	Because of this, it is --not recommended-- to include credentials in the clone url. Set up an ssh key for private plugins.
	You can specify which branch to clone with `-b` option.
	You can also specify a custom folder to clone into with `-d` option (this may break plugins relying on data stored in their directory!)
	"""
    if not alemiBot.allow_plugin_install:
        return await edit_or_reply(message,
                                   "`[!] → ` Plugin management is disabled")
    out = message.text.markdown if is_me(
        message
    ) else f"`→ ` {get_username(message.from_user)} requested plugin install"
    msg = message if is_me(message) else await message.reply(out)
    try:
        if len(message.command) < 1:
            out += "\n`[!] → ` No input"
            return await msg.edit(out)
        user_input = message.command[0]
        branch = message.command["branch"]
        folder = message.command["dir"]

        plugin, author = split_url(user_input)  # clear url or stuff around
        if folder is None:
            folder = plugin

        if user_input.startswith("http") or user_input.startswith("git@"):
            link = user_input
        else:
            if alemiBot.use_ssh or message.command["-ssh"]:
                link = f"[email protected]:{author}/{plugin}.git"
            else:
                link = f"https://github.com/{author}/{plugin}.git"

        out += f"\n`→ ` Installing `{author}/{plugin}`"
        logger.info(f"Installing \"{author}/{plugin}\"")

        if os.path.isfile(".gitmodules"):
            with open(".gitmodules") as f:
                modules = f.read()
            matches = re.findall(r"url = [email protected]:(?P<p>.*).git",
                                 modules)
            for match in matches:
                if match == plugin:
                    out += "`[!] → ` Plugin already installed"
                    return await msg.edit(out)

        if branch is None:
            out += "\n` → ` Checking branches"
            await msg.edit(out)
            proc = await asyncio.create_subprocess_shell(
                f"GIT_TERMINAL_PROMPT=0 git ls-remote --symref {link} HEAD",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.STDOUT)
            stdout, _sterr = await proc.communicate()
            res = cleartermcolor(stdout.decode())
            logger.info(res)
            if res.startswith(("ERROR", "fatal", "remote: Not Found")):
                out += f" [`FAIL`]\n`[!] → ` Could not find `{link}`"
                return await msg.edit(out)
            out += " [`OK`]"
            branch = re.search(r"refs\/heads\/(?P<branch>[^\s]+)(?:\s+)HEAD",
                               res)["branch"]

        out += "\n` → ` Fetching source code"
        await msg.edit(out)

        proc = await asyncio.create_subprocess_shell(
            f"GIT_TERMINAL_PROMPT=0 git submodule add -b {branch} {link} plugins/{folder}",
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT)

        stdout, _sterr = await proc.communicate()
        res = cleartermcolor(stdout.decode())
        logger.info(res)
        if not res.startswith("Cloning"):
            out += f" [`FAIL`]\n`[!] → ` Plugin `{author}/{plugin}` was wrongly uninstalled"
            return await msg.edit(out)
        if "ERROR: Repository not found" in res:
            out += f" [`FAIL`]\n`[!] → ` No plugin `{author}/{plugin}` could be found"
            return await msg.edit(out)
        if re.search(r"fatal: '(.*)' is not a commit", res):
            out += f" [`FAIL`]\n`[!] → ` Non existing branch `{branch}` for `{author}/{plugin}`"
            return await msg.edit(out)
        out += f" [`OK`]\n` → ` Checking dependancies"
        await msg.edit(out)
        if os.path.isfile(f"plugins/{plugin}/requirements.txt"):
            proc = await asyncio.create_subprocess_exec(
                "pip",
                "install",
                "-r",
                f"plugins/{plugin}/requirements.txt",
                "--upgrade",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.STDOUT)
            stdout, _stderr = await proc.communicate()
            logger.info(stdout.decode())
            if b"ERROR" in stdout:
                logger.warn(stdout.decode())
                out += " [`WARN`]"
            else:
                out += f" [`{stdout.count(b'Uninstalling')} new`]"
        else:
            out += " [`N/A`]"
        out += f"\n` → ` Restarting process"
        await msg.edit(out)
        with open("data/lastmsg.json", "w") as f:
            json.dump({
                "message_id": msg.message_id,
                "chat_id": msg.chat.id
            }, f)
        asyncio.get_event_loop().create_task(client.restart())
    except Exception as e:
        logger.exception("Error while installing plugin")
        out += " [`FAIL`]\n`[!] → ` " + str(e)
        await msg.edit(out)
Example #14
0
async def update_cmd(client, message):
    """fetch updates and restart client

	Will pull changes from git (`git pull`), install requirements (`pip install -r requirements.txt --upgrade`) \
	and then restart process with an `execv` call.
	If nothing gets pulled from `git`, update will stop unless the `-force` flag was given.
	"""
    out = message.text.markdown if is_me(
        message
    ) else f"`→ ` {get_username(message.from_user)} requested update"
    msg = message if is_me(message) else await message.reply(out)
    uptime = str(datetime.now() - client.start_time)
    out += f"\n`→ ` --runtime-- `{uptime}`"
    try:
        out += "\n` → ` Fetching updates"
        pulled = False
        await msg.edit(out)
        proc = await asyncio.create_subprocess_exec(
            "git",
            "pull",
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT)
        stdout, _stderr = await proc.communicate()
        logger.info(stdout.decode())
        if b"Aborting" in stdout:
            out += " [`FAIL`]\n"
            if not message.command["-force"]:
                return await msg.edit(out)
        elif b"Already up to date" in stdout:
            out += " [`N/A`]"
        else:
            pulled = True
            out += " [`OK`]"

        if os.path.isfile(".gitmodules"):  # Also update plugins
            out += "\n`  → ` Submodules"
            await msg.edit(out)
            sub_proc = await asyncio.create_subprocess_exec(
                "git",
                "submodule",
                "update",
                "--remote",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.STDOUT)
            sub_stdout, _sub_stderr = await sub_proc.communicate()
            logger.info(sub_stdout.decode())
            sub_count = sub_stdout.count(b"checked out")
            if sub_count > 0:
                out += f" [`{sub_count}`]"
                pulled = True
            else:
                out += " [`N/A`]"

        if not pulled and not message.command["-force"]:
            return await msg.edit(out)

        out += "\n` → ` Checking libraries"
        await msg.edit(out)
        proc = await asyncio.create_subprocess_exec(
            "pip",
            "install",
            "-r",
            "requirements.txt",
            "--upgrade",
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT)
        stdout, _stderr = await proc.communicate()
        logger.info(stdout.decode())
        if b"ERROR" in stdout:
            out += " [`WARN`]"
        else:
            out += f" [`{stdout.count(b'Collecting')} new`]"
        if os.path.isfile(
                ".gitmodules"):  # Also install dependancies from plugins
            out += "\n`  → ` Submodules"
            await msg.edit(out)
            with open(".gitmodules") as f:
                modules = f.read()
            matches = re.findall(r"path = (?P<path>plugins/[^ ]+)\n", modules)
            count = 0
            for match in matches:
                if os.path.isfile(f"{match}/requirements.txt"):
                    proc = await asyncio.create_subprocess_exec(
                        "pip",
                        "install",
                        "-r",
                        f"{match}/requirements.txt",
                        "--upgrade",
                        stdout=asyncio.subprocess.PIPE,
                        stderr=asyncio.subprocess.STDOUT)
                    stdout, _stderr = await proc.communicate()
                    logger.info(stdout.decode())
                    if b"ERROR" in stdout:
                        out += " [`WARN`]"
                    else:
                        count += stdout.count(b'Collecting')
            out += f" [`{count} new`]"
        out += "\n` → ` Restarting process"
        await msg.edit(out)
        with open("data/lastmsg.json", "w") as f:
            json.dump({
                "message_id": msg.message_id,
                "chat_id": msg.chat.id
            }, f)
        asyncio.get_event_loop().create_task(client.restart())
    except Exception as e:
        logger.exception("Error while updating")
        out += " [`FAIL`]\n`[!] → ` " + str(e)
        await msg.edit(out)