1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-27 13:34:28 +00:00

Finally some good ****ing docs

This commit is contained in:
Steffo 2020-02-03 18:23:31 +01:00
parent aa74c8f859
commit 0d5ca685f3
37 changed files with 2417 additions and 260 deletions

View file

@ -10,17 +10,28 @@ A multipurpose bot framework and webserver
- [Telegram](https://core.telegram.org/bots)
- [Discord](https://discordapp.com/developers/docs/)
- [Matrix]() (no E2E support yet)
- [Matrix](https://matrix.org/) (no E2E support yet)
## Installing
To install `royalnet`, run:
```
pip install royalnet
```
To install a specific module, run:
```
pip install royalnet[MODULENAME]
```
To install all `royalnet` modules, run:
```
royalnet[telegram,discord,alchemy_easy,bard,constellation,sentry,herald,coloredlogs]
pip install royalnet[telegram,discord,matrix,alchemy_easy,bard,constellation,sentry,herald,coloredlogs]
```
> You will soon be able to install only the modules you need instead of the full package, but the feature isn't ready yet...
## Documentation
`royalnet`'s documentation is available [here](https://gh.steffo.eu/royalnet).
## Developing `royalnet`
@ -41,13 +52,5 @@ cd royalnet
And finally install all dependencies and the package:
```
poetry install -E telegram -E discord -E alchemy_easy -E bard -E constellation -E sentry -E herald -E coloredlogs
poetry install -E telegram -E discord -E matrix -E alchemy_easy -E bard -E constellation -E sentry -E herald -E coloredlogs
```
## Developing `royalnet` packages
See the [royalnet-pack-template](https://github.com/Steffo99/royalnet-pack-template) project.
## Documentation
`royalnet`'s documentation is available [here](https://gh.steffo.eu/royalnet).

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 444318501c4e921b64eae4e12bd3ce08
config: 59f01b51bca09082ff992b52b6e98cfc
tags: 645f666f9bcd5a90fca523b33c5a78b7

View file

@ -1,4 +1,4 @@
royalnet
Royalnet
====================================
Welcome to the documentation of Royalnet!
@ -6,6 +6,7 @@ Welcome to the documentation of Royalnet!
.. toctree::
:maxdepth: 5
packs/pack
randomdiscoveries
apireference

View file

@ -0,0 +1,350 @@
.. currentmodule:: royalnet.commands
Adding a Command to the Pack
====================================
A Royalnet Command is a small script that is run whenever a specific message is sent to a Royalnet interface.
A Command code looks like this: ::
import royalnet.commands as rc
class PingCommand(rc.Command):
name = "ping"
description = "Play ping-pong with the bot."
def __init__(self, interface):
# This code is run just once, while the bot is starting
super().__init__()
async def run(self, args: rc.CommandArgs, data: rc.CommandData):
# This code is run every time the command is called
await data.reply("Pong!")
Creating a new Command
------------------------------------
First, think of a ``name`` for your command.
It's the name your command will be called with: for example, the "spaghetti" command will be called by typing **/spaghetti** in chat.
Try to keep the name as short as possible, while staying specific enough so no other command will have the same name.
Next, create a new Python file with the ``name`` you have thought of.
The previously mentioned "spaghetti" command should have a file called ``spaghetti.py``.
Then, in the first row of the file, import the :class:`Command` class from royalnet, and create a new class inheriting from it: ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
...
Inside the class, override the attributes ``name`` and ``description`` with respectively the **name of the command** and a **small description of what the command will do**: ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
name = "spaghetti"
description = "Send a spaghetti emoji in the chat."
Now override the :meth:`Command.run` method, adding the code you want the bot to run when the command is called.
To send a message in the chat the command was called in, you can use the :meth:`CommandData.reply` coroutine: ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
name = "spaghetti"
description = "Send a spaghetti emoji in the chat."
async def run(self, args: rc.CommandArgs, data: rc.CommandData):
await data.reply("🍝")
And... it's done! The command is ready to be :doc:`added to your pack <pack>`!
Command arguments
------------------------------------
A command can have some arguments passed by the user: for example, on Telegram an user may type `/spaghetti carbonara al-dente`
to pass the :class:`str` `"carbonara al-dente"` to the command code.
These arguments can be accessed in multiple ways through the ``args`` parameter passed to the :meth:`Command.run`
method.
If you want your command to use arguments, override the ``syntax`` class attribute with a brief description of the
syntax of your command, possibly using {curly braces} for required arguments and [square brackets] for optional
ones. ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
name = "spaghetti"
description = "Send a spaghetti emoji in the chat."
syntax = "(requestedpasta)"
async def run(self, args: rc.CommandArgs, data: rc.CommandData):
await data.reply(f"🍝 Here's your {args[0]}!")
Direct access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can consider arguments as if they were separated by spaces.
You can then access command arguments directly by number as if the args object was a list of :class:`str`.
If you request an argument with a certain number, but the argument does not exist, an
:exc:`.InvalidInputError` is raised, making the arguments accessed in this way **required**. ::
args[0]
# "carbonara"
args[1]
# "al-dente"
args[2]
# raises InvalidInputError
Optional access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you don't want arguments to be required, you can access them through the :meth:`CommandArgs.optional` method: it
will return ``None`` if the argument wasn't passed, making it **optional**. ::
args.optional(0)
# "carbonara"
args.optional(1)
# "al-dente"
args.optional(2)
# None
You can specify a default result too, so that the method will return it instead of returning ``None``: ::
args.optional(2, default="banana")
# "banana"
Full string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want the full argument string, you can use the :meth:`CommandArgs.joined` method. ::
args.joined()
# "carbonara al-dente"
You can specify a minimum number of arguments too, so that an :exc:`.InvalidInputError` will be
raised if not enough arguments are present: ::
args.joined(require_at_least=3)
# raises InvalidInputError
Regular expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For more complex commands, you may want to get arguments through `regular expressions <https://regexr.com/>`_.
You can then use the :meth:`CommandArgs.match` method, which tries to match a pattern to the command argument string,
which returns a tuple of the matched groups and raises an :exc:`.InvalidInputError` if there is no match.
To match a pattern, :func:`re.match` is used, meaning that Python will try to match only at the beginning of the string. ::
args.match(r"(carb\w+)")
# ("carbonara",)
args.match(r"(al-\w+)")
# raises InvalidInputError
args.match(r"\s*(al-\w+)")
# ("al-dente",)
args.match(r"\s*(carb\w+)\s*(al-\w+)")
# ("carbonara", "al-dente")
Raising errors
---------------------------------------------
If you want to display an error message to the user, you can raise a :exc:`.CommandError` using the error message as argument: ::
if not kitchen.is_open():
raise CommandError("The kitchen is closed. Come back later!")
There are some subclasses of :exc:`.CommandError` that can be used for some more specific cases:
:exc:`.UserError`
The user did something wrong, it is not a problem with the bot.
:exc:`.InvalidInputError`
The arguments the user passed to the command by the user are invalid.
Displays the command syntax in the error message.
:exc:`.UnsupportedError`
The command is not supported on the interface it is being called.
:exc:`.ConfigurationError`
The ``config.toml`` file was misconfigured (a value is missing or invalid).
:exc:`.ExternalError`
An external API the command depends on is unavailable or returned an error.
:exc:`.ProgramError`
An error caused by a programming mistake. Equivalent to :exc:`AssertionError`, but includes a message to facilitate debugging.
Coroutines and slow operations
------------------------------------
You may have noticed that in the previous examples we used ``await data.reply("🍝")`` instead of just ``data.reply("🍝")``.
This is because :meth:`CommandData.reply` isn't a simple method: it is a coroutine, a special kind of function that
can be executed separately from the rest of the code, allowing the bot to do other things in the meantime.
By adding the ``await`` keyword before the ``data.reply("🍝")``, we tell the bot that it can do other things, like
receiving new messages, while the message is being sent.
You should avoid running slow normal functions inside bot commands, as they will stop the bot from working until they
are finished and may cause bugs in other parts of the code! ::
async def run(self, args, data):
# Don't do this!
image = download_1_terabyte_of_spaghetti("right_now", from="italy")
...
If the slow function you want does not cause any side effect, you can wrap it with the :func:`royalnet.utils.asyncify`
function: ::
async def run(self, args, data):
# If the called function has no side effect, you can do this!
image = await asyncify(download_1_terabyte_of_spaghetti, "right_now", from="italy")
...
Avoid using :func:`time.sleep` function, as it is considered a slow operation: use instead :func:`asyncio.sleep`,
a coroutine that does the same exact thing but in an asyncronous way.
Delete the invoking message
------------------------------------
The invoking message of a command is the message that the user sent that the bot recognized as a command; for example,
the message ``/spaghetti carbonara`` is the invoking message for the ``spaghetti`` command run.
You can have the bot delete the invoking message for a command by calling the :class:`CommandData.delete_invoking`
method: ::
async def run(self, args, data):
await data.delete_invoking()
Not all interfaces support deleting messages; by default, if the interface does not support deletions, the call is
ignored.
You can have the method raise an error if the message can't be deleted by setting the ``error_if_unavailable`` parameter
to True: ::
async def run(self, args, data):
try:
await data.delete_invoking(error_if_unavailable=True)
except royalnet.error.UnsupportedError:
await data.reply("🚫 The message could not be deleted.")
else:
await data.reply("✅ The message was deleted!")
Using the database
------------------------------------
Bots can be connected to a PostgreSQL database through a special SQLAlchemy interface called
:class:`royalnet.alchemy.Alchemy`.
If the connection is established, the ``self.alchemy`` and ``data.session`` fields will be
available for use in commands.
``self.alchemy`` is an instance of :class:`royalnet.alchemy.Alchemy`, which contains the
:class:`sqlalchemy.engine.Engine`, metadata and tables, while ``data.session`` is a
:class:`sqlalchemy.orm.session.Session`, and can be interacted in the same way as one.
Querying the database
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can :class:`sqlalchemy.orm.query.Query` the database using the SQLAlchemy ORM.
The SQLAlchemy tables can be found inside :class:`royalnet.alchemy.Alchemy` with the :meth:`royalnet.alchemy.Alchemy.get` method: ::
import royalnet.backpack.tables as rbt
User = self.alchemy.get(rbt.User)
Adding filters to the query
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can filter the query results with the :meth:`sqlalchemy.orm.query.Query.filter` method.
.. note:: Remember to always use a table column as first comparision element, as it won't work otherwise.
::
query = query.filter(User.role == "Member")
Ordering the results of a query
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can order the query results in **ascending order** with the :meth:`sqlalchemy.orm.query.Query.order_by` method. ::
query = query.order_by(User.username)
Additionally, you can append the `.desc()` method to a table column to sort in **descending order**: ::
query = query.order_by(User.username.desc())
Fetching the results of a query
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can fetch the query results with the :meth:`sqlalchemy.orm.query.Query.all`,
:meth:`sqlalchemy.orm.query.Query.first`, :meth:`sqlalchemy.orm.query.Query.one` and
:meth:`sqlalchemy.orm.query.Query.one_or_none` methods.
Remember to use :func:`royalnet.utils.asyncify` when fetching results, as it may take a while!
Use :meth:`sqlalchemy.orm.query.Query.all` if you want a :class:`list` of **all results**: ::
results: list = await asyncify(query.all)
Use :meth:`sqlalchemy.orm.query.Query.first` if you want **the first result** of the list, or ``None`` if
there are no results: ::
result: typing.Union[..., None] = await asyncify(query.first)
Use :meth:`sqlalchemy.orm.query.Query.one` if you expect to have **a single result**, and you want the command to
raise an error if any different number of results is returned: ::
result: ... = await asyncify(query.one) # Raises an error if there are no results or more than a result.
Use :meth:`sqlalchemy.orm.query.Query.one_or_none` if you expect to have **a single result**, or **nothing**, and
if you want the command to raise an error if the number of results is greater than one. ::
result: typing.Union[..., None] = await asyncify(query.one_or_none) # Raises an error if there is more than a result.
More Alchemy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can read more about :mod:`sqlalchemy` at their `website <https://www.sqlalchemy.org/>`_.
Calling Events
------------------------------------
This section is not documented yet.
Displaying Keyboards
------------------------------------
This section is not documented yet.
Running code at the initialization of the bot
---------------------------------------------
This section is not documented yet.
Running repeating jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This section is not documented yet.

View file

@ -0,0 +1,4 @@
Using Events
====================================
This section is not documented yet.

View file

@ -0,0 +1,12 @@
Creating a Royalnet Pack
====================================
This section is not documented yet.
.. toctree::
:maxdepth: 5
command
star
event
table

View file

@ -0,0 +1,4 @@
Adding a Star to the Pack
====================================
This section is not documented yet.

View file

@ -0,0 +1,4 @@
Using Tables and databases
====================================
This section is not documented yet.

View file

@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '5.3.2',
VERSION: '5.4a2',
LANGUAGE: 'None',
COLLAPSE_INDEX: false,
BUILDER: 'html',

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Reference &mdash; Royalnet 5.3.2 documentation</title>
<title>API Reference &mdash; Royalnet 5.4a2 documentation</title>
@ -60,7 +60,7 @@
<div class="version">
5.3.2
5.4a2
</div>
@ -85,6 +85,7 @@
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a></li>
<li class="toctree-l1"><a class="reference internal" href="randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">API Reference</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#module-royalnet.alchemy">Alchemy</a></li>
@ -166,7 +167,17 @@
<p>This page is autogenerated from the docstrings inside the code.</p>
<div class="section" id="module-royalnet.alchemy">
<span id="alchemy"></span><h2>Alchemy<a class="headerlink" href="#module-royalnet.alchemy" title="Permalink to this headline"></a></h2>
<p>Relational database classes and methods.</p>
<p>The subpackage providing all functions and classes related to databases and tables.</p>
<p>It requires either the <code class="docutils literal notranslate"><span class="pre">alchemy_easy</span></code> or the <code class="docutils literal notranslate"><span class="pre">alchemy_hard</span></code> extras to be installed.</p>
<p>You can install <code class="docutils literal notranslate"><span class="pre">alchemy_easy</span></code> with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span><span class="p">[</span><span class="n">alchemy_easy</span><span class="p">]</span>
</pre></div>
</div>
<p>To install <code class="docutils literal notranslate"><span class="pre">alchemy_hard</span></code>, refer to the <a href="#id5"><span class="problematic" id="id6">`psycopg2 &lt;https://pypi.org/project/psycopg2/&gt;}`_</span></a> installation instructions,
then run:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span><span class="p">[</span><span class="n">alchemy_hard</span><span class="p">]</span>
</pre></div>
</div>
<dl class="class">
<dt id="royalnet.alchemy.Alchemy">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.alchemy.</code><code class="sig-name descname">Alchemy</code><span class="sig-paren">(</span><em class="sig-param">database_uri: str</em>, <em class="sig-param">tables: Set</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.alchemy.Alchemy" title="Permalink to this definition"></a></dt>
@ -262,10 +273,15 @@ Check the tables submodule for more details.</p></li>
<div class="section" id="module-royalnet.backpack">
<span id="backpack"></span><h2>Backpack<a class="headerlink" href="#module-royalnet.backpack" title="Permalink to this headline"></a></h2>
<p>A Pack that is imported by default by all Royalnet instances.</p>
<p>Keep things here to a minimum!</p>
</div>
<div class="section" id="module-royalnet.bard">
<span id="bard"></span><h2>Bard<a class="headerlink" href="#module-royalnet.bard" title="Permalink to this headline"></a></h2>
<p>The subpackage providing all classes related to music files.</p>
<p>It requires the <code class="docutils literal notranslate"><span class="pre">bard</span></code> extra to be installed (the <code class="xref py py-mod docutils literal notranslate"><span class="pre">ffmpeg_python</span></code>, <code class="xref py py-mod docutils literal notranslate"><span class="pre">youtube_dl</span></code> and <code class="xref py py-mod docutils literal notranslate"><span class="pre">eyed3</span></code> packages).</p>
<p>You can install it with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span><span class="p">[</span><span class="n">bard</span><span class="p">]</span>
</pre></div>
</div>
<dl class="class">
<dt id="royalnet.bard.YtdlInfo">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">YtdlInfo</code><span class="sig-paren">(</span><em class="sig-param">info: Dict[str, Any]</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.bard.YtdlInfo" title="Permalink to this definition"></a></dt>
@ -367,92 +383,34 @@ Check the tables submodule for more details.</p></li>
</dd></dl>
<dl class="class">
<dt id="royalnet.bard.YtdlMp3">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">YtdlMp3</code><span class="sig-paren">(</span><em class="sig-param">ytdl_file: royalnet.bard.ytdlfile.YtdlFile</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.bard.YtdlMp3" title="Permalink to this definition"></a></dt>
<dd><p>A representation of a <a class="reference internal" href="#royalnet.bard.YtdlFile" title="royalnet.bard.YtdlFile"><code class="xref py py-class docutils literal notranslate"><span class="pre">YtdlFile</span></code></a> conversion to mp3.</p>
<dl class="method">
<dt id="royalnet.bard.YtdlMp3.convert_to_mp3">
<em class="property">async </em><code class="sig-name descname">convert_to_mp3</code><span class="sig-paren">(</span><span class="sig-paren">)</span> &#x2192; None<a class="headerlink" href="#royalnet.bard.YtdlMp3.convert_to_mp3" title="Permalink to this definition"></a></dt>
<dd><p>Convert the file to mp3 with <a class="reference external" href="https://kkroening.github.io/ffmpeg-python/index.html#module-ffmpeg" title="(in ffmpeg-python)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">ffmpeg</span></code></a>.</p>
<dl class="exception">
<dt id="royalnet.bard.BardError">
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">BardError</code><a class="headerlink" href="#royalnet.bard.BardError" title="Permalink to this definition"></a></dt>
<dd><p>Base class for <code class="xref py py-mod docutils literal notranslate"><span class="pre">bard</span></code> errors.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlMp3.delete_asap">
<em class="property">async </em><code class="sig-name descname">delete_asap</code><span class="sig-paren">(</span><span class="sig-paren">)</span> &#x2192; None<a class="headerlink" href="#royalnet.bard.YtdlMp3.delete_asap" title="Permalink to this definition"></a></dt>
<dd><p>Delete the mp3 file.</p>
<dl class="exception">
<dt id="royalnet.bard.YtdlError">
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">YtdlError</code><a class="headerlink" href="#royalnet.bard.YtdlError" title="Permalink to this definition"></a></dt>
<dd><p>Base class for errors caused by <code class="xref py py-mod docutils literal notranslate"><span class="pre">youtube_dl</span></code>.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlMp3.from_url">
<em class="property">classmethod </em><code class="sig-name descname">from_url</code><span class="sig-paren">(</span><em class="sig-param">url</em>, <em class="sig-param">**ytdl_args</em><span class="sig-paren">)</span> &#x2192; List[royalnet.bard.ytdlmp3.YtdlMp3]<a class="headerlink" href="#royalnet.bard.YtdlMp3.from_url" title="Permalink to this definition"></a></dt>
<dd><p>Create a <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#list" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">list</span></code></a> of <a class="reference internal" href="#royalnet.bard.YtdlMp3" title="royalnet.bard.YtdlMp3"><code class="xref py py-class docutils literal notranslate"><span class="pre">YtdlMp3</span></code></a> from a URL.</p>
<dl class="exception">
<dt id="royalnet.bard.NotFoundError">
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">NotFoundError</code><a class="headerlink" href="#royalnet.bard.NotFoundError" title="Permalink to this definition"></a></dt>
<dd><p>The requested resource wasnt found.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlMp3.info">
<em class="property">property </em><code class="sig-name descname">info</code><a class="headerlink" href="#royalnet.bard.YtdlMp3.info" title="Permalink to this definition"></a></dt>
<dd><p>Shortcut to get the <a class="reference internal" href="#royalnet.bard.YtdlInfo" title="royalnet.bard.YtdlInfo"><code class="xref py py-class docutils literal notranslate"><span class="pre">YtdlInfo</span></code></a> of the object.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlMp3.is_converted">
<em class="property">property </em><code class="sig-name descname">is_converted</code><a class="headerlink" href="#royalnet.bard.YtdlMp3.is_converted" title="Permalink to this definition"></a></dt>
<dd><p>Has the file been converted?</p>
</dd></dl>
</dd></dl>
<dl class="class">
<dt id="royalnet.bard.YtdlDiscord">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">YtdlDiscord</code><span class="sig-paren">(</span><em class="sig-param">ytdl_file: royalnet.bard.ytdlfile.YtdlFile</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.bard.YtdlDiscord" title="Permalink to this definition"></a></dt>
<dd><p>A representation of a <a class="reference internal" href="#royalnet.bard.YtdlFile" title="royalnet.bard.YtdlFile"><code class="xref py py-class docutils literal notranslate"><span class="pre">YtdlFile</span></code></a> conversion to the <code class="xref py py-mod docutils literal notranslate"><span class="pre">discord</span></code> PCM format.</p>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.convert_to_pcm">
<em class="property">async </em><code class="sig-name descname">convert_to_pcm</code><span class="sig-paren">(</span><span class="sig-paren">)</span> &#x2192; None<a class="headerlink" href="#royalnet.bard.YtdlDiscord.convert_to_pcm" title="Permalink to this definition"></a></dt>
<dd><p>Convert the file to pcm with <a class="reference external" href="https://kkroening.github.io/ffmpeg-python/index.html#module-ffmpeg" title="(in ffmpeg-python)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">ffmpeg</span></code></a>.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.delete_asap">
<em class="property">async </em><code class="sig-name descname">delete_asap</code><span class="sig-paren">(</span><span class="sig-paren">)</span> &#x2192; None<a class="headerlink" href="#royalnet.bard.YtdlDiscord.delete_asap" title="Permalink to this definition"></a></dt>
<dd><p>Delete the mp3 file.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.embed">
<code class="sig-name descname">embed</code><span class="sig-paren">(</span><span class="sig-paren">)</span> &#x2192; discord.embeds.Embed<a class="headerlink" href="#royalnet.bard.YtdlDiscord.embed" title="Permalink to this definition"></a></dt>
<dd><p>Return this info as a <a class="reference external" href="https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed" title="(in discord.py v1.3.1)"><code class="xref py py-class docutils literal notranslate"><span class="pre">discord.Embed</span></code></a>.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.from_url">
<em class="property">classmethod </em><code class="sig-name descname">from_url</code><span class="sig-paren">(</span><em class="sig-param">url</em>, <em class="sig-param">**ytdl_args</em><span class="sig-paren">)</span> &#x2192; List[royalnet.bard.ytdldiscord.YtdlDiscord]<a class="headerlink" href="#royalnet.bard.YtdlDiscord.from_url" title="Permalink to this definition"></a></dt>
<dd><p>Create a <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#list" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">list</span></code></a> of <a class="reference internal" href="#royalnet.bard.YtdlMp3" title="royalnet.bard.YtdlMp3"><code class="xref py py-class docutils literal notranslate"><span class="pre">YtdlMp3</span></code></a> from a URL.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.info">
<em class="property">property </em><code class="sig-name descname">info</code><a class="headerlink" href="#royalnet.bard.YtdlDiscord.info" title="Permalink to this definition"></a></dt>
<dd><p>Shortcut to get the <a class="reference internal" href="#royalnet.bard.YtdlInfo" title="royalnet.bard.YtdlInfo"><code class="xref py py-class docutils literal notranslate"><span class="pre">YtdlInfo</span></code></a> of the object.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.is_converted">
<em class="property">property </em><code class="sig-name descname">is_converted</code><a class="headerlink" href="#royalnet.bard.YtdlDiscord.is_converted" title="Permalink to this definition"></a></dt>
<dd><p>Has the file been converted?</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.bard.YtdlDiscord.spawn_audiosource">
<code class="sig-name descname">spawn_audiosource</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.bard.YtdlDiscord.spawn_audiosource" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="exception">
<dt id="royalnet.bard.MultipleFilesError">
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.bard.</code><code class="sig-name descname">MultipleFilesError</code><a class="headerlink" href="#royalnet.bard.MultipleFilesError" title="Permalink to this definition"></a></dt>
<dd><p>The resource contains multiple media files.</p>
</dd></dl>
</div>
<div class="section" id="module-royalnet.commands">
<span id="commands"></span><h2>Commands<a class="headerlink" href="#module-royalnet.commands" title="Permalink to this headline"></a></h2>
<p>The subpackage providing all classes related to Royalnet commands.</p>
<dl class="class">
<dt id="royalnet.commands.CommandInterface">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">CommandInterface</code><span class="sig-paren">(</span><em class="sig-param">config: Dict[str, Any]</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.CommandInterface" title="Permalink to this definition"></a></dt>
@ -515,6 +473,17 @@ Check the tables submodule for more details.</p></li>
<p>A reference to a <code class="xref py py-class docutils literal notranslate"><span class="pre">TelegramSerf</span></code>.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.commands.CommandInterface.table">
<em class="property">property </em><code class="sig-name descname">table</code><a class="headerlink" href="#royalnet.commands.CommandInterface.table" title="Permalink to this definition"></a></dt>
<dd><p>A shortcut for <code class="xref py py-func docutils literal notranslate"><span class="pre">serf.alchemy.get()</span></code>.</p>
<dl class="field-list simple">
<dt class="field-odd">Raises</dt>
<dd class="field-odd"><p><a class="reference internal" href="#royalnet.commands.UnsupportedError" title="royalnet.commands.UnsupportedError"><strong>UnsupportedError</strong></a> if <a class="reference internal" href="#royalnet.constellation.Constellation.alchemy" title="royalnet.constellation.Constellation.alchemy"><code class="xref py py-attr docutils literal notranslate"><span class="pre">alchemy</span></code></a> is <code class="xref py py-const docutils literal notranslate"><span class="pre">None</span></code>.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
<dl class="class">
@ -631,12 +600,14 @@ That probably means, the database row identifying the user.</p>
<dl class="method">
<dt id="royalnet.commands.CommandData.session_close">
<em class="property">async </em><code class="sig-name descname">session_close</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.CommandData.session_close" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dd><p>Asyncronously close the <code class="xref py py-attr docutils literal notranslate"><span class="pre">session</span></code> of this object.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.commands.CommandData.session_commit">
<em class="property">async </em><code class="sig-name descname">session_commit</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.CommandData.session_commit" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dd><p>Asyncronously commit the <code class="xref py py-attr docutils literal notranslate"><span class="pre">session</span></code> of this object.</p>
</dd></dl>
</dd></dl>
@ -850,8 +821,17 @@ down).</p>
</div>
<div class="section" id="module-royalnet.constellation">
<span id="constellation"></span><h2>Constellation<a class="headerlink" href="#module-royalnet.constellation" title="Permalink to this headline"></a></h2>
<p>The part of <code class="xref py py-mod docutils literal notranslate"><span class="pre">royalnet</span></code> that handles the webserver and webpages.</p>
<p>It uses many features of <code class="xref py py-mod docutils literal notranslate"><span class="pre">starlette</span></code>.</p>
<p>The subpackage providing all functions and classes that handle the webserver and the webpages.</p>
<p>It requires the <code class="docutils literal notranslate"><span class="pre">constellation</span></code> extra to be installed (<code class="xref py py-mod docutils literal notranslate"><span class="pre">starlette</span></code>).</p>
<p>You can install it with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span><span class="p">[</span><span class="n">constellation</span><span class="p">]</span>
</pre></div>
</div>
<p>It optionally uses the <code class="docutils literal notranslate"><span class="pre">sentry</span></code> extra for error reporting.</p>
<p>You can install them with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span><span class="p">[</span><span class="n">constellation</span><span class="p">,</span><span class="n">sentry</span><span class="p">]</span>
</pre></div>
</div>
<dl class="class">
<dt id="royalnet.constellation.Constellation">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.constellation.</code><code class="sig-name descname">Constellation</code><span class="sig-paren">(</span><em class="sig-param">alchemy_cfg: Dict[str, Any], herald_cfg: Dict[str, Any], packs_cfg: Dict[str, Any], constellation_cfg: Dict[str, Any], logging_cfg: Dict[str, Any]</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.constellation.Constellation" title="Permalink to this definition"></a></dt>
@ -1075,6 +1055,13 @@ or the <a class="reference external" href="https://docs.python.org/3.8/library/f
</div>
<div class="section" id="module-royalnet.herald">
<span id="herald"></span><h2>Herald<a class="headerlink" href="#module-royalnet.herald" title="Permalink to this headline"></a></h2>
<p>The subpackage providing all functions and classes to handle communication between process (even over the Internet).</p>
<p>It is based on <code class="xref py py-mod docutils literal notranslate"><span class="pre">websockets</span></code>.</p>
<p>It requires the <code class="docutils literal notranslate"><span class="pre">herald</span></code> extra to be installed.</p>
<p>You can install it with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span><span class="p">[</span><span class="n">herald</span><span class="p">]</span>
</pre></div>
</div>
<dl class="class">
<dt id="royalnet.herald.Config">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.herald.</code><code class="sig-name descname">Config</code><span class="sig-paren">(</span><em class="sig-param">name: str</em>, <em class="sig-param">address: str</em>, <em class="sig-param">port: int</em>, <em class="sig-param">secret: str</em>, <em class="sig-param">secure: bool = False</em>, <em class="sig-param">path: str = '/'</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.herald.Config" title="Permalink to this definition"></a></dt>
@ -1367,6 +1354,7 @@ Akin to the sequence number on IP packets.</p></li>
</div>
<div class="section" id="module-royalnet.serf">
<span id="serf"></span><h2>Serf<a class="headerlink" href="#module-royalnet.serf" title="Permalink to this headline"></a></h2>
<p>The subpackage providing all Serf implementations.</p>
<dl class="class">
<dt id="royalnet.serf.Serf">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.serf.</code><code class="sig-name descname">Serf</code><span class="sig-paren">(</span><em class="sig-param">loop: asyncio.events.AbstractEventLoop, alchemy_cfg: Dict[str, Any], herald_cfg: Dict[str, Any], packs_cfg: Dict[str, Any], **_</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.serf.Serf" title="Permalink to this definition"></a></dt>
@ -1691,53 +1679,6 @@ table and the identity table.</p>
</dd></dl>
<dl class="class">
<dt id="royalnet.utils.FileAudioSource">
<em class="property">class </em><code class="sig-prename descclassname">royalnet.utils.</code><code class="sig-name descname">FileAudioSource</code><span class="sig-paren">(</span><em class="sig-param">file</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.utils.FileAudioSource" title="Permalink to this definition"></a></dt>
<dd><p>A <a class="reference external" href="https://discordpy.readthedocs.io/en/latest/api.html#discord.AudioSource" title="(in discord.py v1.3.1)"><code class="xref py py-class docutils literal notranslate"><span class="pre">discord.AudioSource</span></code></a> that uses a <a class="reference external" href="https://docs.python.org/3.8/library/io.html#io.BufferedIOBase" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">io.BufferedIOBase</span></code></a> as an input instead of memory.</p>
<p>The stream should be in the usual PCM encoding.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This AudioSource will consume (and close) the passed stream.</p>
</div>
<dl class="method">
<dt id="royalnet.utils.FileAudioSource.__init__">
<code class="sig-name descname">__init__</code><span class="sig-paren">(</span><em class="sig-param">file</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.utils.FileAudioSource.__init__" title="Permalink to this definition"></a></dt>
<dd><p>Create a FileAudioSource.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>file</strong> the file to be played back.</p>
</dd>
</dl>
</dd></dl>
<dl class="method">
<dt id="royalnet.utils.FileAudioSource.is_opus">
<code class="sig-name descname">is_opus</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.utils.FileAudioSource.is_opus" title="Permalink to this definition"></a></dt>
<dd><p>This audio file isnt Opus-encoded, but PCM-encoded.</p>
<dl class="field-list simple">
<dt class="field-odd">Returns</dt>
<dd class="field-odd"><p><code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
</dd>
</dl>
</dd></dl>
<dl class="method">
<dt id="royalnet.utils.FileAudioSource.read">
<code class="sig-name descname">read</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.utils.FileAudioSource.read" title="Permalink to this definition"></a></dt>
<dd><p>Reads 20ms of audio.</p>
<p>If the stream has ended, then return an empty <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#bytes" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">bytes</span></code></a>-like object.</p>
</dd></dl>
<dl class="method">
<dt id="royalnet.utils.FileAudioSource.stop">
<code class="sig-name descname">stop</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.utils.FileAudioSource.stop" title="Permalink to this definition"></a></dt>
<dd><p>Stop the FileAudioSource. Once stopped, a FileAudioSource will immediatly stop reading more bytes from the
file.</p>
</dd></dl>
</dd></dl>
<dl class="function">
<dt id="royalnet.utils.init_sentry">
<code class="sig-prename descclassname">royalnet.utils.</code><code class="sig-name descname">init_sentry</code><span class="sig-paren">(</span><em class="sig-param">sentry_cfg: Dict[str, Any]</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.utils.init_sentry" title="Permalink to this definition"></a></dt>

View file

@ -9,7 +9,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index &mdash; Royalnet 5.3.2 documentation</title>
<title>Index &mdash; Royalnet 5.4a2 documentation</title>
@ -60,7 +60,7 @@
<div class="version">
5.3.2
5.4a2
</div>
@ -85,6 +85,7 @@
<ul>
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a></li>
<li class="toctree-l1"><a class="reference internal" href="randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="apireference.html">API Reference</a></li>
</ul>
@ -192,8 +193,6 @@
<li><a href="apireference.html#royalnet.commands.Event.__init__">(royalnet.commands.Event method)</a>
</li>
<li><a href="apireference.html#royalnet.herald.Package.__init__">(royalnet.herald.Package method)</a>
</li>
<li><a href="apireference.html#royalnet.utils.FileAudioSource.__init__">(royalnet.utils.FileAudioSource method)</a>
</li>
</ul></li>
</ul></td>
@ -240,10 +239,12 @@
<h2 id="B">B</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.herald.Broadcast">Broadcast (class in royalnet.herald)</a>
<li><a href="apireference.html#royalnet.bard.BardError">BardError</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.herald.Broadcast">Broadcast (class in royalnet.herald)</a>
</li>
<li><a href="apireference.html#royalnet.herald.Link.broadcast">broadcast() (royalnet.herald.Link method)</a>
</li>
</ul></td>
@ -272,6 +273,8 @@
</li>
<li><a href="apireference.html#royalnet.commands.CommandInterface.config">config (royalnet.commands.CommandInterface attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.commands.Command.config">config() (royalnet.commands.Command property)</a>
<ul>
@ -280,8 +283,6 @@
<li><a href="apireference.html#royalnet.constellation.Star.config">(royalnet.constellation.Star property)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.commands.ConfigurationError">ConfigurationError</a>
</li>
<li><a href="apireference.html#royalnet.herald.Link.connect">connect() (royalnet.herald.Link method)</a>
@ -293,10 +294,6 @@
<li><a href="apireference.html#royalnet.commands.CommandInterface.constellation">constellation (royalnet.commands.CommandInterface attribute)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.Star.constellation">constellation() (royalnet.constellation.Star property)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlMp3.convert_to_mp3">convert_to_mp3() (royalnet.bard.YtdlMp3 method)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.convert_to_pcm">convert_to_pcm() (royalnet.bard.YtdlDiscord method)</a>
</li>
<li><a href="apireference.html#royalnet.herald.Config.copy">copy() (royalnet.herald.Config method)</a>
</li>
@ -308,14 +305,8 @@
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.bard.YtdlFile.default_ytdl_args">default_ytdl_args (royalnet.bard.YtdlFile attribute)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.delete_asap">delete_asap() (royalnet.bard.YtdlDiscord method)</a>
<ul>
<li><a href="apireference.html#royalnet.bard.YtdlFile.delete_asap">(royalnet.bard.YtdlFile method)</a>
<li><a href="apireference.html#royalnet.bard.YtdlFile.delete_asap">delete_asap() (royalnet.bard.YtdlFile method)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlMp3.delete_asap">(royalnet.bard.YtdlMp3 method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.commands.CommandData.delete_invoking">delete_invoking() (royalnet.commands.CommandData method)</a>
@ -330,8 +321,6 @@
<h2 id="E">E</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.embed">embed() (royalnet.bard.YtdlDiscord method)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.ExceptionStar.error">error (royalnet.constellation.ExceptionStar attribute)</a>
</li>
<li><a href="apireference.html#royalnet.commands.Event">Event (class in royalnet.commands)</a>
@ -356,8 +345,6 @@
<h2 id="F">F</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.utils.FileAudioSource">FileAudioSource (class in royalnet.utils)</a>
</li>
<li><a href="apireference.html#royalnet.herald.Server.find_client">find_client() (royalnet.herald.Server method)</a>
</li>
<li><a href="apireference.html#royalnet.herald.Server.find_destination">find_destination() (royalnet.herald.Server method)</a>
@ -380,14 +367,10 @@
</li>
<li><a href="apireference.html#royalnet.herald.Package.from_json_string">from_json_string() (royalnet.herald.Package static method)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.from_url">from_url() (royalnet.bard.YtdlDiscord class method)</a>
<li><a href="apireference.html#royalnet.bard.YtdlFile.from_url">from_url() (royalnet.bard.YtdlFile class method)</a>
<ul>
<li><a href="apireference.html#royalnet.bard.YtdlFile.from_url">(royalnet.bard.YtdlFile class method)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlInfo.from_url">(royalnet.bard.YtdlInfo class method)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlMp3.from_url">(royalnet.bard.YtdlMp3 class method)</a>
</li>
</ul></li>
<li><a href="apireference.html#royalnet.utils.from_urluuid">from_urluuid() (in module royalnet.utils)</a>
@ -440,12 +423,6 @@
</li>
<li><a href="apireference.html#royalnet.serf.Serf.identity_table">identity_table (royalnet.serf.Serf attribute)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.info">info() (royalnet.bard.YtdlDiscord property)</a>
<ul>
<li><a href="apireference.html#royalnet.bard.YtdlMp3.info">(royalnet.bard.YtdlMp3 property)</a>
</li>
</ul></li>
<li><a href="apireference.html#royalnet.serf.Serf.init_alchemy">init_alchemy() (royalnet.serf.Serf method)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.Constellation.init_herald">init_herald() (royalnet.constellation.Constellation method)</a>
@ -457,11 +434,11 @@
<li><a href="apireference.html#royalnet.utils.init_logging">init_logging() (in module royalnet.utils)</a>
</li>
<li><a href="apireference.html#royalnet.utils.init_sentry">init_sentry() (in module royalnet.utils)</a>
</li>
<li><a href="apireference.html#royalnet.commands.Event.interface">interface (royalnet.commands.Event attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.commands.Event.interface">interface (royalnet.commands.Event attribute)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.Constellation.Interface">Interface (royalnet.constellation.Constellation attribute)</a>
<ul>
@ -480,15 +457,7 @@
</li>
<li><a href="apireference.html#royalnet.herald.InvalidServerResponseError">InvalidServerResponseError</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.is_converted">is_converted() (royalnet.bard.YtdlDiscord property)</a>
<ul>
<li><a href="apireference.html#royalnet.bard.YtdlMp3.is_converted">(royalnet.bard.YtdlMp3 property)</a>
</li>
</ul></li>
<li><a href="apireference.html#royalnet.bard.YtdlFile.is_downloaded">is_downloaded() (royalnet.bard.YtdlFile property)</a>
</li>
<li><a href="apireference.html#royalnet.utils.FileAudioSource.is_opus">is_opus() (royalnet.utils.FileAudioSource method)</a>
</li>
</ul></td>
</tr></table>
@ -553,6 +522,8 @@
<li><a href="apireference.html#royalnet.constellation.PageStar.methods">methods (royalnet.constellation.PageStar attribute)</a>
</li>
<li><a href="apireference.html#royalnet.utils.MultiLock">MultiLock (class in royalnet.utils)</a>
</li>
<li><a href="apireference.html#royalnet.bard.MultipleFilesError">MultipleFilesError</a>
</li>
</ul></td>
</tr></table>
@ -568,15 +539,17 @@
<li><a href="apireference.html#royalnet.commands.Event.name">(royalnet.commands.Event attribute)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.constellation.Constellation.network_handler">network_handler() (royalnet.constellation.Constellation method)</a>
<ul>
<li><a href="apireference.html#royalnet.serf.Serf.network_handler">(royalnet.serf.Serf method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.utils.MultiLock.normal">normal() (royalnet.utils.MultiLock method)</a>
</li>
<li><a href="apireference.html#royalnet.bard.NotFoundError">NotFoundError</a>
</li>
<li><a href="apireference.html#royalnet.utils.numberemojiformat">numberemojiformat() (in module royalnet.utils)</a>
</li>
@ -626,8 +599,6 @@
<h2 id="R">R</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.utils.FileAudioSource.read">read() (royalnet.utils.FileAudioSource method)</a>
</li>
<li><a href="apireference.html#royalnet.herald.Link.receive">receive() (royalnet.herald.Link method)</a>
</li>
<li><a href="apireference.html#royalnet.serf.Serf.register_commands">register_commands() (royalnet.serf.Serf method)</a>
@ -735,11 +706,11 @@
<li><a href="apireference.html#royalnet.herald.ServerError">ServerError</a>
</li>
<li><a href="apireference.html#royalnet.commands.CommandData.session">session() (royalnet.commands.CommandData property)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.Star.Session">Session() (royalnet.constellation.Star property)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.constellation.Star.Session">Session() (royalnet.constellation.Star property)</a>
</li>
<li><a href="apireference.html#royalnet.alchemy.Alchemy.session_acm">session_acm() (royalnet.alchemy.Alchemy method)</a>
<ul>
@ -757,14 +728,10 @@
<li><a href="apireference.html#royalnet.constellation.shoot">shoot() (in module royalnet.constellation)</a>
</li>
<li><a href="apireference.html#royalnet.utils.sleep_until">sleep_until() (in module royalnet.utils)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord.spawn_audiosource">spawn_audiosource() (royalnet.bard.YtdlDiscord method)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.Star">Star (class in royalnet.constellation)</a>
</li>
<li><a href="apireference.html#royalnet.constellation.Constellation.starlette">starlette (royalnet.constellation.Constellation attribute)</a>
</li>
<li><a href="apireference.html#royalnet.utils.FileAudioSource.stop">stop() (royalnet.utils.FileAudioSource method)</a>
</li>
<li><a href="apireference.html#royalnet.commands.Command.syntax">syntax (royalnet.commands.Command attribute)</a>
</li>
@ -774,6 +741,8 @@
<h2 id="T">T</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.commands.CommandInterface.table">table() (royalnet.commands.CommandInterface property)</a>
</li>
<li><a href="apireference.html#royalnet.alchemy.table_dfs">table_dfs() (in module royalnet.alchemy)</a>
</li>
<li><a href="apireference.html#royalnet.alchemy.TableNotFoundError">TableNotFoundError</a>
@ -820,15 +789,13 @@
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.utils.ytdldateformat">ytdldateformat() (in module royalnet.utils)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlDiscord">YtdlDiscord (class in royalnet.bard)</a>
<li><a href="apireference.html#royalnet.bard.YtdlError">YtdlError</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="apireference.html#royalnet.bard.YtdlFile">YtdlFile (class in royalnet.bard)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlInfo">YtdlInfo (class in royalnet.bard)</a>
</li>
<li><a href="apireference.html#royalnet.bard.YtdlMp3">YtdlMp3 (class in royalnet.bard)</a>
</li>
</ul></td>
</tr></table>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>royalnet &mdash; Royalnet 5.3.2 documentation</title>
<title>Royalnet &mdash; Royalnet 5.4a2 documentation</title>
@ -36,7 +36,7 @@
<link rel="stylesheet" href="_static/rygdocs.css" type="text/css" />
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Random discoveries" href="randomdiscoveries.html" />
<link rel="next" title="Creating a Royalnet Pack" href="packs/pack.html" />
</head>
<body class="wy-body-for-nav">
@ -60,7 +60,7 @@
<div class="version">
5.3.2
5.4a2
</div>
@ -85,6 +85,7 @@
<ul>
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a></li>
<li class="toctree-l1"><a class="reference internal" href="randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="apireference.html">API Reference</a></li>
</ul>
@ -132,7 +133,7 @@
<li><a href="#">Docs</a> &raquo;</li>
<li>royalnet</li>
<li>Royalnet</li>
<li class="wy-breadcrumbs-aside">
@ -152,10 +153,44 @@
<div itemprop="articleBody">
<div class="section" id="royalnet">
<h1>royalnet<a class="headerlink" href="#royalnet" title="Permalink to this headline"></a></h1>
<h1>Royalnet<a class="headerlink" href="#royalnet" title="Permalink to this headline"></a></h1>
<p>Welcome to the documentation of Royalnet!</p>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a><ul>
<li class="toctree-l2"><a class="reference internal" href="packs/command.html">Adding a Command to the Pack</a><ul>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#creating-a-new-command">Creating a new Command</a></li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#command-arguments">Command arguments</a><ul>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#direct-access">Direct access</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#optional-access">Optional access</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#full-string">Full string</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#regular-expressions">Regular expressions</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#raising-errors">Raising errors</a></li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#coroutines-and-slow-operations">Coroutines and slow operations</a></li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#delete-the-invoking-message">Delete the invoking message</a></li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#using-the-database">Using the database</a><ul>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#querying-the-database">Querying the database</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#adding-filters-to-the-query">Adding filters to the query</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#ordering-the-results-of-a-query">Ordering the results of a query</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#fetching-the-results-of-a-query">Fetching the results of a query</a></li>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#more-alchemy">More Alchemy</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#calling-events">Calling Events</a></li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#displaying-keyboards">Displaying Keyboards</a></li>
<li class="toctree-l3"><a class="reference internal" href="packs/command.html#running-code-at-the-initialization-of-the-bot">Running code at the initialization of the bot</a><ul>
<li class="toctree-l4"><a class="reference internal" href="packs/command.html#running-repeating-jobs">Running repeating jobs</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="packs/star.html">Adding a Star to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="packs/event.html">Using Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="packs/table.html">Using Tables and databases</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="randomdiscoveries.html">Random discoveries</a><ul>
<li class="toctree-l2"><a class="reference internal" href="randomdiscoveries.html#discord-websocket-undocumented-error-codes">Discord websocket undocumented error codes</a></li>
</ul>
@ -190,7 +225,7 @@
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="randomdiscoveries.html" class="btn btn-neutral float-right" title="Random discoveries" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="packs/pack.html" class="btn btn-neutral float-right" title="Creating a Royalnet Pack" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
</div>

Binary file not shown.

View file

@ -0,0 +1,555 @@
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adding a Command to the Pack &mdash; Royalnet 5.4a2 documentation</title>
<script type="text/javascript" src="../_static/js/modernizr.min.js"></script>
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/language_data.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/rygdocs.css" type="text/css" />
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Adding a Star to the Pack" href="star.html" />
<link rel="prev" title="Creating a Royalnet Pack" href="pack.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="../index.html" class="icon icon-home"> Royalnet
</a>
<div class="version">
5.4a2
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1 current"><a class="reference internal" href="pack.html">Creating a Royalnet Pack</a><ul class="current">
<li class="toctree-l2 current"><a class="current reference internal" href="#">Adding a Command to the Pack</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#creating-a-new-command">Creating a new Command</a></li>
<li class="toctree-l3"><a class="reference internal" href="#command-arguments">Command arguments</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#direct-access">Direct access</a></li>
<li class="toctree-l4"><a class="reference internal" href="#optional-access">Optional access</a></li>
<li class="toctree-l4"><a class="reference internal" href="#full-string">Full string</a></li>
<li class="toctree-l4"><a class="reference internal" href="#regular-expressions">Regular expressions</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#raising-errors">Raising errors</a></li>
<li class="toctree-l3"><a class="reference internal" href="#coroutines-and-slow-operations">Coroutines and slow operations</a></li>
<li class="toctree-l3"><a class="reference internal" href="#delete-the-invoking-message">Delete the invoking message</a></li>
<li class="toctree-l3"><a class="reference internal" href="#using-the-database">Using the database</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#querying-the-database">Querying the database</a></li>
<li class="toctree-l4"><a class="reference internal" href="#adding-filters-to-the-query">Adding filters to the query</a></li>
<li class="toctree-l4"><a class="reference internal" href="#ordering-the-results-of-a-query">Ordering the results of a query</a></li>
<li class="toctree-l4"><a class="reference internal" href="#fetching-the-results-of-a-query">Fetching the results of a query</a></li>
<li class="toctree-l4"><a class="reference internal" href="#more-alchemy">More Alchemy</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#calling-events">Calling Events</a></li>
<li class="toctree-l3"><a class="reference internal" href="#displaying-keyboards">Displaying Keyboards</a></li>
<li class="toctree-l3"><a class="reference internal" href="#running-code-at-the-initialization-of-the-bot">Running code at the initialization of the bot</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#running-repeating-jobs">Running repeating jobs</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="star.html">Adding a Star to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="event.html">Using Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="table.html">Using Tables and databases</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="../apireference.html">API Reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Royalnet</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> &raquo;</li>
<li><a href="pack.html">Creating a Royalnet Pack</a> &raquo;</li>
<li>Adding a Command to the Pack</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/packs/command.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="adding-a-command-to-the-pack">
<h1>Adding a Command to the Pack<a class="headerlink" href="#adding-a-command-to-the-pack" title="Permalink to this headline"></a></h1>
<p>A Royalnet Command is a small script that is run whenever a specific message is sent to a Royalnet interface.</p>
<p>A Command code looks like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.commands</span> <span class="k">as</span> <span class="nn">rc</span>
<span class="k">class</span> <span class="nc">PingCommand</span><span class="p">(</span><span class="n">rc</span><span class="o">.</span><span class="n">Command</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;ping&quot;</span>
<span class="n">description</span> <span class="o">=</span> <span class="s2">&quot;Play ping-pong with the bot.&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">interface</span><span class="p">):</span>
<span class="c1"># This code is run just once, while the bot is starting</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">:</span> <span class="n">rc</span><span class="o">.</span><span class="n">CommandArgs</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">rc</span><span class="o">.</span><span class="n">CommandData</span><span class="p">):</span>
<span class="c1"># This code is run every time the command is called</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">reply</span><span class="p">(</span><span class="s2">&quot;Pong!&quot;</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="creating-a-new-command">
<h2>Creating a new Command<a class="headerlink" href="#creating-a-new-command" title="Permalink to this headline"></a></h2>
<p>First, think of a <code class="docutils literal notranslate"><span class="pre">name</span></code> for your command.
Its the name your command will be called with: for example, the “spaghetti” command will be called by typing <strong>/spaghetti</strong> in chat.
Try to keep the name as short as possible, while staying specific enough so no other command will have the same name.</p>
<p>Next, create a new Python file with the <code class="docutils literal notranslate"><span class="pre">name</span></code> you have thought of.
The previously mentioned “spaghetti” command should have a file called <code class="docutils literal notranslate"><span class="pre">spaghetti.py</span></code>.</p>
<p>Then, in the first row of the file, import the <a class="reference internal" href="../apireference.html#royalnet.commands.Command" title="royalnet.commands.Command"><code class="xref py py-class docutils literal notranslate"><span class="pre">Command</span></code></a> class from royalnet, and create a new class inheriting from it:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.commands</span> <span class="k">as</span> <span class="nn">rc</span>
<span class="k">class</span> <span class="nc">SpaghettiCommand</span><span class="p">(</span><span class="n">rc</span><span class="o">.</span><span class="n">Command</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Inside the class, override the attributes <code class="docutils literal notranslate"><span class="pre">name</span></code> and <code class="docutils literal notranslate"><span class="pre">description</span></code> with respectively the <strong>name of the command</strong> and a <strong>small description of what the command will do</strong>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.commands</span> <span class="k">as</span> <span class="nn">rc</span>
<span class="k">class</span> <span class="nc">SpaghettiCommand</span><span class="p">(</span><span class="n">rc</span><span class="o">.</span><span class="n">Command</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;spaghetti&quot;</span>
<span class="n">description</span> <span class="o">=</span> <span class="s2">&quot;Send a spaghetti emoji in the chat.&quot;</span>
</pre></div>
</div>
<p>Now override the <a class="reference internal" href="../apireference.html#royalnet.commands.Command.run" title="royalnet.commands.Command.run"><code class="xref py py-meth docutils literal notranslate"><span class="pre">Command.run()</span></code></a> method, adding the code you want the bot to run when the command is called.</p>
<p>To send a message in the chat the command was called in, you can use the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandData.reply" title="royalnet.commands.CommandData.reply"><code class="xref py py-meth docutils literal notranslate"><span class="pre">CommandData.reply()</span></code></a> coroutine:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.commands</span> <span class="k">as</span> <span class="nn">rc</span>
<span class="k">class</span> <span class="nc">SpaghettiCommand</span><span class="p">(</span><span class="n">rc</span><span class="o">.</span><span class="n">Command</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;spaghetti&quot;</span>
<span class="n">description</span> <span class="o">=</span> <span class="s2">&quot;Send a spaghetti emoji in the chat.&quot;</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">:</span> <span class="n">rc</span><span class="o">.</span><span class="n">CommandArgs</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">rc</span><span class="o">.</span><span class="n">CommandData</span><span class="p">):</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">reply</span><span class="p">(</span><span class="s2">&quot;🍝&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>And… its done! The command is ready to be <a class="reference internal" href="pack.html"><span class="doc">added to your pack</span></a>!</p>
</div>
<div class="section" id="command-arguments">
<h2>Command arguments<a class="headerlink" href="#command-arguments" title="Permalink to this headline"></a></h2>
<p>A command can have some arguments passed by the user: for example, on Telegram an user may type <cite>/spaghetti carbonara al-dente</cite>
to pass the <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a> <cite>“carbonara al-dente”</cite> to the command code.</p>
<p>These arguments can be accessed in multiple ways through the <code class="docutils literal notranslate"><span class="pre">args</span></code> parameter passed to the <a class="reference internal" href="../apireference.html#royalnet.commands.Command.run" title="royalnet.commands.Command.run"><code class="xref py py-meth docutils literal notranslate"><span class="pre">Command.run()</span></code></a>
method.</p>
<p>If you want your command to use arguments, override the <code class="docutils literal notranslate"><span class="pre">syntax</span></code> class attribute with a brief description of the
syntax of your command, possibly using {curly braces} for required arguments and [square brackets] for optional
ones.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.commands</span> <span class="k">as</span> <span class="nn">rc</span>
<span class="k">class</span> <span class="nc">SpaghettiCommand</span><span class="p">(</span><span class="n">rc</span><span class="o">.</span><span class="n">Command</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;spaghetti&quot;</span>
<span class="n">description</span> <span class="o">=</span> <span class="s2">&quot;Send a spaghetti emoji in the chat.&quot;</span>
<span class="n">syntax</span> <span class="o">=</span> <span class="s2">&quot;(requestedpasta)&quot;</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">:</span> <span class="n">rc</span><span class="o">.</span><span class="n">CommandArgs</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">rc</span><span class="o">.</span><span class="n">CommandData</span><span class="p">):</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">reply</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;🍝 Here&#39;s your </span><span class="si">{args[0]}</span><span class="s2">!&quot;</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="direct-access">
<h3>Direct access<a class="headerlink" href="#direct-access" title="Permalink to this headline"></a></h3>
<p>You can consider arguments as if they were separated by spaces.</p>
<p>You can then access command arguments directly by number as if the args object was a list of <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a>.</p>
<p>If you request an argument with a certain number, but the argument does not exist, an
<a class="reference internal" href="../apireference.html#royalnet.commands.InvalidInputError" title="royalnet.commands.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">InvalidInputError</span></code></a> is raised, making the arguments accessed in this way <strong>required</strong>.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># &quot;carbonara&quot;</span>
<span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="c1"># &quot;al-dente&quot;</span>
<span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># raises InvalidInputError</span>
</pre></div>
</div>
</div>
<div class="section" id="optional-access">
<h3>Optional access<a class="headerlink" href="#optional-access" title="Permalink to this headline"></a></h3>
<p>If you dont want arguments to be required, you can access them through the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandArgs.optional" title="royalnet.commands.CommandArgs.optional"><code class="xref py py-meth docutils literal notranslate"><span class="pre">CommandArgs.optional()</span></code></a> method: it
will return <code class="docutils literal notranslate"><span class="pre">None</span></code> if the argument wasnt passed, making it <strong>optional</strong>.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">args</span><span class="o">.</span><span class="n">optional</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># &quot;carbonara&quot;</span>
<span class="n">args</span><span class="o">.</span><span class="n">optional</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="c1"># &quot;al-dente&quot;</span>
<span class="n">args</span><span class="o">.</span><span class="n">optional</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="c1"># None</span>
</pre></div>
</div>
<p>You can specify a default result too, so that the method will return it instead of returning <code class="docutils literal notranslate"><span class="pre">None</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">args</span><span class="o">.</span><span class="n">optional</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s2">&quot;banana&quot;</span><span class="p">)</span>
<span class="c1"># &quot;banana&quot;</span>
</pre></div>
</div>
</div>
<div class="section" id="full-string">
<h3>Full string<a class="headerlink" href="#full-string" title="Permalink to this headline"></a></h3>
<p>If you want the full argument string, you can use the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandArgs.joined" title="royalnet.commands.CommandArgs.joined"><code class="xref py py-meth docutils literal notranslate"><span class="pre">CommandArgs.joined()</span></code></a> method.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">args</span><span class="o">.</span><span class="n">joined</span><span class="p">()</span>
<span class="c1"># &quot;carbonara al-dente&quot;</span>
</pre></div>
</div>
<p>You can specify a minimum number of arguments too, so that an <a class="reference internal" href="../apireference.html#royalnet.commands.InvalidInputError" title="royalnet.commands.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">InvalidInputError</span></code></a> will be
raised if not enough arguments are present:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">args</span><span class="o">.</span><span class="n">joined</span><span class="p">(</span><span class="n">require_at_least</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="c1"># raises InvalidInputError</span>
</pre></div>
</div>
</div>
<div class="section" id="regular-expressions">
<h3>Regular expressions<a class="headerlink" href="#regular-expressions" title="Permalink to this headline"></a></h3>
<p>For more complex commands, you may want to get arguments through <a class="reference external" href="https://regexr.com/">regular expressions</a>.</p>
<p>You can then use the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandArgs.match" title="royalnet.commands.CommandArgs.match"><code class="xref py py-meth docutils literal notranslate"><span class="pre">CommandArgs.match()</span></code></a> method, which tries to match a pattern to the command argument string,
which returns a tuple of the matched groups and raises an <a class="reference internal" href="../apireference.html#royalnet.commands.InvalidInputError" title="royalnet.commands.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">InvalidInputError</span></code></a> if there is no match.</p>
<p>To match a pattern, <a class="reference external" href="https://docs.python.org/3.8/library/re.html#re.match" title="(in Python v3.8)"><code class="xref py py-func docutils literal notranslate"><span class="pre">re.match()</span></code></a> is used, meaning that Python will try to match only at the beginning of the string.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">args</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;(carb\w+)&quot;</span><span class="p">)</span>
<span class="c1"># (&quot;carbonara&quot;,)</span>
<span class="n">args</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;(al-\w+)&quot;</span><span class="p">)</span>
<span class="c1"># raises InvalidInputError</span>
<span class="n">args</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;\s*(al-\w+)&quot;</span><span class="p">)</span>
<span class="c1"># (&quot;al-dente&quot;,)</span>
<span class="n">args</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;\s*(carb\w+)\s*(al-\w+)&quot;</span><span class="p">)</span>
<span class="c1"># (&quot;carbonara&quot;, &quot;al-dente&quot;)</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="raising-errors">
<h2>Raising errors<a class="headerlink" href="#raising-errors" title="Permalink to this headline"></a></h2>
<p>If you want to display an error message to the user, you can raise a <a class="reference internal" href="../apireference.html#royalnet.commands.CommandError" title="royalnet.commands.CommandError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">CommandError</span></code></a> using the error message as argument:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="ow">not</span> <span class="n">kitchen</span><span class="o">.</span><span class="n">is_open</span><span class="p">():</span>
<span class="k">raise</span> <span class="n">CommandError</span><span class="p">(</span><span class="s2">&quot;The kitchen is closed. Come back later!&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>There are some subclasses of <a class="reference internal" href="../apireference.html#royalnet.commands.CommandError" title="royalnet.commands.CommandError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">CommandError</span></code></a> that can be used for some more specific cases:</p>
<dl class="simple">
<dt><a class="reference internal" href="../apireference.html#royalnet.commands.UserError" title="royalnet.commands.UserError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">UserError</span></code></a></dt><dd><p>The user did something wrong, it is not a problem with the bot.</p>
</dd>
<dt><a class="reference internal" href="../apireference.html#royalnet.commands.InvalidInputError" title="royalnet.commands.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">InvalidInputError</span></code></a></dt><dd><p>The arguments the user passed to the command by the user are invalid.
Displays the command syntax in the error message.</p>
</dd>
<dt><a class="reference internal" href="../apireference.html#royalnet.commands.UnsupportedError" title="royalnet.commands.UnsupportedError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">UnsupportedError</span></code></a></dt><dd><p>The command is not supported on the interface it is being called.</p>
</dd>
<dt><a class="reference internal" href="../apireference.html#royalnet.commands.ConfigurationError" title="royalnet.commands.ConfigurationError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ConfigurationError</span></code></a></dt><dd><p>The <code class="docutils literal notranslate"><span class="pre">config.toml</span></code> file was misconfigured (a value is missing or invalid).</p>
</dd>
<dt><a class="reference internal" href="../apireference.html#royalnet.commands.ExternalError" title="royalnet.commands.ExternalError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ExternalError</span></code></a></dt><dd><p>An external API the command depends on is unavailable or returned an error.</p>
</dd>
<dt><a class="reference internal" href="../apireference.html#royalnet.commands.ProgramError" title="royalnet.commands.ProgramError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ProgramError</span></code></a></dt><dd><p>An error caused by a programming mistake. Equivalent to <a class="reference external" href="https://docs.python.org/3.8/library/exceptions.html#AssertionError" title="(in Python v3.8)"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AssertionError</span></code></a>, but includes a message to facilitate debugging.</p>
</dd>
</dl>
</div>
<div class="section" id="coroutines-and-slow-operations">
<h2>Coroutines and slow operations<a class="headerlink" href="#coroutines-and-slow-operations" title="Permalink to this headline"></a></h2>
<p>You may have noticed that in the previous examples we used <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">data.reply(&quot;🍝&quot;)</span></code> instead of just <code class="docutils literal notranslate"><span class="pre">data.reply(&quot;🍝&quot;)</span></code>.</p>
<p>This is because <a class="reference internal" href="../apireference.html#royalnet.commands.CommandData.reply" title="royalnet.commands.CommandData.reply"><code class="xref py py-meth docutils literal notranslate"><span class="pre">CommandData.reply()</span></code></a> isnt a simple method: it is a coroutine, a special kind of function that
can be executed separately from the rest of the code, allowing the bot to do other things in the meantime.</p>
<p>By adding the <code class="docutils literal notranslate"><span class="pre">await</span></code> keyword before the <code class="docutils literal notranslate"><span class="pre">data.reply(&quot;🍝&quot;)</span></code>, we tell the bot that it can do other things, like
receiving new messages, while the message is being sent.</p>
<p>You should avoid running slow normal functions inside bot commands, as they will stop the bot from working until they
are finished and may cause bugs in other parts of the code!</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="c1"># Don&#39;t do this!</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">download_1_terabyte_of_spaghetti</span><span class="p">(</span><span class="s2">&quot;right_now&quot;</span><span class="p">,</span> <span class="n">from</span><span class="o">=</span><span class="s2">&quot;italy&quot;</span><span class="p">)</span>
<span class="o">...</span>
</pre></div>
</div>
<p>If the slow function you want does not cause any side effect, you can wrap it with the <a class="reference internal" href="../apireference.html#royalnet.utils.asyncify" title="royalnet.utils.asyncify"><code class="xref py py-func docutils literal notranslate"><span class="pre">royalnet.utils.asyncify()</span></code></a>
function:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="c1"># If the called function has no side effect, you can do this!</span>
<span class="n">image</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncify</span><span class="p">(</span><span class="n">download_1_terabyte_of_spaghetti</span><span class="p">,</span> <span class="s2">&quot;right_now&quot;</span><span class="p">,</span> <span class="n">from</span><span class="o">=</span><span class="s2">&quot;italy&quot;</span><span class="p">)</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Avoid using <a class="reference external" href="https://docs.python.org/3.8/library/time.html#time.sleep" title="(in Python v3.8)"><code class="xref py py-func docutils literal notranslate"><span class="pre">time.sleep()</span></code></a> function, as it is considered a slow operation: use instead <a class="reference external" href="https://docs.python.org/3.8/library/asyncio-task.html#asyncio.sleep" title="(in Python v3.8)"><code class="xref py py-func docutils literal notranslate"><span class="pre">asyncio.sleep()</span></code></a>,
a coroutine that does the same exact thing but in an asyncronous way.</p>
</div>
<div class="section" id="delete-the-invoking-message">
<h2>Delete the invoking message<a class="headerlink" href="#delete-the-invoking-message" title="Permalink to this headline"></a></h2>
<p>The invoking message of a command is the message that the user sent that the bot recognized as a command; for example,
the message <code class="docutils literal notranslate"><span class="pre">/spaghetti</span> <span class="pre">carbonara</span></code> is the invoking message for the <code class="docutils literal notranslate"><span class="pre">spaghetti</span></code> command run.</p>
<p>You can have the bot delete the invoking message for a command by calling the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandData.delete_invoking" title="royalnet.commands.CommandData.delete_invoking"><code class="xref py py-class docutils literal notranslate"><span class="pre">CommandData.delete_invoking</span></code></a>
method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">delete_invoking</span><span class="p">()</span>
</pre></div>
</div>
<p>Not all interfaces support deleting messages; by default, if the interface does not support deletions, the call is
ignored.</p>
<p>You can have the method raise an error if the message cant be deleted by setting the <code class="docutils literal notranslate"><span class="pre">error_if_unavailable</span></code> parameter
to True:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">delete_invoking</span><span class="p">(</span><span class="n">error_if_unavailable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">except</span> <span class="n">royalnet</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">UnsupportedError</span><span class="p">:</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">reply</span><span class="p">(</span><span class="s2">&quot;🚫 The message could not be deleted.&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">await</span> <span class="n">data</span><span class="o">.</span><span class="n">reply</span><span class="p">(</span><span class="s2">&quot;✅ The message was deleted!&quot;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="using-the-database">
<h2>Using the database<a class="headerlink" href="#using-the-database" title="Permalink to this headline"></a></h2>
<p>Bots can be connected to a PostgreSQL database through a special SQLAlchemy interface called
<a class="reference internal" href="../apireference.html#royalnet.alchemy.Alchemy" title="royalnet.alchemy.Alchemy"><code class="xref py py-class docutils literal notranslate"><span class="pre">royalnet.alchemy.Alchemy</span></code></a>.</p>
<p>If the connection is established, the <code class="docutils literal notranslate"><span class="pre">self.alchemy</span></code> and <code class="docutils literal notranslate"><span class="pre">data.session</span></code> fields will be
available for use in commands.</p>
<p><code class="docutils literal notranslate"><span class="pre">self.alchemy</span></code> is an instance of <a class="reference internal" href="../apireference.html#royalnet.alchemy.Alchemy" title="royalnet.alchemy.Alchemy"><code class="xref py py-class docutils literal notranslate"><span class="pre">royalnet.alchemy.Alchemy</span></code></a>, which contains the
<a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/connections.html#sqlalchemy.engine.Engine" title="(in SQLAlchemy v1.3)"><code class="xref py py-class docutils literal notranslate"><span class="pre">sqlalchemy.engine.Engine</span></code></a>, metadata and tables, while <code class="docutils literal notranslate"><span class="pre">data.session</span></code> is a
<a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session" title="(in SQLAlchemy v1.3)"><code class="xref py py-class docutils literal notranslate"><span class="pre">sqlalchemy.orm.session.Session</span></code></a>, and can be interacted in the same way as one.</p>
<div class="section" id="querying-the-database">
<h3>Querying the database<a class="headerlink" href="#querying-the-database" title="Permalink to this headline"></a></h3>
<p>You can <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query" title="(in SQLAlchemy v1.3)"><code class="xref py py-class docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query</span></code></a> the database using the SQLAlchemy ORM.</p>
<p>The SQLAlchemy tables can be found inside <a class="reference internal" href="../apireference.html#royalnet.alchemy.Alchemy" title="royalnet.alchemy.Alchemy"><code class="xref py py-class docutils literal notranslate"><span class="pre">royalnet.alchemy.Alchemy</span></code></a> with the <a class="reference internal" href="../apireference.html#royalnet.alchemy.Alchemy.get" title="royalnet.alchemy.Alchemy.get"><code class="xref py py-meth docutils literal notranslate"><span class="pre">royalnet.alchemy.Alchemy.get()</span></code></a> method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.backpack.tables</span> <span class="k">as</span> <span class="nn">rbt</span>
<span class="n">User</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">alchemy</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">rbt</span><span class="o">.</span><span class="n">User</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="adding-filters-to-the-query">
<h3>Adding filters to the query<a class="headerlink" href="#adding-filters-to-the-query" title="Permalink to this headline"></a></h3>
<p>You can filter the query results with the <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.filter" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.filter()</span></code></a> method.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Remember to always use a table column as first comparision element, as it wont work otherwise.</p>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">query</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">role</span> <span class="o">==</span> <span class="s2">&quot;Member&quot;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="ordering-the-results-of-a-query">
<h3>Ordering the results of a query<a class="headerlink" href="#ordering-the-results-of-a-query" title="Permalink to this headline"></a></h3>
<p>You can order the query results in <strong>ascending order</strong> with the <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.order_by" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.order_by()</span></code></a> method.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">query</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">username</span><span class="p">)</span>
</pre></div>
</div>
<p>Additionally, you can append the <cite>.desc()</cite> method to a table column to sort in <strong>descending order</strong>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">query</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">username</span><span class="o">.</span><span class="n">desc</span><span class="p">())</span>
</pre></div>
</div>
</div>
<div class="section" id="fetching-the-results-of-a-query">
<h3>Fetching the results of a query<a class="headerlink" href="#fetching-the-results-of-a-query" title="Permalink to this headline"></a></h3>
<p>You can fetch the query results with the <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.all" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.all()</span></code></a>,
<a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.first" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.first()</span></code></a>, <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.one" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.one()</span></code></a> and
<a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.one_or_none" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.one_or_none()</span></code></a> methods.</p>
<p>Remember to use <a class="reference internal" href="../apireference.html#royalnet.utils.asyncify" title="royalnet.utils.asyncify"><code class="xref py py-func docutils literal notranslate"><span class="pre">royalnet.utils.asyncify()</span></code></a> when fetching results, as it may take a while!</p>
<p>Use <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.all" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.all()</span></code></a> if you want a <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#list" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">list</span></code></a> of <strong>all results</strong>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="p">:</span> <span class="nb">list</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncify</span><span class="p">(</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">)</span>
</pre></div>
</div>
<p>Use <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.first" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.first()</span></code></a> if you want <strong>the first result</strong> of the list, or <code class="docutils literal notranslate"><span class="pre">None</span></code> if
there are no results:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">result</span><span class="p">:</span> <span class="n">typing</span><span class="o">.</span><span class="n">Union</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncify</span><span class="p">(</span><span class="n">query</span><span class="o">.</span><span class="n">first</span><span class="p">)</span>
</pre></div>
</div>
<p>Use <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.one" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.one()</span></code></a> if you expect to have <strong>a single result</strong>, and you want the command to
raise an error if any different number of results is returned:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">result</span><span class="p">:</span> <span class="o">...</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncify</span><span class="p">(</span><span class="n">query</span><span class="o">.</span><span class="n">one</span><span class="p">)</span> <span class="c1"># Raises an error if there are no results or more than a result.</span>
</pre></div>
</div>
<p>Use <a class="reference external" href="https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.one_or_none" title="(in SQLAlchemy v1.3)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sqlalchemy.orm.query.Query.one_or_none()</span></code></a> if you expect to have <strong>a single result</strong>, or <strong>nothing</strong>, and
if you want the command to raise an error if the number of results is greater than one.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">result</span><span class="p">:</span> <span class="n">typing</span><span class="o">.</span><span class="n">Union</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncify</span><span class="p">(</span><span class="n">query</span><span class="o">.</span><span class="n">one_or_none</span><span class="p">)</span> <span class="c1"># Raises an error if there is more than a result.</span>
</pre></div>
</div>
</div>
<div class="section" id="more-alchemy">
<h3>More Alchemy<a class="headerlink" href="#more-alchemy" title="Permalink to this headline"></a></h3>
<p>You can read more about <code class="xref py py-mod docutils literal notranslate"><span class="pre">sqlalchemy</span></code> at their <a class="reference external" href="https://www.sqlalchemy.org/">website</a>.</p>
</div>
</div>
<div class="section" id="calling-events">
<h2>Calling Events<a class="headerlink" href="#calling-events" title="Permalink to this headline"></a></h2>
<p>This section is not documented yet.</p>
</div>
<div class="section" id="displaying-keyboards">
<h2>Displaying Keyboards<a class="headerlink" href="#displaying-keyboards" title="Permalink to this headline"></a></h2>
<p>This section is not documented yet.</p>
</div>
<div class="section" id="running-code-at-the-initialization-of-the-bot">
<h2>Running code at the initialization of the bot<a class="headerlink" href="#running-code-at-the-initialization-of-the-bot" title="Permalink to this headline"></a></h2>
<p>This section is not documented yet.</p>
<div class="section" id="running-repeating-jobs">
<h3>Running repeating jobs<a class="headerlink" href="#running-repeating-jobs" title="Permalink to this headline"></a></h3>
<p>This section is not documented yet.</p>
</div>
</div>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="star.html" class="btn btn-neutral float-right" title="Adding a Star to the Pack" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="pack.html" class="btn btn-neutral float-left" title="Creating a Royalnet Pack" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2019, Stefano Pigozzi
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

218
docs/html/packs/event.html Normal file
View file

@ -0,0 +1,218 @@
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Using Events &mdash; Royalnet 5.4a2 documentation</title>
<script type="text/javascript" src="../_static/js/modernizr.min.js"></script>
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/language_data.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/rygdocs.css" type="text/css" />
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Using Tables and databases" href="table.html" />
<link rel="prev" title="Adding a Star to the Pack" href="star.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="../index.html" class="icon icon-home"> Royalnet
</a>
<div class="version">
5.4a2
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1 current"><a class="reference internal" href="pack.html">Creating a Royalnet Pack</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="command.html">Adding a Command to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="star.html">Adding a Star to the Pack</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">Using Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="table.html">Using Tables and databases</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="../apireference.html">API Reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Royalnet</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> &raquo;</li>
<li><a href="pack.html">Creating a Royalnet Pack</a> &raquo;</li>
<li>Using Events</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/packs/event.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="using-events">
<h1>Using Events<a class="headerlink" href="#using-events" title="Permalink to this headline"></a></h1>
<p>This section is not documented yet.</p>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="table.html" class="btn btn-neutral float-right" title="Using Tables and databases" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="star.html" class="btn btn-neutral float-left" title="Adding a Star to the Pack" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2019, Stefano Pigozzi
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

251
docs/html/packs/pack.html Normal file
View file

@ -0,0 +1,251 @@
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Creating a Royalnet Pack &mdash; Royalnet 5.4a2 documentation</title>
<script type="text/javascript" src="../_static/js/modernizr.min.js"></script>
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/language_data.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/rygdocs.css" type="text/css" />
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Adding a Command to the Pack" href="command.html" />
<link rel="prev" title="Royalnet" href="../index.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="../index.html" class="icon icon-home"> Royalnet
</a>
<div class="version">
5.4a2
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1 current"><a class="current reference internal" href="#">Creating a Royalnet Pack</a><ul>
<li class="toctree-l2"><a class="reference internal" href="command.html">Adding a Command to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="star.html">Adding a Star to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="event.html">Using Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="table.html">Using Tables and databases</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="../apireference.html">API Reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Royalnet</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> &raquo;</li>
<li>Creating a Royalnet Pack</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/packs/pack.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="creating-a-royalnet-pack">
<h1>Creating a Royalnet Pack<a class="headerlink" href="#creating-a-royalnet-pack" title="Permalink to this headline"></a></h1>
<p>This section is not documented yet.</p>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="command.html">Adding a Command to the Pack</a><ul>
<li class="toctree-l2"><a class="reference internal" href="command.html#creating-a-new-command">Creating a new Command</a></li>
<li class="toctree-l2"><a class="reference internal" href="command.html#command-arguments">Command arguments</a><ul>
<li class="toctree-l3"><a class="reference internal" href="command.html#direct-access">Direct access</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#optional-access">Optional access</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#full-string">Full string</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#regular-expressions">Regular expressions</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="command.html#raising-errors">Raising errors</a></li>
<li class="toctree-l2"><a class="reference internal" href="command.html#coroutines-and-slow-operations">Coroutines and slow operations</a></li>
<li class="toctree-l2"><a class="reference internal" href="command.html#delete-the-invoking-message">Delete the invoking message</a></li>
<li class="toctree-l2"><a class="reference internal" href="command.html#using-the-database">Using the database</a><ul>
<li class="toctree-l3"><a class="reference internal" href="command.html#querying-the-database">Querying the database</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#adding-filters-to-the-query">Adding filters to the query</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#ordering-the-results-of-a-query">Ordering the results of a query</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#fetching-the-results-of-a-query">Fetching the results of a query</a></li>
<li class="toctree-l3"><a class="reference internal" href="command.html#more-alchemy">More Alchemy</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="command.html#calling-events">Calling Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="command.html#displaying-keyboards">Displaying Keyboards</a></li>
<li class="toctree-l2"><a class="reference internal" href="command.html#running-code-at-the-initialization-of-the-bot">Running code at the initialization of the bot</a><ul>
<li class="toctree-l3"><a class="reference internal" href="command.html#running-repeating-jobs">Running repeating jobs</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="star.html">Adding a Star to the Pack</a></li>
<li class="toctree-l1"><a class="reference internal" href="event.html">Using Events</a></li>
<li class="toctree-l1"><a class="reference internal" href="table.html">Using Tables and databases</a></li>
</ul>
</div>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="command.html" class="btn btn-neutral float-right" title="Adding a Command to the Pack" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="../index.html" class="btn btn-neutral float-left" title="Royalnet" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2019, Stefano Pigozzi
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

218
docs/html/packs/star.html Normal file
View file

@ -0,0 +1,218 @@
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adding a Star to the Pack &mdash; Royalnet 5.4a2 documentation</title>
<script type="text/javascript" src="../_static/js/modernizr.min.js"></script>
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/language_data.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/rygdocs.css" type="text/css" />
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Using Events" href="event.html" />
<link rel="prev" title="Adding a Command to the Pack" href="command.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="../index.html" class="icon icon-home"> Royalnet
</a>
<div class="version">
5.4a2
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1 current"><a class="reference internal" href="pack.html">Creating a Royalnet Pack</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="command.html">Adding a Command to the Pack</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">Adding a Star to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="event.html">Using Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="table.html">Using Tables and databases</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="../apireference.html">API Reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Royalnet</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> &raquo;</li>
<li><a href="pack.html">Creating a Royalnet Pack</a> &raquo;</li>
<li>Adding a Star to the Pack</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/packs/star.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="adding-a-star-to-the-pack">
<h1>Adding a Star to the Pack<a class="headerlink" href="#adding-a-star-to-the-pack" title="Permalink to this headline"></a></h1>
<p>This section is not documented yet.</p>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="event.html" class="btn btn-neutral float-right" title="Using Events" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="command.html" class="btn btn-neutral float-left" title="Adding a Command to the Pack" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2019, Stefano Pigozzi
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

218
docs/html/packs/table.html Normal file
View file

@ -0,0 +1,218 @@
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Using Tables and databases &mdash; Royalnet 5.4a2 documentation</title>
<script type="text/javascript" src="../_static/js/modernizr.min.js"></script>
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/language_data.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/rygdocs.css" type="text/css" />
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Random discoveries" href="../randomdiscoveries.html" />
<link rel="prev" title="Using Events" href="event.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="../index.html" class="icon icon-home"> Royalnet
</a>
<div class="version">
5.4a2
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1 current"><a class="reference internal" href="pack.html">Creating a Royalnet Pack</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="command.html">Adding a Command to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="star.html">Adding a Star to the Pack</a></li>
<li class="toctree-l2"><a class="reference internal" href="event.html">Using Events</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">Using Tables and databases</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="../apireference.html">API Reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Royalnet</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> &raquo;</li>
<li><a href="pack.html">Creating a Royalnet Pack</a> &raquo;</li>
<li>Using Tables and databases</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/packs/table.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="using-tables-and-databases">
<h1>Using Tables and databases<a class="headerlink" href="#using-tables-and-databases" title="Permalink to this headline"></a></h1>
<p>This section is not documented yet.</p>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="../randomdiscoveries.html" class="btn btn-neutral float-right" title="Random discoveries" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="event.html" class="btn btn-neutral float-left" title="Using Events" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2019, Stefano Pigozzi
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Python Module Index &mdash; Royalnet 5.3.2 documentation</title>
<title>Python Module Index &mdash; Royalnet 5.4a2 documentation</title>
@ -62,7 +62,7 @@
<div class="version">
5.3.2
5.4a2
</div>
@ -87,6 +87,7 @@
<ul>
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a></li>
<li class="toctree-l1"><a class="reference internal" href="randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="apireference.html">API Reference</a></li>
</ul>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random discoveries &mdash; Royalnet 5.3.2 documentation</title>
<title>Random discoveries &mdash; Royalnet 5.4a2 documentation</title>
@ -37,7 +37,7 @@
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="API Reference" href="apireference.html" />
<link rel="prev" title="royalnet" href="index.html" />
<link rel="prev" title="Using Tables and databases" href="packs/table.html" />
</head>
<body class="wy-body-for-nav">
@ -61,7 +61,7 @@
<div class="version">
5.3.2
5.4a2
</div>
@ -86,6 +86,7 @@
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Random discoveries</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#discord-websocket-undocumented-error-codes">Discord websocket undocumented error codes</a></li>
</ul>
@ -196,7 +197,7 @@
<a href="apireference.html" class="btn btn-neutral float-right" title="API Reference" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="index.html" class="btn btn-neutral float-left" title="royalnet" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
<a href="packs/table.html" class="btn btn-neutral float-left" title="Using Tables and databases" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Search &mdash; Royalnet 5.3.2 documentation</title>
<title>Search &mdash; Royalnet 5.4a2 documentation</title>
@ -60,7 +60,7 @@
<div class="version">
5.3.2
5.4a2
</div>
@ -85,6 +85,7 @@
<ul>
<li class="toctree-l1"><a class="reference internal" href="packs/pack.html">Creating a Royalnet Pack</a></li>
<li class="toctree-l1"><a class="reference internal" href="randomdiscoveries.html">Random discoveries</a></li>
<li class="toctree-l1"><a class="reference internal" href="apireference.html">API Reference</a></li>
</ul>

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
royalnet
Royalnet
====================================
Welcome to the documentation of Royalnet!
@ -6,6 +6,7 @@ Welcome to the documentation of Royalnet!
.. toctree::
:maxdepth: 5
packs/pack
randomdiscoveries
apireference

View file

@ -0,0 +1,350 @@
.. currentmodule:: royalnet.commands
Adding a Command to the Pack
====================================
A Royalnet Command is a small script that is run whenever a specific message is sent to a Royalnet interface.
A Command code looks like this: ::
import royalnet.commands as rc
class PingCommand(rc.Command):
name = "ping"
description = "Play ping-pong with the bot."
def __init__(self, interface):
# This code is run just once, while the bot is starting
super().__init__()
async def run(self, args: rc.CommandArgs, data: rc.CommandData):
# This code is run every time the command is called
await data.reply("Pong!")
Creating a new Command
------------------------------------
First, think of a ``name`` for your command.
It's the name your command will be called with: for example, the "spaghetti" command will be called by typing **/spaghetti** in chat.
Try to keep the name as short as possible, while staying specific enough so no other command will have the same name.
Next, create a new Python file with the ``name`` you have thought of.
The previously mentioned "spaghetti" command should have a file called ``spaghetti.py``.
Then, in the first row of the file, import the :class:`Command` class from royalnet, and create a new class inheriting from it: ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
...
Inside the class, override the attributes ``name`` and ``description`` with respectively the **name of the command** and a **small description of what the command will do**: ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
name = "spaghetti"
description = "Send a spaghetti emoji in the chat."
Now override the :meth:`Command.run` method, adding the code you want the bot to run when the command is called.
To send a message in the chat the command was called in, you can use the :meth:`CommandData.reply` coroutine: ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
name = "spaghetti"
description = "Send a spaghetti emoji in the chat."
async def run(self, args: rc.CommandArgs, data: rc.CommandData):
await data.reply("🍝")
And... it's done! The command is ready to be :doc:`added to your pack <pack>`!
Command arguments
------------------------------------
A command can have some arguments passed by the user: for example, on Telegram an user may type `/spaghetti carbonara al-dente`
to pass the :class:`str` `"carbonara al-dente"` to the command code.
These arguments can be accessed in multiple ways through the ``args`` parameter passed to the :meth:`Command.run`
method.
If you want your command to use arguments, override the ``syntax`` class attribute with a brief description of the
syntax of your command, possibly using {curly braces} for required arguments and [square brackets] for optional
ones. ::
import royalnet.commands as rc
class SpaghettiCommand(rc.Command):
name = "spaghetti"
description = "Send a spaghetti emoji in the chat."
syntax = "(requestedpasta)"
async def run(self, args: rc.CommandArgs, data: rc.CommandData):
await data.reply(f"🍝 Here's your {args[0]}!")
Direct access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can consider arguments as if they were separated by spaces.
You can then access command arguments directly by number as if the args object was a list of :class:`str`.
If you request an argument with a certain number, but the argument does not exist, an
:exc:`.InvalidInputError` is raised, making the arguments accessed in this way **required**. ::
args[0]
# "carbonara"
args[1]
# "al-dente"
args[2]
# raises InvalidInputError
Optional access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you don't want arguments to be required, you can access them through the :meth:`CommandArgs.optional` method: it
will return ``None`` if the argument wasn't passed, making it **optional**. ::
args.optional(0)
# "carbonara"
args.optional(1)
# "al-dente"
args.optional(2)
# None
You can specify a default result too, so that the method will return it instead of returning ``None``: ::
args.optional(2, default="banana")
# "banana"
Full string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want the full argument string, you can use the :meth:`CommandArgs.joined` method. ::
args.joined()
# "carbonara al-dente"
You can specify a minimum number of arguments too, so that an :exc:`.InvalidInputError` will be
raised if not enough arguments are present: ::
args.joined(require_at_least=3)
# raises InvalidInputError
Regular expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For more complex commands, you may want to get arguments through `regular expressions <https://regexr.com/>`_.
You can then use the :meth:`CommandArgs.match` method, which tries to match a pattern to the command argument string,
which returns a tuple of the matched groups and raises an :exc:`.InvalidInputError` if there is no match.
To match a pattern, :func:`re.match` is used, meaning that Python will try to match only at the beginning of the string. ::
args.match(r"(carb\w+)")
# ("carbonara",)
args.match(r"(al-\w+)")
# raises InvalidInputError
args.match(r"\s*(al-\w+)")
# ("al-dente",)
args.match(r"\s*(carb\w+)\s*(al-\w+)")
# ("carbonara", "al-dente")
Raising errors
---------------------------------------------
If you want to display an error message to the user, you can raise a :exc:`.CommandError` using the error message as argument: ::
if not kitchen.is_open():
raise CommandError("The kitchen is closed. Come back later!")
There are some subclasses of :exc:`.CommandError` that can be used for some more specific cases:
:exc:`.UserError`
The user did something wrong, it is not a problem with the bot.
:exc:`.InvalidInputError`
The arguments the user passed to the command by the user are invalid.
Displays the command syntax in the error message.
:exc:`.UnsupportedError`
The command is not supported on the interface it is being called.
:exc:`.ConfigurationError`
The ``config.toml`` file was misconfigured (a value is missing or invalid).
:exc:`.ExternalError`
An external API the command depends on is unavailable or returned an error.
:exc:`.ProgramError`
An error caused by a programming mistake. Equivalent to :exc:`AssertionError`, but includes a message to facilitate debugging.
Coroutines and slow operations
------------------------------------
You may have noticed that in the previous examples we used ``await data.reply("🍝")`` instead of just ``data.reply("🍝")``.
This is because :meth:`CommandData.reply` isn't a simple method: it is a coroutine, a special kind of function that
can be executed separately from the rest of the code, allowing the bot to do other things in the meantime.
By adding the ``await`` keyword before the ``data.reply("🍝")``, we tell the bot that it can do other things, like
receiving new messages, while the message is being sent.
You should avoid running slow normal functions inside bot commands, as they will stop the bot from working until they
are finished and may cause bugs in other parts of the code! ::
async def run(self, args, data):
# Don't do this!
image = download_1_terabyte_of_spaghetti("right_now", from="italy")
...
If the slow function you want does not cause any side effect, you can wrap it with the :func:`royalnet.utils.asyncify`
function: ::
async def run(self, args, data):
# If the called function has no side effect, you can do this!
image = await asyncify(download_1_terabyte_of_spaghetti, "right_now", from="italy")
...
Avoid using :func:`time.sleep` function, as it is considered a slow operation: use instead :func:`asyncio.sleep`,
a coroutine that does the same exact thing but in an asyncronous way.
Delete the invoking message
------------------------------------
The invoking message of a command is the message that the user sent that the bot recognized as a command; for example,
the message ``/spaghetti carbonara`` is the invoking message for the ``spaghetti`` command run.
You can have the bot delete the invoking message for a command by calling the :class:`CommandData.delete_invoking`
method: ::
async def run(self, args, data):
await data.delete_invoking()
Not all interfaces support deleting messages; by default, if the interface does not support deletions, the call is
ignored.
You can have the method raise an error if the message can't be deleted by setting the ``error_if_unavailable`` parameter
to True: ::
async def run(self, args, data):
try:
await data.delete_invoking(error_if_unavailable=True)
except royalnet.error.UnsupportedError:
await data.reply("🚫 The message could not be deleted.")
else:
await data.reply("✅ The message was deleted!")
Using the database
------------------------------------
Bots can be connected to a PostgreSQL database through a special SQLAlchemy interface called
:class:`royalnet.alchemy.Alchemy`.
If the connection is established, the ``self.alchemy`` and ``data.session`` fields will be
available for use in commands.
``self.alchemy`` is an instance of :class:`royalnet.alchemy.Alchemy`, which contains the
:class:`sqlalchemy.engine.Engine`, metadata and tables, while ``data.session`` is a
:class:`sqlalchemy.orm.session.Session`, and can be interacted in the same way as one.
Querying the database
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can :class:`sqlalchemy.orm.query.Query` the database using the SQLAlchemy ORM.
The SQLAlchemy tables can be found inside :class:`royalnet.alchemy.Alchemy` with the :meth:`royalnet.alchemy.Alchemy.get` method: ::
import royalnet.backpack.tables as rbt
User = self.alchemy.get(rbt.User)
Adding filters to the query
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can filter the query results with the :meth:`sqlalchemy.orm.query.Query.filter` method.
.. note:: Remember to always use a table column as first comparision element, as it won't work otherwise.
::
query = query.filter(User.role == "Member")
Ordering the results of a query
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can order the query results in **ascending order** with the :meth:`sqlalchemy.orm.query.Query.order_by` method. ::
query = query.order_by(User.username)
Additionally, you can append the `.desc()` method to a table column to sort in **descending order**: ::
query = query.order_by(User.username.desc())
Fetching the results of a query
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can fetch the query results with the :meth:`sqlalchemy.orm.query.Query.all`,
:meth:`sqlalchemy.orm.query.Query.first`, :meth:`sqlalchemy.orm.query.Query.one` and
:meth:`sqlalchemy.orm.query.Query.one_or_none` methods.
Remember to use :func:`royalnet.utils.asyncify` when fetching results, as it may take a while!
Use :meth:`sqlalchemy.orm.query.Query.all` if you want a :class:`list` of **all results**: ::
results: list = await asyncify(query.all)
Use :meth:`sqlalchemy.orm.query.Query.first` if you want **the first result** of the list, or ``None`` if
there are no results: ::
result: typing.Union[..., None] = await asyncify(query.first)
Use :meth:`sqlalchemy.orm.query.Query.one` if you expect to have **a single result**, and you want the command to
raise an error if any different number of results is returned: ::
result: ... = await asyncify(query.one) # Raises an error if there are no results or more than a result.
Use :meth:`sqlalchemy.orm.query.Query.one_or_none` if you expect to have **a single result**, or **nothing**, and
if you want the command to raise an error if the number of results is greater than one. ::
result: typing.Union[..., None] = await asyncify(query.one_or_none) # Raises an error if there is more than a result.
More Alchemy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can read more about :mod:`sqlalchemy` at their `website <https://www.sqlalchemy.org/>`_.
Calling Events
------------------------------------
This section is not documented yet.
Displaying Keyboards
------------------------------------
This section is not documented yet.
Running code at the initialization of the bot
---------------------------------------------
This section is not documented yet.
Running repeating jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This section is not documented yet.

View file

@ -0,0 +1,4 @@
Using Events
====================================
This section is not documented yet.

View file

@ -0,0 +1,12 @@
Creating a Royalnet Pack
====================================
This section is not documented yet.
.. toctree::
:maxdepth: 5
command
star
event
table

View file

@ -0,0 +1,4 @@
Adding a Star to the Pack
====================================
This section is not documented yet.

View file

@ -0,0 +1,4 @@
Using Tables and databases
====================================
This section is not documented yet.

80
poetry.lock generated
View file

@ -162,7 +162,7 @@ tzlocal = "*"
category = "main"
description = "A library to handle automated deprecations"
name = "deprecation"
optional = false
optional = true
python-versions = "*"
version = "2.0.7"
@ -197,7 +197,7 @@ version = "0.16"
category = "main"
description = "Python audio data toolkit (ID3 and MP3)"
name = "eyed3"
optional = false
optional = true
python-versions = "*"
version = "0.9"
@ -228,7 +228,7 @@ dev = ["future (0.17.1)", "numpy (1.16.4)", "pytest-mock (1.10.4)", "pytest (4.6
category = "main"
description = "Infer file type and MIME type of any file/buffer. No external dependencies."
name = "filetype"
optional = false
optional = true
python-versions = "*"
version = "1.0.5"
@ -318,7 +318,7 @@ description = "A very fast and expressive template engine."
name = "jinja2"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.11.0"
version = "2.11.1"
[package.dependencies]
MarkupSafe = ">=0.23"
@ -473,7 +473,7 @@ description = "Cryptographic library for Python"
name = "pycryptodome"
optional = true
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.9.4"
version = "3.9.6"
[[package]]
category = "dev"
@ -915,7 +915,7 @@ version = "2020.1.24"
[extras]
alchemy_easy = ["sqlalchemy", "psycopg2_binary"]
alchemy_hard = ["sqlalchemy", "psycopg2"]
bard = ["ffmpeg_python", "youtube_dl"]
bard = ["ffmpeg_python", "youtube_dl", "eyed3"]
coloredlogs = ["coloredlogs"]
constellation = ["starlette", "uvicorn", "python-multipart"]
discord = ["discord.py", "pynacl"]
@ -925,7 +925,7 @@ sentry = ["sentry_sdk"]
telegram = ["python_telegram_bot"]
[metadata]
content-hash = "e46d65bd8228040eb92de09eb31a8a0987a1afbc6f39e62656f9a306df049ba4"
content-hash = "f275cd948fe28423a90d37d2825eabfec97e8ac0cdf52ee2d20f803d61987b40"
python-versions = "^3.8"
[metadata.files]
@ -1104,8 +1104,8 @@ imagesize = [
{file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
]
jinja2 = [
{file = "Jinja2-2.11.0-py2.py3-none-any.whl", hash = "sha256:6e7a3c2934694d59ad334c93dd1b6c96699cf24c53fdb8ec848ac6b23e685734"},
{file = "Jinja2-2.11.0.tar.gz", hash = "sha256:d6609ae5ec3d56212ca7d802eda654eaf2310000816ce815361041465b108be4"},
{file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"},
{file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"},
]
jsonschema = [
{file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
@ -1248,38 +1248,36 @@ pycparser = [
{file = "pycparser-2.19.tar.gz", hash = "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"},
]
pycryptodome = [
{file = "pycryptodome-3.9.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6c2720696b10ae356040e888bde1239b8957fe18885ccf5e7b4e8dec882f0856"},
{file = "pycryptodome-3.9.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:5c485ed6e9718ebcaa81138fa70ace9c563d202b56a8cee119b4085b023931f5"},
{file = "pycryptodome-3.9.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:56fdd0e425f1b8fd3a00b6d96351f86226674974814c50534864d0124d48871f"},
{file = "pycryptodome-3.9.4-cp27-cp27m-win32.whl", hash = "sha256:2de33ed0a95855735d5a0fc0c39603314df9e78ee8bbf0baa9692fb46b3b8bbb"},
{file = "pycryptodome-3.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:eec0689509389f19875f66ae8dedd59f982240cdab31b9f78a8dc266011df93a"},
{file = "pycryptodome-3.9.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:68fab8455efcbfe87c5d75015476f9b606227ffe244d57bfd66269451706e899"},
{file = "pycryptodome-3.9.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4b9533d4166ca07abdd49ce9d516666b1df944997fe135d4b21ac376aa624aff"},
{file = "pycryptodome-3.9.4-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:d3fe3f33ad52bf0c19ee6344b695ba44ffbfa16f3c29ca61116b48d97bd970fb"},
{file = "pycryptodome-3.9.4-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:319e568baf86620b419d53063b18c216abf924875966efdfe06891b987196a45"},
{file = "pycryptodome-3.9.4-cp34-cp34m-win32.whl", hash = "sha256:042ae873baadd0c33b4d699a5c5b976ade3233a979d972f98ca82314632d868c"},
{file = "pycryptodome-3.9.4-cp34-cp34m-win_amd64.whl", hash = "sha256:a30f501bbb32e01a49ef9e09ca1260e5ab49bf33a257080ec553e08997acc487"},
{file = "pycryptodome-3.9.4-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:b55c60c321ac91945c60a40ac9896ac7a3d432bb3e8c14006dfd82ad5871c331"},
{file = "pycryptodome-3.9.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:9d9945ac8375d5d8e60bd2a2e1df5882eaa315522eedf3ca868b1546dfa34eba"},
{file = "pycryptodome-3.9.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:4372ec7518727172e1605c0843cdc5375d4771e447b8148c787b860260aae151"},
{file = "pycryptodome-3.9.4-cp35-cp35m-win32.whl", hash = "sha256:0502876279772b1384b660ccc91563d04490d562799d8e2e06b411e2d81128a9"},
{file = "pycryptodome-3.9.4-cp35-cp35m-win_amd64.whl", hash = "sha256:72166c2ac520a5dbd2d90208b9c279161ec0861662a621892bd52fb6ca13ab91"},
{file = "pycryptodome-3.9.4-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:b4af098f2a50f8d048ab12cabb59456585c0acf43d90ee79782d2d6d0ed59dba"},
{file = "pycryptodome-3.9.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:8a799bea3c6617736e914a2e77c409f52893d382f619f088f8a80e2e21f573c1"},
{file = "pycryptodome-3.9.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7c52308ac5b834331b2f107a490b2c27de024a229b61df4cdc5c131d563dfe98"},
{file = "pycryptodome-3.9.4-cp36-cp36m-win32.whl", hash = "sha256:63c103a22cbe9752f6ea9f1a0de129995bad91c4d03a66c67cffcf6ee0c9f1e1"},
{file = "pycryptodome-3.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:54456cf85130e01674d21fb1ab89ffccacb138a8ade88d72fa2b0ac898d2798b"},
{file = "pycryptodome-3.9.4-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:aec4d42deb836b8fb3ba32f2ba1ef0d33dd3dc9d430b1479ee7a914490d15b5e"},
{file = "pycryptodome-3.9.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:896e9b6fd0762aa07b203c993fbbee7a1f1a4674c6886afd7bfa86f3d1be98a8"},
{file = "pycryptodome-3.9.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:57b1b707363490c495ad0eeb38bd1b0e1697c497af25fad78d3a1ebf0477fd5b"},
{file = "pycryptodome-3.9.4-cp37-cp37m-win32.whl", hash = "sha256:87d8d85b4792ca5e730fb7a519fbc3ed976c59dcf79c5204589c59afd56b9926"},
{file = "pycryptodome-3.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:e3a79a30d15d9c7c284a7734036ee8abdb5ca3a6f5774d293cdc9e1358c1dc10"},
{file = "pycryptodome-3.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:48821950ffb9c836858d8fa09d7840b6df52eadd387a3c5acece55cb387743f9"},
{file = "pycryptodome-3.9.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cbfd97f9e060f0d30245cd29fa267a9a84de9da97559366fca0a3f7655acc63f"},
{file = "pycryptodome-3.9.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9ef966c727de942de3e41aa8462c4b7b4bca70f19af5a3f99e31376589c11aac"},
{file = "pycryptodome-3.9.4-cp38-cp38-win32.whl", hash = "sha256:a8ca2450394d3699c9f15ef25e8de9a24b401933716a1e39d37fa01f5fe3c58b"},
{file = "pycryptodome-3.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:c53348358408d94869059e16fba5ff3bef8c52c25b18421472aba272b9bb450f"},
{file = "pycryptodome-3.9.4.tar.gz", hash = "sha256:a168e73879619b467072509a223282a02c8047d932a48b74fbd498f27224aa04"},
{file = "pycryptodome-3.9.6-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:5029c46b0d41dfb763c3981c0af68eab029f06fe2b94f2299112fc18cf9e8d6d"},
{file = "pycryptodome-3.9.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:95d324e603c5cec5d89e8595236bbf59ade5fe3a72d100ce61eebb323d598750"},
{file = "pycryptodome-3.9.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2a57daef18a2022a5e4b6f7376c9ddd0c2d946e4b1f1e59b837f5bf295be7380"},
{file = "pycryptodome-3.9.6-cp27-cp27m-win32.whl", hash = "sha256:a719bd708207fa219fcbf4c8ebbcbc52846045f78179d00445b429fdabdbc1c4"},
{file = "pycryptodome-3.9.6-cp27-cp27m-win_amd64.whl", hash = "sha256:39e5ca2f66d1eac7abcba5ce1a03370d123dc6085620f1cd532dfee27e650178"},
{file = "pycryptodome-3.9.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f4d2174e168d0eabd1fffaf88b4f62c2b6f30a67b8816f31024b8e48be3e2d75"},
{file = "pycryptodome-3.9.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ec7d39589f9cfc2a8b83b1d2fc673441757c99d43283e97b2dd46e0e23730db8"},
{file = "pycryptodome-3.9.6-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:9163fec630495c10c767991e3f8dab32f4427bfb2dfeaa59bb28fe3e52ba66f2"},
{file = "pycryptodome-3.9.6-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:0a8d5f2dbb4bbe830ace54286b829bfa529f0853bedaab6225fcb2e6d1f7e356"},
{file = "pycryptodome-3.9.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:5817c0b3c263025d851da96b90cbc7e95348008f88b990e90d10683dba376666"},
{file = "pycryptodome-3.9.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c41b7e10b72cef00cd63410f31fe50e72dc3a40eafbd146e288384fbe4208064"},
{file = "pycryptodome-3.9.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f3204006869ab037604b1d9f045c4e84882ddd365e4ee8caa5eb1ff47a59188e"},
{file = "pycryptodome-3.9.6-cp35-cp35m-win32.whl", hash = "sha256:cdb0ad83a5d6bac986a37fcb7562bcbef0aabae8ea19505bab5cf83c4d18af12"},
{file = "pycryptodome-3.9.6-cp35-cp35m-win_amd64.whl", hash = "sha256:1259b8ca49662b8a941177357f08147d858595c0042e63ff81e9628e925b5c9d"},
{file = "pycryptodome-3.9.6-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:09bf05a489fe10f9280a5e0163f195e7b9630cafb15f7d72fb9c8f5eb2afa84f"},
{file = "pycryptodome-3.9.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fcff8c9d88d58880f7eda2139c7c444552a38f98a9e77ba5970b6e78f54ac358"},
{file = "pycryptodome-3.9.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9948c2d5c5c0ee45ed44cee0e2eba2ce60a03be006ed3074521f3da3be162e72"},
{file = "pycryptodome-3.9.6-cp36-cp36m-win32.whl", hash = "sha256:79320f1fc5c9ca682869087c565bb29ca6f334692e940d7365771e9a94382e12"},
{file = "pycryptodome-3.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:d8e480f65ac7105cbc288eec2417dc61eaac6ed6e75595aa15b8c7c77c53a68b"},
{file = "pycryptodome-3.9.6-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:07daddb98f98f771ba027f8f835bdb675aeb84effe41ed5221f520b267429354"},
{file = "pycryptodome-3.9.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:da2d581da279bc7408d38e16ff77754f5448c4352f2acfe530a5d14d8fc6934a"},
{file = "pycryptodome-3.9.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:012ca77c2105600e3c6aef43188101ac1d95052c633a4ae8fbebffab20c25f8a"},
{file = "pycryptodome-3.9.6-cp37-cp37m-win32.whl", hash = "sha256:05b4d865710f9a6378d3ada28195ff78e52642d3ecffe6fa9d379d870b9bf29d"},
{file = "pycryptodome-3.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:9927aa8a8cb4af681279b6f28a1dcb14e0eb556c1aea8413a1e27608a8516e0c"},
{file = "pycryptodome-3.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de61091dd68326b600422cf731eb4810c4c6363f18a65bccd6061784b7454f5b"},
{file = "pycryptodome-3.9.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:238d8b6dd27bd1a04816a68aa90a739e6dd23b192fcd83b50f9360958bff192a"},
{file = "pycryptodome-3.9.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d516df693c195b8da3795e381429bd420e87081b7e6c2871c62c9897c812cda"},
{file = "pycryptodome-3.9.6-cp38-cp38-win32.whl", hash = "sha256:3e486c5b7228e864665fc479e9f596b2547b5fe29c6f5c8ed3807784d06faed7"},
{file = "pycryptodome-3.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:887d08beca6368d3d70dc75126607ad76317a9fd07fe61323d8c3cb42add12b6"},
{file = "pycryptodome-3.9.6.tar.gz", hash = "sha256:bc22ced26ebc46546798fa0141f4418f1db116dec517f0aeaecec87cf7b2416c"},
]
pygments = [
{file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"},