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))
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)
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()})
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()
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())
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)
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()})
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")
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)
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)
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)
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))
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
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)