1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-30 15:04:18 +00:00
royalnet/docs/html/packs/command.html

555 lines
48 KiB
HTML
Raw Normal View History

2020-02-03 17:23:31 +00:00
<!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>