[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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user