Beispiel #1
0
    async def multi_user_markov(self, ctx: Context, *users: discord.User):
        """Generate a markov chain based off a list of users messages.

        `users`: The list of users who's messages should be used to generate the markov chain.
        """
        if len(set(users)) < 2:
            raise commands.BadArgument(
                "You need to specify at least two users.")

        is_nsfw = ctx.channel.is_nsfw() if ctx.guild is not None else False

        async with ctx.typing():
            async with ctx.db as connection:

                coros = []
                for user in users:
                    if user == ctx.author:
                        await OptInStatus.is_opted_in(connection, ctx)
                    else:
                        await OptInStatus.is_public(connection, ctx, user)

                    coros.append(
                        MessageLog.get_user_log(connection, user, is_nsfw))

                query = ("mum", is_nsfw, 3) + tuple(user.id for user in users)
                model = await self.get_model(query, *coros, order=3)

            await self.send_markov(ctx, model, 3)
Beispiel #2
0
    async def status_pie(
        self,
        ctx: Context,
        user: Optional[discord.User] = None,
        *,
        flags: StatusPieOptions,
    ):
        """Display a status pie.

        `user`: The user who's status log to look at, defaults to you.
        `show_totals`: Sets whether status percentages should be shown, defaults to True.
        """
        user = cast(discord.User, user or ctx.author)

        if flags.num_days < MIN_DAYS:
            raise commands.BadArgument(f"You must display at least {MIN_DAYS} days.")

        async with ctx.typing():
            async with ctx.db as connection:
                await OptInStatus.is_public(connection, ctx, user)
                data = await get_status_totals(connection, user, days=flags.num_days)

                if not data:
                    raise commands.BadArgument(
                        f'User "{user}" currently has no status log data, please try again later.'
                    )

            avatar_fp = BytesIO()
            await user.avatar.replace(format="png", size=IMAGE_SIZE // 2).save(avatar_fp)

            draw_call = partial(draw_status_pie, data, avatar_fp, show_totals=flags.show_totals)
            image = await self.bot.loop.run_in_executor(None, draw_call)

            await ctx.send(file=discord.File(image, f"{user.id}_status_{ctx.message.created_at}.png"))
Beispiel #3
0
    async def status_log(
        self,
        ctx: Context,
        user: Optional[discord.User] = None,
        *,
        flags: StatusLogOptions,
    ):
        """Display a status log.

        `user`: The user who's status log to look at, defaults to you.
        `--labels`: Sets whether date and time labels should be shown, defaults to False.
        `--timezone`: The timezone offset to use in hours, defaults to the users set timezone or UTC+0.
        `--days`: The number of days to fetch status log data for. Defaults to 30.
        """
        if not flags._square and not await commands.is_owner().predicate(ctx):
            raise commands.BadArgument("Only the bot owner can set this flag.")

        user = cast(discord.User, user or ctx.author)

        timezone_offset = flags.timezone

        if timezone_offset is not None and not -14 < timezone_offset < 14:
            raise commands.BadArgument("Invalid timezone offset passed.")

        async with ctx.db as connection:
            if timezone_offset is None:
                timezone = await TimeZones.get_timezone(connection, user) or datetime.timezone.utc
            else:
                timezone = datetime.timezone(datetime.timedelta(hours=timezone_offset))

            if flags.num_days < MIN_DAYS:
                raise commands.BadArgument(f"You must display at least {MIN_DAYS} days.")

            async with ctx.typing():
                await OptInStatus.is_public(connection, ctx, user)
                data = await get_status_log(connection, user, days=flags.num_days)

                if not data:
                    raise commands.BadArgument(
                        f'User "{user}" currently has no status log data, please try again later.'
                    )

            delta = (ctx.message.created_at - data[0].start).days
            days = max(min(flags.num_days, delta), MIN_DAYS)

            draw_call = partial(
                draw_status_log,
                data,
                timezone=timezone,
                show_labels=flags.show_labels,
                num_days=days,
                square=flags._square,
            )
            image = await self.bot.loop.run_in_executor(None, draw_call)

            await ctx.send(file=discord.File(image, f"{user.id}_status_{ctx.message.created_at}.png"))
Beispiel #4
0
    async def code_guild_markov(self, ctx: Context):
        """Generate a markov chain code block."""
        async with ctx.typing():
            async with ctx.db as connection:
                is_nsfw = ctx.channel.is_nsfw() if ctx.guild is not None else False
                query = ("cgm", is_nsfw, 2, ctx.guild.id)

                coro = MessageLog.get_guild_log(connection, ctx.guild, is_nsfw)
                model = await self.get_model(query, coro, order=2)

            await self.send_markov(ctx, model, 2, callable=make_code)
Beispiel #5
0
    async def guild_markov(self, ctx: Context):
        """Generate a markov chain based off messages in the server."""
        async with ctx.typing():
            async with ctx.db as connection:
                is_nsfw = ctx.channel.is_nsfw() if ctx.guild is not None else False
                query = ("gm", is_nsfw, 3, ctx.guild.id)

                coro = MessageLog.get_guild_log(connection, ctx.guild, is_nsfw)
                model = await self.get_model(query, coro, order=3)

            await self.send_markov(ctx, model, 3)
Beispiel #6
0
    async def seeded_guild_markov(self, ctx: Context, *, seed: str):
        """Generate a markov chain based off messages in the server which starts with a given seed.

        `seed`: The string to attempt to seed the markov chain with.
        """
        async with ctx.typing():
            async with ctx.db as connection:
                is_nsfw = ctx.channel.is_nsfw() if ctx.guild is not None else False
                query = ("gm", is_nsfw, 3, ctx.guild.id)

                coro = MessageLog.get_guild_log(connection, ctx.guild, is_nsfw, False)
                model = await self.get_model(query, coro, order=3)

            await self.send_markov(ctx, model, 3, seed=seed.lower())
Beispiel #7
0
    async def code_user_markov(self, ctx: Context, user: Optional[discord.User] = None):
        """Generate a markov chain code block."""
        user = cast(discord.User, user or ctx.author)

        async with ctx.typing():
            async with ctx.db as connection:
                await OptInStatus.is_public(connection, ctx, user)

                is_nsfw = ctx.channel.is_nsfw() if ctx.guild is not None else False
                query = ("cum", is_nsfw, 2, user.id)

                coro = MessageLog.get_user_log(connection, user, is_nsfw)
                model = await self.get_model(query, coro, order=2)

            await self.send_markov(ctx, model, 2, callable=make_code)
Beispiel #8
0
    async def user_markov(self, ctx: Context, *, user: Optional[discord.User] = None):
        """Generate a markov chain based off a users messages.

        `user`: The user who's messages should be used to generate the markov chain, defaults to you.
        """
        user = cast(discord.User, user or ctx.author)

        async with ctx.typing():
            async with ctx.db as connection:
                await OptInStatus.is_public(connection, ctx, user)

                is_nsfw = ctx.channel.is_nsfw() if ctx.guild is not None else False
                query = ("um", is_nsfw, 2, user.id)

                coro = MessageLog.get_user_log(connection, user, is_nsfw)
                model = await self.get_model(query, coro, order=2)

            await self.send_markov(ctx, model, 2)
Beispiel #9
0
    async def gameboy(self,
                      ctx: Context,
                      *,
                      game_name: str = "red") -> None:  # type: ignore
        if self.game is not None:
            raise commands.BadArgument("You are already playing!")

        game = ROOT_DIR / f"{game_name}.gb"
        if not game.exists():
            game = ROOT_DIR / f"{game_name}.gbc"
            if not game.exists():
                raise commands.BadArgument(f"{game_name} is not a game!")

        async with ctx.typing():
            self.game = GameBoyView(self, str(game), ctx.author)
            image_url = await self.game.render()
            await ctx.send(embed=self.game.embed.set_image(url=image_url),
                           view=self.game)  # type: ignore
Beispiel #10
0
    async def tags(self, ctx: Context, *, options: TagOptions):
        def check(message: discord.Message):
            if message.channel != ctx.channel or message.author != CONFIG.R_DANNY:
                return False
            if len(message.attachments
                   ) != 1 or message.attachments[0].filename != "tags.txt":
                return False
            return True

        try:
            await ctx.send(
                f"{PAUSE_BUTTON} Use `@{CONFIG.R_DANNY.name}#{CONFIG.R_DANNY.discriminator} tag all --text` to retrieve the tag list.",
                delete_after=15,
            )

            message: discord.Message = await ctx.bot.wait_for("message",
                                                              check=check,
                                                              timeout=30)
            prefix = message.content[:-14]
        except asyncio.TimeoutError:
            return

        await ctx.send(f"{LOADING_BUTTON} Processing...", delete_after=15)

        async with ctx.typing():

            contents = await message.attachments[0].read()
            contents = contents.decode("utf-8")
            sep, contents = contents.split("\n", 1)
            key, contents = contents.split("\n", 1)

            all_tags: dict[str, Tag] = {}
            tag_owners: dict[int, list[str]] = defaultdict(list)

            for match in TAG_FILE_REGEX.finditer(contents):
                tag, owner_id, uses, can_delete, is_alias = match.groups()
                tag = tag.rstrip()
                owner_id = int(owner_id)
                uses = int(uses)
                can_delete = can_delete == "True"
                is_alias = is_alias == "True"

                all_tags[tag] = Tag(tag, owner_id, uses, can_delete, is_alias,
                                    match.group())
                tag_owners[owner_id].append(tag)

            orphaned_tags: list[str] = []

            for user_id, tags in tag_owners.items():
                if ctx.guild.get_member(user_id) is None:
                    orphaned_tags.extend(tags)

            tag_file = io.BytesIO()

            if options.claim:
                for tag in sorted(orphaned_tags,
                                  key=lambda t: all_tags[t].uses,
                                  reverse=True):
                    tag_file.write(
                        (f"{prefix}tag claim {tag}\n").encode("utf-8"))
            else:
                tag_file.write((sep + "\n").encode("utf-8"))
                tag_file.write((key + "\n").encode("utf-8"))
                tag_file.write((sep + "\n").encode("utf-8"))

                for tag in sorted(orphaned_tags,
                                  key=lambda t: all_tags[t].uses,
                                  reverse=True):
                    tag_file.write(
                        (all_tags[tag].match + "\n").encode("utf-8"))

                tag_file.write((sep + "\n").encode("utf-8"))
            tag_file.seek(0)

        await ctx.send(file=discord.File(tag_file, "available_tags.txt"))