[commands] Add support for cogs.

Cogs are basically class instances that have commands and event
listeners. They allow for better organisation and grouping of
commands and state. Similar to subclassing discord.Client.
This commit is contained in:
Rapptz
2016-01-09 02:18:03 -05:00
parent 968e5514cb
commit ec6b1997ad
2 changed files with 128 additions and 6 deletions

View File

@@ -28,7 +28,7 @@ import asyncio
import discord
import inspect
from .core import GroupMixin
from .core import GroupMixin, Command
from .view import StringView
from .context import Context
from .errors import CommandNotFound
@@ -67,6 +67,7 @@ class Bot(GroupMixin, discord.Client):
super().__init__(**options)
self.command_prefix = command_prefix
self.extra_events = {}
self.cogs = {}
# internal helpers
@@ -231,20 +232,40 @@ class Bot(GroupMixin, discord.Client):
name = func.__name__ if name is None else name
if not asyncio.iscoroutinefunction(func):
func = asyncio.coroutine(func)
raise discord.ClientException('Listeners must be coroutines')
if name in self.extra_events:
self.extra_events[name].append(func)
else:
self.extra_events[name] = [func]
def remove_listener(self, func, name=None):
"""Removes a listener from the pool of listeners.
Parameters
-----------
func
The function that was used as a listener to remove.
name
The name of the event we want to remove. Defaults to
``func.__name__``.
"""
name = func.__name__ if name is None else name
if name in self.extra_events:
try:
self.extra_events[name].remove(func)
except ValueError:
pass
def listen(self, name=None):
"""A decorator that registers another function as an external
event listener. Basically this allows you to listen to multiple
events from different places e.g. such as :func:`discord.on_ready`
If the function being listened to is not a coroutine, it makes it into
a coroutine a la :meth:`Client.async_event`.
The functions being listened to must be a coroutine.
Examples
---------
@@ -262,6 +283,11 @@ class Bot(GroupMixin, discord.Client):
print('two')
Would print one and two in an unspecified order.
Raises
-------
discord.ClientException
The function being listened to is not a coroutine.
"""
def decorator(func):
@@ -270,6 +296,82 @@ class Bot(GroupMixin, discord.Client):
return decorator
# cogs
def add_cog(self, cog):
"""Adds a "cog" to the bot.
A cog is a class that has its own event listeners and commands.
They are meant as a way to organize multiple relevant commands
into a singular class that shares some state or no state at all.
More information will be documented soon.
Parameters
-----------
cog
The cog to register to the bot.
"""
self.cogs[type(cog).__name__] = cog
members = inspect.getmembers(cog)
for name, member in members:
# register commands the cog has
if isinstance(member, Command):
member.instance = cog
if member.parent is None:
self.add_command(member)
continue
# register event listeners the cog has
if name.startswith('on_'):
self.add_listener(member)
def get_cog(self, name):
"""Gets the cog instance requested.
If the cog is not found, ``None`` is returned instead.
Parameters
-----------
name : str
The name of the cog you are requesting.
"""
return self.cogs.get(name)
def remove_cog(self, name):
"""Removes a cog the bot.
All registered commands and event listeners that the
cog has registered will be removed as well.
If no cog is found then ``None`` is returned, otherwise
the cog instance that is being removed is returned.
Parameters
-----------
name : str
The name of the cog to remove.
"""
cog = self.cogs.pop(name, None)
if cog is None:
return cog
members = inspect.getmembers(cog)
for name, member in members:
# remove commands the cog has
if isinstance(member, Command):
member.instance = None
if member.parent is None:
self.remove_command(member.name)
continue
# remove event listeners the cog has
if name.startswith('on_'):
self.remove_listener(member)
# command processing
@asyncio.coroutine