Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


# v2.10.0

### Added
- `anonreply` command to anonymously reply to the recipient.
The username of the anonymous user defaults to the `mod_tag` (the footer text of a mod reply). The avatar defaults the guild icon url. However you can change both of these via the `anon_username`, `anon_avatar_url` and `anon_tag` config variables.

### Changed
Your bot now logs all messages sent in a thread channel, including discussions that take place. You can now toggle to view them in the log viewer app.

# v2.9.4
Fixed a small bug due to a typo.

Expand Down
11 changes: 9 additions & 2 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
SOFTWARE.
"""

__version__ = '2.9.4'
__version__ = '2.10.0'

import asyncio
import uvloop
Expand Down Expand Up @@ -362,8 +362,15 @@ async def on_message(self, message):
cmd = message.content[len(prefix):].strip()
if cmd in self.snippets:
message.content = f'{prefix}reply {self.snippets[cmd]}'

ctx = await self.get_context(message)
if ctx.command:
return await self.invoke(ctx)

thread = await self.threads.find(channel=ctx.channel)

await self.process_commands(message)
if thread is not None:
await self.modmail_api.append_log(message, type='internal')

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though there isn't a command being invoked here, the message should still propagate through discord. Or else it might mask issues in the future.

Suggested change
await self.process_commands(message)

Copy link
Collaborator Author

@kyb3r kyb3r Jan 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation says that its the equivalent of those two calls, plus we need to know whether or not a command exists before we log an internal message.

Copy link
Collaborator

@Taaku18 Taaku18 Jan 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kyb3r I mean add it at the end of on_message, that "suggested change" section might be a bit misleading. So...

...
thread = await self.threads.find(channel=ctx.channel)
if thread is not None:
    await self.modmail_api.append_log(message, type='internal')
return await self.process_commands(message)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we are not logging command messages

Copy link
Collaborator

@Taaku18 Taaku18 Jan 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I know we're not logging commands, but we should still include return await self.invoke(ctx) at the end. This way "command_error" event will still be dispatched when an invalid command was received.

It doesn't really matter right now, but it may cause problems later as this suppresses the CommandNotFound from on_command_error.

Btw, also change:

if ctx.command:

to:

if ctx.command is not None:

async def on_guild_channel_delete(self, channel):
if channel.guild != self.modmail_guild:
Expand Down
13 changes: 11 additions & 2 deletions cogs/modmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ async def logs(self, ctx, *, member: Union[discord.Member, discord.User, obj]=No
await session.run()

@commands.command()
@trigger_typing
async def reply(self, ctx, *, msg=''):
"""Reply to users using this command.

Expand All @@ -376,15 +375,25 @@ async def reply(self, ctx, *, msg=''):
ctx.message.content = msg
thread = await self.bot.threads.find(channel=ctx.channel)
if thread:
await ctx.trigger_typing()
await thread.reply(ctx.message)

@commands.command()
@trigger_typing
async def anonreply(self, ctx, *, msg=''):
ctx.message.content = msg
thread = await self.bot.threads.find(channel=ctx.channel)
if thread:
await ctx.trigger_typing()
await thread.reply(ctx.message, anonymous=True)

@commands.command()
async def note(self, ctx, *, msg=''):
"""Take a note about the current thread, useful for noting context."""
ctx.message.content = msg
thread = await self.bot.threads.find(channel=ctx.channel)

if thread:
await ctx.trigger_typing()
await thread.note(ctx.message)

@commands.command()
Expand Down
24 changes: 18 additions & 6 deletions core/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,15 @@ def append_log(self, message, channel_id='', type='thread_message'):
},
# message properties
'content': message.content,
'attachments': [i.url for i in message.attachments],
'type': type
'type': type,
'attachments': [
{
'id': a.id,
'filename': a.filename,
'is_image': a.width is not None,
'size': a.size,
'url': a.url
} for a in message.attachments ]
}
}
return self.request(self.logs + f'/{channel_id}', method='PATCH', payload=payload)
Expand Down Expand Up @@ -253,18 +260,23 @@ async def append_log(self, message, channel_id='', type='thread_message'):
payload = {
'timestamp': str(message.created_at),
'message_id': str(message.id),
# author
'author': {
'id': str(message.author.id),
'name': message.author.name,
'discriminator': message.author.discriminator,
'avatar_url': message.author.avatar_url,
'mod': not isinstance(message.channel, discord.DMChannel),
},
# message properties
'content': message.content,
'attachments': [i.url for i in message.attachments],
'type': type
'type': type,
'attachments': [
{
'id': a.id,
'filename': a.filename,
'is_image': a.width is not None,
'size': a.size,
'url': a.url
} for a in message.attachments ]
}

return await self.logs.find_one_and_update(
Expand Down
3 changes: 2 additions & 1 deletion core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class ConfigManager:
'mention', 'disable_autoupdates', 'prefix',
'main_category_id', 'sent_emoji', 'blocked_emoji',
'thread_creation_response', 'twitch_url', 'mod_color',
'recipient_color', 'mod_tag'
'recipient_color', 'mod_tag', 'anon_username', 'anon_avatar_url',
'anon_tag'
}

internal_keys = {
Expand Down
35 changes: 26 additions & 9 deletions core/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,8 @@ async def note(self, message):
self.bot.modmail_api.append_log(message, self.channel.id, type='system'),
self.send(message, self.channel, note=True)
)


async def reply(self, message):
async def reply(self, message, anonymous=False):
if not message.content and not message.attachments:
raise commands.UserInputError
if all(not g.get_member(self.id) for g in self.bot.guilds):
Expand All @@ -196,13 +195,14 @@ async def reply(self, message):

tasks = [
# in thread channel
self.send(message, self.channel, from_mod=True),
self.send(message, destination=self.channel, from_mod=True, anonymous=anonymous),
# to user
self.send(message, self.recipient, from_mod=True)
self.send(message, destination=self.recipient, from_mod=True, anonymous=anonymous)
]


self.bot.loop.create_task(
self.bot.modmail_api.append_log(message, self.channel.id)
self.bot.modmail_api.append_log(message, self.channel.id, type='anonymous' if anonymous else 'thread_message')
)

if self.close_task is not None:
Expand All @@ -215,7 +215,7 @@ async def reply(self, message):

await asyncio.gather(*tasks)

async def send(self, message, destination=None, from_mod=False, note=False):
async def send(self, message, destination=None, from_mod=False, note=False, anonymous=False):
if self.close_task is not None:
# cancel closing if a thread message is sent.
await self.cancel_closure()
Expand Down Expand Up @@ -244,10 +244,21 @@ async def send(self, message, destination=None, from_mod=False, note=False):

# store message id in hidden url
if not note:
em.set_author(name=author,
icon_url=author.avatar_url,

if anonymous and from_mod and not isinstance(destination, discord.TextChannel):
# Anonymously sending to the user.
name = self.bot.config.get('anon_username', self.bot.config.get('mod_tag', 'Moderator'))
avatar_url = self.bot.config.get('anon_avatar_url', self.bot.guild.icon_url)
else:
# Normal message
name = str(author)
avatar_url = author.avatar_url

em.set_author(name=name,
icon_url=avatar_url,
url=message.jump_url)
else:
# Special note messages
em.set_author(
name=f'Note ({author.name})',
icon_url=system_avatar_url,
Expand Down Expand Up @@ -301,7 +312,13 @@ def is_image_url(u, _):

if from_mod:
em.color = self.bot.mod_color
em.set_footer(text=self.bot.config.get('mod_tag', 'Moderator'))
if anonymous and isinstance(destination, discord.TextChannel): # Anonymous reply sent in thread channel
em.set_footer(text='Anonymous Reply')
elif not anonymous:
em.set_footer(text=self.bot.config.get('mod_tag', 'Moderator')) # Normal messages
else:
em.set_footer(text=self.bot.config.get('anon_tag', 'Response')) # Anonymous reply sent to user

elif note:
em.color = discord.Color.blurple()
else:
Expand Down