|
22 | 22 | SOFTWARE.
|
23 | 23 | """
|
24 | 24 |
|
25 |
| -__version__ = '2.13.10' |
| 25 | +__version__ = '2.13.11' |
26 | 26 |
|
27 | 27 | import asyncio
|
28 | 28 | import logging
|
|
37 | 37 | from discord.ext import commands
|
38 | 38 | from discord.ext.commands.view import StringView
|
39 | 39 |
|
| 40 | +import isodate |
| 41 | + |
40 | 42 | from aiohttp import ClientSession
|
41 | 43 | from colorama import init, Fore, Style
|
42 | 44 | from emoji import UNICODE_EMOJI
|
43 | 45 | from motor.motor_asyncio import AsyncIOMotorClient
|
44 | 46 |
|
45 | 47 | from core.changelog import Changelog
|
46 |
| -from core.clients import ModmailApiClient, SelfHostedClient |
47 |
| -from core.clients import PluginDatabaseClient |
| 48 | +from core.clients import ModmailApiClient, SelfHostedClient, PluginDatabaseClient |
48 | 49 | from core.config import ConfigManager
|
49 | 50 | from core.utils import info, error
|
50 | 51 | from core.models import Bot
|
51 | 52 | from core.thread import ThreadManager
|
| 53 | +from core.time import human_timedelta |
52 | 54 |
|
53 | 55 |
|
54 | 56 | init()
|
@@ -431,33 +433,102 @@ async def on_ready(self):
|
431 | 433 | )
|
432 | 434 | logger.info(LINE)
|
433 | 435 |
|
434 |
| - async def process_modmail(self, message): |
435 |
| - """Processes messages sent to the bot.""" |
436 |
| - |
| 436 | + async def retrieve_emoji(self): |
437 | 437 | ctx = SimpleNamespace(bot=self, guild=self.modmail_guild)
|
438 | 438 | converter = commands.EmojiConverter()
|
439 | 439 |
|
440 |
| - blocked_emoji = self.config.get('blocked_emoji', '🚫') |
441 | 440 | sent_emoji = self.config.get('sent_emoji', '✅')
|
| 441 | + blocked_emoji = self.config.get('blocked_emoji', '🚫') |
| 442 | + |
| 443 | + if sent_emoji not in UNICODE_EMOJI: |
| 444 | + try: |
| 445 | + sent_emoji = await converter.convert( |
| 446 | + ctx, sent_emoji.strip(':') |
| 447 | + ) |
| 448 | + except commands.BadArgument: |
| 449 | + logger.warning(info(f'Sent Emoji ({sent_emoji}) ' |
| 450 | + f'is not a valid emoji.')) |
| 451 | + del self.config.cache['sent_emoji'] |
| 452 | + await self.config.update() |
442 | 453 |
|
443 | 454 | if blocked_emoji not in UNICODE_EMOJI:
|
444 | 455 | try:
|
445 | 456 | blocked_emoji = await converter.convert(
|
446 | 457 | ctx, blocked_emoji.strip(':')
|
447 | 458 | )
|
448 | 459 | except commands.BadArgument:
|
449 |
| - pass |
| 460 | + logger.warning(info(f'Blocked emoji ({blocked_emoji}) ' |
| 461 | + 'is not a valid emoji.')) |
| 462 | + del self.config.cache['blocked_emoji'] |
| 463 | + await self.config.update() |
| 464 | + return sent_emoji, blocked_emoji |
450 | 465 |
|
451 |
| - if sent_emoji not in UNICODE_EMOJI: |
| 466 | + async def process_modmail(self, message): |
| 467 | + """Processes messages sent to the bot.""" |
| 468 | + sent_emoji, blocked_emoji = await self.retrieve_emoji() |
| 469 | + |
| 470 | + account_age = self.config.get('account_age') |
| 471 | + if account_age is None: |
| 472 | + account_age = isodate.duration.Duration() |
| 473 | + else: |
452 | 474 | try:
|
453 |
| - sent_emoji = await converter.convert( |
454 |
| - ctx, sent_emoji.strip(':') |
455 |
| - ) |
456 |
| - except commands.BadArgument: |
457 |
| - pass |
| 475 | + account_age = isodate.parse_duration(account_age) |
| 476 | + except isodate.ISO8601Error: |
| 477 | + logger.warning('The account age limit needs to be a ' |
| 478 | + 'ISO-8601 duration formatted duration string ' |
| 479 | + f'greater than 0 days, not "%s".', str(account_age)) |
| 480 | + del self.config.cache['account_age'] |
| 481 | + await self.config.update() |
| 482 | + account_age = isodate.duration.Duration() |
458 | 483 |
|
459 |
| - if str(message.author.id) in self.blocked_users: |
| 484 | + reason = self.blocked_users.get(str(message.author.id)) |
| 485 | + if reason is None: |
| 486 | + reason = '' |
| 487 | + try: |
| 488 | + min_account_age = message.author.created_at + account_age |
| 489 | + except ValueError as e: |
| 490 | + logger.warning(e.args[0]) |
| 491 | + del self.config.cache['account_age'] |
| 492 | + await self.config.update() |
| 493 | + min_account_age = message.author.created_at |
| 494 | + |
| 495 | + if min_account_age > datetime.utcnow(): |
| 496 | + # user account has not reached the required time |
460 | 497 | reaction = blocked_emoji
|
| 498 | + changed = False |
| 499 | + delta = human_timedelta(min_account_age) |
| 500 | + |
| 501 | + if str(message.author.id) not in self.blocked_users: |
| 502 | + new_reason = f'System Message: New Account. Required to wait for {delta}.' |
| 503 | + self.config.blocked[str(message.author.id)] = new_reason |
| 504 | + await self.config.update() |
| 505 | + changed = True |
| 506 | + |
| 507 | + if reason.startswith('System Message: New Account.') or changed: |
| 508 | + await message.channel.send(embed=discord.Embed( |
| 509 | + title='Message not sent!', |
| 510 | + description=f'Your must wait for {delta} ' |
| 511 | + f'before you can contact {self.user.mention}.', |
| 512 | + color=discord.Color.red() |
| 513 | + )) |
| 514 | + |
| 515 | + elif str(message.author.id) in self.blocked_users: |
| 516 | + reaction = blocked_emoji |
| 517 | + if reason.startswith('System Message: New Account.'): |
| 518 | + # Met the age limit already |
| 519 | + reaction = sent_emoji |
| 520 | + del self.config.blocked[str(message.author.id)] |
| 521 | + await self.config.update() |
| 522 | + else: |
| 523 | + end_time = re.search(r'%(.+?)%$', reason) |
| 524 | + if end_time is not None: |
| 525 | + after = (datetime.fromisoformat(end_time.group(1)) - |
| 526 | + datetime.utcnow()).total_seconds() |
| 527 | + if after <= 0: |
| 528 | + # No longer blocked |
| 529 | + reaction = sent_emoji |
| 530 | + del self.config.blocked[str(message.author.id)] |
| 531 | + await self.config.update() |
461 | 532 | else:
|
462 | 533 | reaction = sent_emoji
|
463 | 534 |
|
@@ -542,8 +613,8 @@ async def on_message(self, message):
|
542 | 613 | 'Command "{}" is not found'.format(ctx.invoked_with)
|
543 | 614 | )
|
544 | 615 | self.dispatch('command_error', ctx, exc)
|
545 |
| - |
546 |
| - async def on_typing(self, channel, user, when): |
| 616 | + |
| 617 | + async def on_typing(self, channel, user, _): |
547 | 618 | if isinstance(channel, discord.DMChannel):
|
548 | 619 | if not self.config.get('user_typing'):
|
549 | 620 | return
|
@@ -720,13 +791,13 @@ async def autoupdate_loop(self):
|
720 | 791 |
|
721 | 792 | if self.config.get('disable_autoupdates'):
|
722 | 793 | logger.warning(info('Autoupdates disabled.'))
|
723 |
| - logger.warning(LINE) |
| 794 | + logger.info(LINE) |
724 | 795 | return
|
725 | 796 |
|
726 | 797 | if self.self_hosted and not self.config.get('github_access_token'):
|
727 | 798 | logger.warning(info('GitHub access token not found.'))
|
728 | 799 | logger.warning(info('Autoupdates disabled.'))
|
729 |
| - logger.warning(LINE) |
| 800 | + logger.info(LINE) |
730 | 801 | return
|
731 | 802 |
|
732 | 803 | while not self.is_closed():
|
|
0 commit comments