Browse Source

[commands] Split process_commands into lower level bits.

pull/468/head
Rapptz 8 years ago
parent
commit
ce9d5b4f4a
  1. 142
      discord/ext/commands/bot.py
  2. 5
      discord/ext/commands/context.py

142
discord/ext/commands/bot.py

@ -164,17 +164,6 @@ class BotBase(GroupMixin):
# internal helpers
@asyncio.coroutine
def _get_prefix(self, message):
prefix = self.command_prefix
if callable(prefix):
ret = prefix(self, message)
if asyncio.iscoroutine(ret):
ret = yield from ret
return ret
else:
return prefix
@asyncio.coroutine
def _run_extra(self, coro, event_name, *args, **kwargs):
try:
@ -552,72 +541,127 @@ class BotBase(GroupMixin):
# command processing
@asyncio.coroutine
def process_commands(self, message):
def get_prefix(self, message):
"""|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.
Retrieves the prefix the bot is listening to
with the message as a context.
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.
Parameters
-----------
message: :class:`discord.Message`
The message context to get the prefix of.
Warning
Returns
--------
This function is necessary for :meth:`say`, :meth:`whisper`,
:meth:`type`, :meth:`reply`, and :meth:`upload` to work due to the
way they are written. It is also required for the :func:`on_command`
and :func:`on_command_completion` events.
Union[List[str], str]
A list of prefixes or a single prefix that the bot is
listening for.
"""
prefix = self.command_prefix
if callable(prefix):
ret = prefix(self, message)
if asyncio.iscoroutine(ret):
ret = yield from ret
return ret
else:
return prefix
@asyncio.coroutine
def get_context(self, message):
"""|coro|
Returns the invocation context from the message.
This is a more low-level counter-part for :meth:`process_message`
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:`invoke`.
Parameters
-----------
message : discord.Message
The message to process commands for.
message: :class:`discord.Message`
The message to get the invocation context from.
Returns
--------
:class:`Context`
The invocation context.
"""
_internal_channel = message.channel
_internal_author = message.author
view = StringView(message.content)
ctx = Context(prefix=None, view=view, bot=self, message=message)
if self._skip_check(message.author.id, self.user.id):
return
return ctx
prefix = yield from self._get_prefix(message)
prefix = yield from self.get_prefix(message)
invoked_prefix = prefix
if not isinstance(prefix, (tuple, list)):
if not view.skip_string(prefix):
return
return ctx
else:
invoked_prefix = discord.utils.find(view.skip_string, prefix)
if invoked_prefix is None:
return
return ctx
invoker = view.get_word()
tmp = {
'bot': self,
'invoked_with': invoker,
'message': message,
'view': view,
'prefix': invoked_prefix
}
ctx = Context(**tmp)
del tmp
if invoker in self.commands:
command = self.commands[invoker]
self.dispatch('command', command, ctx)
ctx.invoked_with = invoker
ctx.prefix = invoked_prefix
ctx.command = self.commands.get(invoker)
return ctx
@asyncio.coroutine
def invoke(self, ctx):
"""|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:
yield from command.invoke(ctx)
yield from ctx.command.invoke(ctx)
except CommandError as e:
ctx.command.dispatch_error(e, ctx)
else:
self.dispatch('command_completion', command, ctx)
elif invoker:
exc = CommandNotFound('Command "{}" is not found'.format(invoker))
self.dispatch('command_completion', ctx)
elif ctx.invoked_with:
exc = CommandNotFound('Command "{}" is not found'.format(ctx.invoked_with))
self.dispatch('command_error', exc, ctx)
@asyncio.coroutine
def process_commands(self, message):
"""|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:`get_context` followed by a call to :meth:`invoke`.
Parameters
-----------
message : discord.Message
The message to process commands for.
"""
ctx = yield from self.get_context(message)
yield from self.invoke(ctx)
@asyncio.coroutine
def on_message(self, message):
yield from self.process_commands(message)

5
discord/ext/commands/context.py

@ -117,6 +117,11 @@ class Context(discord.abc.Messageable):
ret = yield from command.callback(*arguments, **kwargs)
return ret
@property
def valid(self):
"""Checks if the invocation context is valid to be invoked with."""
return self.prefix is not None and self.command is not None
@asyncio.coroutine
def _get_channel(self):
return self.channel

Loading…
Cancel
Save