Beispiel #1
0
    def test_humanize_delta_raises_for_invalid_max_units(self):
        """humanize_delta should raises ValueError('max_units must be positive') for invalid max_units."""
        test_cases = (-1, 0)

        for max_units in test_cases:
            with self.subTest(max_units=max_units), self.assertRaises(ValueError) as error:
                time.humanize_delta(relativedelta(days=2, hours=2), 'hours', max_units)
            self.assertEqual(str(error.exception), 'max_units must be positive')
Beispiel #2
0
 def test_humanize_delta_handle_unknown_units(self):
     """humanize_delta should be able to handle unknown units, and will not abort."""
     # Does not abort for unknown units, as the unit name is checked
     # against the attribute of the relativedelta instance.
     self.assertEqual(
         time.humanize_delta(relativedelta(days=2, hours=2), 'elephants',
                             2), '2 days and 2 hours')
Beispiel #3
0
 def test_humanize_delta_handle_high_units(self):
     """humanize_delta should be able to handle very high units."""
     # Very high maximum units, but it only ever iterates over
     # each value the relativedelta might have.
     self.assertEqual(
         time.humanize_delta(relativedelta(days=2, hours=2), 'hours', 20),
         '2 days and 2 hours')
Beispiel #4
0
    async def new_reminder(self, ctx: Context, expiration: Duration, *, content: str) -> t.Optional[discord.Message]:
        """
        Set yourself a simple reminder.

        Expiration is parsed per: http://strftime.org/
        """
        embed = discord.Embed()

        # If the user is not staff, we need to verify whether or not to make a reminder at all.
        if without_role_check(ctx, *STAFF_ROLES):

            # If they don't have permission to set a reminder in this channel
            if ctx.channel.id not in WHITELISTED_CHANNELS:
                embed.colour = discord.Colour.red()
                embed.title = random.choice(NEGATIVE_REPLIES)
                embed.description = "Sorry, you can't do that here!"

                return await ctx.send(embed=embed)

            # Get their current active reminders
            active_reminders = await self.bot.api_client.get(
                'bot/reminders',
                params={
                    'author__id': str(ctx.author.id)
                }
            )

            # Let's limit this, so we don't get 10 000
            # reminders from kip or something like that :P
            if len(active_reminders) > MAXIMUM_REMINDERS:
                embed.colour = discord.Colour.red()
                embed.title = random.choice(NEGATIVE_REPLIES)
                embed.description = "You have too many active reminders!"

                return await ctx.send(embed=embed)

        # Now we can attempt to actually set the reminder.
        reminder = await self.bot.api_client.post(
            'bot/reminders',
            json={
                'author': ctx.author.id,
                'channel_id': ctx.message.channel.id,
                'jump_url': ctx.message.jump_url,
                'content': content,
                'expiration': expiration.isoformat()
            }
        )

        now = datetime.utcnow() - timedelta(seconds=1)
        humanized_delta = humanize_delta(relativedelta(expiration, now))

        # Confirm to the user that it worked.
        await self._send_confirmation(
            ctx,
            on_success=f"Your reminder will arrive in {humanized_delta}!",
            reminder_id=reminder["id"],
            delivery_dt=expiration,
        )

        self.schedule_task(reminder["id"], reminder)
Beispiel #5
0
    async def predicate(ctx: Context) -> bool:
        user_bucket = usercd.get_bucket(ctx.message)

        if all(role.id not in ignore for role in ctx.author.roles):
            user_rate = user_bucket.update_rate_limit()

            if user_rate:
                # Can't use api; cause: member limit
                delta = relativedelta(seconds=int(user_rate))
                cooldown = humanize_delta(delta)
                message = (
                    "You've used up your limit for Wolfram|Alpha requests.\n"
                    f"Cooldown: {cooldown}")
                await send_embed(ctx, message)
                return False

        guild_bucket = guildcd.get_bucket(ctx.message)
        guild_rate = guild_bucket.update_rate_limit()

        # Repr has a token attribute to read requests left
        log.debug(guild_bucket)

        if guild_rate:
            # Can't use api; cause: guild limit
            message = (
                "The max limit of requests for the server has been reached for today.\n"
                f"Cooldown: {int(guild_rate)}")
            await send_embed(ctx, message)
            return False

        return True
Beispiel #6
0
    async def set_slowmode(self, ctx: Context, channel: Optional[TextChannel],
                           delay: DurationDelta) -> None:
        """Set the slowmode delay for a text channel."""
        # Use the channel this command was invoked in if one was not given
        if channel is None:
            channel = ctx.channel

        # Convert `dateutil.relativedelta.relativedelta` to `datetime.timedelta`
        # Must do this to get the delta in a particular unit of time
        utcnow = datetime.utcnow()
        slowmode_delay = (utcnow + delay - utcnow).total_seconds()

        humanized_delay = time.humanize_delta(delay)

        # Ensure the delay is within discord's limits
        if slowmode_delay <= SLOWMODE_MAX_DELAY:
            log.info(
                f'{ctx.author} set the slowmode delay for #{channel} to {humanized_delay}.'
            )

            await channel.edit(slowmode_delay=slowmode_delay)
            await ctx.send(
                f'{Emojis.check_mark} The slowmode delay for {channel.mention} is now {humanized_delay}.'
            )

        else:
            log.info(
                f'{ctx.author} tried to set the slowmode delay of #{channel} to {humanized_delay}, '
                'which is not between 0 and 6 hours.')

            await ctx.send(
                f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.'
            )
Beispiel #7
0
    async def list_reminders(self, ctx: Context) -> None:
        """View a paginated embed of all reminders for your user."""
        # Get all the user's reminders from the database.
        data = await self.bot.api_client.get(
            'bot/reminders',
            params={'author__id': str(ctx.author.id)}
        )

        now = datetime.utcnow()

        # Make a list of tuples so it can be sorted by time.
        reminders = sorted(
            (
                (rem['content'], rem['expiration'], rem['id'], rem['mentions'])
                for rem in data
            ),
            key=itemgetter(1)
        )

        lines = []

        for content, remind_at, id_, mentions in reminders:
            # Parse and humanize the time, make it pretty :D
            remind_datetime = isoparse(remind_at).replace(tzinfo=None)
            time = humanize_delta(relativedelta(remind_datetime, now))

            mentions = ", ".join(
                # Both Role and User objects have the `name` attribute
                mention.name for mention in self.get_mentionables(mentions)
            )
            mention_string = f"\n**Mentions:** {mentions}" if mentions else ""

            text = textwrap.dedent(f"""
            **Reminder #{id_}:** *expires in {time}* (ID: {id_}){mention_string}
            {content}
            """).strip()

            lines.append(text)

        embed = discord.Embed()
        embed.colour = discord.Colour.blurple()
        embed.title = f"Reminders for {ctx.author}"

        # Remind the user that they have no reminders :^)
        if not lines:
            embed.description = "No active reminders could be found."
            await ctx.send(embed=embed)
            return

        # Construct the embed and paginate it.
        embed.colour = discord.Colour.blurple()

        await LinePaginator.paginate(
            lines,
            ctx, embed,
            max_lines=3,
            empty=True
        )
Beispiel #8
0
    async def punish(self, msg: Message, member: Member, reason: str, messages: List[Message]):
        # Sanity check to ensure we're not lagging behind
        if self.muted_role not in member.roles:
            remove_role_after = AntiSpamConfig.punishment['remove_after']
            duration_delta = relativedelta(seconds=remove_role_after)
            human_duration = humanize_delta(duration_delta)

            mod_alert_message = (
                f"**Triggered by:** {member.display_name}#{member.discriminator} (`{member.id}`)\n"
                f"**Channel:** {msg.channel.mention}\n"
                f"**Reason:** {reason}\n"
            )

            # For multiple messages, use the logs API
            if len(messages) > 1:
                url = await self.mod_log.upload_log(messages)
                mod_alert_message += f"A complete log of the offending messages can be found [here]({url})"
            else:
                mod_alert_message += "Message:\n"
                content = messages[0].clean_content
                remaining_chars = 2040 - len(mod_alert_message)

                if len(content) > remaining_chars:
                    content = content[:remaining_chars] + "..."

                mod_alert_message += f"{content}"

            await self.mod_log.send_log_message(
                icon_url=Icons.filtering,
                colour=Colour(Colours.soft_red),
                title=f"Spam detected!",
                text=mod_alert_message,
                thumbnail=msg.author.avatar_url_as(static_format="png"),
                channel_id=Channels.mod_alerts,
                ping_everyone=AntiSpamConfig.ping_everyone
            )

            await member.add_roles(self.muted_role, reason=reason)
            description = textwrap.dedent(f"""
                **Channel**: {msg.channel.mention}
                **User**: {msg.author.mention} (`{msg.author.id}`)
                **Reason**: {reason}
                Role will be removed after {human_duration}.
            """)

            await self.mod_log.send_log_message(
                icon_url=Icons.user_mute, colour=Colour(Colours.soft_red),
                title="User muted", text=description
            )

            await asyncio.sleep(remove_role_after)
            await member.remove_roles(self.muted_role, reason="AntiSpam mute expired")

            await self.mod_log.send_log_message(
                icon_url=Icons.user_mute, colour=Colour(Colours.soft_green),
                title="User unmuted",
                text=f"Was muted by `AntiSpam` cog for {human_duration}."
            )
Beispiel #9
0
    async def list_reminders(self, ctx: Context):
        """
        View a paginated embed of all reminders for your user.
        """

        # Get all the user's reminders from the database.
        response = await self.bot.http_session.get(
            url=URLs.site_reminders_user_api,
            params={"user_id": str(ctx.author.id)},
            headers=self.headers
        )

        data = await response.json()
        now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)

        # Make a list of tuples so it can be sorted by time.
        reminders = [
            (rem["content"], rem["remind_at"], rem["friendly_id"]) for rem in data["reminders"]
        ]

        reminders.sort(key=lambda rem: rem[1])

        lines = []

        for index, (content, remind_at, friendly_id) in enumerate(reminders):
            # Parse and humanize the time, make it pretty :D
            remind_datetime = parse_rfc1123(remind_at)
            time = humanize_delta(relativedelta(remind_datetime, now))

            text = textwrap.dedent(f"""
            **Reminder #{index}:** *expires in {time}* (ID: {friendly_id})
            {content}
            """).strip()

            lines.append(text)

        embed = Embed()
        embed.colour = Colour.blurple()
        embed.title = f"Reminders for {ctx.author}"

        # Remind the user that they have no reminders :^)
        if not lines:
            embed.description = "No active reminders could be found."
            return await ctx.send(embed=embed)

        # Construct the embed and paginate it.
        embed.colour = Colour.blurple()

        await LinePaginator.paginate(
            lines,
            ctx, embed,
            max_lines=3,
            empty=True
        )
Beispiel #10
0
    async def get_slowmode(self, ctx: Context,
                           channel: Optional[TextChannel]) -> None:
        """Get the slowmode delay for a text channel."""
        # Use the channel this command was invoked in if one was not given
        if channel is None:
            channel = ctx.channel

        delay = relativedelta(seconds=channel.slowmode_delay)
        humanized_delay = time.humanize_delta(delay)

        await ctx.send(
            f'The slowmode delay for {channel.mention} is {humanized_delay}.')
Beispiel #11
0
    def test_humanize_delta_should_normal_usage(self):
        """Testing humanize delta."""
        test_cases = (
            (relativedelta(days=2), 'seconds', 1, '2 days'),
            (relativedelta(days=2, hours=2), 'seconds', 2, '2 days and 2 hours'),
            (relativedelta(days=2, hours=2), 'seconds', 1, '2 days'),
            (relativedelta(days=2, hours=2), 'days', 2, '2 days'),
        )

        for delta, precision, max_units, expected in test_cases:
            with self.subTest(delta=delta, precision=precision, max_units=max_units, expected=expected):
                self.assertEqual(time.humanize_delta(delta, precision, max_units), expected)
Beispiel #12
0
    async def list_reminders(self, ctx: Context) -> Optional[Message]:
        """View a paginated embed of all reminders for your user."""
        # Get all the user's reminders from the database.
        data = await self.bot.api_client.get(
            'bot/reminders',
            params={'author__id': str(ctx.author.id)}
        )

        now = datetime.utcnow()

        # Make a list of tuples so it can be sorted by time.
        reminders = sorted(
            (
                (rem['content'], rem['expiration'], rem['id'])
                for rem in data
            ),
            key=itemgetter(1)
        )

        lines = []

        for content, remind_at, id_ in reminders:
            # Parse and humanize the time, make it pretty :D
            remind_datetime = datetime.fromisoformat(remind_at[:-1])
            time = humanize_delta(relativedelta(remind_datetime, now))

            text = textwrap.dedent(f"""
            **Reminder #{id_}:** *expires in {time}* (ID: {id_})
            {content}
            """).strip()

            lines.append(text)

        embed = Embed()
        embed.colour = Colour.blurple()
        embed.title = f"Reminders for {ctx.author}"

        # Remind the user that they have no reminders :^)
        if not lines:
            embed.description = "No active reminders could be found."
            return await ctx.send(embed=embed)

        # Construct the embed and paginate it.
        embed.colour = Colour.blurple()

        await LinePaginator.paginate(
            lines,
            ctx, embed,
            max_lines=3,
            empty=True
        )
Beispiel #13
0
    async def mention(self, ctx: Context, *, role: Role) -> None:
        """Set a role to be mentionable for a limited time."""
        if role.mentionable:
            await ctx.send(f"{role} is already mentionable!")
            return

        await role.edit(reason=f"Role unlocked by {ctx.author}",
                        mentionable=True)

        human_time = humanize_delta(
            relativedelta.relativedelta(seconds=Mention.message_timeout))
        await ctx.send(
            f"{role} has been made mentionable. I will reset it in {human_time}, or when someone mentions this role."
        )

        def check(m: Message) -> bool:
            """Checks that the message contains the role mention."""
            return role in m.role_mentions

        try:
            msg = await self.bot.wait_for("message",
                                          check=check,
                                          timeout=Mention.message_timeout)
        except TimeoutError:
            await role.edit(mentionable=False,
                            reason="Automatic role lock - timeout.")
            await ctx.send(
                f"{ctx.author.mention}, you took too long. I have reset {role} to be unmentionable."
            )
            return

        if any(r.id in MODERATION_ROLES for r in msg.author.roles):
            await sleep(Mention.reset_delay)
            await role.edit(mentionable=False,
                            reason=f"Automatic role lock by {msg.author}")
            await ctx.send(
                f"{ctx.author.mention}, I have reset {role} to be unmentionable as "
                f"{msg.author if msg.author != ctx.author else 'you'} sent a message mentioning it."
            )
            return

        await role.edit(
            mentionable=False,
            reason=f"Automatic role lock - unauthorised use by {msg.author}")
        await ctx.send(
            f"{ctx.author.mention}, I have reset {role} to be unmentionable "
            f"as I detected unauthorised use by {msg.author} (ID: {msg.author.id})."
        )
Beispiel #14
0
    async def _activity_review(self, member: Member) -> str:
        """
        Format the activity of the nominee.

        Adds details on how long they've been on the server, their total message count,
        and the channels they're the most active in.
        """
        log.trace(f"Fetching the metricity data for {member.id}'s review")
        try:
            user_activity = await self.bot.api_client.get(
                f"bot/users/{member.id}/metricity_review_data")
        except ResponseCodeError as e:
            if e.status == 404:
                log.trace(
                    f"The user {member.id} seems to have no activity logged in Metricity."
                )
                messages = "no"
                channels = ""
            else:
                log.trace(
                    f"An unexpected error occured while fetching information of user {member.id}."
                )
                raise
        else:
            log.trace(f"Activity found for {member.id}, formatting review.")
            messages = user_activity["total_messages"]
            # Making this part flexible to the amount of expected and returned channels.
            first_channel = user_activity["top_channel_activity"][0]
            channels = f", with {first_channel[1]} messages in {first_channel[0]}"

            if len(user_activity["top_channel_activity"]) > 1:
                channels += ", " + ", ".join(
                    f"{count} in {channel}" for channel, count in
                    user_activity["top_channel_activity"][1:-1])
                last_channel = user_activity["top_channel_activity"][-1]
                channels += f", and {last_channel[1]} in {last_channel[0]}"

        time_on_server = humanize_delta(relativedelta(datetime.utcnow(),
                                                      member.joined_at),
                                        max_units=2)
        review = (
            f"{member.name} has been on the server for **{time_on_server}**"
            f" and has **{messages} messages**{channels}.")

        return review
Beispiel #15
0
    async def on_member_join(self, member: discord.Member) -> None:
        """Log member join event to user log."""
        if member.guild.id != GuildConstant.id:
            return

        now = datetime.utcnow()
        difference = abs(relativedelta(now, member.created_at))

        message = format_user(member) + "\n\n**Account age:** " + humanize_delta(difference)

        if difference.days < 1 and difference.months < 1 and difference.years < 1:  # New user account!
            message = f"{Emojis.new} {message}"

        await self.send_log_message(
            Icons.sign_in, Colours.soft_green,
            "User joined", message,
            thumbnail=member.avatar_url_as(static_format="png"),
            channel_id=Channels.user_log
        )
Beispiel #16
0
    async def on_member_join(self, member: Member):
        if member.guild.id != GuildConstant.id:
            return

        message = f"{member.name}#{member.discriminator} (`{member.id}`)"
        now = datetime.datetime.utcnow()
        difference = abs(relativedelta(now, member.created_at))

        message += "\n\n**Account age:** " + humanize_delta(difference)

        if difference.days < 1 and difference.months < 1 and difference.years < 1:  # New user account!
            message = f"{Emojis.new} {message}"

        await self.send_log_message(
            Icons.sign_in,
            Colour(Colours.soft_green),
            "User joined",
            message,
            thumbnail=member.avatar_url_as(static_format="png"))
Beispiel #17
0
    async def predicate(ctx: Context) -> bool:
        if ctx.invoked_with == 'help':
            # if the invoked command is help we don't want to increase the ratelimits since it's not actually
            # invoking the command/making a request, so instead just check if the user/guild are on cooldown.
            guild_cooldown = not guildcd.get_bucket(
                ctx.message).get_tokens() == 0  # if guild is on cooldown
            if not any(r.id in ignore for r in ctx.author.roles
                       ):  # check user bucket if user is not ignored
                return guild_cooldown and not usercd.get_bucket(
                    ctx.message).get_tokens() == 0
            return guild_cooldown

        user_bucket = usercd.get_bucket(ctx.message)

        if all(role.id not in ignore for role in ctx.author.roles):
            user_rate = user_bucket.update_rate_limit()

            if user_rate:
                # Can't use api; cause: member limit
                delta = relativedelta(seconds=int(user_rate))
                cooldown = humanize_delta(delta)
                message = (
                    "You've used up your limit for Wolfram|Alpha requests.\n"
                    f"Cooldown: {cooldown}")
                await send_embed(ctx, message)
                return False

        guild_bucket = guildcd.get_bucket(ctx.message)
        guild_rate = guild_bucket.update_rate_limit()

        # Repr has a token attribute to read requests left
        log.debug(guild_bucket)

        if guild_rate:
            # Can't use api; cause: guild limit
            message = (
                "The max limit of requests for the server has been reached for today.\n"
                f"Cooldown: {int(guild_rate)}")
            await send_embed(ctx, message)
            return False

        return True
Beispiel #18
0
    async def on_message_edit(self, msg_before: discord.Message, msg_after: discord.Message) -> None:
        """Log message edit event to message change log."""
        if (
            not msg_before.guild
            or msg_before.guild.id != GuildConstant.id
            or msg_before.channel.id in GuildConstant.modlog_blacklist
            or msg_before.author.bot
        ):
            return

        self._cached_edits.append(msg_before.id)

        if msg_before.content == msg_after.content:
            return

        channel = msg_before.channel
        channel_name = f"{channel.category}/#{channel.name}" if channel.category else f"#{channel.name}"

        cleaned_contents = (escape_markdown(msg.clean_content).split() for msg in (msg_before, msg_after))
        # Getting the difference per words and group them by type - add, remove, same
        # Note that this is intended grouping without sorting
        diff = difflib.ndiff(*cleaned_contents)
        diff_groups = tuple(
            (diff_type, tuple(s[2:] for s in diff_words))
            for diff_type, diff_words in itertools.groupby(diff, key=lambda s: s[0])
        )

        content_before: t.List[str] = []
        content_after: t.List[str] = []

        for index, (diff_type, words) in enumerate(diff_groups):
            sub = ' '.join(words)
            if diff_type == '-':
                content_before.append(f"[{sub}](http://o.hi)")
            elif diff_type == '+':
                content_after.append(f"[{sub}](http://o.hi)")
            elif diff_type == ' ':
                if len(words) > 2:
                    sub = (
                        f"{words[0] if index > 0 else ''}"
                        " ... "
                        f"{words[-1] if index < len(diff_groups) - 1 else ''}"
                    )
                content_before.append(sub)
                content_after.append(sub)

        response = (
            f"**Author:** {format_user(msg_before.author)}\n"
            f"**Channel:** {channel_name} (`{channel.id}`)\n"
            f"**Message ID:** `{msg_before.id}`\n"
            "\n"
            f"**Before**:\n{' '.join(content_before)}\n"
            f"**After**:\n{' '.join(content_after)}\n"
            "\n"
            f"[Jump to message]({msg_after.jump_url})"
        )

        if msg_before.edited_at:
            # Message was previously edited, to assist with self-bot detection, use the edited_at
            # datetime as the baseline and create a human-readable delta between this edit event
            # and the last time the message was edited
            timestamp = msg_before.edited_at
            delta = humanize_delta(relativedelta(msg_after.edited_at, msg_before.edited_at))
            footer = f"Last edited {delta} ago"
        else:
            # Message was not previously edited, use the created_at datetime as the baseline, no
            # delta calculation needed
            timestamp = msg_before.created_at
            footer = None

        await self.send_log_message(
            Icons.message_edit, Colour.blurple(), "Message edited", response,
            channel_id=Channels.message_log, timestamp_override=timestamp, footer=footer
        )
Beispiel #19
0
    async def new_reminder(self, ctx: Context, mentions: Greedy[Mentionable],
                           expiration: Duration, *, content: str) -> None:
        """
        Set yourself a simple reminder.

        Expiration is parsed per: http://strftime.org/
        """
        # If the user is not staff, we need to verify whether or not to make a reminder at all.
        if without_role_check(ctx, *STAFF_ROLES):

            # If they don't have permission to set a reminder in this channel
            if ctx.channel.id not in WHITELISTED_CHANNELS:
                await send_denial(ctx, "Sorry, you can't do that here!")
                return

            # Get their current active reminders
            active_reminders = await self.bot.api_client.get(
                'bot/reminders', params={'author__id': str(ctx.author.id)})

            # Let's limit this, so we don't get 10 000
            # reminders from kip or something like that :P
            if len(active_reminders) > MAXIMUM_REMINDERS:
                await send_denial(ctx, "You have too many active reminders!")
                return

        # Remove duplicate mentions
        mentions = set(mentions)
        mentions.discard(ctx.author)

        # Filter mentions to see if the user can mention members/roles
        if not await self.validate_mentions(ctx, mentions):
            return

        mention_ids = [mention.id for mention in mentions]

        # Now we can attempt to actually set the reminder.
        reminder = await self.bot.api_client.post('bot/reminders',
                                                  json={
                                                      'author':
                                                      ctx.author.id,
                                                      'channel_id':
                                                      ctx.message.channel.id,
                                                      'jump_url':
                                                      ctx.message.jump_url,
                                                      'content':
                                                      content,
                                                      'expiration':
                                                      expiration.isoformat(),
                                                      'mentions':
                                                      mention_ids,
                                                  })

        now = datetime.utcnow() - timedelta(seconds=1)
        humanized_delta = humanize_delta(relativedelta(expiration, now))
        mention_string = (f"Your reminder will arrive in {humanized_delta} "
                          f"and will mention {len(mentions)} other(s)!")

        # Confirm to the user that it worked.
        await self._send_confirmation(
            ctx,
            on_success=mention_string,
            reminder_id=reminder["id"],
            delivery_dt=expiration,
        )

        self.schedule_reminder(reminder)
Beispiel #20
0
        return role_stats

    def get_extended_server_info(self, ctx: Context) -> str:
        """Return additional server info only visible in moderation channels."""
        talentpool_info = ""
        if cog := self.bot.get_cog("Talentpool"):
            num_nominated = len(cog.cache) if cog.cache else "-"
            talentpool_info = f"Nominated: {num_nominated}\n"

        bb_info = ""
        if cog := self.bot.get_cog("Big Brother"):
            bb_info = f"BB-watched: {len(cog.watched_users)}\n"

        defcon_info = ""
        if cog := self.bot.get_cog("Defcon"):
            threshold = humanize_delta(cog.threshold) if cog.threshold else "-"
            defcon_info = f"Defcon threshold: {threshold}\n"

        verification = f"Verification level: {ctx.guild.verification_level.name}\n"

        python_general = self.bot.get_channel(
            constants.Channels.python_general)

        return textwrap.dedent(f"""
            {talentpool_info}\
            {bb_info}\
            {defcon_info}\
            {verification}\
            {python_general.mention} cooldown: {python_general.slowmode_delay}s
        """)
Beispiel #21
0
            name = escape_markdown(user['name'])
            user_str = f"<@{user['id']}> ({name}#{user['discriminator']:04})"

        if active:
            remaining = time.until_expiration(expires_at) or "Expired"
        else:
            remaining = "Inactive"

        if expires_at is None:
            duration = "*Permanent*"
        else:
            date_from = datetime.fromtimestamp(
                float(time.DISCORD_TIMESTAMP_REGEX.match(created).group(1)),
                timezone.utc)
            date_to = dateutil.parser.isoparse(expires_at)
            duration = humanize_delta(relativedelta(date_to, date_from))

        # Format `dm_sent`
        if dm_sent is None:
            dm_sent_text = "N/A"
        else:
            dm_sent_text = "Yes" if dm_sent else "No"

        lines = textwrap.dedent(f"""
            {"**===============**" if active else "==============="}
            Status: {"__**Active**__" if active else "Inactive"}
            User: {user_str}
            Type: **{infraction["type"]}**
            DM Sent: {dm_sent_text}
            Shadow: {infraction["hidden"]}
            Created: {created}
Beispiel #22
0
    async def on_message_edit(self, before: Message, after: Message) -> None:
        """Log message edit event to message change log."""
        if (not before.guild or before.guild.id != GuildConstant.id
                or before.channel.id in GuildConstant.ignored
                or before.author.bot):
            return

        self._cached_edits.append(before.id)

        if before.content == after.content:
            return

        author = before.author
        channel = before.channel

        if channel.category:
            before_response = (
                f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
                f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
                f"**Message ID:** `{before.id}`\n"
                "\n"
                f"{before.clean_content}")

            after_response = (
                f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
                f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
                f"**Message ID:** `{before.id}`\n"
                "\n"
                f"{after.clean_content}")
        else:
            before_response = (
                f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
                f"**Channel:** #{channel.name} (`{channel.id}`)\n"
                f"**Message ID:** `{before.id}`\n"
                "\n"
                f"{before.clean_content}")

            after_response = (
                f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
                f"**Channel:** #{channel.name} (`{channel.id}`)\n"
                f"**Message ID:** `{before.id}`\n"
                "\n"
                f"{after.clean_content}")

        if before.edited_at:
            # Message was previously edited, to assist with self-bot detection, use the edited_at
            # datetime as the baseline and create a human-readable delta between this edit event
            # and the last time the message was edited
            timestamp = before.edited_at
            delta = humanize_delta(
                relativedelta(after.edited_at, before.edited_at))
            footer = f"Last edited {delta} ago"
        else:
            # Message was not previously edited, use the created_at datetime as the baseline, no
            # delta calculation needed
            timestamp = before.created_at
            footer = None

        await self.send_log_message(Icons.message_edit,
                                    Colour.blurple(),
                                    "Message edited (Before)",
                                    before_response,
                                    channel_id=Channels.message_log,
                                    timestamp_override=timestamp,
                                    footer=footer)

        await self.send_log_message(Icons.message_edit,
                                    Colour.blurple(),
                                    "Message edited (After)",
                                    after_response,
                                    channel_id=Channels.message_log,
                                    timestamp_override=after.edited_at)
Beispiel #23
0
def test_humanize_delta(delta: relativedelta, precision: str, max_units: int,
                        expected: str):
    assert time.humanize_delta(delta, precision, max_units) == expected
Beispiel #24
0
def test_humanize_delta_raises_for_invalid_max_units(max_units: int):
    with pytest.raises(ValueError, match='max_units must be positive'):
        time.humanize_delta(relativedelta(days=2, hours=2), 'hours', max_units)
Beispiel #25
0
    async def server(self, ctx: Context) -> None:
        """
        Returns information about the guild the command was run in.
        Information included:
        - A brief server description
        - Server icon
        - Date server was created (and how long ago that was)
        - Human member count
        - Bot count
        - How many members are online/offline
        - Current server owner
        - Boost information
        """
        # Save guild to variable instead of using `ctx.message.guild` each time
        guild: Guild = ctx.message.guild

        # Calculate when the guild was created and how long ago that was and present it in a human-friendly format
        now: datetime = datetime.utcnow()
        delta: relativedelta = abs(relativedelta(now, guild.created_at))
        humanized: str = humanize_delta(delta)
        created_at: str = guild.created_at.strftime('%d %B %Y')
        created_ago: str = f'{humanized} ago'

        # Get guild's icon
        icon: str = guild.icon_url

        # Get information about the current members of the guild
        humans: int = len(
            [x for x in filter(lambda member: not member.bot, guild.members)])
        bots: int = guild.member_count - humans
        total: int = humans + bots
        invite: Invite = await self.bot.fetch_invite('discord.gg/RqPtwNxd8h')
        online: int = invite.approximate_presence_count
        offline: int = invite.approximate_member_count - invite.approximate_presence_count

        # Get owner information
        owner: str = guild.owner.mention

        # Get server boost information
        number_of_boosts: int = guild.premium_subscription_count
        boost_level: int = guild.premium_tier

        # Declare embed with all fields added
        embed = Embed(
            title='Server information',
            description='SMETCH is a community that helps those in need!',
            colour=0x00d166).add_field(
                name='Server was created on:',
                value=f'{created_at}\n{created_ago}',
                inline=False
            ).add_field(
                name='Member count:',
                value=f'👥 {humans} humans\n🤖 {bots} bots\nTotal: {total}\
                  \n<:online:832185731845062716> {online} <:offline:832185762618671164> {offline}',
                inline=False).add_field(
                    name='Current owner:', value=owner, inline=False
                ).add_field(
                    name='Current boosts:',
                    value=f'{number_of_boosts} boosts at Level {boost_level}',
                    inline=True).set_thumbnail(url=icon)

        await ctx.send(embed=embed)
        return