Skip to content

Commit 168ad64

Browse files
authored
Update close-cmd branch with master (#123)
* Add support for multiple image and file attachments * Add support for custom sent emoji and blocked emoji resolves #112 * Fix emoji handling * Fixed a bug where blocked users are still able to message modmail * Improve about command style * Increment version * Fix changelog version parsing * Add changelog docstring * Add dhooks to requirements for eval stuff * Add discord server badge * Update README.md added forks badge * Use blurple color * Use stars instead * Let anyone use alias invoked eval commands * Add an instances badge * Update README.md * Update README.md * Update README.md * Remove the first desc * Update README.md * Add patreon in readme * Update README.md * Improve logs format * Update README.md * Rainbow colors + patreon * Change order
1 parent 6f6f6a0 commit 168ad64

File tree

9 files changed

+124
-44
lines changed

9 files changed

+124
-44
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
# v2.0.10
8+
9+
### Security
10+
- Fix a bug where blocked users were still able to message modmail.
11+
12+
# v2.0.9
13+
14+
### Added
15+
- Support for custom blocked emoji and sent emoji
16+
- Use the `config set blocked_emoji` or `sent_emoji` commands.
17+
18+
### Quick Fix
19+
- Support multiple image and file attachments in one message
20+
- This is only possible on mobile so its good to handle it in code.
721

822
# v2.0.8
923

README.md

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
1-
<h1 align="center">Discord Mod Mail Bot</h1>
2-
31
<div align="center">
4-
<strong><i>A simple and functional modmail bot.</i></strong>
2+
<img src='https://i.imgur.com/o558Qnq.png' align='center'>
3+
<br>
4+
<strong><i>A simple and functional modmail bot for Discord.</i></strong>
55
<br>
66
<br>
7-
87

98

109
<a href="https://heroku.com/deploy?template=https://github.com/kyb3r/modmail">
1110
<img src="https://img.shields.io/badge/deploy_to-heroku-997FBC.svg?style=for-the-badge" />
1211
</a>
1312

14-
<a href="">
15-
<img src="https://img.shields.io/badge/python-3.7-7289DA.svg?style=for-the-badge" alt="Travis" />
13+
14+
<a href="https://discord.gg/j5e9p8w">
15+
<img src="https://img.shields.io/discord/515071617815019520.svg?style=for-the-badge&colorB=7289DA" alt="Support" />
16+
</a>
17+
18+
19+
20+
<a href="https://github.com/kyb3r/modmail/">
21+
<img src="https://api.modmail.tk/badges/instances.svg" alt="Bot instances" />
22+
</a>
23+
24+
25+
<a href="https://patreon.com/kyber">
26+
<img src="https://img.shields.io/badge/patreon-donate-orange.svg?style=for-the-badge" alt="Python 3.7" />
1627
</a>
1728

29+
1830
<a href="https://github.com/kyb3r/modmail/blob/master/LICENSE">
19-
<img src="https://img.shields.io/badge/license-mit-7289DA.svg?style=for-the-badge" alt="Travis" />
31+
<img src="https://img.shields.io/badge/license-mit-e74c3c.svg?style=for-the-badge" alt="MIT License" />
2032
</a>
2133

2234

2335
</div>
2436
<br>
25-
<div align="center">
26-
This is an open source discord bot made by kyb3r and improved upon suggestions by the users! It enables server members to DM it, the messages then get relayed to the server moderators who can then respond through the bot. In essence this bot serves as a means for members to easily communicate with server leadership in an organised manner.
27-
28-
</div>
2937

3038
## How does it work?
3139

@@ -52,3 +60,5 @@ The bot checks for new updates every hour and automatically updates to a newer v
5260

5361
## Contributing
5462
This project is licenced under MIT. If you have ideas for commands create an issue or pull request. Contributions are always welcome, whether it be documentation improvements or new functionality, please feel free to create a pull request.
63+
64+
I have also set up a [Patreon](https://www.patreon.com/kyber), so if you want to support the development of Modmail, you now can :smile:

bot.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@
2222
SOFTWARE.
2323
"""
2424

25-
__version__ = '2.0.8'
25+
__version__ = '2.0.10'
2626

2727
import asyncio
2828
import textwrap
2929
import datetime
3030
import os
3131
import re
32+
from types import SimpleNamespace
3233

3334
import discord
3435
import aiohttp
3536
from discord.ext import commands
3637
from discord.ext.commands.view import StringView
3738
from colorama import init, Fore, Style
39+
import emoji
3840

3941
from core.api import Github, ModmailApiClient
4042
from core.thread import ThreadManager
@@ -183,10 +185,29 @@ async def on_ready(self):
183185
await self.threads.populate_cache()
184186

185187

188+
186189
async def process_modmail(self, message):
187190
"""Processes messages sent to the bot."""
188191

189-
reaction = '🚫' if message.author.id in self.blocked_users else '✅'
192+
ctx = SimpleNamespace(bot=self, guild=self.modmail_guild)
193+
converter = commands.EmojiConverter()
194+
195+
blocked_emoji = self.config.get('blocked_emoji', '🚫')
196+
sent_emoji = self.config.get('sent_emoji', '✅')
197+
198+
if blocked_emoji not in emoji.UNICODE_EMOJI:
199+
try:
200+
blocked_emoji = await converter.convert(ctx, blocked_emoji.strip(':'))
201+
except:
202+
pass
203+
204+
if sent_emoji not in emoji.UNICODE_EMOJI:
205+
try:
206+
sent_emoji = await converter.convert(ctx, sent_emoji.strip(':'))
207+
except:
208+
pass
209+
210+
reaction = blocked_emoji if str(message.author.id) in self.blocked_users else sent_emoji
190211

191212
try:
192213
await message.add_reaction(reaction)
@@ -199,7 +220,7 @@ async def process_modmail(self, message):
199220
description='You have been blocked from using modmail.'
200221
)
201222

202-
if message.author.id in self.blocked_users:
223+
if str(message.author.id) in self.blocked_users:
203224
await message.author.send(embed=blocked_em)
204225
else:
205226
thread = await self.threads.find_or_create(message.author)
@@ -238,9 +259,10 @@ async def get_context(self, message, *, cls=commands.Context):
238259
ctx.invoked_with = invoker
239260
ctx.prefix = self.prefix # Sane prefix (No mentions)
240261
ctx.command = self.all_commands.get(invoker)
241-
242-
# if hasattr(ctx, '_alias_invoked'):
243-
# ctx.command.checks = None # Let anyone use the command.
262+
263+
if ctx.command is self.get_command('eval') and hasattr(ctx, '_alias_invoked'):
264+
# ctx.command.checks = None # Let anyone use the command.
265+
pass
244266

245267
return ctx
246268

cogs/modmail.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ class Modmail:
1313

1414
def __init__(self, bot):
1515
self.bot = bot
16+
17+
def obj(arg):
18+
return discord.Object(int(arg))
1619

1720
@commands.command()
1821
@trigger_typing
@@ -137,7 +140,7 @@ async def nsfw(self, ctx):
137140
@commands.command()
138141
@commands.has_permissions(manage_messages=True)
139142
@trigger_typing
140-
async def logs(self, ctx, *, member: Union[discord.Member, discord.User]=None):
143+
async def logs(self, ctx, *, member: Union[discord.Member, discord.User, obj]=None):
141144
"""Shows a list of previous modmail thread logs of a member."""
142145

143146
if not member:
@@ -147,13 +150,15 @@ async def logs(self, ctx, *, member: Union[discord.Member, discord.User]=None):
147150

148151
user = member or thread.recipient
149152

153+
icon_url = getattr(user, 'avatar_url', 'https://cdn.discordapp.com/embed/avatars/0.png')
154+
150155
logs = await self.bot.modmail_api.get_user_logs(user.id)
151156

152157
if not any(not e['open'] for e in logs):
153158
return await ctx.send('This user has no previous logs.')
154159

155160
em = discord.Embed(color=discord.Color.green())
156-
em.set_author(name='Previous Logs', icon_url=user.avatar_url)
161+
em.set_author(name='Previous Logs', icon_url=icon_url)
157162

158163
embeds = [em]
159164

@@ -166,20 +171,31 @@ async def logs(self, ctx, *, member: Union[discord.Member, discord.User]=None):
166171
for index, entry in enumerate(closed_logs):
167172
if len(embeds[-1].fields) == 3:
168173
em = discord.Embed(color=discord.Color.green())
169-
em.set_author(name='Previous Logs', icon_url=user.avatar_url)
174+
em.set_author(name='Previous Logs', icon_url=icon_url)
170175
embeds.append(em)
171176

172177
date = dateutil.parser.parse(entry['created_at'])
178+
173179
new_day = date.strftime(r'%d %b %Y')
180+
time = date.strftime(r'%H:%M')
174181

175182
key = entry['key']
176183
user_id = entry['user_id']
177184
log_url = f"https://logs.modmail.tk/{user_id}/{key}"
178185

179-
fmt += f"[`{key}`]({log_url})\n"
186+
truncate = lambda c: c[:47] + '...' if len(c) > 50 else c
187+
188+
if entry['messages']:
189+
short_desc = truncate(entry['messages'][0]['content']) or 'No content'
190+
else:
191+
short_desc = 'No content'
192+
193+
fmt += f"[`[{time}]{key}`]({log_url}) - {short_desc}\n"
194+
195+
print(fmt)
180196

181197
if current_day != new_day or index == len(closed_logs) - 1:
182-
embeds[-1].add_field(name=current_day, value=fmt)
198+
embeds[-1].add_field(name=current_day, value=fmt, inline=False)
183199
current_day = new_day
184200
fmt = ''
185201

@@ -252,9 +268,6 @@ async def contact(self, ctx, *, user: Union[discord.Member, discord.User]):
252268

253269
await ctx.send(embed=em)
254270

255-
def obj(arg):
256-
return discord.Object(int(arg))
257-
258271
@commands.command()
259272
@trigger_typing
260273
@commands.has_permissions(manage_channels=True)
@@ -308,7 +321,7 @@ async def block(self, ctx, user: Union[discord.Member, discord.User, obj]=None,
308321
await self.bot.config.update()
309322

310323
em.title = 'Success'
311-
em.description = f'{mention} is now blocked ' + f'for `{reason}`' if reason else ''
324+
em.description = f'{mention} is now blocked ' + (f'for `{reason}`' if reason else '')
312325

313326
await ctx.send(embed=em)
314327
else:

cogs/utility.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,21 +156,22 @@ async def help(self, ctx, *, command: str=None):
156156
@commands.command()
157157
@trigger_typing
158158
async def changelog(self, ctx):
159+
'''Show a paginated changelog of the bot.'''
159160
changelog = await ChangeLog.from_repo(self.bot)
160161
p = PaginatorSession(ctx, *changelog.embeds)
161162
await p.run()
162163

163-
@commands.command()
164+
@commands.command(aliases=['bot', 'info'])
164165
@trigger_typing
165166
async def about(self, ctx):
166167
"""Shows information about the bot."""
167168
em = discord.Embed(color=discord.Color.green(), timestamp=datetime.datetime.utcnow())
168-
em.set_author(name='Mod Mail - Information', icon_url=self.bot.user.avatar_url)
169+
em.set_author(name='Mod Mail - About', icon_url=self.bot.user.avatar_url)
169170
em.set_thumbnail(url=self.bot.user.avatar_url)
170171

171-
em.description = 'This is an open source discord bot made by kyb3r and '\
172-
'improved upon suggestions by the users! This bot serves as a means for members to '\
173-
'easily communicate with server leadership in an organised manner.'
172+
em.description = 'This is an open source discord bot that serves'\
173+
' as a means for members to easily communicate with'\
174+
' server leadership in an organised manner.'
174175

175176
try:
176177
async with self.bot.session.get('https://api.modmail.tk/metadata') as resp:

core/changelog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def embed(self):
4242
class ChangeLog:
4343

4444
changelog_url = 'https://raw.githubusercontent.com/kyb3r/modmail/master/CHANGELOG.md'
45-
regex = re.compile(r'# (v\d\.\d\.\d)([\S\s]*?(?=# v))')
45+
regex = re.compile(r'# (v\d+\.\d+\.\d+)([\S\s]*?(?=# v))')
4646

4747
def __init__(self, bot, text):
4848
self.bot = bot

core/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class ConfigManager:
88

99
allowed_to_change_in_command = {
1010
'status', 'log_channel_id', 'mention', 'disable_autoupdates', 'prefix',
11-
'main_category_id'
11+
'main_category_id', 'sent_emoji', 'blocked_emoji'
1212
}
1313

1414
internal_keys = {

core/thread.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,21 +146,39 @@ async def send(self, message, destination=None, from_mod=False, delete_message=T
146146
em.set_author(name=str(author), icon_url=author.avatar_url, url=message.jump_url) # store message id in hidden url
147147

148148
image_types = ['.png', '.jpg', '.gif', '.jpeg', '.webp']
149-
is_image_url = lambda u: any(urlparse(u.lower()).path.endswith(x) for x in image_types)
149+
is_image_url = lambda u, _: any(urlparse(u.lower()).path.endswith(x) for x in image_types)
150150

151151
delete_message = not bool(message.attachments)
152-
attachments = list(filter(lambda a: not is_image_url(a.url), message.attachments))
153152

154-
image_urls = [a.url for a in message.attachments]
155-
image_urls.extend(re.findall(r'(https?://[^\s]+)', message.content))
156-
image_urls = list(filter(is_image_url, image_urls))
153+
attachments = [(a.url, a.filename) for a in message.attachments]
154+
155+
images = [x for x in attachments if is_image_url(*x)]
156+
attachments = [x for x in attachments if not is_image_url(*x)]
157+
158+
image_links = [(link, None) for link in re.findall(r'(https?://[^\s]+)', message.content)]
159+
image_links = [x for x in image_links if is_image_url(*x)]
160+
images.extend(image_links)
161+
162+
embedded_image = False
157163

158-
if image_urls:
159-
em.set_image(url=image_urls[0])
164+
prioritize_uploads = any(i[1] is not None for i in images)
165+
166+
additional_count = 1
167+
168+
for att in images:
169+
if is_image_url(*att) and not embedded_image and att[1] if prioritize_uploads else True:
170+
em.set_image(url=att[0])
171+
embedded_image = True
172+
elif att[1] is not None:
173+
link = f'[{att[1]}]({att[0]})'
174+
em.add_field(name=f'Additional Image upload ({additional_count})', value=link, inline=False)
175+
additional_count += 1
176+
177+
file_upload_count = 1
160178

161-
if attachments:
162-
att = attachments[0]
163-
em.add_field(name='File Attachment', value=f'[{att.filename}]({att.url})')
179+
for att in attachments:
180+
em.add_field(name=f'File upload ({file_upload_count})', value=f'[{att[1]}]({att[0]})')
181+
file_upload_count += 1
164182

165183
if from_mod:
166184
em.color = discord.Color.green()

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ colorama
33
cachetools
44
python-dateutil
55
python-box
6-
colorthief
6+
colorthief
7+
emoji
8+
dhooks

0 commit comments

Comments
 (0)