예제 #1
0
async def _http_error_handler(ctx: Context, error: HTTPException):
    """Handle HTTP errors."""
    if error.code == 50006:
        # empty message
        await safe_send(
            ctx,
            "Uh oh! Unable to send an empty message." + _report_bug_message,
        )
        if safe_bug_report(ctx):
            await safe_send(ctx, f"```{error}```")
        raise error

    if error.code == 50035:
        # invalid body
        await safe_send(
            ctx,
            ("Uh oh! The response had an invalid body, likely due to length." +
             _report_bug_message),
        )
        if safe_bug_report(ctx):
            await safe_send(ctx, f"```{error}```")
        raise error

    if error.code == 30003:
        # pin limit
        await safe_send(ctx, "Uh oh! The pin limit has been reached.")
        raise error
예제 #2
0
    async def end(self, ctx: "DayContext"):
        """End the day."""
        # cleanup effects
        for player in ctx.bot.game.seating_order:
            effect_list = [x for x in player.effects]
            for effect in effect_list:
                effect.evening_cleanup(ctx.bot.game)

        # remove the current vote
        if self.current_vote:
            await self.current_vote.cancel(ctx)

        # announcement
        await safe_send(
            ctx.bot.channel,
            f"{ctx.bot.player_role.mention}, go to sleep!",
        )

        # message tally
        await self._send_message_tally(ctx)

        # remove the day
        ctx.bot.game.past_days.append(self)
        ctx.bot.game.current_day = None

        # complete
        if safe_bug_report(ctx):
            await safe_send(ctx, "Successfully ended the day.")

        # new night
        await ctx.bot.game.start_night(ctx)
예제 #3
0
    async def execute(self, ctx: "GameContext"):
        """Execute the player."""
        message_text = f"{self.nick} has been executed, "
        if self.ghost(ctx.bot.game):
            await safe_send(
                ctx.bot.channel,
                message_text + "but is already dead.",
            )

        elif self.is_status(ctx.bot.game, "safe"):
            await safe_send(
                ctx.bot.channel,
                message_text + "but does not die.",
            )

        else:
            await safe_send(ctx.bot.channel, message_text + "and dies.")
            self.add_effect(ctx.bot.game, Dead, self)

        # Day.end has a "successfully ended the day" message so this is above that
        if safe_bug_report(ctx):
            await safe_send(ctx, f"Successfully executed {self.nick}.")

        if ctx.bot.game.current_day:
            # TODO: currently doesn't support extra-nomination effects
            await ctx.bot.game.current_day.end(ctx)
예제 #4
0
        async def inner_wrapper(*args, **kwargs):
            if args[0].parent.functioning(args[1]):
                return await func(*args, **kwargs)
            if safe_bug_report(args[1]):
                if args[0].parent.ghost(args[1]):
                    status = "dead"
                elif args[0].parent.is_status(args[1], "drunk"):
                    status = "drunk"
                elif args[0].parent.is_status(args[1], "poisoned"):
                    status = "poisoned"
                else:
                    status = "not functioning"
                if run_if_drunkpoisoned and status in ("drunk", "poisoned"):
                    kwargs["enabled"] = False
                    kwargs["epithet_string"] = f"({status})"
                    return await func(*args, **kwargs)
                pronouns = load_preferences(args[0].parent).pronouns
                await safe_send(
                    args[1],
                    "Skipping {epithet}, as {pronoun} {verb} {status}.".format(
                        epithet=args[0].parent.epithet,
                        pronoun=pronouns[0],
                        verb=("is", "are")[pronouns[5]],
                        status=status,
                    ),
                )

            # this return is hella janky but basically we want this to work for any
            # of the character methods (ex morning, evening) and they have different
            # return types so we need to grab whatever the generic return is.
            # the character initializes with no parent to let us check in the method
            # if it's actually being called or just being called to get the return
            # so we can hide side effects in an "if self.parent" block
            return await getattr(Character(None), func.__name__)(*args[1:],
                                                                 **kwargs)
예제 #5
0
 async def current_step(self, ctx: "GameContext"):
     """Send a reminder of the current step in the night."""
     message_text = await self._current_character.morning_call(ctx)
     if message_text:
         if safe_bug_report(ctx):
             await safe_send(ctx, message_text)
     else:
         await self._increment_night(ctx)
예제 #6
0
 async def wrapper(*args, **kwargs):
     if not args[0].parent.is_status(args[1], "used_ability"):
         return await func(*args, **kwargs)
     if safe_bug_report(args[1]):
         pronouns = load_preferences(args[0].parent).pronouns
         await safe_send(
             args[1],
             ("Skipping {epithet}, as "
              "{subjective} {verb} used {posessive} ability.").format(
                  epithet=args[0].parent.epithet,
                  subjective=pronouns[0],
                  verb=["has", "have"][pronouns[5]],
                  posessive=pronouns[2],
              ),
         )
     return await getattr(Character(None), func.__name__)(*args[1:],
                                                          **kwargs)
예제 #7
0
 async def wrapper(character: Character, ctx: "GameContext", *args,
                   **kwargs):
     if not character.parent.is_status(ctx.bot.game, "used_ability"):
         return await func(character, ctx, *args, **kwargs)
     if safe_bug_report(ctx):
         pronouns = load_preferences(character.parent).pronouns
         await safe_send(
             ctx,
             ("Skipping {epithet}, as "
              "{subjective} {verb} used {posessive} ability.").format(
                  epithet=character.parent.epithet,
                  subjective=pronouns[0],
                  verb=["has", "have"][pronouns[5]],
                  posessive=pronouns[2],
              ),
         )
     return await getattr(Character(None), func.__name__)(  # type: ignore
         ctx, *args, **kwargs)
예제 #8
0
    async def _end(self, ctx: "GameContext"):
        for player in ctx.bot.game.seating_order:
            player.morning(ctx.bot.inactive_role)
            effect_list = [x for x in player.effects]
            for effect in effect_list:
                effect.morning_cleanup(ctx)

        # announcements
        # kills
        shuffle(self._kills)
        text = list_to_plural_string([x.nick for x in self._kills],
                                     alt="No one")
        await safe_send(
            ctx.bot.channel,
            "{text} {verb} died.".format(text=text[0],
                                         verb=("has", "have")[text[1]]),
        )

        # other
        for content in self._messages:
            if content:
                await safe_send(ctx.bot.channel, content, pin=True)

        # start day
        await safe_send(
            ctx.bot.channel,
            f"{ctx.bot.player_role.mention}, wake up!",
            pin=bool(self._kills),
        )

        # end night
        ctx.bot.game.past_nights.append(self)
        ctx.bot.game.current_night = None

        # make the day
        ctx.bot.game.current_day = Day()

        # complete
        if safe_bug_report(ctx):
            await safe_send(ctx, "Successfully started the day.")
예제 #9
0
async def _generic_error_handler(ctx: Context, error, text):
    await safe_send(ctx, f"Uh oh! {text} {_report_bug_message}")
    if safe_bug_report(ctx):
        await safe_send(ctx, f"```{error}```")
    raise error
예제 #10
0
    async def on_command_error(self, ctx: Context, error: Exception):
        """Handle command errors."""

        # Ignore commands with local handling
        if hasattr(ctx.command, "on_error"):
            return

        # Check original exceptions for commands.CommandInvokeError
        error = getattr(error, "original", error)

        if isinstance(error, commands.CheckFailure):
            # commands which fail contextual checks

            if isinstance(error, commands.NotOwner):
                # commands.is_owner()
                return await safe_send(
                    ctx,
                    "Stop trying to play around with debug tools, please! ;)")

            if str(error):
                # most checks
                return await safe_send(ctx, str(error))

            # checks.is_dm() and checks.is_in_channel()
            return

        if isinstance(error, ValueError):
            # value errors

            if str(error) == "cancelled":
                # raised by lib.utils.get_input if input is cancel
                await safe_send(ctx, "Cancelled!")
                return

            if str(error) == "command called":
                # raised by lib.utils.get_input if another command is called
                return

            if str(error) == "player not found":
                # raised by lib.utils.get_player
                raise error

            if str(error) == "unmatched seating order length":
                # raised by lib.logic.Game.Game.reseat
                await safe_send(
                    ctx,
                    "The new and old seating orders have differing lengths.")
                return

        elif isinstance(error, HTTPException):
            # errors in HTTP request operations
            await _http_error_handler(ctx, error)

        elif isinstance(error, SyntaxError):
            # syntax errors
            await safe_send(
                ctx,
                "Uh oh! There's a syntax error somewhere." +
                _report_bug_message,
            )
            if safe_bug_report(ctx):
                await safe_send(ctx, f"```{error}```")
            raise error

        elif isinstance(error, asyncio.TimeoutError):
            # timeout error
            return await safe_send(ctx, "Timed out.")

        elif isinstance(error, commands.CommandNotFound):
            # unknown commands
            if ctx.guild is None:
                return await safe_send(
                    ctx,
                    (f"Command not found: {ctx.invoked_with}. "
                     f"For a list of commands, try `{ctx.prefix}help`."),
                )
            return

        elif isinstance(error, commands.errors.MissingRequiredArgument):
            # not enough arguments
            await safe_send(
                ctx, "Uh oh! You're missing arguments. Hopefully this helps:")
            return await ctx.send_help(ctx.command)

        elif isinstance(error, commands.BadArgument):
            # converter error
            return await safe_send(ctx, str(error))

        elif isinstance(error, commands.DisabledCommand):
            # disabled command
            return await safe_send(ctx,
                                   f"{ctx.command.name} has been disabled.")

        await safe_send(
            ctx,
            "Uh oh! An unknown error occured." + _report_bug_message,
        )
        if safe_bug_report(ctx):
            await safe_send(ctx, f"```{error}```")
        raise error
예제 #11
0
    async def start_game(self, ctx: "Context", script: "Script"):
        """Handle startgame logic."""
        await safe_send(ctx, f"Starting a {script.name} game.")

        # ask for the list of players
        users = await to_member_list(
            ctx,
            (await get_input(
                ctx,
                ("What is the seating order? (Separate "
                 "users with line breaks. Do not include "
                 "travelers.)"),
            )).split("\n"),
        )

        # ask for the list of characters
        characters = to_character_list(
            ctx,
            (await get_input(
                ctx,
                ("What are the corresponding characters? "
                 "(Separate characters with line breaks.)"),
            )).split("\n"),
            script,
        )

        with ctx.typing():  # doing a lot of computation here

            # verify 1:1 user:character ratio
            if len(users) != len(characters):
                raise commands.BadArgument(
                    "There are a different number of players and characters.")

            # role cleanup
            await self._startgame_role_cleanup(users)

            # generate seating order
            seating_order = [
                Player(person, characters[index], index)
                for index, person in enumerate(users)
            ]

            # script message
            posts = []
            for content in list(script.info(ctx)):
                posts.append(await safe_send(self.channel, content))

            for post in posts[::
                              -1]:  # Reverse the _order so the pins are right
                await post.pin()

            # welcome message
            await safe_send(
                self.channel,
                (f"{self.player_role.mention}, "
                 "welcome to Blood on the Clocktower! Go to "
                 "sleep."),
            )

            # seating order message
            seating_order_message = await safe_send(
                self.channel,
                generate_game_info_message(seating_order, ctx.bot.game),
                pin=True,
            )

            # storytellers
            storytellers = [
                Player(person, Storyteller, None)
                for person in self.storyteller_role.members
            ]

            # start the game
            self.game = Game(seating_order, seating_order_message, script,
                             storytellers)
            if safe_bug_report(ctx):
                await safe_send(ctx, "Started the game successfully.")
            await self.game.start_night(ctx)