updated after a while because local stuff

This commit is contained in:
2017-11-10 22:58:01 +01:00
parent 4e44b73542
commit 21df9b0df5
95 changed files with 27441 additions and 25 deletions
+183
View File
@@ -0,0 +1,183 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/discord.py.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/discord.py.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/discord.py"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/discord.py"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+31
View File
@@ -0,0 +1,31 @@
$(document).ready(function () {
var sections = $('div.section');
var activeLink = null;
var bottomHeightThreshold = $(document).height() - 30;
$(window).scroll(function (event) {
var distanceFromTop = $(this).scrollTop();
var currentSection = null;
if(distanceFromTop + window.innerHeight > bottomHeightThreshold) {
currentSection = $(sections[sections.length - 1]);
}
else {
sections.each(function () {
var section = $(this);
if (section.offset().top - 1 < distanceFromTop) {
currentSection = section;
}
});
}
if (activeLink) {
activeLink.parent().removeClass('active');
}
if (currentSection) {
activeLink = $('.sphinxsidebar a[href="#' + currentSection.attr('id') + '"]');
activeLink.parent().addClass('active');
}
});
});
+542
View File
@@ -0,0 +1,542 @@
/* this stuff uses a couple of themes as a base with some custom stuff added
In particular thanks to:
- Alabaster for being a good base
- Which thanks Flask + KR theme
- Sphinx Readable Theme
- Which also proved to be a great base
*/
@import url('basic.css');
body {
font-family: 'Georgia', serif;
font-size: 16px;
margin: 0;
padding: 0;
}
p {
margin-bottom: 8px;
}
div.document {
margin: 10px auto 0 auto;
max-width: 940px; /* page width */
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 220px; /* sidebar width */
}
div.body {
background-color: #ffffff;
color: #3e4349;
padding: 0 30px 30px 30px;
}
div.footer {
color: #555;
font-size: 14px;
margin: 20px auto 30px auto;
text-align: right;
max-width: 880px;
}
div.footer a {
color: #444;
text-decoration: underline;
}
div.related {
padding: 10px 10px;
width: auto;
}
div.sphinxsidebar {
float: left;
font-size: 14px;
line-height: 1.5em;
margin-left: -100%;
width: 220px; /* sidebar width */
}
div.sphinxsidebarwrapper {
font-size: 14px;
line-height: 1.5em;
padding: 10px 0 10px 10px;
/* sticky sidebar */
position: fixed;
width: 220px; /* sidebar width */
height: 90%;
overflow: hidden;
}
/* show scrollbar on hover */
div.sphinxsidebarwrapper:hover {
overflow: auto;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
color: #333;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 1.1em;
}
div.sphinxsidebar h3 a {
color: #333;
}
div.sphinxsidebar p {
color: #888;
}
div.sphinxsidebar p.searchtip {
line-height: 1.4em;
}
div.sphinxsidebar ul {
color: #000;
margin: 10px 0 20px;
padding: 0;
}
div.sphinxsidebar a {
color: #444;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
font-size: 1em;
margin-top: 10px;
}
/* -- body styles --------------------------------------------------------- */
a {
color: #2591c4;
text-decoration: none;
}
a:hover {
color: #0b3a44;
text-decoration: underline;
}
hr {
border: 1px solid #b1b4b6;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 { font-weight: normal; }
div.body h1,
div.body h2,
div.body h3,
div.body h4 { color: #212224; }
div.body h5 { color: #000; }
div.body h6 { color: #777; }
div.body h1 { margin: 0 0 10px 0; }
div.body h2,
div.body h3 { margin: 10px 0px 10px 0px; }
div.body h4,
div.body h5,
div.body h6 { margin: 20px 0px 10px 0px; }
div.body h1 { padding: 0 0 10px 0; }
div.body h2,
div.body h3 { padding: 10px 0 10px 0; }
div.body h4 { padding: 10px 0 10px 0; }
div.body h5,
div.body h6 { padding: 10px 0 0 0; }
div.body h1,
div.body h2,
div.body h3 { border-bottom: 1px solid #ddd; }
div.body h4 { border-bottom: 1px solid #e5e5e5; }
div.body h1 { font-size: 230%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 130%; }
div.body h4 { font-size: 110%; }
div.body h5 { font-size: 105%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #3e4349;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #3e4349;
color: #fff;
}
div.body ul {
list-style: disc;
margin: 1em 0;
padding-left: 1.3em;
}
div.body ul ul, div.body ol ul {
margin: .2em 0;
padding-left: 1.2em;
}
div.body ul li {
padding: 2px 0;
}
div.body ul.search li {
padding: 5px 0 5px 20px;
}
div.body ol {
counter-reset: li;
margin-left: 0;
padding-left: 0;
}
div.body ol ol {
margin: .2em 0;
}
div.body ol > li {
list-style: none;
margin: 0 0 0 1.9em;
padding: 2px 1px;
position: relative;
}
div.body ol > li:before {
content: counter(li) ".";
counter-increment: li;
top: -2px;
left: -1.9em;
width: 1.9em;
padding: 4px 0;
position: absolute;
text-align: left;
}
div.body p,
div.body dd,
div.body li {
line-height: 1.4em;
}
/* weird margins */
li > p {
margin: 2px;
}
li > blockquote {
margin: 10px;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.highlight {
background-color: #fff;
}
div.important, div.note, div.hint, div.tip {
background-color: #eee;
border: 1px solid #ccc;
}
div.attention, div.warning, div.caution, div.seealso {
background-color: #fef9e9;
border: 1px solid #fbe091;
}
/* no disgusting background in the FAQ */
div.topic {
background-color: transparent;
border: none;
}
/* don't link-ify the FAQ page */
a.toc-backref {
text-decoration: none;
color: #3e4349;
}
/* bold and fix the Parameter, Raises, etc. */
dl.field-list > dt {
font-weight: bold;
}
div.danger, div.error {
background-color: #ffe4e4;
border: 1px solid #f66;
}
div.admonition {
padding: 10px;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ':';
}
/* helpful admonitions */
div.helpful {
background-color: #e4f2ff;
border: 1px solid #66b3ff;
}
div.helpful > p.admonition-title {
display: block;
}
div.helpful > p.admonition-title:after {
content: unset;
}
pre {
background-color: #f5f5f5;
border: 1px solid #C6C9CB;
color: #222;
font-size: 0.75em;
line-height: 1.5em;
margin: 1.5em 0 1.5em 0;
padding: 10px;
}
pre, tt, code {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
tt, code {
background-color: #ecf0f3;
}
tt.descname, code.descname {
font-size: 0.95em;
}
tt.xref, a tt, code.xref, a code {
font-weight: normal;
}
span.pre {
padding: 0 2px;
}
dl.class {
margin-bottom: 50px;
}
dl.describe > dt,
dl.function > dt,
dl.attribute > dt,
dl.classmethod > dt,
dl.method > dt,
dl.class > dt,
dl.exception > dt {
background-color: #f5f5f5;
padding: 1px 10px;
}
dd {
margin-top: 10px;
}
.container.operations {
padding: 10px;
border: 1px solid #ddd;
margin-bottom: 20px;
}
.container.operations::before {
content: 'Supported Operations';
color: #212224;
display: block;
padding-bottom: 5px;
}
.container.operations > dl.describe > dt {
background-color: #f8f8f8;
}
table.docutils {
width: 100%;
}
table.docutils.footnote {
width: auto;
}
table.docutils thead,
table.docutils tfoot {
background: #f5f5f5;
}
table.docutils thead tr th {
color: #000;
font-weight: normal;
padding: 7px 5px;
vertical-align: middle;
}
table.docutils tbody tr th,
table.docutils tbody tr td {
border-bottom: 0;
border-top: solid 1px #ddd;
padding: 7px 5px;
vertical-align: top;
}
table.docutils tbody tr:last-child th,
table.docutils tbody tr:last-child td {
border-bottom: solid 1px #ddd;
}
table.docutils thead tr td p,
table.docutils tfoot tr td p,
table.docutils tbody tr td p,
table.docutils thead tr td ul,
table.docutils tfoot tr td ul,
table.docutils tbody tr td ul,
table.docutils thead tr td ol,
table.docutils tfoot tr td ol,
table.docutils tbody tr td ol {
margin: 0 0 .5em;
}
table.docutils thead tr td p.last,
table.docutils tfoot tr td p.last,
table.docutils tbody tr td p.last,
table.docutils thead tr td ul.last,
table.docutils tfoot tr td ul.last,
table.docutils tbody tr td ul.last,
table.docutils thead tr td ol.last,
table.docutils tfoot tr td ol.last,
table.docutils tbody tr td ol.last {
margin-bottom: 0;
}
.viewcode-back {
font-family: Arial, sans-serif;
}
div.viewcode-block:target {
background-color: #fef9e9;
border-top: 1px solid #fbe091;
border-bottom: 1px solid #fbe091;
}
/* hide the welcome text */
div#welcome-to-discord-py > h1 {
display: none;
}
.active {
background-color: #dbdbdb;
border-left: 5px solid #dbdbdb;
}
@media screen and (max-width: 870px) {
div.document {
width: auto;
margin: 0;
}
div.documentwrapper {
float: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0 20px 30px 20px;
}
div.footer {
background-color: #333;
color: #888;
margin: 0;
padding: 10px 20px 20px;
text-align: left;
width: auto;
}
div.footer a {
color: #bbb;
}
div.footer a:hover {
color: #fff;
}
div.sphinxsidebar {
background-color: #333;
color: #fff;
float: none;
margin: 0;
padding: 10px 20px;
width: auto;
}
/* sticky sidebar */
div.sphinxsidebarwrapper {
position: relative;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4,
div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: #fff;
}
div.sphinxsidebar ul {
color: #999;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar a:hover {
color: #fff;
}
.active {
background-color: transparent;
border-left: none;
}
}
+29
View File
@@ -0,0 +1,29 @@
{%- extends "basic/layout.html" %}
{% set show_source = False %}
{% set style = 'style.css' %}
{%- block extrahead %}
{{ super() }}
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class="indexwrapper">
{% endif %}
{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
{% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
</div>
{% if pagename == 'index' %}
</div>
{% endif %}
{%- endblock %}
+1
View File
@@ -0,0 +1 @@
<!-- purposefully empty -->
File diff suppressed because it is too large Load Diff
+314
View File
@@ -0,0 +1,314 @@
# -*- coding: utf-8 -*-
#
# discord.py documentation build configuration file, created by
# sphinx-quickstart on Fri Aug 21 05:43:30 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import re
on_rtd = os.getenv('READTHEDOCS') == 'True'
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
sys.path.append(os.path.abspath('extensions'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.extlinks',
'sphinxcontrib.asyncio',
'details'
]
if on_rtd:
extensions.append('sphinxcontrib.napoleon')
else:
extensions.append('sphinx.ext.napoleon')
autodoc_member_order = 'bysource'
extlinks = {
'issue': ('https://github.com/Rapptz/discord.py/issues/%s', 'issue '),
}
rst_prolog = """
.. |coro| replace:: This function is a |corourl|_.
.. |maybecoro| replace:: This function *could be a* |corourl|_.
.. |corourl| replace:: *coroutine*
.. _corourl: https://docs.python.org/3/library/asyncio-task.html#coroutine
"""
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'discord.py'
copyright = u'2015-2017, Rapptz'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = ''
with open('../discord/__init__.py') as f:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1)
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'friendly'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
html_experimental_html5_writer = True
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'basic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {
# }
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'discord.pydoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'discord.py.tex', u'discord.py Documentation',
u'Rapptz', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'discord.py', u'discord.py Documentation',
[u'Rapptz'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'discord.py', u'discord.py Documentation',
u'Rapptz', 'discord.py', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
def setup(app):
app.add_javascript('custom.js')
+97
View File
@@ -0,0 +1,97 @@
.. _discord-intro:
Creating a Bot Account
========================
In order to work with the library and the Discord API in general, we must first create a Discord Bot account.
Creating a Bot account is a pretty straightforward process.
1. Make sure you're logged on to the `Discord website <https://discordapp.com>`_.
2. Navigate to the `application page <https://discordapp.com/developers/applications/me>`_
3. Click on the "New App" button.
.. image:: /images/discord_create_app_button.png
:alt: The new app button.
4. Give the application a name and a description if wanted and click "Create App".
- You can also put an avatar you want your bot to use, don't worry you can change this later.
- **Leave the Redirect URI(s) blank** unless are creating a service.
.. image:: /images/discord_create_app_form.png
:alt: The new application form filled in.
5. Create a Bot User by clicking on the accompanying button and confirming it.
.. image:: /images/discord_create_bot_user.png
:alt: The Create a Bot User button.
6. Make sure that **Public Bot** is ticked if you want others to invite your bot.
- You should also make sure that **Require OAuth2 Code Grant** is unchecked unless you
are developing a service that needs it. If you're unsure, then **leave it unchecked**.
.. image:: /images/discord_bot_user_options.png
:alt: How the Bot User options should look like for most people.
7. Click to reveal the token.
- **This is not the Client Secret**
- Look at the image above to see where the **Token** is.
.. warning::
It should be worth noting that this token is essentially your bot's
password. You should **never** share this to someone else. In doing so,
someone can log in to your bot and do malicious things, such as leaving
servers, ban all members inside a server, or pinging everyone maliciously.
The possibilities are endless, so **do not share this token.**
And that's it. You now have a bot account and you can login with that token.
.. _discord_invite_bot:
Inviting Your Bot
-------------------
So you've made a Bot User but it's not actually in any server.
If you want to invite your bot you must create an invite URL for your bot.
First, you must fetch the Client ID of the Bot. You can find this in the Bot's application page.
.. image:: /images/discord_client_id.png
:alt: The Bot's Client ID.
Copy paste that into the pre-formatted URL:
.. code-block:: none
https://discordapp.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&scope=bot&permissions=0
Replace ``YOUR_CLIENT_ID`` with the Client ID we got in the previous step. For example,
in the image above our client ID is 312777964700041216 so the resulting URL would be
https://discordapp.com/oauth2/authorize?client_id=312777964700041216&scope=bot&permissions=0
(note that this bot has been deleted).
Now you can click the link and invite your bot to any server you have "Manage Server" permissions on.
Adding Permissions
~~~~~~~~~~~~~~~~~~~~
In the above URL, you might have noticed an interesting bit, the ``permissions=0`` fragment.
Bot accounts can request specific permissions to be granted upon joining. When the bot joins
the guild, they will be granted a managed role that contains the permissions you requested.
If the permissions is 0, then no special role is created.
This ``permissions`` value is calculated based on bit-wise arithmetic. Thankfully, people have
created a calculator that makes it easy to calculate the permissions necessary visually.
- https://discordapi.com/permissions.html
- https://finitereality.github.io/permissions/
Feel free to use whichever is easier for you to grasp.
If you want to generate this URL dynamically at run-time inside your bot and using the
:class:`discord.Permissions` interface, you can use :func:`discord.utils.oauth_url`.
@@ -0,0 +1,225 @@
.. currentmodule:: discord
API Reference
===============
The following section outlines the API of discord.py's command extension module.
.. _ext_commands_api_bot:
Bot
----
.. autoclass:: discord.ext.commands.Bot
:members:
:inherited-members:
.. autoclass:: discord.ext.commands.AutoShardedBot
:members:
.. autofunction:: discord.ext.commands.when_mentioned
.. autofunction:: discord.ext.commands.when_mentioned_or
.. _ext_commands_api_events:
Event Reference
-----------------
These events function similar to :ref:`the regular events <discord-api-events>`, except they
are custom to the command extension module.
.. function:: on_command_error(ctx, error)
An error handler that is called when an error is raised
inside a command either through user input error, check
failure, or an error in your own code.
A default one is provided (:meth:`.Bot.on_command_error`).
:param ctx: The invocation context.
:type ctx: :class:`Context`
:param error: The error that was raised.
:type error: :class:`CommandError` derived
.. function:: on_command(ctx)
An event that is called when a command is found and is about to be invoked.
This event is called regardless of whether the command itself succeeds via
error or completes.
:param ctx: The invocation context.
:type ctx: :class:`Context`
.. function:: on_command_completion(ctx)
An event that is called when a command has completed its invocation.
This event is called only if the command succeeded, i.e. all checks have
passed and the user input it correctly.
:param ctx: The invocation context.
:type ctx: :class:`Context`
.. _ext_commands_api_command:
Command
--------
.. autofunction:: discord.ext.commands.command
.. autofunction:: discord.ext.commands.group
.. autoclass:: discord.ext.commands.Command
:members:
.. autoclass:: discord.ext.commands.Group
:members:
:inherited-members:
.. autoclass:: discord.ext.commands.GroupMixin
:members:
.. _ext_commands_api_formatters:
Formatters
-----------
.. autoclass:: discord.ext.commands.Paginator
:members:
.. autoclass:: discord.ext.commands.HelpFormatter
:members:
.. _ext_commands_api_checks:
Checks
-------
.. autofunction:: discord.ext.commands.check
.. autofunction:: discord.ext.commands.has_role
.. autofunction:: discord.ext.commands.has_permissions
.. autofunction:: discord.ext.commands.has_any_role
.. autofunction:: discord.ext.commands.bot_has_role
.. autofunction:: discord.ext.commands.bot_has_permissions
.. autofunction:: discord.ext.commands.bot_has_any_role
.. autofunction:: discord.ext.commands.cooldown
.. autofunction:: discord.ext.commands.guild_only
.. autofunction:: discord.ext.commands.is_owner
.. autofunction:: discord.ext.commands.is_nsfw
.. _ext_commands_api_context:
Context
--------
.. autoclass:: discord.ext.commands.Context
:members:
:inherited-members:
:exclude-members: history, typing
.. autocomethod:: discord.ext.commands.Context.history
:async-for:
.. autocomethod:: discord.ext.commands.Context.typing
:async-with:
.. _ext_commands_api_converters:
Converters
------------
.. autoclass:: discord.ext.commands.Converter
:members:
.. autoclass:: discord.ext.commands.MemberConverter
:members:
.. autoclass:: discord.ext.commands.UserConverter
:members:
.. autoclass:: discord.ext.commands.TextChannelConverter
:members:
.. autoclass:: discord.ext.commands.VoiceChannelConverter
:members:
.. autoclass:: discord.ext.commands.CategoryChannelConverter
:members:
.. autoclass:: discord.ext.commands.InviteConverter
:members:
.. autoclass:: discord.ext.commands.RoleConverter
:members:
.. autoclass:: discord.ext.commands.GameConverter
:members:
.. autoclass:: discord.ext.commands.ColourConverter
:members:
.. autoclass:: discord.ext.commands.EmojiConverter
:members:
.. autoclass:: discord.ext.commands.clean_content
:members:
.. _ext_commands_api_errors:
Errors
-------
.. autoexception:: discord.ext.commands.CommandError
:members:
.. autoexception:: discord.ext.commands.MissingRequiredArgument
:members:
.. autoexception:: discord.ext.commands.BadArgument
:members:
.. autoexception:: discord.ext.commands.NoPrivateMessage
:members:
.. autoexception:: discord.ext.commands.CheckFailure
:members:
.. autoexception:: discord.ext.commands.CommandNotFound
:members:
.. autoexception:: discord.ext.commands.DisabledCommand
:members:
.. autoexception:: discord.ext.commands.CommandInvokeError
:members:
.. autoexception:: discord.ext.commands.TooManyArguments
:members:
.. autoexception:: discord.ext.commands.UserInputError
:members:
.. autoexception:: discord.ext.commands.CommandOnCooldown
:members:
.. autoexception:: discord.ext.commands.NotOwner
:members:
.. autoexception:: discord.ext.commands.MissingPermissions
:members:
.. autoexception:: discord.ext.commands.BotMissingPermissions
:members:
@@ -0,0 +1,587 @@
.. currentmodule:: discord
.. _ext_commands_commands:
Commands
==========
One of the most appealing aspect of the command extension is how easy it is to define commands and
how you can arbitrarily nest groups and commands to have a rich sub-command system.
Commands are defined by attaching it to a regular Python function. The command is then invoked by the user using a similar
signature to the Python function.
For example, in the given command definition:
.. code-block:: python3
@bot.command()
async def foo(ctx, arg):
await ctx.send(arg)
With the following prefix (``$``), it would be invoked by the user via:
.. code-block:: none
$foo abc
A command must always have at least one parameter, ``ctx``, which is the :class:`.Context` as the first one.
There are two ways of registering a command. The first one is by using :meth:`.Bot.command` decorator,
as seen in the example above. The second is using the :func:`~ext.commands.command` decorator followed by
:meth:`.Bot.add_command` on the instance.
Essentially, these two are equivalent: ::
from discord.ext import commands
bot = commands.Bot(command_prefix='$')
@bot.command()
async def test(ctx):
pass
# or:
@commands.command()
async def test(ctx):
pass
bot.add_command(test)
Since the :meth:`.Bot.command` decorator is shorter and easier to comprehend, it will be the one used throughout the
documentation here.
Any parameter that is accepted by the :class:`.Command` constructor can be passed into the decorator. For example, to change
the name to something other than the function would be as simple as doing this:
.. code-block:: python3
@bot.command(name='list')
async def _list(ctx, arg):
pass
Parameters
------------
Since we define commands by making Python functions, we also define the argument passing behaviour by the function
parameters.
Certain parameter types do different things in the user side and most forms of parameter types are supported.
Positional
++++++++++++
The most basic form of parameter passing is the positional parameter. This is where we pass a parameter as-is:
.. code-block:: python3
@bot.command()
async def test(ctx, arg):
await ctx.send(arg)
On the bot using side, you can provide positional arguments by just passing a regular string:
.. image:: /images/commands/positional1.png
To make use of a word with spaces in between, you should quote it:
.. image:: /images/commands/positional2.png
As a note of warning, if you omit the quotes, you will only get the first word:
.. image:: /images/commands/positional3.png
Since positional arguments are just regular Python arguments, you can have as many as you want:
.. code-block:: python3
@bot.command()
async def test(ctx, arg1, arg2):
await ctx.send('You passed {} and {}'.format(arg1, arg2))
Variable
++++++++++
Sometimes you want users to pass in an undetermined number of parameters. The library supports this
similar to how variable list parameters are done in Python:
.. code-block:: python3
@bot.command()
async def test(ctx, *args):
await ctx.send('{} arguments: {}'.format(len(args), ', '.join(args)))
This allows our user to accept either one or many arguments as they please. This works similar to positional arguments,
so multi-word parameters should be quoted.
For example, on the bot side:
.. image:: /images/commands/variable1.png
If the user wants to input a multi-word argument, they have to quote it like earlier:
.. image:: /images/commands/variable2.png
Do note that similar to the Python function behaviour, a user can technically pass no arguments
at all:
.. image:: /images/commands/variable3.png
Since the ``args`` variable is a `tuple <https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range>`_,
you can do anything you would usually do with one.
Keyword-Only Arguments
++++++++++++++++++++++++
When you want to handle parsing of the argument yourself or do not feel like you want to wrap multi-word user input into
quotes, you can ask the library to give you the rest as a single argument. We do this by using a **keyword-only argument**,
seen below:
.. code-block:: python3
@bot.command()
async def test(ctx, *, arg):
await ctx.send(arg)
.. warning::
You can only have one keyword-only argument due to parsing ambiguities.
On the bot side, we do not need to quote input with spaces:
.. image:: /images/commands/keyword1.png
Do keep in mind that wrapping it in quotes leaves it as-is:
.. image:: /images/commands/keyword2.png
By default, the keyword-only arguments are stripped of white space to make it easier to work with. This behaviour can be
toggled by the :attr:`.Command.rest_is_raw` argument in the decorator.
.. _ext_commands_context:
Invocation Context
-------------------
As seen earlier, every command must take at least a single parameter, called the :class:`~ext.commands.Context`.
This parameter gives you access to something called the "invocation context". Essentially all the information you need to
know how the command was executed. It contains a lot of useful information:
- :attr:`.Context.guild` to fetch the :class:`Guild` of the command, if any.
- :attr:`.Context.message` to fetch the :class:`Message` of the command.
- :attr:`.Context.author` to fetch the :class:`Member` or :class:`User` that called the command.
- :meth:`.Context.send` to send a message to the channel the command was used in.
The context implements the :class:`abc.Messageable` interface, so anything you can do on a :class:`abc.Messageable` you
can do on the :class:`~ext.commands.Context`.
Converters
------------
Adding bot arguments with function parameters is only the first step in defining your bot's command interface. To actually
make use of the arguments, we usually want to convert the data into a target type. We call these
:ref:`ext_commands_api_converters`.
Converters come in a few flavours:
- A regular callable object that takes an argument as a sole parameter and returns a different type.
- These range from your own function, to something like ``bool`` or ``int``.
- A custom class that inherits from :class:`~ext.commands.Converter`.
Basic Converters
++++++++++++++++++
At its core, a basic converter is a callable that takes in an argument and turns it into something else.
For example, if we wanted to add two numbers together, we could request that they are turned into integers
for us by specifying the converter:
.. code-block:: python3
@bot.command()
async def add(ctx, a: int, b: int):
await ctx.send(a + b)
We specify converters by using something called a **function annotation**. This is a Python 3 exclusive feature that was
introduced in :pep:`3107`.
This works with any callable, such as a function that would convert a string to all upper-case:
.. code-block:: python3
def to_upper(argument):
return argument.upper()
@bot.command()
async def up(ctx, *, content: to_upper):
await ctx.send(content)
.. _ext_commands_adv_converters:
Advanced Converters
+++++++++++++++++++++
Sometimes a basic converter doesn't have enough information that we need. For example, sometimes we want to get some
information from the :class:`Message` that called the command or we want to do some asynchronous processing.
For this, the library provides the :class:`~ext.commands.Converter` interface. This allows you to have access to the
:class:`.Context` and have the callable be asynchronous. Defining a custom converter using this interface requires
overriding a single method, :meth:`.Converter.convert`.
An example converter:
.. code-block:: python3
import random
class Slapper(commands.Converter):
async def convert(self, ctx, argument):
to_slap = random.choice(ctx.guild.members)
return '{0.author} slapped {1} because *{2}*'.format(ctx, to_slap, argument)
@bot.command()
async def slap(ctx, *, reason: Slapper):
await ctx.send(reason)
The converter provided can either be constructed or not. Essentially these two are equivalent:
.. code-block:: python3
@bot.command()
async def slap(ctx, *, reason: Slapper):
await ctx.send(reason)
# is the same as...
@bot.command()
async def slap(ctx, *, reason: Slapper()):
await ctx.send(reason)
Having the possibility of the converter be constructed allows you to set up some state in the converter's ``__init__`` for
fine tuning the converter. An example of this is actually in the library, :class:`~ext.commands.clean_content`.
.. code-block:: python3
@bot.command()
async def clean(ctx, *, content: commands.clean_content):
await ctx.send(content)
# or for fine-tuning
@bot.command()
async def clean(ctx, *, content: commands.clean_content(use_nicknames=False)):
await ctx.send(content)
If a converter fails to convert an argument to its designated target type, the :exc:`.BadArgument` exception must be
raised.
Discord Converters
++++++++++++++++++++
Working with :ref:`discord_api_models` is a fairly common thing when defining commands, as a result the library makes
working with them easy.
For example, to receive a :class:`Member`, you can just pass it as a converter:
.. code-block:: python3
@bot.command()
async def joined(ctx, *, member: discord.Member):
await ctx.send('{0} joined on {0.joined_at}'.format(member))
When this command is executed, it attempts to convert the string given into a :class:`Member` and then passes it as a
parameter for the function. This works by checking if the string is a mention, an ID, a nickname, a username + discriminator,
or just a regular username. The default set of converters have been written to be as easy to use as possible.
A lot of discord models work out of the gate as a parameter:
- :class:`Member`
- :class:`User`
- :class:`TextChannel`
- :class:`VoiceChannel`
- :class:`CategoryChannel`
- :class:`Role`
- :class:`Invite`
- :class:`Game`
- :class:`Emoji`
- :class:`Colour`
Having any of these set as the converter will intelligently convert the argument to the appropriate target type you
specify.
Under the hood, these are implemented by the :ref:`ext_commands_adv_converters` interface. A table of the equivalent
converter is given below:
+-----------------------+-------------------------------------------------+
| Discord Class | Converter |
+-----------------------+-------------------------------------------------+
| :class:`Member` | :class:`~ext.commands.MemberConverter` |
+-----------------------+-------------------------------------------------+
| :class:`User` | :class:`~ext.commands.UserConverter` |
+-----------------------+-------------------------------------------------+
| :class:`TextChannel` | :class:`~ext.commands.TextChannelConverter` |
+-----------------------+-------------------------------------------------+
| :class:`VoiceChannel` | :class:`~ext.commands.VoiceChannelConverter` |
+-----------------------+-------------------------------------------------+
| :class:`VoiceChannel` | :class:`~ext.commands.CategoryChannelConverter` |
+-----------------------+-------------------------------------------------+
| :class:`Role` | :class:`~ext.commands.RoleConverter` |
+-----------------------+-------------------------------------------------+
| :class:`Invite` | :class:`~ext.commands.InviteConverter` |
+-----------------------+-------------------------------------------------+
| :class:`Game` | :class:`~ext.commands.GameConverter` |
+-----------------------+-------------------------------------------------+
| :class:`Emoji` | :class:`~ext.commands.EmojiConverter` |
+-----------------------+-------------------------------------------------+
| :class:`Colour` | :class:`~ext.commands.ColourConverter` |
+-----------------------+-------------------------------------------------+
By providing the converter it allows us to use them as building blocks for another converter:
.. code-block:: python3
class MemberRoles(commands.MemberConverter):
async def convert(self, ctx, argument):
member = await super().convert(ctx, argument)
return member.roles
@bot.command()
async def roles(ctx, *, member: MemberRoles):
"""Tells you a member's roles."""
await ctx.send('I see the following roles: ' + ', '.join(member))
Inline Advanced Converters
+++++++++++++++++++++++++++++
If we don't want to inherit from :class:`~ext.commands.Converter`, we can still provide a converter that has the
advanced functionalities of an advanced converter and save us from specifying two types.
For example, a common idiom would be to have a class and a converter for that class:
.. code-block:: python3
class JoinDistance:
def __init__(self, joined, created):
self.joined = joined
self.created = created
@property
def delta(self):
return self.joined - self.created
class JoinDistanceConverter(commands.MemberConverter):
async def convert(self, ctx, argument):
member = await super().convert(ctx, argument)
return JoinDistance(member.joined_at, member.created_at)
@bot.command()
async def delta(ctx, *, member: JoinDistanceConverter):
is_new = member.delta.days < 100
if is_new:
await ctx.send("Hey you're pretty new!")
else:
await ctx.send("Hm you're not so new.")
This can get tedious, so an inline advanced converter is possible through a ``classmethod`` inside the type:
.. code-block:: python3
class JoinDistance:
def __init__(self, joined, created):
self.joined = joined
self.created = created
@classmethod
async def convert(cls, ctx, argument):
member = await commands.MemberConverter().convert(ctx, argument)
return cls(member.joined_at, member.created_at)
@property
def delta(self):
return self.joined - self.created
@bot.command()
async def delta(ctx, *, member: JoinDistance):
is_new = member.delta.days < 100
if is_new:
await ctx.send("Hey you're pretty new!")
else:
await ctx.send("Hm you're not so new.")
.. _ext_commands_error_handler:
Error Handling
----------------
When our commands fail to either parse we will, by default, receive a noisy error in ``stderr`` of our console that tells us
that an error has happened and has been silently ignored.
In order to handle our errors, we must use something called an error handler. There is a global error handler, called
:func:`on_command_error` which works like any other event in the :ref:`discord-api-events`. This global error handler is
called for every error reached.
Most of the time however, we want to handle an error local to the command itself. Luckily, commands come with local error
handlers that allow us to do just that. First we decorate an error handler function with :meth:`.Command.error`:
.. code-block:: python3
@bot.command()
async def info(ctx, *, member: discord.Member):
"""Tells you some info about the member."""
fmt = '{0} joined on {0.joined_at} and has {1} roles.'
await ctx.send(fmt.format(member, len(member.roles)))
@info.error
async def info_error(ctx, error):
if isinstance(error, commands.BadArgument):
await ctx.send('I could not find that member...')
The first parameter of the error handler is the :class:`.Context` while the second one is an exception that is derived from
:exc:`~ext.commands.CommandError`. A list of errors is found in the :ref:`ext_commands_api_errors` page of the documentation.
Checks
-------
There are cases when we don't want a user to use our commands. They don't have permissions to do so or maybe we blocked
them from using our bot earlier. The commands extension comes with full support for these things in a concept called a
:ref:`ext_commands_api_checks`.
A check is a basic predicate that can take in a :class:`.Context` as its sole parameter. Within it, you have the following
options:
- Return ``True`` to signal that the person can run the command.
- Return ``False`` to signal that the person cannot run the command.
- Raise a :exc:`~ext.commands.CommandError` derived exception to signal the person cannot run the command.
- This allows you to have custom error messages for you to handle in the
:ref:`error handlers <ext_commands_error_handler>`.
To register a check for a command, we would have two ways of doing so. The first is using the :meth:`~ext.commands.check`
decorator. For example:
.. code-block:: python3
async def is_owner(ctx):
return ctx.author.id == 316026178463072268
@bot.command(name='eval')
@commands.check(is_owner)
async def _eval(ctx, *, code):
"""A bad example of an eval command"""
await ctx.send(eval(code))
This would only evaluate the command if the function ``is_owner`` returns ``True``. Sometimes we re-use a check often and
want to split it into its own decorator. To do that we can just add another level of depth:
.. code-block:: python3
def is_owner():
async def predicate(ctx):
return ctx.author.id == 316026178463072268
return commands.check(predicate)
@bot.command(name='eval')
@is_owner()
async def _eval(ctx, *, code):
"""A bad example of an eval command"""
await ctx.send(eval(code))
Since an owner check is so common, the library provides it for you (:func:`~ext.commands.is_owner`):
.. code-block:: python3
@bot.command(name='eval')
@commands.is_owner()
async def _eval(ctx, *, code):
"""A bad example of an eval command"""
await ctx.send(eval(code))
When multiple checks are specified, **all** of them must be ``True``:
.. code-block:: python3
def is_in_guild(guild_id):
async def predicate(ctx):
return ctx.guild and ctx.guild.id == guild_id
return commands.check(is_in_guild)
@bot.command()
@is_in_guild(41771983423143937)
async def secretguilddata(ctx):
"""super secret stuff"""
await ctx.send('secret stuff')
If any of those checks fail in the example above, then the command will not be run.
When an error happens, the error is propagated to the :ref:`error handlers <ext_commands_error_handler>`. If you do not
raise a custom :exc:`~ext.commands.CommandError` derived exception, then it will get wrapped up into a
:exc:`~ext.commands.CheckFailure` exception as so:
.. code-block:: python3
@bot.command()
@is_in_guild(41771983423143937)
async def secretguilddata(ctx):
"""super secret stuff"""
await ctx.send('secret stuff')
@secretguilddata.error
async def secretguilddata_error(ctx, error):
if isinstance(error, commands.CheckFailure):
await ctx.send('nothing to see here comrade.')
If you want a more robust error system, you can derive from the exception and raise it instead of returning ``False``:
.. code-block:: python3
class NoPrivateMessages(commands.CheckFailure):
pass
def guild_only():
async def predicate(ctx):
if ctx.guild is None:
raise NoPrivateMessages('Hey no DMs!')
return True
return commands.check(predicate)
@guild_only()
async def test(ctx):
await ctx.send('Hey this is not a DM! Nice.')
@test.error
async def test_error(ctx, error):
if isinstance(error, NoPrivateMessages):
await ctx.send(error)
.. note::
Since having a ``guild_only`` decorator is pretty common, it comes built-in via :func:`~ext.commands.guild_only`.
Global Checks
++++++++++++++
Sometimes we want to apply a check to **every** command, not just certain commands. The library supports this as well
using the global check concept.
Global checks work similarly to regular checks except they are registered with the :func:`.Bot.check` decorator.
For example, to block all DMs we could do the following:
.. code-block:: python3
@bot.check
async def globally_block_dms(ctx):
return ctx.guild is not None
.. warning::
Be careful on how you write your global checks, as it could also lock you out of your own bot.
.. need a note on global check once here I think
@@ -0,0 +1,14 @@
``discord.ext.commands`` -- Bot commands framework
====================================================
``discord.py`` offers a lower level aspect on interacting with Discord. Often times, the library is used for the creation of
bots. However this task can be daunting and confusing to get correctly the first time. Many times there comes a repetition in
creating a bot command framework that is extensible, flexible, and powerful. For this reason, ``discord.py`` comes with an
extension library that handles this for you.
.. toctree::
:maxdepth: 2
commands
api
@@ -0,0 +1,55 @@
from docutils.parsers.rst import Directive
from docutils.parsers.rst import states, directives
from docutils.parsers.rst.roles import set_classes
from docutils import nodes
class details(nodes.General, nodes.Element):
pass
class summary(nodes.General, nodes.Element):
pass
def visit_details_node(self, node):
self.body.append(self.starttag(node, 'details', CLASS=node.attributes.get('class', '')))
def visit_summary_node(self, node):
self.body.append(self.starttag(node, 'summary', CLASS=node.attributes.get('summary-class', '')))
self.body.append(node.rawsource)
def depart_details_node(self, node):
self.body.append('</details>\n')
def depart_summary_node(self, node):
self.body.append('</summary>')
class DetailsDirective(Directive):
final_argument_whitespace = True
optional_arguments = 1
option_spec = {
'class': directives.class_option,
'summary-class': directives.class_option,
}
has_content = True
def run(self):
set_classes(self.options)
self.assert_has_content()
text = '\n'.join(self.content)
node = details(text, **self.options)
if self.arguments:
summary_node = summary(self.arguments[0], **self.options)
summary_node.source, summary_node.line = self.state_machine.get_source_and_line(self.lineno)
node += summary_node
self.state.nested_parse(self.content, self.content_offset, node)
return [node]
def setup(app):
app.add_node(details, html=(visit_details_node, depart_details_node))
app.add_node(summary, html=(visit_summary_node, depart_summary_node))
app.add_directive('details', DetailsDirective)
+333
View File
@@ -0,0 +1,333 @@
.. currentmodule:: discord
.. _faq:
Frequently Asked Questions
===========================
This is a list of Frequently Asked Questions regarding using ``discord.py`` and its extension modules. Feel free to suggest a
new question or submit one via pull requests.
.. contents:: Questions
:local:
Coroutines
------------
Questions regarding coroutines and asyncio belong here.
I get a SyntaxError around the word ``async``\! What should I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This ``SyntaxError`` happens because you're using a Python version lower than 3.5. Python 3.4 uses ``@asyncio.coroutine`` and
``yield from`` instead of ``async def`` and ``await``.
Thus you must do the following instead: ::
async def foo():
await bar()
# into
@asyncio.coroutine
def foo():
yield from bar()
Don't forget to ``import asyncio`` on the top of your files.
**It is heavily recommended that you update to Python 3.5 or higher as it simplifies asyncio massively.**
What is a coroutine?
~~~~~~~~~~~~~~~~~~~~~~
A coroutine is a function that must be invoked with ``await`` or ``yield from``. When Python encounters an ``await`` it stops
the function's execution at that point and works on other things until it comes back to that point and finishes off its work.
This allows for your program to be doing multiple things at the same time without using threads or complicated
multiprocessing.
**If you forget to await a coroutine then the coroutine will not run. Never forget to await a coroutine.**
Where can I use ``await``\?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can only use ``await`` inside ``async def`` functions and nowhere else.
What does "blocking" mean?
~~~~~~~~~~~~~~~~~~~~~~~~~~~
In asynchronous programming a blocking call is essentially all the parts of the function that are not ``await``. Do not
despair however, because not all forms of blocking are bad! Using blocking calls is inevitable, but you must work to make
sure that you don't excessively block functions. Remember, if you block for too long then your bot will freeze since it has
not stopped the function's execution at that point to do other things.
A common source of blocking for too long is something like ``time.sleep(n)``. Don't do that. Use ``asyncio.sleep(n)``
instead. Similar to this example: ::
# bad
time.sleep(10)
# good
await asyncio.sleep(10)
Another common source of blocking for too long is using HTTP requests with the famous module ``requests``. While ``requests``
is an amazing module for non-asynchronous programming, it is not a good choice for ``asyncio`` because certain requests can
block the event loop too long. Instead, use the ``aiohttp`` library which is installed on the side with this library.
Consider the following example: ::
# bad
r = requests.get('http://random.cat/meow')
if r.status_code == 200:
js = r.json()
await channel.send(js['file'])
# good
async with aiohttp.ClientSession() as session:
async with session.get('http://random.cat/meow') as r:
if r.status == 200:
js = await r.json()
await channel.send(js['file'])
General
---------
General questions regarding library usage belong here.
How do I set the "Playing" status?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is a method for this under :class:`Client` called :meth:`Client.change_presence`. The relevant aspect of this is its
``game`` keyword argument which takes in a :class:`Game` object. Putting both of these pieces of info together, you get the
following: ::
await client.change_presence(game=discord.Game(name='my game'))
How do I send a message to a specific channel?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You must fetch the channel directly and then call the appropriate method. Example: ::
channel = client.get_channel(12324234183172)
await channel.send('hello')
How do I upload an image?
~~~~~~~~~~~~~~~~~~~~~~~~~~
To upload something to Discord you have to use the :class:`File` object.
A :class:`File` accepts two parameters, the file-like object (or file path) and the filename
to pass to Discord when uploading.
If you want to upload an image it's as simple as: ::
await channel.send(file=discord.File('my_file.png'))
If you have a file-like object you can do as follows: ::
with open('my_file.png', 'rb') as fp:
await channel.send(file=discord.File(fp, 'new_filename.png'))
To upload multiple files, you can use the ``files`` keyword argument instead of ``file``\: ::
my_files = [
discord.File('result.zip'),
discord.File('teaser_graph.png'),
]
await channel.send(files=my_files)
If you want to upload something from a URL, you will have to use an HTTP request using ``aiohttp``
and then pass an ``io.BytesIO`` instance to :class:`File` like so:
.. code-block:: python3
import io
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(my_url) as resp:
if resp.status != 200:
return await channel.send('Could not download file...')
data = io.BytesIO(await resp.read())
await channel.send(file=discord.File(data, 'cool_image.png'))
How can I add a reaction to a message?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You use the :meth:`Message.add_reaction` method.
If you want to use unicode emoji, you must pass a valid unicode code point in a string. In your code, you can write this in a few different ways:
- ``'👍'``
- ``'\U0001F44D'``
- ``'\N{THUMBS UP SIGN}'``
Quick example: ::
await message.add_reaction('\N{THUMBS UP SIGN}')
In case you want to use emoji that come from a message, you already get their code points in the content without needing
to do anything special. You **cannot** send ``':thumbsup:'`` style shorthands.
For custom emoji, you should pass an instance of :class:`Emoji`. You can also pass a ``'name:id'`` string, but if you
can use said emoji, you should be able to use :meth:`Client.get_emoji` to get an emoji via ID or use :func:`utils.find`/
:func:`utils.get` on :attr:`Client.emojis` or :attr:`Guild.emojis` collections.
Quick example: ::
# if you have the ID already
emoji = client.get_emoji(310177266011340803)
await message.add_reaction(emoji)
# no ID, do a lookup
emoji = discord.utils.get(guild.emojis, name='LUL')
if emoji:
await message.add_reaction(emoji)
How do I pass a coroutine to the player's "after" function?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The library's music player launches on a separate thread, ergo it does not execute inside a coroutine.
This does not mean that it is not possible to call a coroutine in the ``after`` parameter. To do so you must pass a callable
that wraps up a couple of aspects.
The first gotcha that you must be aware of is that calling a coroutine is not a thread-safe operation. Since we are
technically in another thread, we must take caution in calling thread-safe operations so things do not bug out. Luckily for
us, ``asyncio`` comes with a ``asyncio.run_coroutine_threadsafe``
`function <https://docs.python.org/3.5/library/asyncio-task.html#asyncio.run_coroutine_threadsafe>`_ that allows us to call
a coroutine from another thread.
.. warning::
This function is only part of 3.5.1+ and 3.4.4+. If you are not using these Python versions then use
``discord.compat.run_coroutine_threadsafe``.
However, this function returns a ``concurrent.Future`` and to actually call it we have to fetch its result. Putting all of
this together we can do the following: ::
def my_after(error):
coro = some_channel.send('Song is done!')
fut = asyncio.run_coroutine_threadsafe(coro, client.loop)
try:
fut.result()
except:
# an error happened sending the message
pass
voice.play(discord.FFmpegPCMAudio(url), after=my_after)
How do I run something in the background?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`Check the background_task.py example. <https://github.com/Rapptz/discord.py/blob/rewrite/examples/background_task.py>`_
How do I get a specific model?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are multiple ways of doing this. If you have a specific model's ID then you can use
one of the following functions:
- :meth:`Client.get_channel`
- :meth:`Client.get_guild`
- :meth:`Client.get_user`
- :meth:`Client.get_emoji`
- :meth:`Guild.get_member`
- :meth:`Guild.get_channel`
The following use an HTTP request:
- :meth:`abc.Messageable.get_message`
- :meth:`Client.get_user_info`
If the functions above do not help you, then use of :func:`utils.find` or :func:`utils.get` would serve some use in finding
specific models.
Quick example: ::
# find a guild by name
guild = discord.utils.get(client.guilds, name='My Server')
# make sure to check if it's found
if guild is not None:
# find a channel by name
channel = discord.utils.get(guild.text_channels, name='cool-channel')
Commands Extension
-------------------
Questions regarding ``discord.ext.commands`` belong here.
Is there any documentation for this?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Not at the moment. Writing documentation for stuff takes time. A lot of people get by reading the docstrings in the source
code. Others get by via asking questions in the `Discord server <https://discord.gg/discord-api>`_. Others look at the
source code of `other existing bots <https://github.com/Rapptz/RoboDanny>`_.
There is a `basic example <https://github.com/Rapptz/discord.py/blob/rewrite/examples/basic_bot.py>`_ showcasing some
functionality.
**Documentation is being worked on, it will just take some time to polish it**.
Why does ``on_message`` make my commands stop working?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Overriding the default provided ``on_message`` forbids any extra commands from running. To fix this, add a
``bot.process_commands(message)`` line at the end of your ``on_message``. For example: ::
@bot.event
async def on_message(message):
# do some extra stuff here
await bot.process_commands(message)
Why do my arguments require quotes?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In a simple command defined as: ::
@bot.command()
async def echo(ctx, message: str):
await ctx.send(message)
Calling it via ``?echo a b c`` will only fetch the first argument and disregard the rest. To fix this you should either call
it via ``?echo "a b c"`` or change the signature to have "consume rest" behaviour. Example: ::
@bot.command()
async def echo(ctx, *, message: str):
await ctx.send(message)
This will allow you to use ``?echo a b c`` without needing the quotes.
How do I get the original ``message``\?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :class:`~ext.commands.Context` contains an attribute, :attr:`~.Context.message` to get the original
message.
Example: ::
@bot.command()
async def joined_at(ctx, member: discord.Member = None):
member = member or ctx.author
await ctx.send('{0} joined at {0.joined_at}'.format(member))
How do I make a subcommand?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use the ``group`` decorator. This will transform the callback into a ``Group`` which will allow you to add commands into
the group operating as "subcommands". These groups can be arbitrarily nested as well.
Example: ::
@bot.group()
async def git(ctx):
if ctx.invoked_subcommand is None:
await ctx.send('Invalid git command passed...')
@git.command()
async def push(ctx, remote: str, branch: str):
await ctx.send('Pushing to {} {}'.format(remote, branch))
This could then be used as ``?git push origin master``.
+59
View File
@@ -0,0 +1,59 @@
.. discord.py documentation master file, created by
sphinx-quickstart on Fri Aug 21 05:43:30 2015.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to discord.py
===========================
.. image:: /images/snake.png
discord.py is a modern, easy to use, feature-rich, and async ready API wrapper
for Discord.
**Features:**
- Modern Pythonic API using ``async``\/``await`` syntax
- Sane rate limit handling that prevents 429s
- Implements the entire Discord API
- Command extension to aid with bot creation
- Easy to use with an object oriented design
- Optimised for both speed and memory
Documentation Contents
-----------------------
.. toctree::
:maxdepth: 2
intro
quickstart
migrating
logging
api
Extensions
-----------
.. toctree::
:maxdepth: 3
ext/commands/index.rst
Additional Information
-----------------------
.. toctree::
:maxdepth: 2
discord
faq
whats_new
If you still can't find what you're looking for, try in one of the following pages:
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+112
View File
@@ -0,0 +1,112 @@
.. currentmodule:: discord
.. _intro:
Introduction
==============
This is the documentation for discord.py, a library for Python to aid
in creating applications that utilise the Discord API.
Prerequisites
---------------
discord.py works with Python 3.4.2 or higher. Support for earlier versions of Python
is not provided. Python 2.7 or lower is not supported. Python 3.3 is not supported
due to one of the dependencies (``aiohttp``) not supporting Python 3.3.
.. _installing:
Installing
-----------
You can get the library directly from PyPI: ::
python3 -m pip install -U discord.py
If you are using Windows, then the following should be used instead: ::
py -3 -m pip install -U discord.py
To get voice support, you should use ``discord.py[voice]`` instead of ``discord.py``, e.g. ::
python3 -m pip install -U discord.py[voice]
On Linux environments, installing voice requires getting the following dependencies:
- libffi
- libnacl
- python3-dev
For a debian-based system, the following command will help get those dependencies:
.. code-block:: shell
$ apt install libffi-dev libnacl-dev python3-dev
Remember to check your permissions!
Virtual Environments
~~~~~~~~~~~~~~~~~~~~~
Sometimes we don't want to pollute our system installs with a library or we want to maintain
different versions of a library than the currently system installed one. Or we don't have permissions to
install a library along side with the system installed ones. For this purpose, the standard library as
of 3.3 comes with a concept called "Virtual Environment" to help maintain these separate versions.
A more in-depth tutorial is found on `the official documentation. <https://docs.python.org/3/tutorial/venv.html>`_
However, for the quick and dirty:
1. Go to your project's working directory:
.. code-block:: shell
$ cd your-bot-source
$ python3 -m venv bot-env
2. Activate the virtual environment:
.. code-block:: shell
$ source bot-env/bin/activate
On Windows you activate it with:
.. code-block:: shell
$ bot-env\Scripts\activate.bat
3. Use pip like usual:
.. code-block:: shell
$ pip install -U discord.py
Congratulations. You now have a virtual environment all set up without messing with your system installation.
Basic Concepts
---------------
discord.py revolves around the concept of :ref:`events <discord-api-events>`.
An event is something you listen to and then respond to. For example, when a message
happens, you will receive an event about it and you can then respond to it.
A quick example to showcase how events work:
.. code-block:: python3
import discord
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as {0}!'.format(self.user))
async def on_message(self, message):
print('Message from {0.author}: {0.content}'.format(message))
client = MyClient()
client.run('my token goes here')
+46
View File
@@ -0,0 +1,46 @@
.. versionadded:: 0.6.0
.. _logging_setup:
Setting Up Logging
===================
*discord.py* logs errors and debug information via the `logging`_ python
module. It is strongly recommended that the logging module is
configured, as no errors or warnings will be output if it is not set up.
Configuration of the ``logging`` module can be as simple as::
import logging
logging.basicConfig(level=logging.INFO)
Placed at the start of the application. This will output the logs from
discord as well as other libraries that uses the ``logging`` module
directly to the console.
The optional ``level`` argument specifies what level of events to log
out and can any of ``CRITICAL``, ``ERROR``, ``WARNING``, ``INFO``, and
``DEBUG`` and if not specified defaults to ``WARNING``.
More advance setups are possible with the ``logging`` module. To for
example write the logs to a file called ``discord.log`` instead of
outputting them to to the console the following snippet can be used::
import discord
import logging
logger = logging.getLogger('discord')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
logger.addHandler(handler)
This is recommended, especially at verbose levels such as ``INFO``,
and ``DEBUG`` as there are a lot of events logged and it would clog the
stdout of your program.
For more information, check the documentation and tutorial of the
`logging`_ module.
.. _logging: https://docs.python.org/2/library/logging.html
+263
View File
@@ -0,0 +1,263 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. xml to make Docutils-native XML files
echo. pseudoxml to make pseudoxml-XML files for display purposes
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
echo. coverage to run coverage check of the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
REM Check if sphinx-build is available and fallback to Python version if any
%SPHINXBUILD% 2> nul
if errorlevel 9009 goto sphinx_python
goto sphinx_ok
:sphinx_python
set SPHINXBUILD=python -m sphinx.__init__
%SPHINXBUILD% 2> nul
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
:sphinx_ok
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\discord.py.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\discord.py.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf
cd %~dp0
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdfja" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf-ja
cd %~dp0
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "coverage" (
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
if errorlevel 1 exit /b 1
echo.
echo.Testing of coverage in the sources finished, look at the ^
results in %BUILDDIR%/coverage/python.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
:end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,322 @@
:orphan:
.. currentmodule:: discord
.. _migrating-to-async:
Migrating to v0.10.0
======================
v0.10.0 is one of the biggest breaking changes in the library due to massive
fundamental changes in how the library operates.
The biggest major change is that the library has dropped support to all versions prior to
Python 3.4.2. This was made to support ``asyncio``, in which more detail can be seen
:issue:`in the corresponding issue <50>`. To reiterate this, the implication is that
**python version 2.7 and 3.3 are no longer supported**.
Below are all the other major changes from v0.9.0 to v0.10.0.
Event Registration
--------------------
All events before were registered using :meth:`Client.event`. While this is still
possible, the events must be decorated with ``@asyncio.coroutine``.
Before:
.. code-block:: python3
@client.event
def on_message(message):
pass
After:
.. code-block:: python3
@client.event
@asyncio.coroutine
def on_message(message):
pass
Or in Python 3.5+:
.. code-block:: python3
@client.event
async def on_message(message):
pass
Because there is a lot of typing, a utility decorator (:meth:`Client.async_event`) is provided
for easier registration. For example:
.. code-block:: python3
@client.async_event
def on_message(message):
pass
Be aware however, that this is still a coroutine and your other functions that are coroutines must
be decorated with ``@asyncio.coroutine`` or be ``async def``.
Event Changes
--------------
Some events in v0.9.0 were considered pretty useless due to having no separate states. The main
events that were changed were the ``_update`` events since previously they had no context on what
was changed.
Before:
.. code-block:: python3
def on_channel_update(channel): pass
def on_member_update(member): pass
def on_status(member): pass
def on_server_role_update(role): pass
def on_voice_state_update(member): pass
def on_socket_raw_send(payload, is_binary): pass
After:
.. code-block:: python3
def on_channel_update(before, after): pass
def on_member_update(before, after): pass
def on_server_role_update(before, after): pass
def on_voice_state_update(before, after): pass
def on_socket_raw_send(payload): pass
Note that ``on_status`` was removed. If you want its functionality, use :func:`on_member_update`.
See :ref:`discord-api-events` for more information. Other removed events include ``on_socket_closed``, ``on_socket_receive``, and ``on_socket_opened``.
Coroutines
-----------
The biggest change that the library went through is that almost every function in :class:`Client`
was changed to be a `coroutine <https://docs.python.org/3/library/asyncio-task.html>`_. Functions
that are marked as a coroutine in the documentation must be awaited from or yielded from in order
for the computation to be done. For example...
Before:
.. code-block:: python3
client.send_message(message.channel, 'Hello')
After:
.. code-block:: python3
yield from client.send_message(message.channel, 'Hello')
# or in python 3.5+
await client.send_message(message.channel, 'Hello')
In order for you to ``yield from`` or ``await`` a coroutine then your function must be decorated
with ``@asyncio.coroutine`` or ``async def``.
Iterables
----------
For performance reasons, many of the internal data structures were changed into a dictionary to support faster
lookup. As a consequence, this meant that some lists that were exposed via the API have changed into iterables
and not sequences. In short, this means that certain attributes now only support iteration and not any of the
sequence functions.
The affected attributes are as follows:
- :attr:`Client.servers`
- :attr:`Client.private_channels`
- :attr:`Server.channels`
- :attr:`Server.members`
Some examples of previously valid behaviour that is now invalid
.. code-block:: python3
if client.servers[0].name == "test":
# do something
Since they are no longer ``list``\s, they no longer support indexing or any operation other than iterating.
In order to get the old behaviour you should explicitly cast it to a list.
.. code-block:: python3
servers = list(client.servers)
# work with servers
.. warning::
Due to internal changes of the structure, the order you receive the data in
is not in a guaranteed order.
Enumerations
------------
Due to dropping support for versions lower than Python 3.4.2, the library can now use
`enumerations <https://docs.python.org/3/library/enum.html>`_ in places where it makes sense.
The common places where this was changed was in the server region, member status, and channel type.
Before:
.. code-block:: python3
server.region == 'us-west'
member.status == 'online'
channel.type == 'text'
After:
.. code-block:: python3
server.region == discord.ServerRegion.us_west
member.status = discord.Status.online
channel.type == discord.ChannelType.text
The main reason for this change was to reduce the use of finicky strings in the API as this
could give users a false sense of power. More information can be found in the :ref:`discord-api-enums` page.
Properties
-----------
A lot of function calls that returned constant values were changed into Python properties for ease of use
in format strings.
The following functions were changed into properties:
+----------------------------------------+--------------------------------------+
| Before | After |
+----------------------------------------+--------------------------------------+
| ``User.avatar_url()`` | :attr:`User.avatar_url` |
+----------------------------------------+--------------------------------------+
| ``User.mention()`` | :attr:`User.mention` |
+----------------------------------------+--------------------------------------+
| ``Channel.mention()`` | :attr:`Channel.mention` |
+----------------------------------------+--------------------------------------+
| ``Channel.is_default_channel()`` | :attr:`Channel.is_default` |
+----------------------------------------+--------------------------------------+
| ``Role.is_everyone()`` | :attr:`Role.is_everyone` |
+----------------------------------------+--------------------------------------+
| ``Server.get_default_role()`` | :attr:`Server.default_role` |
+----------------------------------------+--------------------------------------+
| ``Server.icon_url()`` | :attr:`Server.icon_url` |
+----------------------------------------+--------------------------------------+
| ``Server.get_default_channel()`` | :attr:`Server.default_channel` |
+----------------------------------------+--------------------------------------+
| ``Message.get_raw_mentions()`` | :attr:`Message.raw_mentions` |
+----------------------------------------+--------------------------------------+
| ``Message.get_raw_channel_mentions()`` | :attr:`Message.raw_channel_mentions` |
+----------------------------------------+--------------------------------------+
Member Management
-------------------
Functions that involved banning and kicking were changed.
+--------------------------------+--------------------------+
| Before | After |
+--------------------------------+--------------------------+
| ``Client.ban(server, user)`` | ``Client.ban(member)`` |
+--------------------------------+--------------------------+
| ``Client.kick(server, user)`` | ``Client.kick(member)`` |
+--------------------------------+--------------------------+
.. migrating-renames:
Renamed Functions
-------------------
Functions have been renamed.
+------------------------------------+-------------------------------------------+
| Before | After |
+------------------------------------+-------------------------------------------+
| ``Client.set_channel_permissions`` | :meth:`Client.edit_channel_permissions` |
+------------------------------------+-------------------------------------------+
All the :class:`Permissions` related attributes have been renamed and the `can_` prefix has been
dropped. So for example, ``can_manage_messages`` has become ``manage_messages``.
Forced Keyword Arguments
-------------------------
Since 3.0+ of Python, we can now force questions to take in forced keyword arguments. A keyword argument is when you
explicitly specify the name of the variable and assign to it, for example: ``foo(name='test')``. Due to this support,
some functions in the library were changed to force things to take said keyword arguments. This is to reduce errors of
knowing the argument order and the issues that could arise from them.
The following parameters are now exclusively keyword arguments:
- :meth:`Client.send_message`
- ``tts``
- :meth:`Client.logs_from`
- ``before``
- ``after``
- :meth:`Client.edit_channel_permissions`
- ``allow``
- ``deny``
In the documentation you can tell if a function parameter is a forced keyword argument if it is after ``\*,``
in the function signature.
.. _migrating-running:
Running the Client
--------------------
In earlier versions of discord.py, ``client.run()`` was a blocking call to the main thread
that called it. In v0.10.0 it is still a blocking call but it handles the event loop for you.
However, in order to do that you must pass in your credentials to :meth:`Client.run`.
Basically, before:
.. code-block:: python3
client.login('token')
client.run()
After:
.. code-block:: python3
client.run('token')
.. warning::
Like in the older ``Client.run`` function, the newer one must be the one of
the last functions to call. This is because the function is **blocking**. Registering
events or doing anything after :meth:`Client.run` will not execute until the function
returns.
This is a utility function that abstracts the event loop for you. There's no need for
the run call to be blocking and out of your control. Indeed, if you want control of the
event loop then doing so is quite straightforward:
.. code-block:: python3
import discord
import asyncio
client = discord.Client()
@asyncio.coroutine
def main_task():
yield from client.login('token')
yield from client.connect()
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main_task())
except:
loop.run_until_complete(client.logout())
finally:
loop.close()
+76
View File
@@ -0,0 +1,76 @@
.. _quickstart:
.. currentmodule:: discord
Quickstart
============
This page gives a brief introduction to the library. It assumes you have the library installed,
if you don't check the :ref:`installing` portion.
A Minimal Bot
---------------
Let's make a bot that replies to a specific message and walk you through it.
It looks something like this:
.. code-block:: python3
import discord
client = discord.Client()
@client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
@client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('$hello'):
await message.channel.send('Hello!')
client.run('your token here')
Let's name this file ``example_bot.py``. Make sure not to name it ``discord.py`` as that'll conflict
with the library.
There's a lot going on here, so let's walk you through it step by step.
1. The first line just imports the library, if this raises a `ModuleNotFoundError` or `ImportError`
then head on over to :ref:`installing` section to properly install.
2. Next, we create an instance of a :class:`Client`. This client is our connection to Discord.
3. We then use the :meth:`Client.event` decorator to register an event. This library has many events.
Since this library is asynchronous, we do things in a "callback" style manner.
A callback is essentially a function that is called when something happens. In our case,
the :func:`on_ready` event is called when the bot has finished logging in and setting things
up and the :func:`on_message` event is called when the bot has received a message.
4. Since the :func:`on_message` event triggers for *every* message received, we have to make
sure that we ignore messages from ourselves. We do this by checking if the :attr:`Message.author`
is the same as the :attr:`Client.user`.
5. Afterwards, we check if the :class:`Message.content` starts with ``'$hello'``. If it is,
then we reply in the channel it was used in with ``'Hello!'``.
6. Finally, we run the bot with our login token. If you need help getting your token or creating a bot,
look in the :ref:`discord-intro` section.
Now that we've made a bot, we have to *run* the bot. Luckily, this is simple since this is just a
Python script, we can run it directly.
On Windows:
.. code-block:: shell
$ py -3 example_bot.py
On other systems:
.. code-block:: shell
$ python3 example_bot.py
Now you can try playing around with your basic bot.
+364
View File
@@ -0,0 +1,364 @@
.. currentmodule:: discord
.. _whats_new:
Changelog
============
This page keeps a detailed human friendly rendering of what's new and changed
in specific versions.
.. _vp0p16p6:
v0.16.6
--------
Bug Fixes
~~~~~~~~~~
- Fix issue with :meth:`Client.create_server` that made it stop working.
- Fix main thread being blocked upon calling ``StreamPlayer.stop``.
- Handle HEARTBEAT_ACK and resume gracefully when it occurs.
- Fix race condition when pre-emptively rate limiting that caused releasing an already released lock.
- Fix invalid state errors when immediately cancelling a coroutine.
.. _vp0p16p1:
v0.16.1
--------
This release is just a bug fix release with some better rate limit implementation.
Bug Fixes
~~~~~~~~~~~
- Servers are now properly chunked for user bots.
- The CDN URL is now used instead of the API URL for assets.
- Rate limit implementation now tries to use header information if possible.
- Event loop is now properly propagated (:issue:`420`)
- Allow falsey values in :meth:`Client.send_message` and :meth:`Client.send_file`.
.. _vp0p16p0:
v0.16.0
---------
New Features
~~~~~~~~~~~~~~
- Add :attr:`Channel.overwrites` to get all the permission overwrites of a channel.
- Add :attr:`Server.features` to get information about partnered servers.
Bug Fixes
~~~~~~~~~~
- Timeout when waiting for offline members while triggering :func:`on_ready`.
- The fact that we did not timeout caused a gigantic memory leak in the library that caused
thousands of duplicate :class:`Member` instances causing big memory spikes.
- Discard null sequences in the gateway.
- The fact these were not discarded meant that :func:`on_ready` kept being called instead of
:func:`on_resumed`. Since this has been corrected, in most cases :func:`on_ready` will be
called once or twice with :func:`on_resumed` being called much more often.
.. _vp0p15p1:
v0.15.1
---------
- Fix crash on duplicate or out of order reactions.
.. _vp0p15p0:
v0.15.0
--------
New Features
~~~~~~~~~~~~~~
- Rich Embeds for messages are now supported.
- To do so, create your own :class:`Embed` and pass the instance to the ``embed`` keyword argument to :meth:`Client.send_message` or :meth:`Client.edit_message`.
- Add :meth:`Client.clear_reactions` to remove all reactions from a message.
- Add support for MESSAGE_REACTION_REMOVE_ALL event, under :func:`on_reaction_clear`.
- Add :meth:`Permissions.update` and :meth:`PermissionOverwrite.update` for bulk permission updates.
- This allows you to use e.g. ``p.update(read_messages=True, send_messages=False)`` in a single line.
- Add :meth:`PermissionOverwrite.is_empty` to check if the overwrite is empty (i.e. has no overwrites set explicitly as true or false).
For the command extension, the following changed:
- ``Context`` is no longer slotted to facilitate setting dynamic attributes.
.. _vp0p14p3:
v0.14.3
---------
Bug Fixes
~~~~~~~~~~~
- Fix crash when dealing with MESSAGE_REACTION_REMOVE
- Fix incorrect buckets for reactions.
.. _v0p14p2:
v0.14.2
---------
New Features
~~~~~~~~~~~~~~
- :meth:`Client.wait_for_reaction` now returns a namedtuple with ``reaction`` and ``user`` attributes.
- This is for better support in the case that ``None`` is returned since tuple unpacking can lead to issues.
Bug Fixes
~~~~~~~~~~
- Fix bug that disallowed ``None`` to be passed for ``emoji`` parameter in :meth:`Client.wait_for_reaction`.
.. _v0p14p1:
v0.14.1
---------
Bug fixes
~~~~~~~~~~
- Fix bug with `Reaction` not being visible at import.
- This was also breaking the documentation.
.. _v0p14p0:
v0.14.0
--------
This update adds new API features and a couple of bug fixes.
New Features
~~~~~~~~~~~~~
- Add support for Manage Webhooks permission under :attr:`Permissions.manage_webhooks`
- Add support for ``around`` argument in 3.5+ :meth:`Client.logs_from`.
- Add support for reactions.
- :meth:`Client.add_reaction` to add a reactions
- :meth:`Client.remove_reaction` to remove a reaction.
- :meth:`Client.get_reaction_users` to get the users that reacted to a message.
- :attr:`Permissions.add_reactions` permission bit support.
- Two new events, :func:`on_reaction_add` and :func:`on_reaction_remove`.
- :attr:`Message.reactions` to get reactions from a message.
- :meth:`Client.wait_for_reaction` to wait for a reaction from a user.
Bug Fixes
~~~~~~~~~~
- Fix bug with Paginator still allowing lines that are too long.
- Fix the :attr:`Permissions.manage_emojis` bit being incorrect.
.. _v0p13p0:
v0.13.0
---------
This is a backwards compatible update with new features.
New Features
~~~~~~~~~~~~~
- Add the ability to manage emojis.
- :meth:`Client.create_custom_emoji` to create new emoji.
- :meth:`Client.edit_custom_emoji` to edit an old emoji.
- :meth:`Client.delete_custom_emoji` to delete a custom emoji.
- Add new :attr:`Permissions.manage_emojis` toggle.
- This applies for :class:`PermissionOverwrite` as well.
- Add new statuses for :class:`Status`.
- :attr:`Status.dnd` (aliased with :attr:`Status.do_not_disturb`\) for Do Not Disturb.
- :attr:`Status.invisible` for setting your status to invisible (please see the docs for a caveat).
- Deprecate :meth:`Client.change_status`
- Use :meth:`Client.change_presence` instead for better more up to date functionality.
- This method is subject for removal in a future API version.
- Add :meth:`Client.change_presence` for changing your status with the new Discord API change.
- This is the only method that allows changing your status to invisible or do not disturb.
Bug Fixes
~~~~~~~~~~
- Paginator pages do not exceed their max_size anymore (:issue:`340`)
- Do Not Disturb users no longer show up offline due to the new :class:`Status` changes.
.. _v0p12p0:
v0.12.0
---------
This is a bug fix update that also comes with new features.
New Features
~~~~~~~~~~~~~
- Add custom emoji support.
- Adds a new class to represent a custom Emoji named :class:`Emoji`
- Adds a utility generator function, :meth:`Client.get_all_emojis`.
- Adds a list of emojis on a server, :attr:`Server.emojis`.
- Adds a new event, :func:`on_server_emojis_update`.
- Add new server regions to :class:`ServerRegion`
- :attr:`ServerRegion.eu_central` and :attr:`ServerRegion.eu_west`.
- Add support for new pinned system message under :attr:`MessageType.pins_add`.
- Add order comparisons for :class:`Role` to allow it to be compared with regards to hierarchy.
- This means that you can now do ``role_a > role_b`` etc to check if ``role_b`` is lower in the hierarchy.
- Add :attr:`Server.role_hierarchy` to get the server's role hierarchy.
- Add :attr:`Member.server_permissions` to get a member's server permissions without their channel specific overwrites.
- Add :meth:`Client.get_user_info` to retrieve a user's info from their ID.
- Add a new ``Player`` property, ``Player.error`` to fetch the error that stopped the player.
- To help with this change, a player's ``after`` function can now take a single parameter denoting the current player.
- Add support for server verification levels.
- Adds a new enum called :class:`VerificationLevel`.
- This enum can be used in :meth:`Client.edit_server` under the ``verification_level`` keyword argument.
- Adds a new attribute in the server, :attr:`Server.verification_level`.
- Add :attr:`Server.voice_client` shortcut property for :meth:`Client.voice_client_in`.
- This is technically old (was added in v0.10.0) but was undocumented until v0.12.0.
For the command extension, the following are new:
- Add custom emoji converter.
- All default converters that can take IDs can now convert via ID.
- Add coroutine support for ``Bot.command_prefix``.
- Add a method to reset command cooldown.
Bug Fixes
~~~~~~~~~~
- Fix bug that caused the library to not work with the latest ``websockets`` library.
- Fix bug that leaked keep alive threads (:issue:`309`)
- Fix bug that disallowed :class:`ServerRegion` from being used in :meth:`Client.edit_server`.
- Fix bug in :meth:`Channel.permissions_for` that caused permission resolution to happen out of order.
- Fix bug in :attr:`Member.top_role` that did not account for same-position roles.
.. _v0p11p0:
v0.11.0
--------
This is a minor bug fix update that comes with a gateway update (v5 -> v6).
Breaking Changes
~~~~~~~~~~~~~~~~~
- ``Permissions.change_nicknames`` has been renamed to :attr:`Permissions.change_nickname` to match the UI.
New Features
~~~~~~~~~~~~~
- Add the ability to prune members via :meth:`Client.prune_members`.
- Switch the websocket gateway version to v6 from v5. This allows the library to work with group DMs and 1-on-1 calls.
- Add :attr:`AppInfo.owner` attribute.
- Add :class:`CallMessage` for group voice call messages.
- Add :class:`GroupCall` for group voice call information.
- Add :attr:`Message.system_content` to get the system message.
- Add the remaining VIP servers and the Brazil servers into :class:`ServerRegion` enum.
- Add ``stderr`` argument to :meth:`VoiceClient.create_ffmpeg_player` to redirect stderr.
- The library now handles implicit permission resolution in :meth:`Channel.permissions_for`.
- Add :attr:`Server.mfa_level` to query a server's 2FA requirement.
- Add :attr:`Permissions.external_emojis` permission.
- Add :attr:`Member.voice` attribute that refers to a :class:`VoiceState`.
- For backwards compatibility, the member object will have properties mirroring the old behaviour.
For the command extension, the following are new:
- Command cooldown system with the ``cooldown`` decorator.
- ``UserInputError`` exception for the hierarchy for user input related errors.
Bug Fixes
~~~~~~~~~~
- :attr:`Client.email` is now saved when using a token for user accounts.
- Fix issue when removing roles out of order.
- Fix bug where discriminators would not update.
- Handle cases where ``HEARTBEAT`` opcode is received. This caused bots to disconnect seemingly randomly.
For the command extension, the following bug fixes apply:
- ``Bot.check`` decorator is actually a decorator not requiring parentheses.
- ``Bot.remove_command`` and ``Group.remove_command`` no longer throw if the command doesn't exist.
- Command names are no longer forced to be ``lower()``.
- Fix a bug where Member and User converters failed to work in private message contexts.
- ``HelpFormatter`` now ignores hidden commands when deciding the maximum width.
.. _v0p10p0:
v0.10.0
-------
For breaking changes, see :ref:`migrating-to-async`. The breaking changes listed there will not be enumerated below. Since this version is rather a big departure from v0.9.2, this change log will be non-exhaustive.
New Features
~~~~~~~~~~~~~
- The library is now fully ``asyncio`` compatible, allowing you to write non-blocking code a lot more easily.
- The library now fully handles 429s and unconditionally retries on 502s.
- A new command extension module was added but is currently undocumented. Figuring it out is left as an exercise to the reader.
- Two new exception types, :exc:`Forbidden` and :exc:`NotFound` to denote permission errors or 404 errors.
- Added :meth:`Client.delete_invite` to revoke invites.
- Added support for sending voice. Check :class:`VoiceClient` for more details.
- Added :meth:`Client.wait_for_message` coroutine to aid with follow up commands.
- Added :data:`version_info` named tuple to check version info of the library.
- Login credentials are now cached to have a faster login experience. You can disable this by passing in ``cache_auth=False``
when constructing a :class:`Client`.
- New utility function, :func:`discord.utils.get` to simplify retrieval of items based on attributes.
- All data classes now support ``!=``, ``==``, ``hash(obj)`` and ``str(obj)``.
- Added :meth:`Client.get_bans` to get banned members from a server.
- Added :meth:`Client.invites_from` to get currently active invites in a server.
- Added :attr:`Server.me` attribute to get the :class:`Member` version of :attr:`Client.user`.
- Most data classes now support a ``hash(obj)`` function to allow you to use them in ``set`` or ``dict`` classes or subclasses.
- Add :meth:`Message.clean_content` to get a text version of the content with the user and channel mentioned changed into their names.
- Added a way to remove the messages of the user that just got banned in :meth:`Client.ban`.
- Added :meth:`Client.wait_until_ready` to facilitate easy creation of tasks that require the client cache to be ready.
- Added :meth:`Client.wait_until_login` to facilitate easy creation of tasks that require the client to be logged in.
- Add :class:`discord.Game` to represent any game with custom text to send to :meth:`Client.change_status`.
- Add :attr:`Message.nonce` attribute.
- Add :meth:`Member.permissions_in` as another way of doing :meth:`Channel.permissions_for`.
- Add :meth:`Client.move_member` to move a member to another voice channel.
- You can now create a server via :meth:`Client.create_server`.
- Added :meth:`Client.edit_server` to edit existing servers.
- Added :meth:`Client.server_voice_state` to server mute or server deafen a member.
- If you are being rate limited, the library will now handle it for you.
- Add :func:`on_member_ban` and :func:`on_member_unban` events that trigger when a member is banned/unbanned.
Performance Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~
- All data classes now use ``__slots__`` which greatly reduce the memory usage of things kept in cache.
- Due to the usage of ``asyncio``, the CPU usage of the library has gone down significantly.
- A lot of the internal cache lists were changed into dictionaries to change the ``O(n)`` lookup into ``O(1)``.
- Compressed READY is now on by default. This means if you're on a lot of servers (or maybe even a few) you would
receive performance improvements by having to download and process less data.
- While minor, change regex from ``\d+`` to ``[0-9]+`` to avoid unnecessary unicode character lookups.
Bug Fixes
~~~~~~~~~~
- Fix bug where guilds being updated did not edit the items in cache.
- Fix bug where ``member.roles`` were empty upon joining instead of having the ``@everyone`` role.
- Fix bug where :meth:`Role.is_everyone` was not being set properly when the role was being edited.
- :meth:`Client.logs_from` now handles cases where limit > 100 to sidestep the discord API limitation.
- Fix bug where a role being deleted would trigger a ``ValueError``.
- Fix bug where :meth:`Permissions.kick_members` and :meth:`Permissions.ban_members` were flipped.
- Mentions are now triggered normally. This was changed due to the way discord handles it internally.
- Fix issue when a :class:`Message` would attempt to upgrade a :attr:`Message.server` when the channel is
a :class:`Object`.
- Unavailable servers were not being added into cache, this has been corrected.