Example #1
0
    def test_duration_converter_for_invalid(self):
        """Duration raises the right exception for invalid duration strings."""
        test_values = (
            # Units in wrong order
            ('1d1w'),
            ('1s1y'),

            # Duplicated units
            ('1 year 2 years'),
            ('1 M 10 minutes'),

            # Unknown substrings
            ('1MVes'),
            ('1y3breads'),

            # Missing amount
            ('ym'),

            # Incorrect whitespace
            (" 1y"),
            ("1S "),
            ("1y  1m"),

            # Garbage
            ('Guido van Rossum'),
            ('lemon lemon lemon lemon lemon lemon lemon'),
        )

        converter = Duration()

        for invalid_duration in test_values:
            with self.subTest(invalid_duration=invalid_duration):
                exception_message = f'`{invalid_duration}` is not a valid duration string.'
                with self.assertRaises(BadArgument, msg=exception_message):
                    asyncio.run(converter.convert(self.context, invalid_duration))
Example #2
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)
Example #3
0
    async def edit_reminder_duration(self, ctx: Context, id_: int, expiration: Duration) -> None:
        """
        Edit one of your reminder's expiration.

        Expiration is parsed per: http://strftime.org/
        """
        await self.edit_reminder(ctx, id_, {'expiration': expiration.isoformat()})
Example #4
0
    def __init__(self, bot: Bot, validation_errors: bool) -> None:
        self.bot = bot
        self.validation_errors = validation_errors
        role_id = AntiSpamConfig.punishment['role_id']
        self.muted_role = Object(role_id)
        self.expiration_date_converter = Duration()

        self.message_deletion_queue = dict()
        self.queue_consumption_tasks = dict()
Example #5
0
    def __init__(self, bot: Bot, validation_errors: Dict[str, str]) -> None:
        self.bot = bot
        self.validation_errors = validation_errors
        role_id = AntiSpamConfig.punishment['role_id']
        self.muted_role = Object(role_id)
        self.expiration_date_converter = Duration()

        self.message_deletion_queue = dict()

        self.bot.loop.create_task(self.alert_on_validation_error())
Example #6
0
    async def edit_reminder_duration(self, ctx: Context, id_: int,
                                     expiration: Duration) -> None:
        """
         Edit one of your reminder's expiration.

        Expiration is parsed per: http://strftime.org/
        """
        # Send the request to update the reminder in the database
        reminder = await self.bot.api_client.patch(
            'bot/reminders/' + str(id_),
            json={'expiration': expiration.isoformat()})

        # Send a confirmation message to the channel
        await self._send_confirmation(
            ctx, on_success="That reminder has been edited successfully!")

        await self._reschedule_reminder(reminder)
Example #7
0
    async def edit_reminder_duration(self, ctx: Context, id_: int,
                                     expiration: Duration) -> None:
        """
        Edit one of your reminder's expiration.

        The `expiration` duration supports the following symbols for each unit of time:
        - years: `Y`, `y`, `year`, `years`
        - months: `m`, `month`, `months`
        - weeks: `w`, `W`, `week`, `weeks`
        - days: `d`, `D`, `day`, `days`
        - hours: `H`, `h`, `hour`, `hours`
        - minutes: `M`, `minute`, `minutes`
        - seconds: `S`, `s`, `second`, `seconds`

        For example, to edit a reminder to expire in 3 days and 1 minute, you can do `!remind edit duration 1234 3d1M`.
        """
        await self.edit_reminder(ctx, id_,
                                 {'expiration': expiration.isoformat()})
Example #8
0
    def __init__(self, bot: Bot, validation_errors: Dict[str, str]) -> None:
        self.bot = bot
        self.validation_errors = validation_errors
        role_id = AntiSpamConfig.punishment['role_id']
        self.muted_role = Object(role_id)
        self.expiration_date_converter = Duration()

        self.message_deletion_queue = dict()

        # Fetch the rule configuration with the highest rule interval.
        max_interval_config = max(
            AntiSpamConfig.rules.values(),
            key=itemgetter('interval')
        )
        self.max_interval = max_interval_config['interval']
        self.cache = MessageCache(AntiSpamConfig.cache_size, newest_first=True)

        self.bot.loop.create_task(self.alert_on_validation_error(), name="AntiSpam.alert_on_validation_error")
Example #9
0
    async def test_duration_converter_for_invalid(self):
        """Duration raises the right exception for invalid duration strings."""
        test_values = (
            # Units in wrong order
            '1d1w',
            '1s1y',

            # Duplicated units
            '1 year 2 years',
            '1 M 10 minutes',

            # Unknown substrings
            '1MVes',
            '1y3breads',

            # Missing amount
            'ym',

            # Incorrect whitespace
            " 1y",
            "1S ",
            "1y  1m",

            # Garbage
            'Guido van Rossum',
            'lemon lemon lemon lemon lemon lemon lemon',
        )

        converter = Duration()

        for invalid_duration in test_values:
            with self.subTest(invalid_duration=invalid_duration):
                exception_message = f'`{invalid_duration}` is not a valid duration string.'
                with self.assertRaisesRegex(BadArgument,
                                            re.escape(exception_message)):
                    await converter.convert(self.context, invalid_duration)
Example #10
0
    def test_duration_converter_for_valid(self):
        """Duration returns the correct `datetime` for valid duration strings."""
        test_values = (
            # Simple duration strings
            ('1Y', {"years": 1}),
            ('1y', {"years": 1}),
            ('1year', {"years": 1}),
            ('1years', {"years": 1}),
            ('1m', {"months": 1}),
            ('1month', {"months": 1}),
            ('1months', {"months": 1}),
            ('1w', {"weeks": 1}),
            ('1W', {"weeks": 1}),
            ('1week', {"weeks": 1}),
            ('1weeks', {"weeks": 1}),
            ('1d', {"days": 1}),
            ('1D', {"days": 1}),
            ('1day', {"days": 1}),
            ('1days', {"days": 1}),
            ('1h', {"hours": 1}),
            ('1H', {"hours": 1}),
            ('1hour', {"hours": 1}),
            ('1hours', {"hours": 1}),
            ('1M', {"minutes": 1}),
            ('1minute', {"minutes": 1}),
            ('1minutes', {"minutes": 1}),
            ('1s', {"seconds": 1}),
            ('1S', {"seconds": 1}),
            ('1second', {"seconds": 1}),
            ('1seconds', {"seconds": 1}),

            # Complex duration strings
            (
                '1y1m1w1d1H1M1S',
                {
                    "years": 1,
                    "months": 1,
                    "weeks": 1,
                    "days": 1,
                    "hours": 1,
                    "minutes": 1,
                    "seconds": 1
                }
            ),
            ('5y100S', {"years": 5, "seconds": 100}),
            ('2w28H', {"weeks": 2, "hours": 28}),

            # Duration strings with spaces
            ('1 year 2 months', {"years": 1, "months": 2}),
            ('1d 2H', {"days": 1, "hours": 2}),
            ('1 week2 days', {"weeks": 1, "days": 2}),
        )

        converter = Duration()

        for duration, duration_dict in test_values:
            expected_datetime = self.fixed_utc_now + relativedelta(**duration_dict)

            with patch('bot.converters.datetime') as mock_datetime:
                mock_datetime.utcnow.return_value = self.fixed_utc_now

                with self.subTest(duration=duration, duration_dict=duration_dict):
                    converted_datetime = asyncio.run(converter.convert(self.context, duration))
                    self.assertEqual(converted_datetime, expected_datetime)
Example #11
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)
Example #12
0
def test_duration_converter_for_invalid(duration: str):
    converter = Duration()
    with pytest.raises(BadArgument,
                       match=f'`{duration}` is not a valid duration string.'):
        asyncio.run(converter.convert(None, duration))
Example #13
0
def test_duration_converter_for_valid(create_future_datetime: tuple):
    converter = Duration()
    duration, expected = create_future_datetime
    with patch('bot.converters.datetime') as mock_datetime:
        mock_datetime.utcnow.return_value = FIXED_UTC_NOW
        assert asyncio.run(converter.convert(None, duration)) == expected
Example #14
0
    async def new_reminder(self, ctx: Context,
                           mentions: Greedy[ReminderMention],
                           expiration: Duration, *, content: str) -> None:
        """
        Set yourself a simple reminder.

        The `expiration` duration supports the following symbols for each unit of time:
        - years: `Y`, `y`, `year`, `years`
        - months: `m`, `month`, `months`
        - weeks: `w`, `W`, `week`, `weeks`
        - days: `d`, `D`, `day`, `days`
        - hours: `H`, `h`, `hour`, `hours`
        - minutes: `M`, `minute`, `minutes`
        - seconds: `S`, `s`, `second`, `seconds`

        For example, to set a reminder that expires in 3 days and 1 minute, you can do `!remind new 3d1M Do something`.
        """
        # If the user is not staff, partner or part of the python community,
        # we need to verify whether or not to make a reminder at all.
        if await has_no_roles_check(ctx, *STAFF_PARTNERS_COMMUNITY_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,
                                                  })

        mention_string = f"Your reminder will arrive on {discord_timestamp(expiration, TimestampFormats.DAY_TIME)}"

        if mentions:
            mention_string += f" and will mention {len(mentions)} other(s)"
        mention_string += "!"

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

        self.schedule_reminder(reminder)