async def gsheet(self, ctx, url: str):
        """Loads a character sheet from [GSheet v2.0](http://gsheet2.avrae.io) (auto) or [GSheet v1.3](http://gsheet.avrae.io) (manual), resetting all settings.
        The sheet must be shared with Avrae for this to work.
        Avrae's google account is `[email protected]`."""

        loading = await ctx.send('Loading character data from Google... (This usually takes ~30 sec)')
        try:
            url = extract_gsheet_id_from_url(url)
        except NoValidUrlKeyFound:
            return await loading.edit(content="This is not a Google Sheets link.")

        override = await self._confirm_overwrite(ctx, f"google-{url}")
        if not override: return await ctx.send("Character overwrite unconfirmed. Aborting.")

        try:
            parser = GoogleSheet(url, self.gsheet_client)
        except AssertionError:
            await self.init_gsheet_client()  # hmm.
            return await loading.edit(content="I am still connecting to Google. Try again in 15-30 seconds.")

        try:
            await parser.get_character()
        except (KeyError, SpreadsheetNotFound):
            return await loading.edit(content=
                                      "Invalid character sheet. Make sure you've shared it with me at "
                                      "`[email protected]`!")
        except HttpError:
            return await loading.edit(content=
                                      "Error: Google returned an error. Please ensure your sheet is shared with "
                                      "`[email protected]` and try again in a few minutes.")
        except Exception as e:
            return await loading.edit(content='Error: Could not load character sheet.\n' + str(e))

        try:
            sheet = await parser.get_sheet()
        except Exception as e:
            traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
            return await loading.edit(content='Error: Invalid character sheet.\n' + str(e))

        try:
            await loading.edit(content=
                               'Loaded and saved data for {}!'.format(sheet['sheet']['stats']['name']))
        except TypeError as e:
            traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
            return await loading.edit(content=
                                      'Invalid character sheet. Make sure you have shared the sheet so that anyone with the link can view.')

        char = Character(sheet['sheet'], f"google-{url}").initialize_consumables()
        await char.commit(ctx)
        await char.set_active(ctx)

        try:
            await ctx.send(embed=char.get_sheet_embed())
        except:
            await ctx.send(
                "...something went wrong generating your character sheet. Don't worry, your character has been saved. "
                "This is usually due to an invalid image.")
    async def dicecloud(self, ctx, url: str, *, args=""):
        """Loads a character sheet from [Dicecloud](https://dicecloud.com/), resetting all settings.
        Share your character with `avrae` on Dicecloud (edit perms) for live updates.
        __Valid Arguments__
        `-cc` - Will automatically create custom counters for class resources and features."""
        if 'dicecloud.com' in url:
            url = url.split('/character/')[-1].split('/')[0]

        override = await self._confirm_overwrite(ctx, f"dicecloud-{url}")
        if not override: return await ctx.send("Character overwrite unconfirmed. Aborting.")

        loading = await ctx.send('Loading character data from Dicecloud...')
        parser = DicecloudParser(url)
        try:
            await parser.get_character()
        except Exception as eep:
            return await loading.edit(content=f"Dicecloud returned an error: {eep}")

        try:
            sheet = parser.get_sheet()
        except Exception as e:
            traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
            return await loading.edit(content=
                                      'Error: Invalid character sheet. Capitalization matters!\n' + str(e))

        c = Character(sheet['sheet'], f"dicecloud-{url}").initialize_consumables()
        await loading.edit(content=f'Loaded and saved data for {c.get_name()}!')

        if '-cc' in args:
            for counter in parser.get_custom_counters():
                displayType = 'bubble' if c.evaluate_cvar(counter['max']) < 6 else None
                try:
                    c.create_consumable(counter['name'], maxValue=str(counter['max']), minValue=str(counter['min']),
                                        reset=counter['reset'], displayType=displayType, live=counter['live'])
                except InvalidArgument:
                    pass

        del parser  # uh. maybe some weird instance things going on here.
        await c.commit(ctx)
        await c.set_active(ctx)
        try:
            await ctx.send(embed=c.get_sheet_embed())
        except:
            await ctx.send(
                "...something went wrong generating your character sheet. Don't worry, your character has been saved. "
                "This is usually due to an invalid image.")
    async def beyond(self, ctx, url: str):
        """Loads a character sheet from D&D Beyond, resetting all settings."""

        loading = await ctx.send('Loading character data from Beyond...')
        url = re.search(r"/characters/(\d+)", url)
        if url is None:
            return await loading.edit(content="This is not a D&D Beyond link.")
        url = url.group(1)

        override = await self._confirm_overwrite(ctx, f"beyond-{url}")
        if not override: return await ctx.send("Character overwrite unconfirmed. Aborting.")

        parser = BeyondSheetParser(url)

        try:
            character = await parser.get_character()
        except Exception as e:
            return await loading.edit(content='Error: Could not load character sheet.\n' + str(e))

        try:
            sheet = parser.get_sheet()
        except Exception as e:
            traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
            return await loading.edit(content='Error: Invalid character sheet.\n' + str(e))

        await loading.edit(content='Loaded and saved data for {}!'.format(character['name']))

        char = Character(sheet['sheet'], f"beyond-{url}").initialize_consumables()
        await char.commit(ctx)
        await char.set_active(ctx)

        try:
            await ctx.send(embed=char.get_sheet_embed())
        except:
            await ctx.send(
                "...something went wrong generating your character sheet. Don't worry, your character has been saved. "
                "This is usually due to an invalid image.")
    async def update(self, ctx, *, args=''):
        """Updates the current character sheet, preserving all settings.
        Valid Arguments: `-v` - Shows character sheet after update is complete.
        `-cc` - Updates custom counters from Dicecloud."""
        char = await Character.from_ctx(ctx)
        url = char.id
        old_character = char.character

        prefixes = 'dicecloud-', 'pdf-', 'google-', 'beyond-'
        _id = copy.copy(url)
        for p in prefixes:
            if url.startswith(p):
                _id = url[len(p):]
                break
        sheet_type = old_character.get('type', 'dicecloud')
        if sheet_type == 'dicecloud':
            parser = DicecloudParser(_id)
            loading = await ctx.send('Updating character data from Dicecloud...')
        elif sheet_type == 'google':
            try:
                parser = GoogleSheet(_id, self.gsheet_client)
            except AssertionError:
                await self.init_gsheet_client()  # attempt reconnection
                return await ctx.send("I am still connecting to Google. Try again in 15-30 seconds.")
            loading = await ctx.send('Updating character data from Google...')
        elif sheet_type == 'beyond':
            loading = await ctx.send('Updating character data from Beyond...')
            parser = BeyondSheetParser(_id)
        else:
            return await ctx.send("Error: Unknown sheet type.")
        try:
            await parser.get_character()
        except (timeout, aiohttp.ClientResponseError) as e:
            log.warning(
                f"Response error importing char:\n{''.join(traceback.format_exception(type(e), e, e.__traceback__))}")
            return await loading.edit(content=
                                      "I'm having some issues connecting to Dicecloud or Google right now. "
                                      "Please try again in a few minutes.")
        except HttpError:
            return await loading.edit(content=
                                      "Google returned an error trying to access your sheet. "
                                      "Please ensure your sheet is shared and try again in a few minutes.")
        except Exception as e:
            log.warning(
                f"Failed to import character\n{''.join(traceback.format_exception(type(e), e, e.__traceback__))}")
            return await loading.edit(content='Error: Invalid character sheet.\n' + str(e))

        try:
            if sheet_type == 'dicecloud':
                sheet = parser.get_sheet()
            elif sheet_type == 'pdf':
                sheet = parser.get_sheet()
            elif sheet_type == 'google':
                sheet = await parser.get_sheet()
            elif sheet_type == 'beyond':
                sheet = parser.get_sheet()
            else:
                return await ctx.send("Error: Unknown sheet type.")
            await loading.edit(content=
                               'Updated and saved data for {}!'.format(sheet['sheet']['stats']['name']))
        except TypeError as e:
            del parser
            log.info(f"Exception in parser.get_sheet: {e}")
            log.debug('\n'.join(traceback.format_exception(type(e), e, e.__traceback__)))
            return await loading.edit(content=
                                      'Invalid character sheet. '
                                      'If you are using a dicecloud sheet, '
                                      'make sure you have shared the sheet so that anyone with the '
                                      'link can view.')
        except Exception as e:
            del parser
            return await loading.edit(content='Error: Invalid character sheet.\n' + str(e))

        sheet = sheet['sheet']
        sheet['settings'] = old_character.get('settings', {})
        sheet['overrides'] = old_character.get('overrides', {})
        sheet['cvars'] = old_character.get('cvars', {})
        sheet['consumables'] = old_character.get('consumables', {})

        overrides = old_character.get('overrides', {})
        sheet['stats']['description'] = overrides.get('desc') or sheet.get('stats', {}).get("description",
                                                                                            "No description available.")
        sheet['stats']['image'] = overrides.get('image') or sheet.get('stats', {}).get('image', '')
        override_spells = []
        for s in overrides.get('spells', []):
            if isinstance(s, str):
                override_spells.append({'name': s, 'strict': True})
            else:
                override_spells.append(s)
        sheet['spellbook']['spells'].extend(override_spells)

        c = Character(sheet, url).initialize_consumables()

        if '-cc' in args and sheet_type == 'dicecloud':
            counters = parser.get_custom_counters()
            for counter in counters:
                displayType = 'bubble' if c.evaluate_cvar(counter['max']) < 6 else None
                try:
                    c.create_consumable(counter['name'], maxValue=str(counter['max']),
                                        minValue=str(counter['min']),
                                        reset=counter['reset'], displayType=displayType, live=counter['live'])
                except InvalidArgument:
                    pass

        # if c.get_combat_id() and not self.bot.rdb.exists(c.get_combat_id()):
        #     c.leave_combat()
        # reimplement this later

        await c.commit(ctx)
        await c.set_active(ctx)
        del parser, old_character  # pls don't freak out avrae
        if '-v' in args:
            await ctx.send(embed=c.get_sheet_embed())