diff options
| author | Rapptz <[email protected]> | 2016-01-04 22:22:52 -0500 |
|---|---|---|
| committer | Rapptz <[email protected]> | 2016-01-04 22:22:52 -0500 |
| commit | 59ca5ec386e3c2d5e906ed6f8d33421031407c78 (patch) | |
| tree | 3ba335b3952234ad7f3964d4988c34a5d104fc40 /discord/ext/commands/bot.py | |
| parent | Revert "Use super() in classes that could be subclassed." (diff) | |
| download | discord.py-59ca5ec386e3c2d5e906ed6f8d33421031407c78.tar.xz discord.py-59ca5ec386e3c2d5e906ed6f8d33421031407c78.zip | |
Initial implementation of commands extension module.
Diffstat (limited to 'discord/ext/commands/bot.py')
| -rw-r--r-- | discord/ext/commands/bot.py | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py new file mode 100644 index 00000000..a788f670 --- /dev/null +++ b/discord/ext/commands/bot.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- + +""" +The MIT License (MIT) + +Copyright (c) 2015-2016 Rapptz + +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. +""" + +import asyncio +import discord +import inspect + +from .core import GroupMixin +from .view import StringView +from .context import Context + +class Bot(GroupMixin, discord.Client): + """Represents a discord bot. + + This class is a subclass of :class:`discord.Client` and as a result + anything that you can do with a :class:`discord.Client` you can do with + this bot. + + This class also subclasses :class:`GroupMixin` to provide the functionality + to manage commands. + + Parameters + ----------- + 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 a + :class:`discord.Message` as its first parameter and returns the prefix. + This is to facilitate "dynamic" command prefixes. + """ + def __init__(self, command_prefix, **options): + super().__init__(**options) + self.command_prefix = command_prefix + + def _get_variable(self, name): + stack = inspect.stack() + for frames in stack: + current_locals = frames[0].f_locals + if name in current_locals: + return current_locals[name] + + def _get_prefix(self, message): + prefix = self.command_prefix + if callable(prefix): + return prefix(message) + else: + return prefix + + @asyncio.coroutine + def say(self, content): + """|coro| + + A helper function that is equivalent to doing + + .. code-block:: python + + self.send_message(message.channel, content) + + Parameters + ---------- + content : str + The content to pass to :class:`Client.send_message` + """ + destination = self._get_variable('_internal_channel') + result = yield from self.send_message(destination, content) + return result + + @asyncio.coroutine + def whisper(self, content): + """|coro| + + A helper function that is equivalent to doing + + .. code-block:: python + + self.send_message(message.author, content) + + Parameters + ---------- + content : str + The content to pass to :class:`Client.send_message` + """ + destination = self._get_variable('_internal_author') + result = yield from self.send_message(destination, content) + return result + + @asyncio.coroutine + def reply(self, content): + """|coro| + + A helper function that is equivalent to doing + + .. code-block:: python + + msg = '{0.mention}, {1}'.format(message.author, content) + self.send_message(message.channel, msg) + + Parameters + ---------- + content : str + The content to pass to :class:`Client.send_message` + """ + author = self._get_variable('_internal_author') + destination = self._get_variable('_internal_channel') + fmt = '{0.mention}, {1}'.format(author, str(content)) + result = yield from self.send_message(destination, fmt) + return result + + @asyncio.coroutine + def upload(self, fp, name=None): + """|coro| + + A helper function that is equivalent to doing + + .. code-block:: python + + self.send_file(message.channel, fp, name) + + Parameters + ---------- + fp + The first parameter to pass to :meth:`Client.send_file` + name + The second parameter to pass to :meth:`Client.send_file` + """ + destination = self._get_variable('_internal_channel') + result = yield from self.send_file(destination, fp, name) + return result + + @asyncio.coroutine + def type(self): + """|coro| + + A helper function that is equivalent to doing + + .. code-block:: python + + self.send_typing(message.channel) + + See Also + --------- + The :meth:`Client.send_typing` function. + """ + destination = self._get_variable('_internal_channel') + yield from self.send_typing(destination) + + @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. + + Warning + -------- + 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. + + Parameters + ----------- + message : discord.Message + The message to process commands for. + """ + _internal_channel = message.channel + _internal_author = message.author + + view = StringView(message.content) + if message.author == self.user: + return + + prefix = self._get_prefix(message) + if not view.skip_string(prefix): + return + + view.skip_ws() + invoker = view.get_word() + if invoker in self.commands: + command = self.commands[invoker] + tmp = { + 'bot': self, + 'invoked_with': invoker, + 'message': message, + 'view': view, + 'command': command + } + ctx = Context(**tmp) + del tmp + yield from command.invoke(ctx) + + @asyncio.coroutine + def on_message(self, message): + yield from self.process_commands(message) |