From d206ea2cb91082a70fe52132834d023b0428f22f Mon Sep 17 00:00:00 2001 From: Bobloy Date: Tue, 1 May 2018 17:05:08 -0400 Subject: [PATCH] Errors and permissions --- werewolf/game.py | 105 ++++++++++++++++++++++--------- werewolf/werewolf.py | 144 ++++++++++++++++++++++++++++--------------- 2 files changed, 171 insertions(+), 78 deletions(-) diff --git a/werewolf/game.py b/werewolf/game.py index 4ab529f..a46d2ce 100644 --- a/werewolf/game.py +++ b/werewolf/game.py @@ -26,12 +26,11 @@ class Game: day_vote_count = 3 - def __init__(self, guild: discord.Guild, role: discord.Role=None, - category: discord.CategoryChannel=None, village: discord.TextChannel=None, - log_channel: discord.TextChannel=None, game_code=None): + def __init__(self, guild: discord.Guild, role: discord.Role = None, + category: discord.CategoryChannel = None, village: discord.TextChannel = None, + log_channel: discord.TextChannel = None, game_code=None): self.guild = guild self.game_code = game_code - self.game_role = role self.roles = [] # List[Role] self.players = [] # List[Player] @@ -48,10 +47,14 @@ class Game: self.day_count = 0 self.ongoing_vote = False + self.game_role = role # discord.Role self.channel_category = category # discord.CategoryChannel self.village_channel = village # discord.TextChannel self.log_channel = log_channel + self.to_delete = set() + self.save_perms = {} + self.p_channels = {} # uses default_secret_channel self.vote_groups = {} # ID : VoteGroup() @@ -97,14 +100,22 @@ class Game: if self.game_role is None: try: - self.game_role = await ctx.guild.create_role(name="Players", + self.game_role = await ctx.guild.create_role(name="WW Players", hoist=True, mentionable=True, reason="(BOT) Werewolf game role") + self.to_delete.add(self.game_role) except (discord.Forbidden, discord.HTTPException): await ctx.send("Game role not configured and unable to generate one, cannot start") self.roles = [] return False + try: + for player in self.players: + await player.member.add_roles(*[self.game_role]) + except discord.Forbidden: + await ctx.send( + "Unable to add role **{}**\nBot is missing `manage_roles` permissions".format(self.game_role.name)) + return False await self.assign_roles() @@ -112,32 +123,54 @@ class Game: overwrite = { self.guild.default_role: discord.PermissionOverwrite(read_messages=True, send_messages=False, add_reactions=False), - self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, add_reactions=True), + self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, add_reactions=True, + manage_messages=True, manage_channels=True, + manage_roles=True), self.game_role: discord.PermissionOverwrite(read_messages=True, send_messages=True) } if self.channel_category is None: - self.channel_category = await self.guild.create_category("🔴 Werewolf Game (ACTIVE)", + self.channel_category = await self.guild.create_category("Werewolf Game", overwrites=overwrite, reason="(BOT) New game of werewolf") - else: - await self.channel_category.edit(name="🔴 Werewolf Game (ACTIVE)", reason="(BOT) New game of werewolf") - for target, ow in overwrite.items(): - await self.channel_category.set_permissions(target=target, - overwrite=ow, - reason="(BOT) New game of werewolf") + else: # No need to modify categories + pass + # await self.channel_category.edit(name="🔴 Werewolf Game (ACTIVE)", reason="(BOT) New game of werewolf") + # for target, ow in overwrite.items(): + # await self.channel_category.set_permissions(target=target, + # overwrite=ow, + # reason="(BOT) New game of werewolf") if self.village_channel is None: - self.village_channel = await self.guild.create_text_channel("village-square", - overwrites=overwrite, - reason="(BOT) New game of werewolf", - category=self.channel_category) + try: + self.village_channel = await self.guild.create_text_channel("🔵Werewolf", + overwrites=overwrite, + reason="(BOT) New game of werewolf", + category=self.channel_category) + except discord.Forbidden: + await ctx.send("Unable to create Game Channel and none was provided\n" + "Grant Bot appropriate permissions or assign a game_channel") + return False else: - await self.village_channel.edit(name="Village Square", - category=self.channel_category, - reason="(BOT) New game of werewolf") - for target, ow in overwrite.items(): - await self.village_channel.set_permissions(target=target, - overwrite=ow, - reason="(BOT) New game of werewolf") + self.save_perms[self.village_channel] = self.village_channel.overwrites() + try: + await self.village_channel.edit(name="🔵Werewolf", + category=self.channel_category, + reason="(BOT) New game of werewolf") + except discord.Forbidden as e: + print("Unable to rename Game Channel") + print(e) + await ctx.send("Unable to rename Game Channel, ignoring") + + try: + for target, ow in overwrite.items(): + curr = self.village_channel.overwrites_for(target) + curr.update(**{perm: value for perm, value in ow}) + await self.village_channel.set_permissions(target=target, + overwrite=curr, + reason="(BOT) New game of werewolf") + except discord.Forbidden: + await ctx.send("Unable to edit Game Channel permissions\n" + "Grant Bot appropriate permissions to manage permissions") + return self.started = True # Assuming everything worked so far print("Pre at_game_start") @@ -147,7 +180,9 @@ class Game: print("Channel id: " + channel_id) overwrite = { self.guild.default_role: discord.PermissionOverwrite(read_messages=False), - self.guild.me: discord.PermissionOverwrite(read_messages=True) + self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, add_reactions=True, + manage_messages=True, manage_channels=True, + manage_roles=True) } for player in self.p_channels[channel_id]["players"]: @@ -433,7 +468,12 @@ class Game: self.players.append(Player(member)) - await member.add_roles(*[self.game_role]) + if self.game_role is not None: + try: + await member.add_roles(*[self.game_role]) + except discord.Forbidden: + await channel.send( + "Unable to add role **{}**\nBot is missing `manage_roles` permissions".format(self.game_role.name)) await channel.send("{} has been added to the game, " "total players is **{}**".format(member.mention, len(self.players))) @@ -744,7 +784,14 @@ class Game: async def _end_game(self): # Remove game_role access for potential archiving for now reason = '(BOT) End of WW game' - await self.village_channel.set_permissions(self.game_role, overwrite=None, reason=reason) - await self.channel_category.set_permissions(self.game_role, overwrite=None, reason=reason) - await self.channel_category.edit(reason=reason, name="Werewolf Game (INACTIVE)") + for obj in self.to_delete: + print(obj) + await obj.delete(reason=reason) + + try: + await self.village_channel.edit(reason=reason, name="Werewolf") + await self.village_channel.set_permissions(self.game_role, overwrite=None, reason=reason) + except (discord.HTTPException, discord.NotFound, discord.errors.NotFound): + pass + # Optional dynamic channels/categories diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py index 60ce051..1f00dbc 100644 --- a/werewolf/werewolf.py +++ b/werewolf/werewolf.py @@ -1,6 +1,6 @@ import discord from discord.ext import commands -from redbot.core import Config +from redbot.core import Config, checks from redbot.core import RedContext from redbot.core.bot import Red @@ -8,6 +8,7 @@ from werewolf.builder import GameBuilder, role_from_name, role_from_alignment, r from werewolf.game import Game from werewolf.utils.menus import menu, DEFAULT_CONTROLS + class Werewolf: """ Base to host werewolf on a guild @@ -44,6 +45,7 @@ class Werewolf: else: await ctx.send("No code generated") + @checks.guildowner() @commands.group() async def wwset(self, ctx: RedContext): """ @@ -52,47 +54,80 @@ class Werewolf: if ctx.invoked_subcommand is None: await ctx.send_help() + @commands.guild_only() + @wwset.command(name="list") + async def wwset_list(self, ctx: RedContext): + """ + Lists current guild settings + """ + success, role, category, channel, log_channel = await self._get_settings(ctx) + if not success: + await ctx.send("Failed to get settings") + return None + + embed = discord.Embed(title="Current Guild Settings") + embed.add_field(name="Role", value=str(role)) + embed.add_field(name="Category", value=str(category)) + embed.add_field(name="Channel", value=str(channel)) + embed.add_field(name="Log Channel", value=str(log_channel)) + await ctx.send(embed=embed) + @commands.guild_only() @wwset.command(name="role") - async def wwset_role(self, ctx: RedContext, role: discord.Role): + async def wwset_role(self, ctx: RedContext, role: discord.Role=None): """ Assign the game role This role should not be manually assigned """ - await self.config.guild(ctx.guild).role_id.set(role.id) - await ctx.send("Game role has been set to **{}**".format(role.name)) + if role is None: + await self.config.guild(ctx.guild).role_id.set(None) + await ctx.send("Cleared Game Role") + else: + await self.config.guild(ctx.guild).role_id.set(role.id) + await ctx.send("Game Role has been set to **{}**".format(role.name)) @commands.guild_only() @wwset.command(name="category") - async def wwset_category(self, ctx: RedContext, category_id): + async def wwset_category(self, ctx: RedContext, category_id=None): """ Assign the channel category """ - - category = discord.utils.get(ctx.guild.categories, id=int(category_id)) - if category is None: - await ctx.send("Category not found") - return - await self.config.guild(ctx.guild).category_id.set(category.id) - await ctx.send("Channel Category has been set to **{}**".format(category.name)) + if category_id is None: + await self.config.guild(ctx.guild).category_id.set(None) + await ctx.send("Cleared Game Channel Category") + else: + category = discord.utils.get(ctx.guild.categories, id=int(category_id)) + if category is None: + await ctx.send("Category not found") + return + await self.config.guild(ctx.guild).category_id.set(category.id) + await ctx.send("Game Channel Category has been set to **{}**".format(category.name)) @commands.guild_only() @wwset.command(name="channel") - async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel): + async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel=None): """ Assign the village channel """ - await self.config.guild(ctx.guild).channel_id.set(channel.id) - await ctx.send("Game Channel has been set to **{}**".format(channel.mention)) + if channel is None: + await self.config.guild(ctx.guild).channel_id.set(None) + await ctx.send("Cleared Game Channel") + else: + await self.config.guild(ctx.guild).channel_id.set(channel.id) + await ctx.send("Game Channel has been set to **{}**".format(channel.mention)) @commands.guild_only() @wwset.command(name="logchannel") - async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel): + async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel=None): """ Assign the log channel """ - await self.config.guild(ctx.guild).log_channel_id.set(channel.id) - await ctx.send("Log Channel has been set to **{}**".format(channel.mention)) + if channel is None: + await self.config.guild(ctx.guild).log_channel_id.set(None) + await ctx.send("Cleared Game Log Channel") + else: + await self.config.guild(ctx.guild).log_channel_id.set(channel.id) + await ctx.send("Game Log Channel has been set to **{}**".format(channel.mention)) @commands.group() async def ww(self, ctx: RedContext): @@ -165,7 +200,8 @@ class Werewolf: if not game: await ctx.send("No game running, cannot start") - await game.setup(ctx) + if not await game.setup(ctx): + pass # Do something? @commands.guild_only() @ww.command(name="stop") @@ -305,36 +341,11 @@ class Werewolf: return None if guild.id not in self.games or self.games[guild.id].game_over: await ctx.send("Starting a new game...") - role = None - category = None - channel = None - log_channel = None - - role_id = await self.config.guild(guild).role_id() - category_id = await self.config.guild(guild).category_id() - channel_id = await self.config.guild(guild).channel_id() - log_channel_id = await self.config.guild(guild).log_channel_id() - - if role_id is not None: - role = discord.utils.get(guild.roles, id=role_id) - if role is None: - await ctx.send("Game Role is invalid, cannot start new game") - return None - if category_id is not None: - category = discord.utils.get(guild.categories, id=category_id) - if category is None: - await ctx.send("Game Category is invalid, cannot start new game") - return None - if channel_id is not None: - channel = discord.utils.get(guild.text_channels, id=channel_id) - if channel is None: - await ctx.send("Village Channel is invalid, cannot start new game") - return None - if log_channel_id is not None: - log_channel = discord.utils.get(guild.text_channels, id=log_channel_id) - if log_channel is None: - await ctx.send("Log Channel is invalid, cannot start new game") - return None + success, role, category, channel, log_channel = await self._get_settings(ctx) + + if not success: + await ctx.send("Cannot start a new game") + return None self.games[guild.id] = Game(guild, role, category, channel, log_channel, game_code) @@ -342,3 +353,38 @@ class Werewolf: async def _game_start(self, game): await game.start() + + async def _get_settings(self, ctx): + guild = ctx.guild + role = None + category = None + channel = None + log_channel = None + + role_id = await self.config.guild(guild).role_id() + category_id = await self.config.guild(guild).category_id() + channel_id = await self.config.guild(guild).channel_id() + log_channel_id = await self.config.guild(guild).log_channel_id() + + if role_id is not None: + role = discord.utils.get(guild.roles, id=role_id) + if role is None: + await ctx.send("Game Role is invalid") + return False, None, None, None, None + if category_id is not None: + category = discord.utils.get(guild.categories, id=category_id) + if category is None: + await ctx.send("Game Category is invalid") + return False, None, None, None, None + if channel_id is not None: + channel = discord.utils.get(guild.text_channels, id=channel_id) + if channel is None: + await ctx.send("Village Channel is invalid") + return False, None, None, None, None + if log_channel_id is not None: + log_channel = discord.utils.get(guild.text_channels, id=log_channel_id) + if log_channel is None: + await ctx.send("Log Channel is invalid") + return False, None, None, None, None + + return True, role, category, channel, log_channel \ No newline at end of file