async def change_log_level(self, ctx: utils.TypedContext, *, level: str):
        with open("config.ini", "w") as config_file:
            self.bot.config["Main"]["log_level"] = level
            self.bot.config.write(config_file)

        if self.bot.websocket is None:
            return self.bot.logger.setLevel(level.upper())

        try:
            wsjson = utils.data_to_ws_json("SEND",
                                           target="*",
                                           **{
                                               "c": "change_log_level",
                                               "a": {
                                                   "level": level.upper()
                                               },
                                               "t": "*"
                                           })
            await self.bot.websocket.send(wsjson)
            await self.bot.wait_for("change_log_level", timeout=10)
        except asyncio.TimeoutError:
            await ctx.send(f"Didn't recieve broadcast within 10 seconds!")
        else:
            level = logging.getLevelName(self.bot.logger.level)
            await ctx.send(f"Broadcast complete, log level is now: {level}")
    async def check_cluster(self, cluster_id: int, pid: int):
        process = psutil.Process(pid)
        websocket = self.websockets.get(cluster_id)
        if websocket is None:
            logger.error(
                f"Cluster ID {cluster_id} doesn't have a websocket! Killing and restarting."
            )
            return process.kill()

        try:
            coro = self._get_from_clusters(["ping"], target=cluster_id)
            await asyncio.wait_for(coro, timeout=10)
        except (asyncio.TimeoutError, websockets.WebSocketException):
            logger.warning(
                f"Cluster {cluster_id} didn't respond to ping in 10 seconds, restarting safely!"
            )
            ws_json = utils.data_to_ws_json(command="RESTART",
                                            target=cluster_id)
            try:
                await websocket.send(ws_json)
                await asyncio.sleep(15)
            except websockets.WebSocketException:
                pass

            if not process.is_running():
                # Cluster has been killed and hopefully restarted by watcher.
                return

            logger.warning(
                f"{cluster_id} wasn't restarted by websocket message, killing!"
            )
            return process.kill()
    async def on_guild_join(self, guild: utils.TypedGuild):
        _, prefix, owner = await asyncio.gather(
            self.bot.channels["servers"].send(
                f"Just joined {guild}! I am now in {len(self.bot.guilds)} different servers!"
            ), self.bot.settings.get(guild, ["prefix"]),
            guild.fetch_member(guild.owner_id))

        embed = discord.Embed(
            title=f"Welcome to {self.bot.user.name}!",
            description=WELCOME_MESSAGE.format(guild=guild, prefix=prefix[0])
        ).set_footer(
            text=
            "Support Server: https://discord.gg/zWPWwQC | Bot Invite: https://bit.ly/TTSBot"
        ).set_author(name=str(owner), icon_url=owner.avatar.url)

        try:
            await owner.send(embed=embed)
        except discord.errors.HTTPException:
            pass

        if self.bot.websocket is None or self.bot.get_support_server(
        ) is not None:
            return await self.on_ofs_add(owner.id)

        json = {"c": "ofs_add", "a": {"owner_id": owner.id}}
        wsjson = utils.data_to_ws_json("SEND", target="support", **json)

        await self.bot.websocket.send(wsjson)
Example #4
0
    async def request_handler(self, connection: _WSSP, request: WSRequestJSON):
        nonce = request["a"]["nonce"]
        to_get = request["a"]["info"]
        target = request.get("t", "*")
        responses = await self._get_from_clusters(to_get, nonce, target=target)

        response_json = utils.data_to_ws_json(command="RESPONSE",
                                              target=nonce,
                                              responses=responses)
        await connection.send(response_json)
Example #5
0
    async def on_request(self,
                         info: list[str],
                         nonce: str,
                         args: dict[str, dict[str, Any]] = {},
                         *_):
        returns: dict[str, utils.JSON_IN] = {}
        for key in info:
            func, kwargs = data_lookup[key], args.get(key, {})
            returns[key] = await maybe_coroutine(func, self.bot, **kwargs)

        wsjson = utils.data_to_ws_json("RESPONSE", target=nonce, **returns)
        await self.bot.websocket.send(wsjson)
    async def cluster(self, ctx: utils.TypedContext, cluster_id: int):
        if self.bot.websocket is None:
            return await ctx.send("Manager websocket is None!")

        if cluster_id == self.bot.cluster_id:
            await self.bot.close(utils.RESTART_CLUSTER)
        else:
            wsjson = utils.data_to_ws_json("SEND", target=cluster_id, **{
                "c": "restart",
                "a": {},
            })

            await self.bot.websocket.send(wsjson)
            await ctx.send(f"Told cluster {cluster_id} to restart.")
    async def __aexit__(self, etype, error, tb):
        self.handler._cache.pop(self.cache_id, None)
        self.handler._do_not_cache.remove(self.cache_id)
        if error is not None or self.websocket is None or not self.broadcast:
            return

        await self.websocket.send(
            data_to_ws_json("SEND",
                            target="*",
                            **{
                                "c": "invalidate_cache",
                                "a": {
                                    "id": self.cache_id
                                },
                            }))
    async def reload_cog(self, ctx: utils.TypedContext, *, to_reload: str):
        try:
            self.bot.reload_extension(to_reload)
        except Exception as e:
            await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}")
        else:
            await ctx.send("**`SUCCESS`**")
            if self.bot.websocket is None:
                return

            await self.bot.websocket.send(
                utils.data_to_ws_json(
                    "SEND", target="*", **{
                        "c": "reload",
                        "a": {"cog": to_reload},
                })
            )
Example #9
0
    async def _insert_write(self, raw_id: _DK):
        task = self._write_tasks.pop(raw_id)

        try:
            query = self._get_query(raw_id, task)
            await self.pool.execute(*query)
        except Exception as error:
            task.waiter.set_exception(error)
        else:
            task.waiter.set_result(None)

        if self.bot.websocket is not None and self.broadcast:
            await self.bot.websocket.send(
                utils.data_to_ws_json("SEND",
                                      target="*",
                                      **{
                                          "c": "invalidate_cache",
                                          "a": {
                                              "identifer": raw_id
                                          },
                                      }))
    async def on_request(self, info: List[str], nonce: str, *args):
        data = {to_get: data_lookup[to_get](self.bot) for to_get in info}
        wsjson = utils.data_to_ws_json("RESPONSE", target=nonce, **data)

        await self.bot.websocket.send(wsjson)  # type: ignore
Example #11
0
    async def botstats(self, ctx: utils.TypedContext):
        "Shows various different stats"
        await ctx.trigger_typing()

        start_time = time.perf_counter()
        if self.bot.websocket is None:
            guilds = [
                guild for guild in self.bot.guilds if not guild.unavailable
            ]
            total_members = sum(guild.member_count for guild in guilds)
            total_voice_clients = len(self.bot.voice_clients)
            total_guild_count = len(guilds)

            raw_ram_usage = Process(getpid()).memory_info().rss
        else:
            ws_uuid = uuid.uuid4()
            to_fetch = ["guild_count", "member_count", "voice_count"]
            wsjson = utils.data_to_ws_json("REQUEST",
                                           target="*",
                                           info=to_fetch,
                                           nonce=ws_uuid)

            await self.bot.websocket.send(wsjson)
            try:
                check = lambda _, nonce: uuid.UUID(nonce) == ws_uuid
                responses, _ = await self.bot.wait_for(
                    timeout=10,
                    check=check,
                    event="response",
                )
            except asyncio.TimeoutError:
                cog = cast(ErrorEvents, self.bot.get_cog("ErrorEvents"))
                self.bot.logger.error("Timed out fetching botstats!")

                error_msg = "the bot timed out fetching this info"
                return await cog.send_error(ctx, error_msg)

            raw_ram_usage = await utils.to_thread(
                partial(get_ram_recursive,
                        Process(getpid()).parent()))
            total_voice_clients = sum(resp["voice_count"]
                                      for resp in responses)
            total_guild_count = sum(resp["guild_count"] for resp in responses)
            total_members = sum(resp["member_count"] for resp in responses)

        time_to_fetch = (time.perf_counter() - start_time) * 1000
        footer = cleandoc(f"""
            Time to fetch: {time_to_fetch:.2f}ms
            Support Server: https://discord.gg/zWPWwQC
            Repository: https://github.com/Gnome-py/Discord-TTS-Bot
        """)

        sep1, sep2, *_ = utils.OPTION_SEPERATORS
        embed = discord.Embed(title=f"{self.bot.user.name}: Now open source!",
                              url="https://discord.gg/zWPWwQC",
                              colour=utils.NETURAL_COLOUR,
                              description=cleandoc(f"""
                Currently in:
                    {sep2} {total_voice_clients} voice channels
                    {sep2} {total_guild_count} servers
                Currently using:
                    {sep1} {self.bot.shard_count} shards
                    {sep1} {raw_ram_usage / 1024 ** 2:.1f}MB of RAM
                and can be used by {total_members:,} people!
            """)).set_footer(text=footer).set_thumbnail(
                                  url=self.bot.user.avatar.url)

        await ctx.send(embed=embed)
    async def send_unhandled_msg(self,
        event: str,
        traceback: str,
        extra_fields: list[tuple[Any, Any, bool]],
        author_name: Optional[str] = None,
        icon_url: Optional[str] = None,
    ):
        error_webhook = self.bot.channels["errors"]
        row = await self.bot.pool.fetchrow("""
            UPDATE errors SET occurrences = occurrences + 1
            WHERE traceback = $1
            RETURNING *
        """, traceback)
        if row is not None:
            err_msg = await error_webhook.fetch_message(row["message_id"])
            err_msg.embeds[0].set_footer(text=f"This error has occurred {row['occurrences']} times!")
            return await err_msg.edit(embeds=err_msg.embeds)

        fields = [
            ("Event", event, True),
            ("Bot User", self.bot.user, True),
            BLANK, *extra_fields,
            ("Ram Usage", f"{psutil.virtual_memory().percent}%", True),
            ("Running Task Count", len(asyncio.all_tasks()),     True),
            ("Local Thread Count", psutil.Process().num_threads(), True),
        ]
        if self.bot.cluster_id is not None:
            fields.extend((
                ("Cluster ID", self.bot.cluster_id, True),
                ("Handling Shards", self.bot.shard_ids, True)
            ))

        error_msg = discord.Embed(title=traceback.split("\n")[-2][:256], colour=utils.RED)
        if author_name is not None:
            if icon_url is None:
                error_msg.set_author(name=author_name)
            else:
                error_msg.set_author(name=author_name, icon_url=icon_url)

        for name, value, inline in fields:
            value = value if value == "\u200B" else f"`{value}`"
            error_msg.add_field(name=name, value=value, inline=inline)

        view = utils.ShowTracebackView(f"```\n{traceback}```")
        err_msg = await error_webhook.send(embed=error_msg, view=view, wait=True)
        message_id = await self.bot.pool.fetchval("""
            INSERT INTO errors(traceback, message_id)
            VALUES($1, $2)

            ON CONFLICT (traceback)
            DO UPDATE SET occurrences = errors.occurrences + 1
            RETURNING errors.message_id
        """, traceback, err_msg.id)

        assert message_id is not None
        if message_id != err_msg.id:
            return await err_msg.delete()

        if self.bot.websocket is not None:
            ws_json = utils.data_to_ws_json("SEND", target="support", **{
                "c": "load_view",
                "a": {
                    "traceback": traceback,
                    "message_id": message_id
                }
            })
            await self.bot.websocket.send(ws_json)