text: """
  The MIT License (MIT)
  
  Copyright (c) 2015-2021 Rapptz
  Copyright (c) 2021-present Pycord Development
  
  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  DEALINGS IN THE SOFTWARE.
  """
  
  from __future__ import annotations
  
  
  import asyncio
  import collections
  import collections.abc
  import inspect
  import importlib.util
  import sys
  import traceback
  import types
  from typing import Any, Callable, Mapping, List, Dict, TYPE_CHECKING, Optional, TypeVar, Type, Union
  
  import discord
  
  from .core import GroupMixin
  from .view import StringView
  from .context import Context
  from . import errors
  from .help import HelpCommand, DefaultHelpCommand
  from .cog import Cog
  
  if TYPE_CHECKING:
      import importlib.machinery
  
      from discord.message import Message
      from ._types import (
          Check,
          CoroFunc,
      )
  
  __all__ = (
      'when_mentioned',
      'when_mentioned_or',
      'Bot',
      'AutoShardedBot',
  )
  
  MISSING: Any = discord.utils.MISSING
  
  T = TypeVar('T')
  CFT = TypeVar('CFT', bound='CoroFunc')
  CXT = TypeVar('CXT', bound='Context')
  
  def when_mentioned(bot: Union[Bot, AutoShardedBot], msg: Message) -> List[str]:
      """A callable that implements a command prefix equivalent to being mentioned.
  
      These are meant to be passed into the :attr:`.Bot.command_prefix` attribute.
      """
      # bot.user will never be None when this is called
      return [f'<@{bot.user.id}> ', f'<@!{bot.user.id}> ']  # type: ignore
  
  def when_mentioned_or(*prefixes: str) -> Callable[[Union[Bot, AutoShardedBot], Message], List[str]]:
      """A callable that implements when mentioned or other prefixes provided.
  
      These are meant to be passed into the :attr:`.Bot.command_prefix` attribute.
  
      Example
      --------
  
      .. code-block:: python3
  
          bot = commands.Bot(command_prefix=commands.when_mentioned_or('!'))
  
  
      .. note::
  
          This callable returns another callable, so if this is done inside a custom
          callable, you must call the returned callable, for example:
  
          .. code-block:: python3
  
              async def get_prefix(bot, message):
                  extras = await prefixes_for(message.guild) # returns a list
                  return commands.when_mentioned_or(*extras)(bot, message)
  
  
      See Also
      ----------
      :func:`.when_mentioned`
      """
      def inner(bot, msg):
          r = list(prefixes)
          r = when_mentioned(bot, msg) + r
          return r
  
      return inner
  
  def _is_submodule(parent: str, child: str) -> bool:
      return parent == child or child.startswith(parent + ".")
  
  class _DefaultRepr:
      def __repr__(self):
          return '<default-help-command>'
  
  _default = _DefaultRepr()
  
  class BotBase(GroupMixin, discord.cog.CogMixin):
      _supports_prefixed_commands = True
      def __init__(self, command_prefix=when_mentioned, help_command=_default, **options):
          super().__init__(**options)
          self.command_prefix = command_prefix
          self._help_command = None
          self.strip_after_prefix = options.get('strip_after_prefix', False)
  
          if help_command is _default:
              self.help_command = DefaultHelpCommand()
          else:
              self.help_command = help_command
  
      @discord.utils.copy_doc(discord.Client.close)
      async def close(self) -> None:
          for extension in tuple(self.__extensions):
              try:
                  self.unload_extension(extension)
              except Exception:
                  pass
  
          for cog in tuple(self.__cogs):
              try:
                  self.remove_cog(cog)
              except Exception:
                  pass
  
          await super().close()  # type: ignore
  
      async def on_command_error(self, context: Context, exception: errors.CommandError) -> None:
          """|coro|
  
          The default command error handler provided by the bot.
  
          By default this prints to :data:`sys.stderr` however it could be
          overridden to have a different implementation.
  
          This only fires if you do not specify any listeners for command error.
          """
          if self.extra_events.get('on_command_error', None):
              return
  
          command = context.command
          if command and command.has_error_handler():
              return
  
          cog = context.cog
          if cog and cog.has_error_handler():
              return
  
          print(f'Ignoring exception in command {context.command}:', file=sys.stderr)
          traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr)
  
      async def can_run(self, ctx: Context, *, call_once: bool = False) -> bool:
          data = self._check_once if call_once else self._checks
  
          if len(data) == 0:
              return True
  
          # type-checker doesn't distinguish between functions and methods
          return await discord.utils.async_all(f(ctx) for f in data)  # type: ignore
      # help command stuff
  
      @property
      def help_command(self) -> Optional[HelpCommand]:
          return self._help_command
  
      @help_command.setter
      def help_command(self, value: Optional[HelpCommand]) -> None:
          if value is not None:
              if not isinstance(value, HelpCommand):
                  raise TypeError('help_command must be a subclass of HelpCommand')
              if self._help_command is not None:
                  self._help_command._remove_from_bot(self)
              self._help_command = value
              value._add_to_bot(self)
          elif self._help_command is not None:
              self._help_command._remove_from_bot(self)
              self._help_command = None
          else:
              self._help_command = None
  
      # command processing
  
      async def get_prefix(self, message: Message) -> Union[List[str], str]:
          """|coro|
  
          Retrieves the prefix the bot is listening to
          with the message as a context.
  
          Parameters
          -----------
          message: :class:`discord.Message`
              The message context to get the prefix of.
  
          Returns
          --------
          Union[List[:class:`str`], :class:`str`]
              A list of prefixes or a single prefix that the bot is
              listening for.
          """
          prefix = ret = self.command_prefix
          if callable(prefix):
              ret = await discord.utils.maybe_coroutine(prefix, self, message)
  
          if not isinstance(ret, str):
              try:
                  ret = list(ret)
              except TypeError:
                  # It's possible that a generator raised this exception.  Don't
                  # replace it with our own error if that's the case.
                  if isinstance(ret, collections.abc.Iterable):
                      raise
  
                  raise TypeError("command_prefix must be plain string, iterable of strings, or callable "
                                  f"returning either of these, not {ret.__class__.__name__}")
  
              if not ret:
                  raise ValueError("Iterable command_prefix must contain at least one prefix")
  
          return ret
  
      async def get_context(self, message: Message, *, cls: Type[CXT] = Context) -> CXT:
          r"""|coro|
  
          Returns the invocation context from the message.
  
          This is a more low-level counter-part for :meth:`.process_commands`
          to allow users more fine grained control over the processing.
  
          The returned context is not guaranteed to be a valid invocation
          context, :attr:`.Context.valid` must be checked to make sure it is.
          If the context is not valid then it is not a valid candidate to be
          invoked under :meth:`~.Bot.invoke`.
  
          Parameters
          -----------
          message: :class:`discord.Message`
              The message to get the invocation context from.
          cls
              The factory class that will be used to create the context.
              By default, this is :class:`.Context`. Should a custom
              class be provided, it must be similar enough to :class:`.Context`\'s
              interface.
  
          Returns
          --------
          :class:`.Context`
              The invocation context. The type of this can change via the
              ``cls`` parameter.
          """
  
          view = StringView(message.content)
          ctx = cls(prefix=None, view=view, bot=self, message=message)
  
          if message.author.id == self.user.id:  # type: ignore
              return ctx
  
          prefix = await self.get_prefix(message)
          invoked_prefix = prefix
  
          if isinstance(prefix, str):
              if not view.skip_string(prefix):
                  return ctx
          else:
              try:
                  # if the context class' __init__ consumes something from the view this
                  # will be wrong.  That seems unreasonable though.
                  if message.content.startswith(tuple(prefix)):
                      invoked_prefix = discord.utils.find(view.skip_string, prefix)
                  else:
                      return ctx
  
              except TypeError:
                  if not isinstance(prefix, list):
                      raise TypeError("get_prefix must return either a string or a list of string, "
                                      f"not {prefix.__class__.__name__}")
  
                  # It's possible a bad command_prefix got us here.
                  for value in prefix:
                      if not isinstance(value, str):
                          raise TypeError("Iterable command_prefix or list returned from get_prefix must "
                                          f"contain only strings, not {value.__class__.__name__}")
  
                  # Getting here shouldn't happen
                  raise
  
          if self.strip_after_prefix:
              view.skip_ws()
  
          invoker = view.get_word()
          ctx.invoked_with = invoker
          # type-checker fails to narrow invoked_prefix type.
          ctx.prefix = invoked_prefix  # type: ignore
          ctx.command = self.all_commands.get(invoker)
          return ctx
  
      async def invoke(self, ctx: Context) -> None:
          """|coro|
  
          Invokes the command given under the invocation context and
          handles all the internal event dispatch mechanisms.
  
          Parameters
          -----------
          ctx: :class:`.Context`
              The invocation context to invoke.
          """
          if ctx.command is not None:
              self.dispatch('command', ctx)
              try:
                  if await self.can_run(ctx, call_once=True):
                      await ctx.command.invoke(ctx)
                  else:
                      raise errors.CheckFailure('The global check once functions failed.')
              except errors.CommandError as exc:
                  await ctx.command.dispatch_error(ctx, exc)
              else:
                  self.dispatch('command_completion', ctx)
          elif ctx.invoked_with:
              exc = errors.CommandNotFound(f'Command "{ctx.invoked_with}" is not found')
              self.dispatch('command_error', ctx, exc)
  
      async def process_commands(self, message: Message) -> None:
          """|coro|
  
          This function processes the commands that have been registered
          to the bot and other groups. Without this coroutine, none of the
          commands will be triggered.
  
          By default, this coroutine is called inside the :func:`.on_message`
          event. If you choose to override the :func:`.on_message` event, then
          you should invoke this coroutine as well.
  
          This is built using other low level tools, and is equivalent to a
          call to :meth:`~.Bot.get_context` followed by a call to :meth:`~.Bot.invoke`.
  
          This also checks if the message's author is a bot and doesn't
          call :meth:`~.Bot.get_context` or :meth:`~.Bot.invoke` if so.
  
          Parameters
          -----------
          message: :class:`discord.Message`
              The message to process commands for.
          """
          if message.author.bot:
              return
  
          ctx = await self.get_context(message)
          await self.invoke(ctx)
  
      async def on_message(self, message):
          await self.process_commands(message)
  
  
  class Bot(BotBase, discord.Bot):
      """Represents a discord bot.
  
      This class is a subclass of :class:`discord.Bot` and as a result
      anything that you can do with a :class:`discord.Bot` you can do with
      this bot.
  
      This class also subclasses :class:`.GroupMixin` to provide the functionality
      to manage commands.
  
      Attributes
      -----------
      command_prefix
          The command prefix is what the message content must contain initially
          to have a command invoked. This prefix could either be a string to
          indicate what the prefix should be, or a callable that takes in the bot
          as its first parameter and :class:`discord.Message` as its second
          parameter and returns the prefix. This is to facilitate "dynamic"
          command prefixes. This callable can be either a regular function or
          a coroutine.
  
          An empty string as the prefix always matches, enabling prefix-less
          command invocation. While this may be useful in DMs it should be avoided
          in servers, as it's likely to cause performance issues and unintended
          command invocations.
  
          The command prefix could also be an iterable of strings indicating that
          multiple checks for the prefix should be used and the first one to
          match will be the invocation prefix. You can get this prefix via
          :attr:`.Context.prefix`. To avoid confusion empty iterables are not
          allowed.
  
          .. note::
  
              When passing multiple prefixes be careful to not pass a prefix
              that matches a longer prefix occurring later in the sequence.  For
              example, if the command prefix is ``('!', '!?')``  the ``'!?'``
              prefix will never be matched to any message as the previous one
              matches messages starting with ``!?``. This is especially important
              when passing an empty string, it should always be last as no prefix
              after it will be matched.
      case_insensitive: :class:`bool`
          Whether the commands should be case insensitive. Defaults to ``False``. This
          attribute does not carry over to groups. You must set it to every group if
          you require group commands to be case insensitive as well.
      help_command: Optional[:class:`.HelpCommand`]
          The help command implementation to use. This can be dynamically
          set at runtime. To remove the help command pass ``None``. For more
          information on implementing a help command, see :ref:`ext_commands_help_command`.
      strip_after_prefix: :class:`bool`
          Whether to strip whitespace characters after encountering the command
          prefix. This allows for ``!   hello`` and ``!hello`` to both work if
          the ``command_prefix`` is set to ``!``. Defaults to ``False``.
  
          .. versionadded:: 1.7
      """
      pass
  
  class AutoShardedBot(BotBase, discord.AutoShardedBot):
      """This is similar to :class:`.Bot` except that it is inherited from
      :class:`discord.AutoShardedBot` instead.
      """
      pass
  