updated after a while because local stuff
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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">
|
||||
© 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 %}
|
||||
@@ -0,0 +1 @@
|
||||
<!-- purposefully empty -->
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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')
|
||||
@@ -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)
|
||||
|
||||
@@ -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``.
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user