diff --git a/discord/ext/commands/context.py b/discord/ext/commands/context.py index 95996a673..efaa5b43d 100644 --- a/discord/ext/commands/context.py +++ b/discord/ext/commands/context.py @@ -24,14 +24,18 @@ DEALINGS IN THE SOFTWARE. """ import asyncio +import discord.abc +import discord.utils -class Context: +class Context(discord.abc.MessageChannel): """Represents the context in which a command is being invoked under. This class contains a lot of meta data to help you understand more about the invocation context. This class is not created manually and is instead passed around to commands by passing in :attr:`Command.pass_context`. + This class implements the :class:`abc.MessageChannel` ABC. + Attributes ----------- message : :class:`discord.Message` @@ -76,6 +80,7 @@ class Context: self.invoked_with = attrs.pop('invoked_with', None) self.invoked_subcommand = attrs.pop('invoked_subcommand', None) self.subcommand_passed = attrs.pop('subcommand_passed', None) + self._state = self.message._state @asyncio.coroutine def invoke(self, command, *args, **kwargs): @@ -112,6 +117,9 @@ class Context: ret = yield from command.callback(*arguments, **kwargs) return ret + def _get_destination(self): + return self.channel.id, getattr(self.guild, 'id', None) + @property def cog(self): """Returns the cog associated with this context's command. None if it does not exist.""" @@ -119,3 +127,25 @@ class Context: if self.command is None: return None return self.command.instance + + @discord.utils.cached_property + def id(self): + # we need this to meet MessageChannel abc + # it is purposefully undocumented because it makes no logistic sense + # outside of providing the sugar of the main class. + return self.channel.id + + @discord.utils.cached_property + def guild(self): + """Returns the guild associated with this context's command. None if not available.""" + return self.message.guild + + @discord.utils.cached_property + def channel(self): + """Returns the channel associated with this context's command. Shorthand for :attr:`Message.channel`.""" + return self.message.channel + + @discord.utils.cached_property + def author(self): + """Returns the author associated with this context's command. Shorthand for :attr:`Message.author`""" + return self.message.author diff --git a/discord/ext/commands/converter.py b/discord/ext/commands/converter.py index eb1023b03..7e54241fa 100644 --- a/discord/ext/commands/converter.py +++ b/discord/ext/commands/converter.py @@ -32,8 +32,8 @@ import inspect from .errors import BadArgument, NoPrivateMessage __all__ = [ 'Converter', 'MemberConverter', 'UserConverter', - 'ChannelConverter', 'InviteConverter', 'RoleConverter', - 'GameConverter', 'ColourConverter' ] + 'TextChannelConverter', 'InviteConverter', 'RoleConverter', + 'GameConverter', 'ColourConverter', 'VoiceChannelConverter' ] def _get_from_guilds(bot, getter, argument): result = None @@ -103,20 +103,50 @@ class MemberConverter(IDConverter): UserConverter = MemberConverter -class ChannelConverter(IDConverter): +class TextChannelConverter(IDConverter): def convert(self): - message = self.ctx.message bot = self.ctx.bot match = self._get_id_match() or re.match(r'<#([0-9]+)>$', self.argument) result = None - guild = message.guild + guild = self.ctx.guild + + if match is None: + # not a mention + if guild: + result = discord.utils.get(guild.text_channels, name=self.argument) + else: + def check(c): + return isinstance(c, discord.TextChannel) and c.name == self.argument + result = discord.utils.find(check, bot.get_all_channels()) + else: + channel_id = int(match.group(1)) + if guild: + result = guild.get_channel(channel_id) + else: + result = _get_from_guilds(bot, 'get_channel', channel_id) + + if result is None: + raise BadArgument('Channel "{}" not found.'.format(self.argument)) + + return result + +class VoiceChannelConverter(IDConverter): + def convert(self): + bot = self.ctx.bot + + match = self._get_id_match() or re.match(r'<#([0-9]+)>$', self.argument) + result = None + guild = self.ctx.guild + if match is None: # not a mention if guild: - result = discord.utils.get(guild.channels, name=self.argument) + result = discord.utils.get(guild.voice_channels, name=self.argument) else: - result = discord.utils.get(bot.get_all_channels(), name=self.argument) + def check(c): + return isinstance(c, discord.VoiceChannel) and c.name == self.argument + result = discord.utils.find(check, bot.get_all_channels()) else: channel_id = int(match.group(1)) if guild: diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index 8ae3262e6..13f476fee 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -85,7 +85,7 @@ class Command: The list of aliases the command can be invoked under. pass_context : bool A boolean that indicates that the current :class:`Context` should - be passed as the **first parameter**. Defaults to `False`. + be passed as the **first parameter**. Defaults to `True`. enabled : bool A boolean that indicates if the command is currently enabled. If the command is invoked while it is disabled, then @@ -135,7 +135,7 @@ class Command: self.brief = kwargs.get('brief') self.rest_is_raw = kwargs.get('rest_is_raw', False) self.aliases = kwargs.get('aliases', []) - self.pass_context = kwargs.get('pass_context', False) + self.pass_context = kwargs.get('pass_context', True) self.description = inspect.cleandoc(kwargs.get('description', '')) self.hidden = kwargs.get('hidden', False) signature = inspect.signature(callback)