Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3c5d7b9
Initial address of issue #175
Taaku18 Feb 16, 2019
a9e8b89
Updated changelog
Taaku18 Feb 16, 2019
ec5f0e7
Fixed some problems
Taaku18 Feb 16, 2019
3299303
Wording + allow unblock internal blocks
Taaku18 Feb 16, 2019
e8ef831
Addresses some PR concerns and new message
Taaku18 Feb 16, 2019
d9f58ce
Float
Taaku18 Feb 16, 2019
f03477d
Float
Taaku18 Feb 16, 2019
8b5cd3b
Use seconds
Taaku18 Feb 16, 2019
f9a48fc
Use iso 8601 duration format for account_age
Taaku18 Feb 17, 2019
2b91c7d
Merge from master
Taaku18 Feb 17, 2019
cf56268
Updated changelog
Taaku18 Feb 17, 2019
0a8c1aa
Support human readable time when setting account_age with config set
Taaku18 Feb 17, 2019
8fc65af
Bump version to 2.13.9 (for now)
Taaku18 Feb 17, 2019
80d986b
Style + better handle time parsing
Taaku18 Feb 17, 2019
27e4be2
Change unblock message for internal blocks
Taaku18 Feb 17, 2019
b5aca02
Update modmail.py
Taaku18 Feb 17, 2019
da18995
Update modmail.py
Taaku18 Feb 17, 2019
b085faf
Temp block, fixed some problems in 'age' (#181)
Taaku18 Feb 17, 2019
6295ea0
Updated changelog
Taaku18 Feb 17, 2019
3b5b1b4
Address issue
Taaku18 Feb 17, 2019
87da9e6
Add a "that"
Taaku18 Feb 17, 2019
de6d8ae
Comment
Taaku18 Feb 17, 2019
e685f2b
The user for block command is optional: now '?block for 10h' works in…
Taaku18 Feb 17, 2019
9dda435
Some unimportant changes
Taaku18 Feb 18, 2019
3f8004f
Updated changelog
Taaku18 Feb 18, 2019
38e56ec
Updated changelog
Taaku18 Feb 18, 2019
588a2d7
Merge from 'master' + bump version to 2.13.10
Taaku18 Feb 18, 2019
85c3bf9
Forgot to actually bump version
Taaku18 Feb 18, 2019
cfbe0be
Merge branch 'master' into age
Taaku18 Feb 21, 2019
fc29a22
Merge branch 'master' into age
Taaku18 Feb 21, 2019
93d2db7
Cap C
Taaku18 Feb 24, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 80 additions & 88 deletions CHANGELOG.md

Large diffs are not rendered by default.

109 changes: 90 additions & 19 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
SOFTWARE.
"""

__version__ = '2.13.10'
__version__ = '2.13.11'

import asyncio
import logging
Expand All @@ -37,18 +37,20 @@
from discord.ext import commands
from discord.ext.commands.view import StringView

import isodate

from aiohttp import ClientSession
from colorama import init, Fore, Style
from emoji import UNICODE_EMOJI
from motor.motor_asyncio import AsyncIOMotorClient

from core.changelog import Changelog
from core.clients import ModmailApiClient, SelfHostedClient
from core.clients import PluginDatabaseClient
from core.clients import ModmailApiClient, SelfHostedClient, PluginDatabaseClient
from core.config import ConfigManager
from core.utils import info, error
from core.models import Bot
from core.thread import ThreadManager
from core.time import human_timedelta


init()
Expand Down Expand Up @@ -431,33 +433,102 @@ async def on_ready(self):
)
logger.info(LINE)

async def process_modmail(self, message):
"""Processes messages sent to the bot."""

async def retrieve_emoji(self):
ctx = SimpleNamespace(bot=self, guild=self.modmail_guild)
converter = commands.EmojiConverter()

blocked_emoji = self.config.get('blocked_emoji', '🚫')
sent_emoji = self.config.get('sent_emoji', '✅')
blocked_emoji = self.config.get('blocked_emoji', '🚫')

if sent_emoji not in UNICODE_EMOJI:
try:
sent_emoji = await converter.convert(
ctx, sent_emoji.strip(':')
)
except commands.BadArgument:
logger.warning(info(f'Sent Emoji ({sent_emoji}) '
f'is not a valid emoji.'))
del self.config.cache['sent_emoji']
await self.config.update()

if blocked_emoji not in UNICODE_EMOJI:
try:
blocked_emoji = await converter.convert(
ctx, blocked_emoji.strip(':')
)
except commands.BadArgument:
pass
logger.warning(info(f'Blocked emoji ({blocked_emoji}) '
'is not a valid emoji.'))
del self.config.cache['blocked_emoji']
await self.config.update()
return sent_emoji, blocked_emoji

if sent_emoji not in UNICODE_EMOJI:
async def process_modmail(self, message):
"""Processes messages sent to the bot."""
sent_emoji, blocked_emoji = await self.retrieve_emoji()

account_age = self.config.get('account_age')
if account_age is None:
account_age = isodate.duration.Duration()
else:
try:
sent_emoji = await converter.convert(
ctx, sent_emoji.strip(':')
)
except commands.BadArgument:
pass
account_age = isodate.parse_duration(account_age)
except isodate.ISO8601Error:
logger.warning('The account age limit needs to be a '
'ISO-8601 duration formatted duration string '
f'greater than 0 days, not "%s".', str(account_age))
del self.config.cache['account_age']
await self.config.update()
account_age = isodate.duration.Duration()

if str(message.author.id) in self.blocked_users:
reason = self.blocked_users.get(str(message.author.id))
if reason is None:
reason = ''
try:
min_account_age = message.author.created_at + account_age
except ValueError as e:
logger.warning(e.args[0])
del self.config.cache['account_age']
await self.config.update()
min_account_age = message.author.created_at

if min_account_age > datetime.utcnow():
# user account has not reached the required time
reaction = blocked_emoji
changed = False
delta = human_timedelta(min_account_age)

if str(message.author.id) not in self.blocked_users:
new_reason = f'System Message: New Account. Required to wait for {delta}.'
self.config.blocked[str(message.author.id)] = new_reason
await self.config.update()
changed = True

if reason.startswith('System Message: New Account.') or changed:
await message.channel.send(embed=discord.Embed(
title='Message not sent!',
description=f'Your must wait for {delta} '
f'before you can contact {self.user.mention}.',
color=discord.Color.red()
))

elif str(message.author.id) in self.blocked_users:
reaction = blocked_emoji
if reason.startswith('System Message: New Account.'):
# Met the age limit already
reaction = sent_emoji
del self.config.blocked[str(message.author.id)]
await self.config.update()
else:
end_time = re.search(r'%(.+?)%$', reason)
if end_time is not None:
after = (datetime.fromisoformat(end_time.group(1)) -
datetime.utcnow()).total_seconds()
if after <= 0:
# No longer blocked
reaction = sent_emoji
del self.config.blocked[str(message.author.id)]
await self.config.update()
else:
reaction = sent_emoji

Expand Down Expand Up @@ -542,8 +613,8 @@ async def on_message(self, message):
'Command "{}" is not found'.format(ctx.invoked_with)
)
self.dispatch('command_error', ctx, exc)
async def on_typing(self, channel, user, when):

async def on_typing(self, channel, user, _):
if isinstance(channel, discord.DMChannel):
if not self.config.get('user_typing'):
return
Expand Down Expand Up @@ -720,13 +791,13 @@ async def autoupdate_loop(self):

if self.config.get('disable_autoupdates'):
logger.warning(info('Autoupdates disabled.'))
logger.warning(LINE)
logger.info(LINE)
return

if self.self_hosted and not self.config.get('github_access_token'):
logger.warning(info('GitHub access token not found.'))
logger.warning(info('Autoupdates disabled.'))
logger.warning(LINE)
logger.info(LINE)
return

while not self.is_closed():
Expand Down
95 changes: 75 additions & 20 deletions cogs/modmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import discord
from discord.ext import commands

import re

from dateutil import parser
from natural.date import duration

Expand Down Expand Up @@ -603,7 +605,7 @@ async def contact(self, ctx,
category: Optional[discord.CategoryChannel] = None, *,
user: Union[discord.Member, discord.User]):
"""Create a thread with a specified member.

If the optional category argument is passed, the thread
will be created in the specified category.
"""
Expand All @@ -630,7 +632,7 @@ async def contact(self, ctx,
embed = discord.Embed(
title='Created thread',
description=f'Thread started in {thread.channel.mention} '
f'for {user.mention}',
f'for {user.mention}.',
color=self.bot.main_color
)

Expand Down Expand Up @@ -665,15 +667,22 @@ async def blocked(self, ctx):
embed.add_field(name='Unknown', value=val, inline=False)

if not users and not not_reachable:
embed.description = 'Currently there are no blocked users'
embed.description = 'Currently there are no blocked users.'

await ctx.send(embed=embed)

@commands.command()
@trigger_typing
@checks.has_permissions(manage_channels=True)
async def block(self, ctx, user: User = None, *, reason=None):
"""Block a user from using Modmail."""
async def block(self, ctx, user: Optional[User] = None, *,
after: UserFriendlyTime = None):
"""
Block a user from using Modmail.

Note: reasons that start with "System Message: " are reserved for internal
use only.
"""
reason = ''

if user is None:
thread = ctx.thread
Expand All @@ -682,22 +691,48 @@ async def block(self, ctx, user: User = None, *, reason=None):
else:
raise commands.UserInputError

if after is not None:
reason = after.arg
if reason.startswith('System Message: '):
raise commands.UserInputError
elif re.search(r'%(.+?)%$', reason) is not None:
raise commands.UserInputError
elif after.dt > after.now:
reason = f'{reason} %{after.dt.isoformat()}%'

if not reason:
reason = None

mention = user.mention if hasattr(user, 'mention') else f'`{user.id}`'

if str(user.id) not in self.bot.blocked_users:
extend = f' for `{reason}`' if reason is not None else ''
msg = self.bot.blocked_users.get(str(user.id))
if msg is None:
msg = ''

if str(user.id) not in self.bot.blocked_users or extend or msg.startswith('System Message: '):
if str(user.id) in self.bot.blocked_users:

old_reason = msg.strip().rstrip('.') or 'no reason'
embed = discord.Embed(
title='Success',
description=f'{mention} was previously blocked for '
f'"{old_reason}". {mention} is now blocked{extend}.',
color=self.bot.main_color
)
else:
embed = discord.Embed(
title='Success',
color=self.bot.main_color,
description=f'{mention} is now blocked{extend}.'
)
self.bot.config.blocked[str(user.id)] = reason
await self.bot.config.update()
extend = f'for `{reason}`' if reason else ''
embed = discord.Embed(
title='Success',
color=self.bot.main_color,
description=f'{mention} is now blocked ' + extend
)
else:
embed = discord.Embed(
title='Error',
color=discord.Color.red(),
description=f'{mention} is already blocked'
description=f'{mention} is already blocked.'
)

return await ctx.send(embed=embed)
Expand All @@ -706,7 +741,12 @@ async def block(self, ctx, user: User = None, *, reason=None):
@trigger_typing
@checks.has_permissions(manage_channels=True)
async def unblock(self, ctx, *, user: User = None):
"""Unblocks a user from using Modmail."""
"""
Unblocks a user from using Modmail.

Note: reasons start with "System Message: " are reserved for internal
use only.
"""

if user is None:
thread = ctx.thread
Expand All @@ -718,17 +758,32 @@ async def unblock(self, ctx, *, user: User = None):
mention = user.mention if hasattr(user, 'mention') else f'`{user.id}`'

if str(user.id) in self.bot.blocked_users:
msg = self.bot.blocked_users.get(str(user.id))
if msg is None:
msg = ''
del self.bot.config.blocked[str(user.id)]
await self.bot.config.update()
embed = discord.Embed(
title='Success',
color=self.bot.main_color,
description=f'{mention} is no longer blocked'
)

if msg.startswith('System Message: '):
# If the user is blocked internally (for example: below minimum account age)
# Show an extended message stating the original internal message
reason = msg[16:].strip().rstrip('.') or 'no reason'
embed = discord.Embed(
title='Success',
description=f'{mention} was previously blocked internally due to '
f'"{reason}". {mention} is no longer blocked.',
color=self.bot.main_color
)
else:
embed = discord.Embed(
title='Success',
color=self.bot.main_color,
description=f'{mention} is no longer blocked.'
)
else:
embed = discord.Embed(
title='Error',
description=f'{mention} is not blocked',
description=f'{mention} is not blocked.',
color=discord.Color.red()
)

Expand Down
7 changes: 3 additions & 4 deletions cogs/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,8 @@ async def set_presence(self, *,
else:
url = None
activity_message = (
activity_message or
self.bot.config.get('activity_message', '')
activity_message or
self.bot.config.get('activity_message', '')
).strip()

if activity_type == ActivityType.listening:
Expand All @@ -571,7 +571,6 @@ async def set_presence(self, *,
presence = {'activity': (None, 'No activity has been set.'),
'status': (None, 'No status has been set.')}
if activity is not None:
# TODO: Trim message
to = 'to ' if activity.type == ActivityType.listening else ''
msg = f'Activity set to: {activity.type.name.capitalize()} '
msg += f'{to}{activity.name}.'
Expand Down Expand Up @@ -676,7 +675,7 @@ async def set(self, ctx, key: str.lower, *, value):

if key in keys:
try:
value, value_text = self.bot.config.clean_data(key, value)
value, value_text = await self.bot.config.clean_data(key, value)
except InvalidConfigError as exc:
embed = exc.embed
else:
Expand Down
Loading