async def inspect(ctx, *args):
    """Provides details about a given entity

    EXAMPLE: ~inspect "Evan's Halcyon" 0,0
    OR
    EXAMPLE: ~inspect "Evan's Halcyon" Earth North

    entity_name -- The display name of the target entity
    target_xy -- The (x,y) coordinates of the region containing the target
    OR
    celestial_name -- The name of the celestial the entity is on
    territory_name -- The name of the territory the entity is in"""
    if len(args) == 2:
        # if region
        entity_name = args[0]
        target_xy = args[1]
        # get the obj we want
        target = get_entity_obj(entity_name, target_xy=target_xy)
    elif len(args) == 3:
        # if territory
        entity_name = args[0]
        celestial_name = args[1]
        territory_name = args[2]
        target = get_entity_obj(entity_name, target_celestial=celestial_name, target_territory=territory_name)
    else:
        await ctx.send('```Error: Improper number of arguments.\n ~scan "target_xy" OR ~scan "celestial_name" "territory_name"')
        return
    # inspect it and send the result to payload manager
    output = payload_manage(target.inspect())
    await ctx.send(output)
async def manual_complete(ctx):
    """Automatically completes all tasks"""
    payloads = manual_complete_all_tasks()
    channel = bot.get_channel(734116611300261939)
    for p in payloads:
        output = payload_manage(p)
        await channel.send(output)
async def on_command_error(ctx, error):
    '''Manages error messages'''
    # This prevents any commands with local handlers being handled here in on_command_error.
    if hasattr(ctx.command, 'on_error'):
        return
    if isinstance(error, commands.errors.CommandInvokeError):
        err_string = str(error)
        if 'KeyError' in err_string:
            missing_ID = err_string.split()[-1]
            messages = [f'A lookup failed when looking under the ID {missing_ID}.',
                        'You may have mistyped the entity name, or are searching in the wrong region/territory.',
                        'Try ~scanning the region or territory to make sure the entity is in it.']
            output = payload_manage(Payload(None, messages))
            await ctx.send(output)
        traceback.print_exception(type(error), error, error.__traceback__)
        return
    if isinstance(error, commands.errors.MissingRequiredArgument):
        command = ctx.command
        signature = command.signature.replace('<', '"').replace('>', '"')
        messages = [f'Improper usage or missing arguments for {command}.',
                    f'{command.name} {signature}']
        output = payload_manage(Payload(None, messages))
        await ctx.send(output)
        return
    if isinstance(error, commands.errors.CommandNotFound):
        attempt = ctx.message.content[1:]
        all_commands = bot.all_commands
        possible_commands = [c for c in all_commands if attempt in c]
        messages = [f'The command "{attempt}" does not exist']
        if possible_commands:
            messages.append(f'Did you mean: {possible_commands}?')
        else:
            messages.append(f'No commands contain "{attempt}".')
        output = payload_manage(Payload(None, messages))
        await ctx.send(output)
        return
    if isinstance(error, KeyError):
        messages = f'No result was found when looking up {error}'
        output = payload_manage(Payload(None, messages))
        await ctx.send(output)
        return
    print('Ignoring exception in command {}:'.format(ctx.command))
    traceback.print_exception(type(error), error, error.__traceback__)
async def task_check_loop():
    await bot.wait_until_ready()
    while not bot.is_closed():
        # check all tasks and register the returning payloads as output
        payloads = check_tasks()
        # send the list of messages
        channel = bot.get_channel(734116611300261939)
        for p in payloads:
            output = payload_manage(p)
            await channel.send(output)
        # in seconds, determine how long between checks
        await asyncio.sleep(300)
async def inspect_territory(ctx, planet, territory):
    """Inspects a given territory of a planet

    EXAMPLE: ~inspect_territory "North" "Earth"

    territory -- The name of the territory
    planet -- The name of the planet"""
    Territories = get_file('Territories.pickle')
    # get the territory ID
    TID = planet.upper() + territory.lower()
    territory_obj = Territories[TID]
    pload = territory_obj.inspect()
    output = payload_manage(pload)
    await ctx.send(output)
async def z_use_ability(ctx, caster_entity_name, caster_xy, ability, *args):
    """A more difficult but faster method of using abilities. Consider use_ability.

    EXAMPLE: z_use_ability "Evan's Halcyon" 0,0 move_region 1,0

    caster_entity_name -- The entity whose ability you wish to use
    caster_xy -- The (x,y) coordinates of the region containing the caster
    ability -- The name of the ability you want to use (shown by ~inspect_entity)
    *args -- Any arguments needed for the ability
    """
    # get the entity object
    caster = get_entity_obj(caster_entity_name, target_xy=caster_xy)
    # get the method linked to the ability
    ability_method = getattr(caster, 'A_' + ability)
    # call the method
    output = payload_manage(ability_method(*args))
    await ctx.send(output)
async def ability_help(ctx, entity_display, target_xy, ability):
    """Sends a help display for an ability of a given entity

    EXAMPLE: ~ability_help "Evan's Halcyon" (0,0) move_region

    entity_display -- The display name of the target, e.g. "Breq's Halcyon" \n
    target_xy -- The (x,y) coordinates of the target \n
    ability -- The ability name to display help for (shown by ~inspect)
    """
    target = get_entity_obj(entity_display, target_xy=target_xy)
    ability_method = getattr(target, 'A_' + ability)
    arg_count = ability_method.__code__.co_argcount
    arguments = str(ability_method.__code__.co_varnames[1:arg_count])
    messages = [str(ability_method.__name__)[2:] + '- Arguments: ' + arguments,
                str(ability_method.__doc__)]
    output = payload_manage(Payload(None, messages))
    await ctx.send(output)
async def scan(ctx, *args):
    """Returns the contents of the given region or territory.

    You must have vision of the target.
    Vision is typically granted by possessing a unit in that area.

    EXAMPLE: ~scan (0,0)
    OR
    EXAMPLE: ~scan Earth North

    target_xy -- The (x,y) coordinates of the region to scan
    OR
    celestial_name -- The name of the celestial the target is on
    territory_name -- The label of the territory the target is in"""
    if len(args) == 1:
        # if the caster is in a region
        target_xy = args[0]
        storage_file = 'Regions.pickle'
        Regions = get_file(storage_file)
        # translate coords to actual region object
        target = Regions[region_string_to_int(target_xy)]
    elif len(args) == 2:
        # if the caster is in a territory
        celestial_name = args[0]
        territory_name = args[1]
        territory_label = celestial_name.upper() + territory_name.lower()
        storage_file = 'Territories.pickle'
        Territories = get_file(storage_file)
        target = Territories[territory_label]
    else:
        await ctx.send('```Error: Improper number of arguments.\n ~scan "target_xy" OR ~scan "celestial_name" "territory_name"')
        return
    # check if the user has vision of the target
    uid = ctx.message.author.id
    if not target.check_vision(uid):
        await ctx.send(f'```You do not have vision of {target}.```')
        return
    # if they do, scan it
    result = target.scan()
    output = payload_manage(result)
    await ctx.send(output)
async def use_ability(ctx, *args):
    """Uses an ability of a given entity in a given region.

    Activates a user interface that displays all of the caster's abilities in an interactive format.
    You can use ~inspect_entity to a view an entity's abilities.
    You can use ~ability_help to view what an ability does.


    EXAMPLE: use_ability "Evan's Halcyon" 0,0
    OR
    EXAMPLE: use_ability "Evan's Halcyon" Earth North

    caster_entity_name -- The entity whose ability you wish to use

    caster_xy -- The (x,y) coordinates of the region containing the caster
    OR
    celestial_name -- The name of the celestial the caster is on
    territory_name -- The name of the territory the caster is in
    """
    if len(args) == 2:
        # if the caster is in a region
        caster_name = args[0]
        caster_xy = args[1]
        # get the obj we want
        caster = get_entity_obj(caster_name, target_xy=caster_xy)
    elif len(args) == 3:
        # if the caster is in a territory
        caster_name = args[0]
        caster_celestial = args[1]
        caster_territory = args[2]
        caster = get_entity_obj(caster_name, target_territory=caster_territory, target_celestial=caster_celestial)
    else:
        await ctx.send('```Error: Improper number of arguments.\n ~use_ability "caster_name" "caster_xy" OR ~use_ability "caster_name" "territory_name" "celestial_name"```')
        return
    # get the names of the abilities
    ability_dict = {i: caster.abilities[i] for i in range(0, len(caster.abilities))}
    text = f'```Abilities: {ability_dict}.\nSelect the ability number you wish to cast.```'
    abilitygui = await ctx.send(text)
    emojis = ['0️⃣', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟']
    for emo in emojis[:len(caster.abilities)]:
        # send as many emojis as there are abilities
        await abilitygui.add_reaction(emo)

    def check(reaction, user):
        # check that message being reacted to is abilitygui
        # check that the reactor is not the bot itself
        return abilitygui.id == reaction.message.id and not user == abilitygui.author

    try:
        reaction, user = await bot.wait_for('reaction_add', check=check, timeout=120)
    except asyncio.TimeoutError:
        await ctx.send('```AbilityGUI has timed out. Re-type the command to re-activate.```')
        return

    # translate the reaction into the string of the method
    ability = ability_dict[emojis.index(reaction.emoji)]
    # get the method linked to the ability
    ability_method = getattr(caster, 'A_' + ability)
    # get the arguments to ask
    requested_args = getfullargspec(ability_method).args[1:]
    # IF there are args
    if requested_args:
        # ask for the args and wait for response
        readable_args = [a.replace('_', ' ') for a in requested_args]
        if len(readable_args) == 1:
            request_str = f'Please enter the {readable_args[0]}'
        else:
            request_str = f'Please enter the {readable_args[0]}'
            # second argument to the pentultimate argument
            for i in range(len(readable_args[1:-1])):
                request_str += f', the {readable_args[i]}'
            # add last argument
            request_str += f' and the {readable_args[-1]}'
        await ctx.send(f'```{request_str}.\nDo NOT use a tilde (~).\nSeparate each argument with spaces and use "quotes".```')

        def check(message):
            return message.author == ctx.message.author

        try:
            msg = await bot.wait_for('message', check=check, timeout=120)
        except asyncio.TimeoutError:
            await ctx.send('```AbilityGUI has timed out. Re-type the command to re-activate.```')
            return
        arg_string1 = msg.content
        # first, split off the tilde from the name
        arg_string2 = arg_string1.replace('~', '')
        # remove spaces
        args = arg_string2.split('"')
        # then, split on quotes
        args = [a for a in args if bool(a)]
        # then we call the method
        output = payload_manage(ability_method(*args))
        await ctx.send(output)
        return
    # IF there are no args, we just call it
    output = payload_manage(ability_method())
    await ctx.send(output)
    return