Skip to content

Commit 7e6b973

Browse files
committed
v2.18.0 - new permission system
1 parent d8af5f4 commit 7e6b973

File tree

8 files changed

+439
-113
lines changed

8 files changed

+439
-113
lines changed

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,39 @@ 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

77

8+
# v2.18.0
9+
10+
### What's new?
11+
12+
- A brand new permission system! Replacing the old guild-based permissions (ie. manage channels, manage messages), the new system enables you to customize your desire permission level specific to a command or a group of commands for a role or user.
13+
- There are five permission groups/levels:
14+
- Owner
15+
- Administrator
16+
- Moderator
17+
- Supporter
18+
- Regular
19+
20+
You may add a role or user to a permission group through any of the following methods:
21+
- `?permissions add level owner @role`
22+
- `?permissions add level supporter member-name`
23+
- `?permissions add level moderator everyone`
24+
- `?permissions add level moderator @member#1234`
25+
- `?permissions add level administrator 78912384930291853`
26+
27+
The same applies for individual commands permissions:
28+
- `?permissions add command-name @member#1234`
29+
- ... and the other methods listed above.
30+
31+
To revoke a permission, use `remove` instead of `add`.
32+
33+
To view all roles and users with permission for a permission group or command do:
34+
- `?permissions get command command-name`
35+
- `?permissions get level owner`
36+
37+
### Note
38+
39+
When updating to this version, all prior permission settings with guild-based permissions will be invalidated. You will need to convert to the above system.
40+
841
# v2.17.1
942

1043
### Changed

bot.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
SOFTWARE.
2323
"""
2424

25-
__version__ = '2.17.1'
25+
__version__ = '2.18.0'
2626

2727
import asyncio
2828
import logging
@@ -48,7 +48,7 @@
4848
from core.clients import ModmailApiClient, SelfHostedClient, PluginDatabaseClient
4949
from core.config import ConfigManager
5050
from core.utils import info, error
51-
from core.models import Bot
51+
from core.models import Bot, PermissionLevel
5252
from core.thread import ThreadManager
5353
from core.time import human_timedelta
5454

@@ -195,11 +195,6 @@ def _load_extensions(self):
195195
except Exception:
196196
logger.exception(error(f'Failed to load {cog}'))
197197

198-
async def is_owner(self, user):
199-
allowed = {int(x) for x in
200-
str(self.config.get('owners', '0')).split(',')}
201-
return user.id in allowed
202-
203198
def run(self, *args, **kwargs):
204199
try:
205200
self.loop.run_until_complete(self.start(self.token))
@@ -599,6 +594,25 @@ async def get_context(self, message, *, cls=commands.Context):
599594

600595
return ctx
601596

597+
async def update_perms(self, name, value, add=True):
598+
if isinstance(name, PermissionLevel):
599+
permissions = self.config.level_permissions
600+
name = name.name
601+
else:
602+
permissions = self.config.command_permissions
603+
if name not in permissions:
604+
if add:
605+
permissions[name] = [value]
606+
else:
607+
if add:
608+
if value not in permissions[name]:
609+
permissions[name].append(value)
610+
else:
611+
if value in permissions[name]:
612+
permissions[name].remove(value)
613+
logger.info(info(f'Updating permissions for {name}, {value} (add={add}).'))
614+
await self.config.update()
615+
602616
async def on_message(self, message):
603617
if message.type == discord.MessageType.pins_add and \
604618
message.author == self.user:
@@ -799,7 +813,7 @@ def overwrites(ctx):
799813
}
800814

801815
for role in ctx.guild.roles:
802-
if role.permissions.manage_guild:
816+
if role.permissions.administrator:
803817
overwrites[role] = discord.PermissionOverwrite(
804818
read_messages=True
805819
)

cogs/modmail.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from core import checks
1414
from core.decorators import trigger_typing
15-
from core.models import Bot
15+
from core.models import Bot, PermissionLevel
1616
from core.paginator import PaginatorSession
1717
from core.time import UserFriendlyTime, human_timedelta
1818
from core.utils import format_preview, User
@@ -26,7 +26,7 @@ def __init__(self, bot: Bot):
2626

2727
@commands.command()
2828
@trigger_typing
29-
@checks.has_permissions(administrator=True)
29+
@checks.has_permissions(PermissionLevel.OWNER)
3030
async def setup(self, ctx):
3131
"""Sets up a server for Modmail"""
3232
if self.bot.main_category:
@@ -62,15 +62,18 @@ async def setup(self, ctx):
6262
self.bot.config['log_channel_id'] = log_channel.id
6363

6464
await self.bot.config.update()
65-
await ctx.send('Successfully set up server.')
65+
await ctx.send('Successfully set up server.\n'
66+
'Consider setting permission groups to give access '
67+
'to roles or users the ability to use Modmail.\n'
68+
f'Type `{self.bot.prefix}permissions` for more info.')
69+
if not self.bot.config.permissions:
70+
await self.bot.update_perms(PermissionLevel.REGULAR, -1)
71+
await self.bot.update_perms(PermissionLevel.OWNER, ctx.author.id)
6672

67-
@commands.group()
68-
@checks.has_permissions(manage_messages=True)
73+
@commands.group(invoke_without_command=True)
74+
@checks.has_permissions(PermissionLevel.SUPPORTER)
6975
async def snippets(self, ctx):
7076
"""Returns a list of snippets that are currently set."""
71-
if ctx.invoked_subcommand is not None:
72-
return
73-
7477
embeds = []
7578

7679
if self.bot.snippets:
@@ -101,7 +104,7 @@ async def snippets(self, ctx):
101104
await session.run()
102105

103106
@snippets.command(name='add')
104-
@checks.has_permissions(manage_messages=True)
107+
@checks.has_permissions(PermissionLevel.SUPPORTER)
105108
async def add_(self, ctx, name: str.lower, *, value):
106109
"""Add a snippet to the bot config."""
107110
if 'snippets' not in self.bot.config.cache:
@@ -118,9 +121,9 @@ async def add_(self, ctx, name: str.lower, *, value):
118121

119122
await ctx.send(embed=embed)
120123

121-
@snippets.command(name='del')
122-
@checks.has_permissions(manage_messages=True)
123-
async def del_(self, ctx, *, name: str.lower):
124+
@snippets.command(name='remove', aliases=['del', 'delete', 'rm'])
125+
@checks.has_permissions(PermissionLevel.SUPPORTER)
126+
async def remove_(self, ctx, *, name: str.lower):
124127
"""Removes a snippet from bot config."""
125128

126129
if self.bot.config.snippets.get(name):
@@ -142,7 +145,7 @@ async def del_(self, ctx, *, name: str.lower):
142145
await ctx.send(embed=embed)
143146

144147
@commands.command()
145-
@checks.has_permissions(manage_messages=True)
148+
@checks.has_permissions(PermissionLevel.MODERATOR)
146149
async def move(self, ctx, *, category: discord.CategoryChannel):
147150
"""Moves a thread to a specified category."""
148151
thread = ctx.thread
@@ -179,6 +182,7 @@ async def send_scheduled_close_message(ctx, after, silent=False):
179182
await ctx.send(embed=embed)
180183

181184
@commands.command(usage='[after] [close message]')
185+
@checks.has_permissions(PermissionLevel.SUPPORTER)
182186
@checks.thread_only()
183187
async def close(self, ctx, *, after: UserFriendlyTime = None):
184188
"""
@@ -236,6 +240,7 @@ async def close(self, ctx, *, after: UserFriendlyTime = None):
236240
)
237241

238242
@commands.command(aliases=['alert'])
243+
@checks.has_permissions(PermissionLevel.SUPPORTER)
239244
@checks.thread_only()
240245
async def notify(self, ctx, *, role=None):
241246
"""
@@ -272,6 +277,7 @@ async def notify(self, ctx, *, role=None):
272277
return await ctx.send(embed=embed)
273278

274279
@commands.command(aliases=['sub'])
280+
@checks.has_permissions(PermissionLevel.SUPPORTER)
275281
@checks.thread_only()
276282
async def subscribe(self, ctx, *, role=None):
277283
"""
@@ -311,6 +317,7 @@ async def subscribe(self, ctx, *, role=None):
311317
return await ctx.send(embed=embed)
312318

313319
@commands.command(aliases=['unsub'])
320+
@checks.has_permissions(PermissionLevel.SUPPORTER)
314321
@checks.thread_only()
315322
async def unsubscribe(self, ctx, *, role=None):
316323
"""Unsubscribe yourself or a given role from a thread."""
@@ -343,13 +350,15 @@ async def unsubscribe(self, ctx, *, role=None):
343350
return await ctx.send(embed=embed)
344351

345352
@commands.command()
353+
@checks.has_permissions(PermissionLevel.SUPPORTER)
346354
@checks.thread_only()
347355
async def nsfw(self, ctx):
348356
"""Flags a Modmail thread as nsfw."""
349357
await ctx.channel.edit(nsfw=True)
350358
await ctx.message.add_reaction('✅')
351359

352360
@commands.command()
361+
@checks.has_permissions(PermissionLevel.SUPPORTER)
353362
@checks.thread_only()
354363
async def loglink(self, ctx):
355364
"""Return the link to the current thread's logs."""
@@ -407,7 +416,7 @@ def format_log_embeds(self, logs, avatar_url):
407416
return embeds
408417

409418
@commands.group(invoke_without_command=True)
410-
@checks.has_permissions(manage_messages=True)
419+
@checks.has_permissions(PermissionLevel.SUPPORTER)
411420
async def logs(self, ctx, *, member: User = None):
412421
"""Shows a list of previous Modmail thread logs of a member."""
413422

@@ -440,7 +449,7 @@ async def logs(self, ctx, *, member: User = None):
440449
await session.run()
441450

442451
@logs.command(name='closed-by')
443-
@checks.has_permissions(manage_messages=True)
452+
@checks.has_permissions(PermissionLevel.SUPPORTER)
444453
async def closed_by(self, ctx, *, user: User = None):
445454
"""Returns all logs closed by a user."""
446455
if not self.bot.self_hosted:
@@ -478,8 +487,8 @@ async def closed_by(self, ctx, *, user: User = None):
478487
session = PaginatorSession(ctx, *embeds)
479488
await session.run()
480489

481-
@logs.command(name='search')
482-
@checks.has_permissions(manage_messages=True)
490+
@logs.command()
491+
@checks.has_permissions(PermissionLevel.SUPPORTER)
483492
async def search(self, ctx, limit: Optional[int] = None, *, query):
484493
"""Searches all logs for a message that contains your query."""
485494

@@ -521,6 +530,7 @@ async def search(self, ctx, limit: Optional[int] = None, *, query):
521530
await session.run()
522531

523532
@commands.command()
533+
@checks.has_permissions(PermissionLevel.SUPPORTER)
524534
@checks.thread_only()
525535
async def reply(self, ctx, *, msg=''):
526536
"""Reply to users using this command.
@@ -533,6 +543,7 @@ async def reply(self, ctx, *, msg=''):
533543
await ctx.thread.reply(ctx.message)
534544

535545
@commands.command()
546+
@checks.has_permissions(PermissionLevel.SUPPORTER)
536547
@checks.thread_only()
537548
async def anonreply(self, ctx, *, msg=''):
538549
"""Reply to a thread anonymously.
@@ -548,6 +559,7 @@ async def anonreply(self, ctx, *, msg=''):
548559
await ctx.thread.reply(ctx.message, anonymous=True)
549560

550561
@commands.command()
562+
@checks.has_permissions(PermissionLevel.SUPPORTER)
551563
@checks.thread_only()
552564
async def note(self, ctx, *, msg=''):
553565
"""Take a note about the current thread, useful for noting context."""
@@ -556,6 +568,7 @@ async def note(self, ctx, *, msg=''):
556568
await ctx.thread.note(ctx.message)
557569

558570
@commands.command()
571+
@checks.has_permissions(PermissionLevel.SUPPORTER)
559572
@checks.thread_only()
560573
async def edit(self, ctx, message_id: Optional[int] = None,
561574
*, new_message):
@@ -599,8 +612,8 @@ async def edit(self, ctx, message_id: Optional[int] = None,
599612
await ctx.message.add_reaction('✅')
600613

601614
@commands.command()
615+
@checks.has_permissions(PermissionLevel.SUPPORTER)
602616
@trigger_typing
603-
@checks.has_permissions(manage_messages=True)
604617
async def contact(self, ctx,
605618
category: Optional[discord.CategoryChannel] = None, *,
606619
user: Union[discord.Member, discord.User]):
@@ -639,8 +652,8 @@ async def contact(self, ctx,
639652
await ctx.send(embed=embed)
640653

641654
@commands.command()
655+
@checks.has_permissions(PermissionLevel.MODERATOR)
642656
@trigger_typing
643-
@checks.has_permissions(manage_channels=True)
644657
async def blocked(self, ctx):
645658
"""Returns a list of blocked users"""
646659
embed = discord.Embed(title='Blocked Users',
@@ -672,8 +685,8 @@ async def blocked(self, ctx):
672685
await ctx.send(embed=embed)
673686

674687
@commands.command()
688+
@checks.has_permissions(PermissionLevel.MODERATOR)
675689
@trigger_typing
676-
@checks.has_permissions(manage_channels=True)
677690
async def block(self, ctx, user: Optional[User] = None, *,
678691
after: UserFriendlyTime = None):
679692
"""
@@ -738,8 +751,8 @@ async def block(self, ctx, user: Optional[User] = None, *,
738751
return await ctx.send(embed=embed)
739752

740753
@commands.command()
754+
@checks.has_permissions(PermissionLevel.MODERATOR)
741755
@trigger_typing
742-
@checks.has_permissions(manage_channels=True)
743756
async def unblock(self, ctx, *, user: User = None):
744757
"""
745758
Unblocks a user from using Modmail.

cogs/plugins.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
from discord.ext import commands
1111

12-
from core.models import Bot
12+
from core import checks
13+
from core.models import Bot, PermissionLevel
1314
from core.utils import info, error
1415

1516
logger = logging.getLogger('Modmail')
@@ -113,15 +114,15 @@ async def load_plugin(self, username, repo, plugin_name):
113114
msg = f'Loaded plugins.{username}-{repo}.{plugin_name}'
114115
logger.info(info(msg))
115116

116-
@commands.group(aliases=['plugins'])
117-
@commands.is_owner()
117+
@commands.group(aliases=['plugins'], invoke_without_command=True)
118+
@checks.has_permissions(PermissionLevel.OWNER)
118119
async def plugin(self, ctx):
119120
"""Plugin handler. Controls the plugins in the bot."""
120-
if ctx.invoked_subcommand is None:
121-
cmd = self.bot.get_command('help')
122-
await ctx.invoke(cmd, command='plugin')
121+
cmd = self.bot.get_command('help')
122+
await ctx.invoke(cmd, command='plugin')
123123

124124
@plugin.command()
125+
@checks.has_permissions(PermissionLevel.OWNER)
125126
async def add(self, ctx, *, plugin_name):
126127
"""Adds a plugin"""
127128
if plugin_name in self.bot.config.plugins:
@@ -161,6 +162,7 @@ async def add(self, ctx, *, plugin_name):
161162
'Use username/repo/plugin.')
162163

163164
@plugin.command()
165+
@checks.has_permissions(PermissionLevel.OWNER)
164166
async def remove(self, ctx, *, plugin_name):
165167
"""Removes a certain plugin"""
166168
if plugin_name in self.bot.config.plugins:
@@ -195,6 +197,7 @@ def onerror(func, path, exc_info):
195197
await ctx.send('Plugin not installed.')
196198

197199
@plugin.command()
200+
@checks.has_permissions(PermissionLevel.OWNER)
198201
async def update(self, ctx, *, plugin_name):
199202
"""Updates a certain plugin"""
200203
if plugin_name not in self.bot.config.plugins:
@@ -227,6 +230,7 @@ async def update(self, ctx, *, plugin_name):
227230
await ctx.send(f'Unable to start plugin: `{exc}`')
228231

229232
@plugin.command(name='list')
233+
@checks.has_permissions(PermissionLevel.OWNER)
230234
async def list_(self, ctx):
231235
"""Shows a list of currently enabled plugins"""
232236
if self.bot.config.plugins:

0 commit comments

Comments
 (0)