[Mod] Added an user muting system

Based on user channel overwrites to avoid role management complications. Retains previous send_message permissions (None/True) when the user is unmuted and respects current overwrites.
Supports channel and server wide muting.
This commit is contained in:
Twentysix 2016-11-07 02:10:54 +01:00
parent d9c4bfa15f
commit 13d8da7e09

View File

@ -47,8 +47,10 @@ class Mod:
self.settings = defaultdict(lambda: default_settings.copy(), settings)
self.cache = defaultdict(lambda: deque(maxlen=3))
self.cases = dataIO.load_json("data/mod/modlog.json")
self.last_case = defaultdict(dict)
self._tmp_banned_cache = []
self.last_case = defaultdict(lambda: dict())
perms_cache = dataIO.load_json("data/mod/perms_cache.json")
self._perms_cache = defaultdict(dict, perms_cache)
@commands.group(pass_context=True, no_pm=True)
@checks.serverowner_or_permissions(administrator=True)
@ -254,6 +256,148 @@ class Mod:
await self.bot.say("I cannot do that, I lack the "
"\"Manage Nicknames\" permission.")
@commands.group(pass_context=True, no_pm=True, invoke_without_command=True)
@checks.mod_or_permissions(administrator=True)
async def mute(self, ctx, user : discord.Member):
"""Mutes user in the channel/server
Defaults to channel"""
if ctx.invoked_subcommand is None:
await ctx.invoke(self.channel_mute, user=user)
@mute.command(name="channel", pass_context=True, no_pm=True)
async def channel_mute(self, ctx, user : discord.Member):
"""Mutes user in the current channel"""
channel = ctx.message.channel
overwrites = channel.overwrites_for(user)
if overwrites.send_messages is False:
await self.bot.say("That user can't send messages in this "
"channel.")
return
self._perms_cache[user.id][channel.id] = overwrites.send_messages
overwrites.send_messages = False
try:
await self.bot.edit_channel_permissions(channel, user, overwrites)
except discord.Forbidden:
await self.bot.say("Failed to mute user. I need the manage roles "
"permission and the user I'm muting must be "
"lower than myself in the role hierarchy.")
else:
dataIO.save_json("data/mod/perms_cache.json", self._perms_cache)
await self.bot.say("User has been muted in this channel.")
@mute.command(name="server", pass_context=True, no_pm=True)
async def server_mute(self, ctx, user : discord.Member):
"""Mutes user in the server"""
server = ctx.message.server
register = {}
for channel in server.channels:
if channel.type != discord.ChannelType.text:
continue
overwrites = channel.overwrites_for(user)
if overwrites.send_messages is False:
continue
register[channel.id] = overwrites.send_messages
overwrites.send_messages = False
try:
await self.bot.edit_channel_permissions(channel, user,
overwrites)
except discord.Forbidden:
await self.bot.say("Failed to mute user. I need the manage roles "
"permission and the user I'm muting must be "
"lower than myself in the role hierarchy.")
return
else:
await asyncio.sleep(0.1)
if not register:
await self.bot.say("That user is already muted in all channels.")
return
self._perms_cache[user.id] = register
dataIO.save_json("data/mod/perms_cache.json", self._perms_cache)
await self.bot.say("User has been muted in this server.")
@commands.group(pass_context=True, no_pm=True, invoke_without_command=True)
@checks.mod_or_permissions(administrator=True)
async def unmute(self, ctx, user : discord.Member):
"""Unmutes user in the channel/server
Defaults to channel"""
if ctx.invoked_subcommand is None:
await ctx.invoke(self.channel_unmute, user=user)
@unmute.command(name="channel", pass_context=True, no_pm=True)
async def channel_unmute(self, ctx, user : discord.Member):
"""Unmutes user in the current channel"""
channel = ctx.message.channel
overwrites = channel.overwrites_for(user)
if overwrites.send_messages:
await self.bot.say("That user doesn't seem to be muted "
"in this channel.")
return
if user.id in self._perms_cache:
old_value = self._perms_cache[user.id].get(channel.id, None)
else:
old_value = None
overwrites.send_messages = old_value
is_empty = self.are_overwrites_empty(overwrites)
try:
if not is_empty:
await self.bot.edit_channel_permissions(channel, user,
overwrites)
else:
await self.bot.delete_channel_permissions(channel, user)
except discord.Forbidden:
await self.bot.say("Failed to unmute user. I need the manage roles"
" permission and the user I'm unmuting must be "
"lower than myself in the role hierarchy.")
else:
try:
del self._perms_cache[user.id][channel.id]
except KeyError:
pass
if user.id in self._perms_cache and not self._perms_cache[user.id]:
del self._perms_cache[user.id] #cleanup
dataIO.save_json("data/mod/perms_cache.json", self._perms_cache)
await self.bot.say("User has been unmuted in this channel.")
@unmute.command(name="server", pass_context=True, no_pm=True)
async def server_unmute(self, ctx, user : discord.Member):
"""Unmutes user in the server"""
server = ctx.message.server
if user.id not in self._perms_cache:
await self.bot.say("That user doesn't seem to have been muted with {0}mute commands. "
"Unmute them in the channels you want with `{0}unmute <user>`"
"".format(ctx.prefix))
return
for channel in server.channels:
if channel.type != discord.ChannelType.text:
continue
if channel.id not in self._perms_cache[user.id]:
continue
value = self._perms_cache[user.id].get(channel.id)
overwrites = channel.overwrites_for(user)
if overwrites.send_messages is False:
overwrites.send_messages = value
is_empty = self.are_overwrites_empty(overwrites)
try:
if not is_empty:
await self.bot.edit_channel_permissions(channel, user,
overwrites)
else:
await self.bot.delete_channel_permissions(channel, user)
except discord.Forbidden:
await self.bot.say("Failed to unmute user. I need the manage roles"
" permission and the user I'm unmuting must be "
"lower than myself in the role hierarchy.")
return
else:
del self._perms_cache[user.id][channel.id]
await asyncio.sleep(0.1)
if user.id in self._perms_cache and not self._perms_cache[user.id]:
del self._perms_cache[user.id] #cleanup
dataIO.save_json("data/mod/perms_cache.json", self._perms_cache)
await self.bot.say("User has been unmuted in this server.")
@commands.group(pass_context=True, no_pm=True)
@checks.mod_or_permissions(manage_messages=True)
async def cleanup(self, ctx):
@ -995,6 +1139,12 @@ class Mod:
dataIO.save_json("data/mod/past_nicknames.json",
self.past_nicknames)
def are_overwrites_empty(self, overwrites):
"""There is currently no cleaner way to check if a
PermissionOverwrite object is empty"""
original = [p for p in iter(overwrites)]
empty = [p for p in iter(discord.PermissionOverwrite())]
return original == empty
def check_folders():
folders = ("data", "data/mod/")
@ -1007,38 +1157,22 @@ def check_folders():
def check_files():
ignore_list = {"SERVERS": [], "CHANNELS": []}
if not os.path.isfile("data/mod/blacklist.json"):
print("Creating empty blacklist.json...")
dataIO.save_json("data/mod/blacklist.json", [])
if not os.path.isfile("data/mod/whitelist.json"):
print("Creating empty whitelist.json...")
dataIO.save_json("data/mod/whitelist.json", [])
if not os.path.isfile("data/mod/ignorelist.json"):
print("Creating empty ignorelist.json...")
dataIO.save_json("data/mod/ignorelist.json", ignore_list)
if not os.path.isfile("data/mod/filter.json"):
print("Creating empty filter.json...")
dataIO.save_json("data/mod/filter.json", {})
if not os.path.isfile("data/mod/past_names.json"):
print("Creating empty past_names.json...")
dataIO.save_json("data/mod/past_names.json", {})
if not os.path.isfile("data/mod/past_nicknames.json"):
print("Creating empty past_nicknames.json...")
dataIO.save_json("data/mod/past_nicknames.json", {})
if not os.path.isfile("data/mod/settings.json"):
print("Creating empty settings.json...")
dataIO.save_json("data/mod/settings.json", {})
if not os.path.isfile("data/mod/modlog.json"):
print("Creating empty modlog.json...")
dataIO.save_json("data/mod/modlog.json", {})
files = {
"blacklist.json" : [],
"whitelist.json" : [],
"ignorelist.json" : ignore_list,
"filter.json" : {},
"past_names.json" : {},
"past_nicknames.json" : {},
"settings.json" : {},
"modlog.json" : {},
"perms_cache.json" : {}
}
for filename, value in files.items():
if not os.path.isfile("data/mod/{}".format(filename)):
print("Creating empty {}".format(filename))
dataIO.save_json("data/mod/{}".format(filename), value)
def setup(bot):
global logger