async def on_command_error(self, ctx, error): # pylint: disable=too-many-return-statements, arguments-differ, too-many-branches if hasattr(ctx.command, 'on_error'): return if isinstance(error, commands.CommandNotFound): args = remove_prefix(ctx.message.content, ctx.bot.global_prefix).split(' ') command = args[0] if ctx.guild is not None: # DM only return games = [ *filter(lambda g: ctx.author in g.players, self.games.values())] if len(games) == 0: return pl_game = games[0] if pl_game.phase != Phase.NIGHT: return player = pl_game.players[ctx.author] if not alive_or_recent_jester(player, pl_game) \ or not hasattr(player.role, 'action'): return valid_actions = [player.role.action, 'noaction'] if not isinstance(player.role.action, list) \ else player.role.action + ['noaction'] if command.lower() not in valid_actions: return await player.role.on_pm_command(ctx, pl_game, player, args) return # ignore invalid commands elif isinstance(error, commands.MissingRequiredArgument): return await ctx.send(f'Missing required argument {error.param}') elif isinstance(error, (commands.ArgumentParsingError, commands.BadArgument)): return await ctx.send('Invalid input') elif isinstance(error, commands.CheckFailure): return await ctx.send(error) elif isinstance(error, commands.CommandOnCooldown): retry_after = round(error.retry_after) return await ctx.send('You are using this command too fast: try again in {} second{}'.format(retry_after, pluralize(retry_after))) elif isinstance(error, PhaseChangeError): # Inform users that game has ended and remove channel id from `self.games`. await ctx.send('There was an error incrementing the phase. The game has ended.') self.games.pop(ctx.channel.id, None) return self.logger.exception(error, exc_info=(type(error), error, error.__traceback__)) await ctx.send(f'Uncaught exception: ```{error}```') if config.get('ENV', '') == "production": # End game only if `env` is set to 'production'. await ctx.send('\nThe game has ended.') self.games.pop(ctx.channel.id, None) self.logger.exception(error, exc_info=( type(error), error, error.__traceback__))
def action_only_filter(player): if not alive_or_recent_jester(player, self.game): return False can_do, _ = player.role.can_do_action(self.game) return can_do
async def increment_phase(self): # If it is day, `phase_t` should be equal to night_duration and vice versa. # `phase_duration` is used at the end of the function. # `phase_t` is used in day/night starting messages. if self.phase == Phase.DAY: phase_duration = self.config['night_duration'] phase_t = round(phase_duration / 60, 1) else: # Set it to day duration for all other phases. # Note that it should almost always be `Phase.NIGHT`. phase_duration = self.config['day_duration'] phase_t = round(phase_duration / 60, 1) # night loop is the same as the pregame loop if self.cycle == 0 or self.phase == Phase.NIGHT: # resolve night actions self.phase = Phase.STANDBY # so the event loop doesn't mess things up here dead_players = await self.night_actions.resolve() for player in dead_players: role_text = 'We could not determine their role.' if player.role.cleaned else f'They were a {player.display_role}.' await self.channel.send( f'{player.user.name} died last night. {role_text}\n') game_ended, winning_faction, independent_wins = self.check_endgame( ) if game_ended: return await self.end(winning_faction, independent_wins) # clear visits for player in self.players: player.visitors.clear() # voting starts self.night_actions.reset() self.phase = Phase.DAY self.cycle = self.cycle + 1 alive_players = self.players.filter(is_alive=True) # populate voting cache self.votes['nolynch'] = [] self.votes['notvoting'] = [] for player in alive_players: self.votes[player.user.id] = [] self.votes['notvoting'].append(player) await self.channel.send( f'Day **{self.cycle}** will last {phase_t} minutes.' f' With {len(alive_players)} alive, it takes {self.majority_votes} to lynch.' ) else: self.phase = Phase.STANDBY # remove all votes from every player self.votes.clear() await self.channel.send( f'Night **{self.cycle}** will last {phase_t} minutes. ' 'Send in those actions quickly!') # recently lynched jesters and alive players are allowed to send in actions for player in filter(lambda p: alive_or_recent_jester(p, self), self.players): if hasattr(player.role, 'on_night'): await player.role.on_night(self.bot, player, self) self.phase = Phase.NIGHT self.phase_end_at = datetime.now() \ + timedelta(seconds=phase_duration)