From 94de67c6ae861686e0c7da241cb5c5c593b2de67 Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 5 Oct 2016 19:18:45 -0500 Subject: [PATCH] documentation 2.0 --- disco/api/http.py | 29 +++++++- disco/api/ratelimit.py | 68 ++++++++++++++---- disco/bot/bot.py | 152 +++++++++++++++++++++++++++------------- disco/bot/command.py | 47 +++++++++---- disco/bot/parser.py | 32 ++++++--- disco/bot/plugin.py | 44 ++++++++---- disco/cli.py | 13 ++++ disco/client.py | 54 +++++++++++--- disco/gateway/client.py | 4 +- disco/state.py | 83 ++++++++++++++++++++-- disco/types/channel.py | 128 ++++++++++++++++++++++++++++++++- disco/types/guild.py | 145 +++++++++++++++++++++++++++++++++++++- disco/types/invite.py | 24 +++++++ disco/types/message.py | 139 ++++++++++++++++++++++++++++++++++++ disco/voice/client.py | 2 +- docs/api.rst | 150 +++++++++++++++++++++++++++++++++++++++ docs/conf.py | 1 + docs/disco.api.rst | 38 ---------- docs/disco.bot.rst | 46 ------------ docs/disco.gateway.rst | 38 ---------- docs/disco.rst | 50 ------------- docs/disco.types.rst | 78 --------------------- docs/disco.util.rst | 62 ---------------- docs/disco.voice.rst | 30 -------- docs/index.rst | 19 +++-- docs/modules.rst | 7 -- docs/setup.rst | 7 -- docs/tutorial.rst | 5 ++ 28 files changed, 1014 insertions(+), 481 deletions(-) create mode 100644 docs/api.rst delete mode 100644 docs/disco.api.rst delete mode 100644 docs/disco.bot.rst delete mode 100644 docs/disco.gateway.rst delete mode 100644 docs/disco.rst delete mode 100644 docs/disco.types.rst delete mode 100644 docs/disco.util.rst delete mode 100644 docs/disco.voice.rst delete mode 100644 docs/modules.rst delete mode 100644 docs/setup.rst create mode 100644 docs/tutorial.rst diff --git a/disco/api/http.py b/disco/api/http.py index be9274b..f1baf95 100644 --- a/disco/api/http.py +++ b/disco/api/http.py @@ -124,14 +124,37 @@ class HTTPClient(LoggingClass): } def __call__(self, route, args=None, **kwargs): + return self.call(route, args, **kwargs) + + def call(self, route, args=None, **kwargs): """ Makes a request to the given route (as specified in :class:`disco.api.http.Routes`) with a set of URL arguments, and keyword arguments passed to requests. - :param route: the method/url route combination to call - :param args: url major arguments (used for Discord rate limits) - :param kwargs: any keyword arguments to be passed along to requests + Parameters + ---------- + route : tuple(:class:`HTTPMethod`, str) + The method.URL combination that when compiled with URL arguments + creates a requestable route which the HTTPClient will make the + request too. + args : dict(str, str) + A dictionary of URL arguments that will be compiled with the raw URL + to create the requestable route. The HTTPClient uses this to track + rate limits as well. + kwargs : dict + Keyword arguments that will be passed along to the requests library + + Raises + ------ + APIException + Raised when an unrecoverable error occurs, or when we've exhausted + the number of retries. + + Returns + ------- + :class:`requests.Response` + The response object for the request """ args = args or {} retry = kwargs.pop('retry_number', 0) diff --git a/disco/api/ratelimit.py b/disco/api/ratelimit.py index 1b03783..1484502 100644 --- a/disco/api/ratelimit.py +++ b/disco/api/ratelimit.py @@ -7,10 +7,26 @@ class RouteState(object): An object which stores ratelimit state for a given method/url route combination (as specified in :class:`disco.api.http.Routes`). - :ivar route: the route this state pertains too - :ivar remaining: the number of requests remaining before the rate limit is hit - :ivar reset_time: unix timestamp (in seconds) when this rate limit is reset - :ivar event: a :class:`gevent.event.Event` used for ratelimit cooldowns + Parameters + ---------- + route : tuple(HTTPMethod, str) + The route which this RouteState is for. + response : :class:`requests.Response` + The response object for the last request made to the route, should contain + the standard rate limit headers. + + Attributes + --------- + route : tuple(HTTPMethod, str) + The route which this RouteState is for. + remaining : int + The number of remaining requests to the route before the rate limit will + be hit, triggering a 429 response. + reset_time : int + A unix epoch timestamp (in seconds) after which this rate limit is reset + event : :class:`gevent.event.Event` + An event that is used to block all requests while a route is in the + cooldown stage. """ def __init__(self, route, response): self.route = route @@ -55,9 +71,18 @@ class RouteState(object): """ Waits until this route is no longer under a cooldown - :param timeout: timeout after which waiting will be given up + Parameters + ---------- + timeout : Optional[int] + A timeout (in seconds) after which we will give up waiting + + + Returns + ------- + bool + False if the timeout period expired before the cooldown was finished """ - self.event.wait(timeout) + return self.event.wait(timeout) def cooldown(self): """ @@ -76,7 +101,11 @@ class RateLimiter(object): """ A in-memory store of ratelimit states for all routes we've ever called. - :ivar states: a Route -> RouteState mapping + Attributes + ---------- + states : dict(tuple(HTTPMethod, str), :class:`RouteState`) + Contains a :class:`RouteState` for each route the RateLimiter is currently + tracking. """ def __init__(self): self.states = {} @@ -89,9 +118,19 @@ class RateLimiter(object): the route is finished being cooled down. This function should be called before making a request to the specified route. - :param route: route to be checked - :param timeout: an optional timeout after which we'll stop waiting for - the cooldown to complete. + Parameters + ---------- + route : tuple(HTTPMethod, str) + The route that will be checked. + timeout : Optional[int] + A timeout after which we'll give up waiting for a routes cooldown + to expire, and immedietly return. + + Returns + ------- + bool + False if the timeout period expired before the route finished cooling + down. """ return self._check(None, timeout) and self._check(route, timeout) @@ -111,8 +150,13 @@ class RateLimiter(object): Updates the given routes state with the rate-limit headers inside the response from a previous call to the route. - :param route: route to update - :param response: requests response to update the route with + Parameters + --------- + route : tuple(HTTPMethod, str) + The route that will be updated. + response : :class:`requests.Response` + The response object for the last request to the route, whose headers + will be used to update the routes rate limit state. """ if 'X-RateLimit-Global' in response.headers: route = None diff --git a/disco/bot/bot.py b/disco/bot/bot.py index 6d8a917..3107ec7 100644 --- a/disco/bot/bot.py +++ b/disco/bot/bot.py @@ -6,36 +6,50 @@ from disco.bot.command import CommandEvent class BotConfig(object): """ - An object which specifies the runtime configuration for a Bot. - - :ivar str token: Authentication token - :ivar bool commands_enabled: whether to enable the command parsing functionality - of the bot - :ivar bool command_require_mention: whether commands require a mention to be - triggered - :ivar dict command_mention_rules: a dictionary of rules about what types of - mentions will trigger a command. A string/bool mapping containing 'here', - 'everyone', 'role', and 'user'. If set to false, the mention type will - not trigger commands. - :ivar str command_prefix: prefix required to trigger a command - :ivar bool command_allow_edit: whether editing the last-sent message in a channel, - which did not previously trigger a command, will cause the bot to recheck - the message contents and possibly trigger a command. - :ivar function plugin_config_provider: an optional function which when called - with a plugin name, returns relevant configuration for it. + An object which is used to configure and define the runtime configuration for + a bot. + + Attributes + ---------- + token : str + The authentication token for this bot. This is passed on to the + :class:`disco.client.DiscoClient` without any validation. + commands_enabled : bool + Whether this bot instance should utilize command parsing. Generally this + should be true, unless your bot is only handling events and has no user + interaction. + commands_require_mention : bool + Whether messages must mention the bot to be considered for command parsing. + commands_mention_rules : dict(str, bool) + A dictionary describing what mention types can be considered a mention + of the bot when using :attr:`commands_require_mention`. This dictionary + can contain the following keys: `here`, `everyone`, `role`, `user`. When + a keys value is set to true, the mention type will be considered for + command parsing. + commands_prefix : str + A string prefix that is required for a message to be considered for + command parsing. + commands_allow_edit : bool + If true, the bot will reparse an edited message if it was the last sent + message in a channel, and did not previously trigger a command. This is + helpful for allowing edits to typod commands. + plugin_config_provider : Optional[function] + If set, this function will be called before loading a plugin, with the + plugins name. Its expected to return a type of configuration object the + plugin understands. """ token = None commands_enabled = True - command_require_mention = True - command_mention_rules = { + commands_require_mention = True + commands_mention_rules = { # 'here': False, 'everyone': False, 'role': True, 'user': True, } - command_prefix = '' - command_allow_edit = True + commands_prefix = '' + commands_allow_edit = True plugin_config_provider = None @@ -45,11 +59,24 @@ class Bot(object): Disco's implementation of a simple but extendable Discord bot. Bots consist of a set of plugins, and a Disco client. - :param client: the client this bot should use for its Discord connection - :param config: a :class:`BotConfig` instance - - :ivar dict plugins: string -> :class:`disco.bot.plugin.Plugin` mapping of - all loaded plugins + Parameters + ---------- + client : Optional[:class:`disco.client.DiscoClient`] + The client this bot should utilize for its connection. If not provided, + will create a new :class:`disco.client.DiscoClient` with the token inside + the bot config (:class:`BotConfig`) + config : Optional[:class:`BotConfig`] + The configuration to use for this bot. If not provided will use the defaults + inside of :class:`BotConfig`. + + Attributes + ---------- + client : `disco.client.DiscoClient` + The client instance for this bot. + config : `BotConfig` + The bot configuration instance for this bot. + plugins : dict(str, :class:`disco.bot.plugin.Plugin`) + Any plugins this bot has loaded """ def __init__(self, client=None, config=None): self.client = client or DiscoClient(config.token) @@ -61,7 +88,7 @@ class Bot(object): if self.config.commands_enabled: self.client.events.on('MessageCreate', self.on_message_create) - if self.config.command_allow_edit: + if self.config.commands_allow_edit: self.client.events.on('MessageUpdate', self.on_message_update) # Stores the last message for every single channel @@ -73,10 +100,15 @@ class Bot(object): @classmethod def from_cli(cls, *plugins): """ - Creates a new instance of the bot using the Disco-CLI utility, and a set - of passed-in plugin classes. + Creates a new instance of the bot using the utilities inside of the + :mod:`disco.cli` module. Allows passing in a set of uninitialized + plugin classes to load. + + Parameters + --------- + plugins : Optional[list(:class:`disco.bot.plugin.Plugin`)] + Any plugins to load after creating the new bot instance - :param plugins: plugins to load after creaing the Bot instance """ from disco.cli import disco_main inst = cls(disco_main()) @@ -107,15 +139,26 @@ class Bot(object): def get_commands_for_message(self, msg): """ - Generator of all commands a given message triggers. + Generator of all commands that a given message object triggers, based on + the bots plugins and configuration. + + Parameters + --------- + msg : :class:`disco.types.message.Message` + The message object to parse and find matching commands for + + Yields + ------- + tuple(:class:`disco.bot.command.Command`, `re.MatchObject`) + All commands the message triggers """ content = msg.content - if self.config.command_require_mention: + if self.config.commands_require_mention: match = any(( - self.config.command_mention_rules['user'] and msg.is_mentioned(self.client.state.me), - self.config.command_mention_rules['everyone'] and msg.mention_everyone, - self.config.command_mention_rules['role'] and any(map(msg.is_mentioned, + self.config.commands_mention_rules['user'] and msg.is_mentioned(self.client.state.me), + self.config.commands_mention_rules['everyone'] and msg.mention_everyone, + self.config.commands_mention_rules['role'] and any(map(msg.is_mentioned, msg.guild.get_member(self.client.state.me).roles )))) @@ -124,10 +167,10 @@ class Bot(object): content = msg.without_mentions.strip() - if self.config.command_prefix and not content.startswith(self.config.command_prefix): + if self.config.commands_prefix and not content.startswith(self.config.commands_prefix): raise StopIteration else: - content = content[len(self.config.command_prefix):] + content = content[len(self.config.commands_prefix):] if not self.command_matches_re or not self.command_matches_re.match(content): raise StopIteration @@ -139,11 +182,18 @@ class Bot(object): def handle_message(self, msg): """ - Attempts to handle a newely created or edited message in the context of + Attempts to handle a newly created or edited message in the context of command parsing/triggering. Calls all relevant commands the message triggers. - :returns: whether any commands where successfully triggered - :rtype: bool + Parameters + --------- + msg : :class:`disco.types.message.Message` + The newly created or updated message object to parse/handle. + + Returns + ------- + bool + whether any commands where successfully triggered by the message """ commands = list(self.get_commands_for_message(msg)) @@ -156,13 +206,13 @@ class Bot(object): return False def on_message_create(self, event): - if self.config.command_allow_edit: + if self.config.commands_allow_edit: self.last_message_cache[event.message.channel_id] = (event.message, False) self.handle_message(event.message) def on_message_update(self, event): - if self.config.command_allow_edit: + if self.config.commands_allow_edit: obj = self.last_message_cache.get(event.message.channel_id) if not obj: return @@ -176,8 +226,12 @@ class Bot(object): def add_plugin(self, cls): """ - Adds and loads a given plugin, based on its class (which must be a subclass - of :class:`disco.bot.plugin.Plugin`). + Adds and loads a plugin, based on its class. + + Parameters + ---------- + cls : subclass of :class:`disco.bot.plugin.Plugin` + Plugin class to initialize and load. """ if cls.__name__ in self.plugins: raise Exception('Cannot add already added plugin: {}'.format(cls.__name__)) @@ -190,8 +244,12 @@ class Bot(object): def rmv_plugin(self, cls): """ - Unloads and removes a given plugin, based on its class (which must be a - sub class of :class:`disco.bot.plugin.Plugin`). + Unloads and removes a plugin based on its class. + + Parameters + ---------- + cls : subclass of :class:`disco.bot.plugin.Plugin` + Plugin class to unload and remove. """ if cls.__name__ not in self.plugins: raise Exception('Cannot remove non-existant plugin: {}'.format(cls.__name__)) @@ -203,6 +261,6 @@ class Bot(object): def run_forever(self): """ - Runs this bot forever + Runs this bots core loop forever """ self.client.run_forever() diff --git a/disco/bot/command.py b/disco/bot/command.py index e6a0095..716ad1a 100644 --- a/disco/bot/command.py +++ b/disco/bot/command.py @@ -13,12 +13,20 @@ class CommandEvent(object): about the message, command, and parsed arguments (along with shortcuts to message information). - :ivar Command command: the command this event was created for (e.g. triggered command) - :ivar Message msg: the message object which triggered the command - :ivar re.MatchObject match: the regex match object for the command - :ivar string name: the name of the command (or alias) which was triggered - :ivar list args: any arguments passed to the command + Attributes + --------- + command : :class:`Command` + The command this event was created for (aka the triggered command). + msg : :class:`disco.types.message.Message` + The message object which triggered this command. + match : :class:`re.MatchObject` + The regex match object for the command. + name : str + The command name (or alias) which was triggered by the command + args : list(str) + Arguments passed to the command """ + def __init__(self, command, msg, match): self.command = command self.msg = msg @@ -67,13 +75,22 @@ class Command(object): An object which defines and handles the triggering of a function based on user input (aka a command). - :ivar disco.bot.plugin.Plugin plugin: the plugin this command is part of - :ivar function func: the function this command is attached too - :ivar str trigger: the primary trigger (aka name) of this command - :ivar str args: argument specification for this command - :ivar list aliases: aliases this command also responds too - :ivar str group: grouping this command is under - :ivar bool is_regex: whether this command is triggered as a regex + Attributes + ---------- + plugin : :class:`disco.bot.plugin.Plugin` + The plugin this command is a member of. + func : function + The function which is called when this command is triggered. + trigger : str + The primary trigger (aka name). + args : Optional[str] + The argument format specification. + aliases : Optional[list(str)] + List of trigger aliases. + group : Optional[str] + The group this command is a member of. + is_regex : Optional[bool] + Whether the triggers for this command should be treated as raw regex. """ def __init__(self, plugin, func, trigger, args=None, aliases=None, group=None, is_regex=False): self.plugin = plugin @@ -107,8 +124,10 @@ class Command(object): Handles the execution of this command given a :class:`CommandEvent` object. - :returns: whether this command was successful - :rtype: bool + Returns + ------- + bool + Whether this command was sucessful """ if len(event.args) < self.args.required_length: raise CommandError('{} requires {} arguments (passed {})'.format( diff --git a/disco/bot/parser.py b/disco/bot/parser.py index a640ff3..513ac52 100644 --- a/disco/bot/parser.py +++ b/disco/bot/parser.py @@ -25,10 +25,16 @@ class Argument(object): """ A single argument, which is normally the member of a :class:`ArgumentSet`. - :ivar str name: name of this argument - :ivar int count: the number of actual raw arguments which compose this argument - :ivar bool required: whether this argument is required - :ivar list types: the types that this argument can be + Attributes + ---------- + name : str + The name of this argument. + count : int + The number of raw arguments that compose this argument. + required : bool + Whether this is a required argument. + types : list(type) + Types this argument supports. """ def __init__(self, raw): self.name = None @@ -73,8 +79,12 @@ class ArgumentSet(object): """ A set of :class:`Argument` instances which forms a larger argument specification - :ivar list args: list of :class:`Argument` instances for this set - :ivar dict types: dict of all possible types + Attributes + ---------- + args : list(:class:`Argument`) + All arguments that are a member of this set. + types : dict(str, type) + All types supported by this ArgumentSet. """ def __init__(self, args=None, custom_types=None): self.args = args or [] @@ -99,10 +109,12 @@ class ArgumentSet(object): """ Attempts to convert a value to one or more types. - :param types: ordered list of types to try conversion to - :param value: the value to attempt conversion on - :type types: list - :param value: string + Parameters + ---------- + types : list(type) + List of types to attempt conversion with. + value : str + The string value to attempt conversion on. """ for typ_name in types: typ = self.types.get(typ_name) diff --git a/disco/bot/plugin.py b/disco/bot/plugin.py index d7bded2..5b67934 100644 --- a/disco/bot/plugin.py +++ b/disco/bot/plugin.py @@ -83,13 +83,23 @@ class Plugin(LoggingClass, PluginDeco): """ A plugin is a set of listeners/commands which can be loaded/unloaded by a bot. - :param disco.bot.Bot bot: the bot this plugin is loaded under - :param config: a untyped object containing configuration for this plugin - - :ivar disco.client.DiscoClient client: an alias to the client - :ivar disco.state.State state: an alias to the client state - :ivar list listeners: all bound listeners for this plugin - :ivar dict commands: all bound commands for this plugin + Parameters + ---------- + bot : :class:`disco.bot.Bot` + The bot this plugin is a member of. + config : any + The configuration data for this plugin. + + Attributes + ---------- + client : :class:`disco.client.DiscoClient` + An alias to the client the bot is running with. + state : :class:`disco.state.State` + An alias to the state object for the client. + listeners : list + List of all bound listeners this plugin owns. + commands : list(:class:`disco.bot.command.Command`) + List of all commands this plugin owns. """ def __init__(self, bot, config): super(Plugin, self).__init__() @@ -149,8 +159,12 @@ class Plugin(LoggingClass, PluginDeco): """ Registers a listener - :param func: function to be called - :param name: name of event to listen for + Parameters + ---------- + func : function + The function to be registered. + name : string + Name of event to listen for. """ func = functools.partial(self._dispatch, 'listener', func) self.listeners.append(self.bot.client.events.on(name, func)) @@ -159,9 +173,15 @@ class Plugin(LoggingClass, PluginDeco): """ Registers a command - :param func: function to be called - :param args: args to be passed to the :class:`Command` object - :param kwargs: kwargs to be passed to the :class:`Command` object + Parameters + ---------- + func : function + The function to be registered. + args + Arguments to pass onto the :class:`disco.bot.command.Command` object. + kwargs + Keyword arguments to pass onto the :class:`disco.bot.command.Command` + object. """ wrapped = functools.partial(self._dispatch, 'command', func) self.commands[func.__name__] = Command(self, wrapped, *args, **kwargs) diff --git a/disco/cli.py b/disco/cli.py index 5bced0e..3e1a716 100644 --- a/disco/cli.py +++ b/disco/cli.py @@ -1,3 +1,7 @@ +""" +The CLI module is a small utility that can be used as an easy entry point for +creating and running bots/clients. +""" from __future__ import print_function import logging @@ -15,6 +19,15 @@ logging.basicConfig(level=logging.INFO) def disco_main(): + """ + Creates an argument parser and parses a standard set of command line arguments, + creating a new :class:`DiscoClient`. + + Returns + ------- + :class:`DiscoClient` + A new DiscoClient from the provided command line arguments + """ args = parser.parse_args() from disco.util.token import is_valid_token diff --git a/disco/client.py b/disco/client.py index 5895360..344f178 100644 --- a/disco/client.py +++ b/disco/client.py @@ -11,6 +11,36 @@ log = logging.getLogger(__name__) class DiscoClient(object): + """ + The DiscoClient represents the base entry point to utilizing the Discord API + through disco. It wraps the functionality of both the REST API, and the realtime + secure websocket gateway. + + Parameters + ---------- + token : str + The Discord authentication token which is used for both the :class:`APIClient` + and the :class:`GatewayClient`. This token can be validated before being + passed in, by using the :func:`disco.util.token.is_valid_token` function. + sharding : Optional[dict(str, int)] + A dictionary containing two pairs with information that is used to control + the sharding behavior of the :class:`GatewayClient`. By setting the `number` + key, the current shard ID can be controlled. While when setting the `total` + key, the total number of running shards can be set. + + Attributes + ---------- + events : :class:`Emitter` + An emitter which emits Gateway events + packets : :class:`Emitter` + An emitter which emits Gateway packets + state : :class:`State` + The state tracking object + api : :class:`APIClient` + The API client + gw : :class:`GatewayClient` + The gateway client + """ def __init__(self, token, sharding=None): self.log = log self.token = token @@ -25,18 +55,26 @@ class DiscoClient(object): @classmethod def from_cli(cls, args): - inst = cls(args.token) - inst.set_shard(args.shard_id, args.shard_count) + """ + Create a new client from a argparse command line argument object, usually + generated from the :func:`disco_main` function. + """ + inst = cls( + token=args.token, + sharding={ + 'number': args.shard_id, + 'total': args.shard_count, + }) return inst - def set_shard(self, shard_number, shard_count): - self.sharding = { - 'number': shard_number, - 'total': shard_count, - } - def run(self): + """ + Run the client (e.g. the :class:`GatewayClient`) in a new greenlet + """ return gevent.spawn(self.gw.run) def run_forever(self): + """ + Run the client (e.g. the :class:`GatewayClient`) in the current greenlet + """ return self.gw.run() diff --git a/disco/gateway/client.py b/disco/gateway/client.py index 55470db..ded1b2d 100644 --- a/disco/gateway/client.py +++ b/disco/gateway/client.py @@ -7,11 +7,11 @@ from disco.util.json import loads, dumps from disco.util.websocket import WebsocketProcessProxy from disco.util.logging import LoggingClass -GATEWAY_VERSION = 6 TEN_MEGABYTES = 10490000 class GatewayClient(LoggingClass): + GATEWAY_VERSION = 6 MAX_RECONNECTS = 5 def __init__(self, client): @@ -90,7 +90,7 @@ class GatewayClient(LoggingClass): def connect_and_run(self): if not self._cached_gateway_url: - self._cached_gateway_url = self.client.api.gateway(version=GATEWAY_VERSION, encoding='json') + self._cached_gateway_url = self.client.api.gateway(version=self.GATEWAY_VERSION, encoding='json') self.log.info('Opening websocket connection to URL `%s`', self._cached_gateway_url) self.ws = WebsocketProcessProxy(self._cached_gateway_url) diff --git a/disco/state.py b/disco/state.py index b2463a4..a9c1292 100644 --- a/disco/state.py +++ b/disco/state.py @@ -5,18 +5,77 @@ from weakref import WeakValueDictionary from disco.gateway.packets import OPCode -StackMessage = namedtuple('StackMessage', ['id', 'channel_id', 'author_id']) + +class StackMessage(namedtuple('StackMessage', ['id', 'channel_id', 'author_id'])): + """ + A message stored on a stack inside of the state object, used for tracking + previously sent messages in channels. + + Attributes + --------- + id : snowflake + the id of the message + channel_id : snowflake + the id of the channel this message was sent in + author_id : snowflake + the id of the author of this message + """ class StateConfig(object): - # Whether to keep a buffer of messages + """ + A configuration object for determining how the State tracking behaves. + + Attributes + ---------- + track_messages : bool + Whether the state store should keep a buffer of previously sent messages. + Message tracking allows for multiple higher-level shortcuts and can be + highly useful when developing bots that need to delete their own messages. + + Message tracking is implemented using a deque and a namedtuple, meaning + it should generally not have a high impact on memory, however users who + find they do not need and may be experiencing memory pressure can disable + this feature entirely using this attribute. + track_messages_size : int + The size of the deque for each channel. Using this you can calculate the + total number of possible :class:`StackMessage` objects kept in memory, + using: `total_mesages_size * total_channels`. This can be tweaked based + on usage to help prevent memory pressure. + """ track_messages = True - - # The number maximum number of messages to store track_messages_size = 100 class State(object): + """ + The State class is used to track global state based on events emitted from + the :class:`GatewayClient`. State tracking is a core component of the Disco + client, providing the mechanism for most of the higher-level utility functions. + + Attributes + ---------- + EVENTS : list(str) + A list of all events the State object binds too. + client : :class:`disco.client.DiscoClient` + The DiscoClient instance this state is attached too + config : :class:`StateConfig` + The configuration for this state instance + me : :class:`disco.types.user.User` + The currently logged in user + dms : dict(snowflake, :class:`disco.types.channel.Channel`) + Mapping of all known DM Channels + guilds : dict(snowflake, :class:`disco.types.guild.Guild`) + Mapping of all known/loaded Guilds + channels : dict(snowflake, :class:`disco.types.channel.Channel`) + Weak mapping of all known/loaded Channels + users : dict(snowflake, :class:`disco.types.user.User`) + Weak mapping of all known/loaded Users + voice_states : dict(str, :class:`disco.types.voice.VoiceState`) + Weak mapping of all known/active Voice States + messages : Optional[dict(snowflake, :class:`deque`)] + Mapping of channel ids to deques containing :class:`StackMessage` objects + """ EVENTS = [ 'Ready', 'GuildCreate', 'GuildUpdate', 'GuildDelete', 'GuildMemberAdd', 'GuildMemberRemove', 'GuildMemberUpdate', 'GuildMembersChunk', 'GuildRoleCreate', 'GuildRoleUpdate', 'GuildRoleDelete', @@ -26,27 +85,37 @@ class State(object): def __init__(self, client, config=None): self.client = client self.config = config or StateConfig() - self.listeners = [] self.me = None - self.dms = {} self.guilds = {} self.channels = WeakValueDictionary() self.users = WeakValueDictionary() self.voice_states = WeakValueDictionary() - self.messages = defaultdict(lambda: deque(maxlen=self.config.track_messages_size)) + # If message tracking is enabled, listen to those events if self.config.track_messages: + self.messages = defaultdict(lambda: deque(maxlen=self.config.track_messages_size)) self.EVENTS += ['MessageCreate', 'MessageDelete'] + # The bound listener objects + self.listeners = [] self.bind() def unbind(self): + """ + Unbinds all bound event listeners for this state object + """ map(lambda k: k.unbind(), self.listeners) self.listeners = [] def bind(self): + """ + Binds all events for this state object, storing the listeners for later + unbinding. + """ + assert not len(self.listeners), 'Binding while already bound is dangerous' + for event in self.EVENTS: func = 'on_' + inflection.underscore(event) self.listeners.append(self.client.events.on(event, getattr(self, func))) diff --git a/disco/types/channel.py b/disco/types/channel.py index 587a9fd..9c41428 100644 --- a/disco/types/channel.py +++ b/disco/types/channel.py @@ -24,6 +24,21 @@ PermissionOverwriteType = Enum( class PermissionOverwrite(BaseType): + """ + A PermissionOverwrite for a :class:`Channel` + + + Attributes + ---------- + id : snowflake + The overwrite ID + type : :const:`disco.types.channel.PermissionsOverwriteType` + The overwrite type + allowed : :class:`PermissionValue` + All allowed permissions + denied : :class:`PermissionValue` + All denied permissions + """ id = skema.SnowflakeType() type = skema.StringType(choices=PermissionOverwriteType.ALL_VALUES) @@ -32,6 +47,30 @@ class PermissionOverwrite(BaseType): class Channel(BaseType, Permissible): + """ + Represents a Discord Channel + + Attributes + ---------- + id : snowflake + The channel ID. + guild_id : Optional[snowflake] + The guild id this channel is part of. + name : str + The channels name. + topic : str + The channels topic. + position : int + The channels position. + bitrate : int + The channels bitrate. + recipients: list(:class:`disco.types.user.User`) + Members of this channel (if this is a DM channel). + type : :const:`ChannelType` + The type of this channel. + overwrites : dict(snowflake, :class:`disco.types.channel.PermissionOverwrite`) + Channel permissions overwrites. + """ id = skema.SnowflakeType() guild_id = skema.SnowflakeType(required=False) @@ -41,12 +80,20 @@ class Channel(BaseType, Permissible): position = skema.IntType() bitrate = skema.IntType(required=False) - recipient = skema.ModelType(User, required=False) + recipients = skema.ListType(skema.ModelType(User)) type = skema.IntType(choices=ChannelType.ALL_VALUES) overwrites = ListToDictType('id', skema.ModelType(PermissionOverwrite), stored_name='permission_overwrites') def get_permissions(self, user): + """ + Get the permissions a user has in the channel + + Returns + ------- + :class:`disco.types.permissions.PermissionValue` + Computed permission value for the user. + """ if not self.guild_id: return Permissions.ADMINISTRATOR @@ -64,49 +111,125 @@ class Channel(BaseType, Permissible): @property def is_guild(self): + """ + Whether this channel belongs to a guild + """ return self.type in (ChannelType.GUILD_TEXT, ChannelType.GUILD_VOICE) @property def is_dm(self): + """ + Whether this channel is a DM (does not belong to a guild) + """ return self.type in (ChannelType.DM, ChannelType.GROUP_DM) @property def is_voice(self): + """ + Whether this channel supports voice + """ return self.type in (ChannelType.GUILD_VOICE, ChannelType.GROUP_DM) @property def last_message_id(self): + """ + Returns the ID of the last message sent in this channel + """ if self.id not in self.client.state.messages: return self._last_message_id return self.client.state.messages[self.id][-1].id @property def messages(self): + """ + a default :class:`MessageIterator` for the channel + """ return self.messages_iter() def messages_iter(self, **kwargs): + """ + Creates a new :class:`MessageIterator` for the channel with the given + keyword arguments + """ return MessageIterator(self.client, self.id, before=self.last_message_id, **kwargs) @cached_property def guild(self): + """ + Guild this channel belongs to (if relevant) + """ return self.client.state.guilds.get(self.guild_id) def get_invites(self): + """ + Returns + ------- + list(:class:`disco.types.invite.Invite`) + All invites for this channel. + """ return self.client.api.channels_invites_list(self.id) def get_pins(self): + """ + Returns + ------- + list(:class:`disco.types.message.Message`) + All pinned messages for this channel. + """ return self.client.api.channels_pins_list(self.id) def send_message(self, content, nonce=None, tts=False): + """ + Send a message in this channel + + Parameters + ---------- + content : str + The message contents to send. + nonce : Optional[snowflake] + The nonce to attach to the message. + tts : Optional[bool] + Whether this is a TTS message. + + Returns + ------- + :class:`disco.types.message.Message` + The created message. + """ return self.client.api.channels_messages_create(self.id, content, nonce, tts) def connect(self, *args, **kwargs): + """ + Connect to this channel over voice + """ + assert self.is_voice, 'Channel must support voice to connect' vc = VoiceClient(self) vc.connect(*args, **kwargs) return vc class MessageIterator(object): + """ + An iterator which supports scanning through the messages for a channel. + + Parameters + ---------- + client : :class:`disco.client.DiscoClient` + The disco client instance to use when making requests. + channel : `Channel` + The channel to iterate within. + direction : :attr:`MessageIterator.Direction` + The direction in which this iterator will move. + bulk : bool + If true, this iterator will yield messages in list batches, otherwise each + message will be yield individually. + before : snowflake + The message to begin scanning at. + after : snowflake + The message to begin scanning at. + chunk_size : int + The number of messages to request per API call. + """ Direction = Enum('UP', 'DOWN') def __init__(self, client, channel, direction=Direction.UP, bulk=False, before=None, after=None, chunk_size=100): @@ -128,6 +251,9 @@ class MessageIterator(object): raise Exception('Must specify either before or after for downward seeking') def fill(self): + """ + Fills the internal buffer up with :class:`disco.types.message.Message` objects from the API + """ self._buffer = self.client.api.channels_messages_list( self.channel, before=self.before, diff --git a/disco/types/guild.py b/disco/types/guild.py index 7802159..dd437fe 100644 --- a/disco/types/guild.py +++ b/disco/types/guild.py @@ -12,6 +12,22 @@ from disco.types.channel import Channel class Emoji(BaseType): + """ + An emoji object + + Attributes + ---------- + id : snowflake + The ID of this emoji. + name : str + The name of this emoji. + require_colons : bool + Whether this emoji requires colons to use. + managed : bool + Whether this emoji is managed by an integration. + roles : list(snowflake) + Roles this emoji is attached to. + """ id = skema.SnowflakeType() name = skema.StringType() require_colons = skema.BooleanType() @@ -20,6 +36,26 @@ class Emoji(BaseType): class Role(BaseType): + """ + A role object + + Attributes + ---------- + id : snowflake + The role ID. + name : string + The role name. + hoist : bool + Whether this role is hoisted (displayed separately in the sidebar). + managed : bool + Whether this role is managed by an integration. + color : int + The RGB color of this role. + permissions : :class:`disco.types.permissions.PermissionsValue` + The permissions this role grants. + position : int + The position of this role in the hierarchy. + """ id = skema.SnowflakeType() name = skema.StringType() hoist = skema.BooleanType() @@ -30,6 +66,24 @@ class Role(BaseType): class GuildMember(BaseType): + """ + A GuildMember object + + Attributes + ---------- + user : :class:`disco.types.user.User` + The user object of this member. + guild_id : snowflake + The guild this member is part of. + mute : bool + Whether this member is server voice-muted. + deaf : bool + Whether this member is server voice-deafend. + joined_at : datetime + When this user joined the guild. + roles : list(snowflake) + Roles this member is part of. + """ user = skema.ModelType(User) guild_id = skema.SnowflakeType(required=False) mute = skema.BooleanType() @@ -38,20 +92,84 @@ class GuildMember(BaseType): roles = skema.ListType(skema.SnowflakeType()) def get_voice_state(self): + """ + Returns + ------- + Optional[:class:`disco.types.voice.VoiceState`] + Returns the voice state for the member if they are currently connected + to the guilds voice server. + """ return self.guild.get_voice_state(self) def kick(self): + """ + Kicks the member from the guild. + """ self.client.api.guilds_members_kick(self.guild.id, self.user.id) def ban(self, delete_message_days=0): + """ + Bans the member from the guild. + + Args + ---- + delete_message_days : int + The number of days to retroactively delete messages for. + """ self.client.api.guilds_bans_create(self.guild.id, self.user.id, delete_message_days) @property def id(self): + """ + Alias to the guild members user id + """ return self.user.id class Guild(BaseType, Permissible): + """ + A guild object + + Attributes + ---------- + id : snowflake + The id of this guild. + owner_id : snowflake + The id of the owner. + afk_channel_id : snowflake + The id of the afk channel. + embed_channel_id : snowflake + The id of the embed channel. + name : str + Guilds name. + icon : str + Guilds icon (as PNG binary data). + splash : str + Guilds splash image (as PNG binary data). + region : str + Voice region. + afk_timeout : int + Delay after which users are automatically moved to the afk channel. + embed_enabled : bool + Whether the guilds embed is enabled. + verification_level : int + The verification level used by the guild. + mfa_level : int + The MFA level used by the guild. + features : list(str) + Extra features enabled for this guild. + members : dict(snowflake, :class:`GuildMember`) + All of the guilds members. + channels : dict(snowflake, :class:`disco.types.channel.Channel`) + All of the guilds channels. + roles : dict(snowflake, :class:`Role`) + All of the guilds roles. + emojis : dict(snowflake, :class:`Emoji`) + All of the guilds emojis. + voice_states : dict(str, :class:`disco.types.voice.VoiceState`) + All of the guilds voice states. + """ + id = skema.SnowflakeType() owner_id = skema.SnowflakeType() @@ -77,6 +195,14 @@ class Guild(BaseType, Permissible): voice_states = ListToDictType('session_id', skema.ModelType(VoiceState)) def get_permissions(self, user): + """ + Get the permissions a user has in this guild. + + Returns + ------- + :class:`disco.types.permissions.PermissionValue` + Computed permission value for the user. + """ if self.owner_id == user.id: return PermissionValue(Permissions.ADMINISTRATOR) @@ -89,6 +215,15 @@ class Guild(BaseType, Permissible): return value def get_voice_state(self, user): + """ + Attempt to get a voice state for a given user (who should be a member of + this guild). + + Returns + ------- + :class:`disco.types.voice.VoiceState` + The voice state for the user in this guild. + """ user = to_snowflake(user) for state in self.voice_states.values(): @@ -96,13 +231,21 @@ class Guild(BaseType, Permissible): return state def get_member(self, user): + """ + Attempt to get a member from a given user. + + Returns + ------- + :class:`GuildMember` + The guild member object for the given user. + """ user = to_snowflake(user) if user not in self.members: try: self.members[user] = self.client.api.guilds_members_get(self.id, user) except APIException: - pass + return return self.members.get(user) diff --git a/disco/types/invite.py b/disco/types/invite.py index 45f08f8..cb7a737 100644 --- a/disco/types/invite.py +++ b/disco/types/invite.py @@ -8,6 +8,30 @@ from disco.types.channel import Channel class Invite(BaseType): + """ + An invite object + + Attributes + ---------- + code : str + The invite code. + inviter : :class:`disco.types.user.User` + The user who created this invite. + guild : :class:`disco.types.guild.Guild` + The guild this invite is for. + channel : :class:`disco.types.channel.Channel` + The channel this invite is for. + max_age : int + The time after this invites creation at which it expires. + max_uses : int + The maximum number of uses. + uses : int + The current number of times the invite was used. + temporary : bool + Whether this invite only grants temporary memborship. + created_at : datetime + When this invite was created. + """ code = skema.StringType() inviter = skema.ModelType(User) diff --git a/disco/types/message.py b/disco/types/message.py index 3e83a5d..d1fbcaa 100644 --- a/disco/types/message.py +++ b/disco/types/message.py @@ -9,6 +9,20 @@ from disco.types.user import User class MessageEmbed(BaseType): + """ + Message embed object + + Attributes + ---------- + title : str + Title of the embed. + type : str + Type of the embed. + description : str + Description of the embed. + url : str + URL of the embed. + """ title = skema.StringType() type = skema.StringType() description = skema.StringType() @@ -16,6 +30,26 @@ class MessageEmbed(BaseType): class MessageAttachment(BaseType): + """ + Message attachment object + + Attributes + ---------- + id : snowflake + The id of this attachment. + filename : str + The filename of this attachment. + url : str + The URL of this attachment. + proxy_url : str + The URL to proxy through when downloading the attachment. + size : int + Size of the attachment. + height : int + Height of the attachment. + width : int + Width of the attachment. + """ id = skema.SnowflakeType() filename = skema.StringType() url = skema.StringType() @@ -26,6 +60,40 @@ class MessageAttachment(BaseType): class Message(BaseType): + """ + Represents a Message created within a Channel on Discord. + + Attributes + ---------- + id : snowflake + The ID of this message. + channel_id : snowflake + The channel ID this message was sent in. + author : :class:`disco.types.user.User` + The author of this message. + content : str + The unicode contents of this message. + nonce : str + The nonce of this message. + timestamp : datetime + When this message was created. + edited_timestamp : Optional[datetime] + When this message was last edited. + tts : bool + Whether this is a TTS (text-to-speech) message. + mention_everyone : bool + Whether this message has an @everyone which mentions everyone. + pinned : bool + Whether this message is pinned in the channel. + mentions : dict(snowflake, :class:`disco.types.user.User`) + All users mentioned within this message. + mention_roles : list(snowflake) + All roles mentioned within this message. + embeds : list(:class:`MessageEmbed`) + All embeds for this message. + attachments : list(:class:`MessageAttachment`) + All attachments for this message. + """ id = skema.SnowflakeType() channel_id = skema.SnowflakeType() @@ -52,32 +120,103 @@ class Message(BaseType): @cached_property def guild(self): + """ + Returns + ------- + :class:`disco.types.guild.Guild` + The guild (if applicable) this message was created in. + """ return self.channel.guild @cached_property def channel(self): + """ + Returns + ------- + :class:`disco.types.channel.Channel` + The channel this message was created in. + """ return self.client.state.channels.get(self.channel_id) def reply(self, *args, **kwargs): + """ + Reply to this message (proxys arguments to + :func:`disco.types.channel.Channel.send_message`) + + Returns + ------- + :class:`Message` + The created message object. + """ return self.channel.send_message(*args, **kwargs) def edit(self, content): + """ + Edit this message + + Args + ---- + content : str + The new edited contents of the message. + + Returns + ------- + :class:`Message` + The edited message object. + """ return self.client.api.channels_messages_modify(self.channel_id, self.id, content) def delete(self): + """ + Delete this message. + + Returns + ------- + :class:`Message` + The deleted message object. + """ return self.client.api.channels_messages_delete(self.channel_id, self.id) def is_mentioned(self, entity): + """ + Returns + ------- + bool + Whether the give entity was mentioned. + """ id = to_snowflake(entity) return id in self.mentions or id in self.mention_roles @cached_property def without_mentions(self): + """ + Returns + ------- + str + the message contents with all valid mentions removed. + """ return self.replace_mentions( lambda u: '', lambda r: '') def replace_mentions(self, user_replace, role_replace): + """ + Replaces user and role mentions with the result of a given lambda/function. + + Args + ---- + user_replace : function + A function taking a single argument, the user object mentioned, and + returning a valid string. + role_replace : function + A function taking a single argument, the role ID mentioned, and + returning a valid string. + + Returns + ------- + str + The message contents with all valid mentions replaced. + """ if not self.mentions and not self.mention_roles: return diff --git a/disco/voice/client.py b/disco/voice/client.py index f6bc138..54be863 100644 --- a/disco/voice/client.py +++ b/disco/voice/client.py @@ -86,7 +86,7 @@ class VoiceClient(LoggingClass): def __init__(self, channel): super(VoiceClient, self).__init__() - assert(channel.is_voice) + assert channel.is_voice, 'Cannot spawn a VoiceClient for a non-voice channel' self.channel = channel self.client = self.channel.client diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..a6270ed --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,150 @@ +.. currentmodule:: disco + +API Reference +============= + +Version Information +------------------- +disco exports a top-level variable that can be used to introspect the current +version information for the installed package. + +.. data:: VERSION + + A string representation of the current version, in standard semantic + versioning format. E.g. ``'5.4.3-rc.2'`` + + +DiscoClient +------------ + +.. autoclass:: disco.client.DiscoClient + :members: + + +State +----- + +.. automodule:: disco.state + :members: + + +CLI +--- + +.. automodule:: disco.cli + :members: + + +Types +----- + +Channel +~~~~~~~ + +.. automodule:: disco.types.channel + :members: + +Guild +~~~~~ + +.. automodule:: disco.types.guild + :members: + +Message +~~~~~~~ + +.. automodule:: disco.types.message + :members: + +User +~~~~ + +.. automodule:: disco.types.user + :members: + +Voice +~~~~~ + +.. automodule:: disco.types.voice + :members: + +Invite +~~~~~~ + +.. automodule:: disco.types.invite + :members: + +Permissions +~~~~~~~~~~~ + +.. automodule:: disco.types.permissions + :members: + + +Bot Toolkit +----------- + +.. automodule:: disco.bot.bot + :members: + +Plugins +~~~~~~~ + +.. automodule:: disco.bot.plugin + :members: + +Commands +~~~~~~~~ + +.. automodule:: disco.bot.command + :members: + +Command Argument Parser +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: disco.bot.parser + :members: + + +Gateway API +----------- + +GatewayClient +~~~~~~~~~~~~~ + +.. autoclass:: disco.gateway.client.GatewayClient + :members: + +Gateway Events +~~~~~~~~~~~~~~ + +.. automodule:: disco.gateway.client.Events + :members: + + +REST API +-------- + +APIClient +~~~~~~~~~ + +.. autoclass:: disco.api.client.APIClient + :members: + :undoc-members: + +HTTP Utilities +~~~~~~~~~~~~~~ +.. autoclass:: disco.api.http.APIException + :members: + +.. autoclass:: disco.api.http.HTTPClient + :members: + +Ratelimit Utilities +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: disco.api.ratelimit.RouteState + :members: + +.. autoclass:: disco.api.ratelimit.RateLimiter + :members: diff --git a/docs/conf.py b/docs/conf.py index 6e8d1ca..8572cf3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,6 +34,7 @@ extensions = [ 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages', + 'sphinx.ext.napoleon' ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/disco.api.rst b/docs/disco.api.rst deleted file mode 100644 index c609998..0000000 --- a/docs/disco.api.rst +++ /dev/null @@ -1,38 +0,0 @@ -disco.api package -================= - -Submodules ----------- - -disco.api.client module ------------------------ - -.. automodule:: disco.api.client - :members: - :undoc-members: - :show-inheritance: - -disco.api.http module ---------------------- - -.. automodule:: disco.api.http - :members: - :undoc-members: - :show-inheritance: - -disco.api.ratelimit module --------------------------- - -.. automodule:: disco.api.ratelimit - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco.api - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/disco.bot.rst b/docs/disco.bot.rst deleted file mode 100644 index 4c60e42..0000000 --- a/docs/disco.bot.rst +++ /dev/null @@ -1,46 +0,0 @@ -disco.bot package -================= - -Submodules ----------- - -disco.bot.bot module --------------------- - -.. automodule:: disco.bot.bot - :members: - :undoc-members: - :show-inheritance: - -disco.bot.command module ------------------------- - -.. automodule:: disco.bot.command - :members: - :undoc-members: - :show-inheritance: - -disco.bot.parser module ------------------------ - -.. automodule:: disco.bot.parser - :members: - :undoc-members: - :show-inheritance: - -disco.bot.plugin module ------------------------ - -.. automodule:: disco.bot.plugin - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco.bot - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/disco.gateway.rst b/docs/disco.gateway.rst deleted file mode 100644 index b090e74..0000000 --- a/docs/disco.gateway.rst +++ /dev/null @@ -1,38 +0,0 @@ -disco.gateway package -===================== - -Submodules ----------- - -disco.gateway.client module ---------------------------- - -.. automodule:: disco.gateway.client - :members: - :undoc-members: - :show-inheritance: - -disco.gateway.events module ---------------------------- - -.. automodule:: disco.gateway.events - :members: - :undoc-members: - :show-inheritance: - -disco.gateway.packets module ----------------------------- - -.. automodule:: disco.gateway.packets - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco.gateway - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/disco.rst b/docs/disco.rst deleted file mode 100644 index b523b85..0000000 --- a/docs/disco.rst +++ /dev/null @@ -1,50 +0,0 @@ -disco package -============= - -Subpackages ------------ - -.. toctree:: - - disco.api - disco.bot - disco.gateway - disco.types - disco.util - disco.voice - -Submodules ----------- - -disco.cli module ----------------- - -.. automodule:: disco.cli - :members: - :undoc-members: - :show-inheritance: - -disco.client module -------------------- - -.. automodule:: disco.client - :members: - :undoc-members: - :show-inheritance: - -disco.state module ------------------- - -.. automodule:: disco.state - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/disco.types.rst b/docs/disco.types.rst deleted file mode 100644 index 63e3a84..0000000 --- a/docs/disco.types.rst +++ /dev/null @@ -1,78 +0,0 @@ -disco.types package -=================== - -Submodules ----------- - -disco.types.base module ------------------------ - -.. automodule:: disco.types.base - :members: - :undoc-members: - :show-inheritance: - -disco.types.channel module --------------------------- - -.. automodule:: disco.types.channel - :members: - :undoc-members: - :show-inheritance: - -disco.types.guild module ------------------------- - -.. automodule:: disco.types.guild - :members: - :undoc-members: - :show-inheritance: - -disco.types.invite module -------------------------- - -.. automodule:: disco.types.invite - :members: - :undoc-members: - :show-inheritance: - -disco.types.message module --------------------------- - -.. automodule:: disco.types.message - :members: - :undoc-members: - :show-inheritance: - -disco.types.permissions module ------------------------------- - -.. automodule:: disco.types.permissions - :members: - :undoc-members: - :show-inheritance: - -disco.types.user module ------------------------ - -.. automodule:: disco.types.user - :members: - :undoc-members: - :show-inheritance: - -disco.types.voice module ------------------------- - -.. automodule:: disco.types.voice - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco.types - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/disco.util.rst b/docs/disco.util.rst deleted file mode 100644 index 79f7b01..0000000 --- a/docs/disco.util.rst +++ /dev/null @@ -1,62 +0,0 @@ -disco.util package -================== - -Submodules ----------- - -disco.util.cache module ------------------------ - -.. automodule:: disco.util.cache - :members: - :undoc-members: - :show-inheritance: - -disco.util.json module ----------------------- - -.. automodule:: disco.util.json - :members: - :undoc-members: - :show-inheritance: - -disco.util.logging module -------------------------- - -.. automodule:: disco.util.logging - :members: - :undoc-members: - :show-inheritance: - -disco.util.token module ------------------------ - -.. automodule:: disco.util.token - :members: - :undoc-members: - :show-inheritance: - -disco.util.types module ------------------------ - -.. automodule:: disco.util.types - :members: - :undoc-members: - :show-inheritance: - -disco.util.websocket module ---------------------------- - -.. automodule:: disco.util.websocket - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco.util - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/disco.voice.rst b/docs/disco.voice.rst deleted file mode 100644 index f0fec9a..0000000 --- a/docs/disco.voice.rst +++ /dev/null @@ -1,30 +0,0 @@ -disco.voice package -=================== - -Submodules ----------- - -disco.voice.client module -------------------------- - -.. automodule:: disco.voice.client - :members: - :undoc-members: - :show-inheritance: - -disco.voice.packets module --------------------------- - -.. automodule:: disco.voice.packets - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: disco.voice - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst index 56e52f1..c94c968 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,17 +1,22 @@ -.. disco documentation master file, created by - sphinx-quickstart on Tue Oct 4 22:15:06 2016. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to disco's documentation! +disco ================================= +Disco is a simple and extendable library for the `Discord API `_. + +* Expressive, functional interface that gets out of the way +* Built for high-performance and efficiency +* Configurable and modular, take the bits you need +* Full support for Python 2.x/3.x +* Evented networking and IO using Gevent + Contents: +--------- .. toctree:: :maxdepth: 2 - + tutorial + api Indices and tables ================== diff --git a/docs/modules.rst b/docs/modules.rst deleted file mode 100644 index 1c618c9..0000000 --- a/docs/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -disco -===== - -.. toctree:: - :maxdepth: 4 - - disco diff --git a/docs/setup.rst b/docs/setup.rst deleted file mode 100644 index 31789b1..0000000 --- a/docs/setup.rst +++ /dev/null @@ -1,7 +0,0 @@ -setup module -============ - -.. automodule:: setup - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/tutorial.rst b/docs/tutorial.rst new file mode 100644 index 0000000..4aed0ba --- /dev/null +++ b/docs/tutorial.rst @@ -0,0 +1,5 @@ +.. currentmodule:: disco + +Tutorial +======== +