mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-24 03:54:20 +00:00
717 lines
No EOL
71 KiB
HTML
717 lines
No EOL
71 KiB
HTML
|
||
|
||
<!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 new Command — Royalnet 5.10.4 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 src="../_static/jquery.js"></script>
|
||
<script src="../_static/underscore.js"></script>
|
||
<script src="../_static/doctools.js"></script>
|
||
<script 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 new Pack" href="newpack.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.10.4
|
||
</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">Royalnet Packs</a><ul class="current">
|
||
<li class="toctree-l2"><a class="reference internal" href="newpack.html">Creating a new Pack</a></li>
|
||
<li class="toctree-l2 current"><a class="current reference internal" href="#">Creating a new Command</a><ul>
|
||
<li class="toctree-l3"><a class="reference internal" href="#id1">Creating a new Command</a></li>
|
||
<li class="toctree-l3"><a class="reference internal" href="#formatting-command-replies">Formatting command replies</a><ul>
|
||
<li class="toctree-l4"><a class="reference internal" href="#available-tags">Available tags</a></li>
|
||
</ul>
|
||
</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="#sharing-data-between-multiple-calls">Sharing data between multiple calls</a></li>
|
||
<li class="toctree-l3"><a class="reference internal" href="#using-the-alchemy">Using the Alchemy</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="#distinguish-between-platforms">Distinguish between platforms</a></li>
|
||
<li class="toctree-l3"><a class="reference internal" href="#displaying-keyboards">Displaying Keyboards</a><ul>
|
||
<li class="toctree-l4"><a class="reference internal" href="#replies-in-callbacks">Replies in callbacks</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l3"><a class="reference internal" href="#reading-data-from-the-configuration-file">Reading data from the configuration file</a></li>
|
||
<li class="toctree-l3"><a class="reference internal" href="#running-code-on-serf-start">Running code on Serf start</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> »</li>
|
||
|
||
<li><a href="pack.html">Royalnet Packs</a> »</li>
|
||
|
||
<li>Creating a new Command</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="creating-a-new-command">
|
||
<h1>Creating a new Command<a class="headerlink" href="#creating-a-new-command" 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 platform.</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">"ping"</span>
|
||
|
||
<span class="n">description</span> <span class="o">=</span> <span class="s2">"Play ping-pong with the bot."</span>
|
||
|
||
<span class="c1"># This code is run just once, while the bot is starting</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">serf</span><span class="p">:</span> <span class="s2">"Serf"</span><span class="p">,</span> <span class="n">config</span><span class="p">):</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="n">serf</span><span class="o">=</span><span class="n">serf</span><span class="p">,</span> <span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">)</span>
|
||
|
||
<span class="c1"># This code is run every time the command is called</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">"Pong!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="id1">
|
||
<h2>Creating a new Command<a class="headerlink" href="#id1" 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.
|
||
It’s 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">"spaghetti"</span>
|
||
|
||
<span class="n">description</span> <span class="o">=</span> <span class="s2">"Send a spaghetti emoji in the chat."</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">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">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">"spaghetti"</span>
|
||
|
||
<span class="n">description</span> <span class="o">=</span> <span class="s2">"Send a spaghetti emoji in the chat."</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">"🍝"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Finally, open the <code class="docutils literal notranslate"><span class="pre">commands/__init__.py</span></code> file, and import your command there, then add a reference to your imported
|
||
command to the <code class="docutils literal notranslate"><span class="pre">available_commands</span></code> list:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Imports go here!</span>
|
||
<span class="kn">from</span> <span class="nn">.spaghetti</span> <span class="kn">import</span> <span class="n">SpaghettiCommand</span>
|
||
|
||
<span class="c1"># Enter the commands of your Pack here!</span>
|
||
<span class="n">available_commands</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="n">SpaghettiCommand</span><span class="p">,</span>
|
||
<span class="p">]</span>
|
||
|
||
<span class="c1"># Don't change this, it should automatically generate __all__</span>
|
||
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="n">command</span><span class="o">.</span><span class="vm">__name__</span> <span class="k">for</span> <span class="n">command</span> <span class="ow">in</span> <span class="n">available_commands</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="formatting-command-replies">
|
||
<h2>Formatting command replies<a class="headerlink" href="#formatting-command-replies" title="Permalink to this headline">¶</a></h2>
|
||
<p>You can use a subset of <a class="reference external" href="https://en.wikipedia.org/wiki/BBCode">BBCode</a> to format messages sent with <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">reply()</span></code></a>:</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">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">"[b]Bold of you to assume that my code has no bugs.[/b]"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="available-tags">
|
||
<h3>Available tags<a class="headerlink" href="#available-tags" title="Permalink to this headline">¶</a></h3>
|
||
<p>Here’s a list of all tags that can be used:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">[b]bold[/b]</span></code></p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">[i]italic[/i]</span></code></p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">[c]code[/c]</span></code></p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">[p]multiline</span> <span class="pre">\n</span> <span class="pre">code[/p]</span></code></p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">[url=https://google.com]inline</span> <span class="pre">link[/url]</span></code> (will be rendered differently on every platform)</p></li>
|
||
</ul>
|
||
</div>
|
||
</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">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">"spaghetti"</span>
|
||
|
||
<span class="n">description</span> <span class="o">=</span> <span class="s2">"Send a spaghetti emoji in the chat."</span>
|
||
|
||
<span class="n">syntax</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{first_pasta}</span><span class="s2"> [second_pasta]"</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="n">first_pasta</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||
<span class="n">second_pasta</span> <span class="o">=</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="k">if</span> <span class="n">second_pasta</span> <span class="ow">is</span> <span class="kc">None</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">"🍝 Here's your </span><span class="si">{</span><span class="n">first_pasta</span><span class="si">}</span><span class="s2">!"</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="sa">f</span><span class="s2">"🍝 Here's your </span><span class="si">{</span><span class="n">first_pasta</span><span class="si">}</span><span class="s2"> and your </span><span class="si">{</span><span class="n">second_pasta</span><span class="si">}</span><span class="s2">!"</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"># "carbonara"</span>
|
||
|
||
<span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||
<span class="c1"># "al-dente"</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 don’t 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">optional()</span></code></a> method: it
|
||
will return <code class="docutils literal notranslate"><span class="pre">None</span></code> if the argument wasn’t 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"># "carbonara"</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"># "al-dente"</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">"banana"</span><span class="p">)</span>
|
||
<span class="c1"># "banana"</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">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"># "carbonara al-dente"</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">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">"(carb\w+)"</span><span class="p">)</span>
|
||
<span class="c1"># ("carbonara",)</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">"(al-\w+)"</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">"\s*(al-\w+)"</span><span class="p">)</span>
|
||
<span class="c1"># ("al-dente",)</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">"\s*(carb\w+)\s*(al-\w+)"</span><span class="p">)</span>
|
||
<span class="c1"># ("carbonara", "al-dente")</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">"The kitchen is closed. Come back later!"</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.
|
||
<em>Additionally displays the command syntax in the error message.</em></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 platform 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>A value is missing or invalid in the <code class="docutils literal notranslate"><span class="pre">config.toml</span></code> section of your pack.</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("🍝")</span></code> instead of just <code class="docutils literal notranslate"><span class="pre">data.reply("🍝")</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">reply()</span></code></a> 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.</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("🍝")</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'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">"right_now"</span><span class="p">,</span> <span class="n">from</span><span class="o">=</span><span class="s2">"italy"</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">"right_now"</span><span class="p">,</span> <span class="n">from</span><span class="o">=</span><span class="s2">"italy"</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">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 platforms support deleting messages; by default, if the platform does not support deletions, the call is
|
||
ignored.</p>
|
||
<p>You can have the method raise an error if the message can’t 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">"🚫 The message could not be deleted."</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">"✅ The message was deleted!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="sharing-data-between-multiple-calls">
|
||
<h2>Sharing data between multiple calls<a class="headerlink" href="#sharing-data-between-multiple-calls" title="Permalink to this headline">¶</a></h2>
|
||
<p>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 is shared between multiple command calls: if you need to store some data, you may store it as a protected/private field of your command class:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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">"spaghetti"</span>
|
||
|
||
<span class="n">description</span> <span class="o">=</span> <span class="s2">"Send a spaghetti emoji in the chat."</span>
|
||
|
||
<span class="n">syntax</span> <span class="o">=</span> <span class="s2">"(requestedpasta)"</span>
|
||
|
||
<span class="n">__total_spaghetti</span> <span class="o">=</span> <span class="mi">0</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="bp">self</span><span class="o">.</span><span class="n">__total_spaghetti</span> <span class="o">+=</span> <span class="mi">1</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">"🍝 Here's your </span><span class="si">{</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s2">!</span><span class="se">\n</span><span class="s2">"</span>
|
||
<span class="sa">f</span><span class="s2">"[i]Spaghetti have been served </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">__total_spaghetti</span><span class="si">}</span><span class="s2"> times.[/i]"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Values stored in this way persist <strong>only until the bot is restarted</strong>, and <strong>won’t be shared between different serfs</strong>; if you need persistent values, it is recommended to use a database through the Alchemy service.</p>
|
||
</div>
|
||
<div class="section" id="using-the-alchemy">
|
||
<h2>Using the Alchemy<a class="headerlink" href="#using-the-alchemy" title="Permalink to this headline">¶</a></h2>
|
||
<p>Royalnet 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 won’t 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">"Member"</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 sqlalchemy 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>You can <strong>call an event</strong> from inside a command, and receive its return value.</p>
|
||
<p>This may be used for example to get data from a different platform, such as getting the users online in a specific Discord server.</p>
|
||
<p>You can call an event with the <a class="reference internal" href="../apireference.html#royalnet.serf.Serf.call_herald_event" title="royalnet.serf.Serf.call_herald_event"><code class="xref py py-meth docutils literal notranslate"><span class="pre">Serf.call_herald_event()</span></code></a> method:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">serf</span><span class="o">.</span><span class="n">call_herald_event</span><span class="p">(</span><span class="s2">"event_name"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>You can also pass parameters to the called event:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">serf</span><span class="o">.</span><span class="n">call_herald_event</span><span class="p">(</span><span class="s2">"event_name"</span><span class="p">,</span> <span class="o">...</span><span class="p">,</span> <span class="n">kwarg</span><span class="o">=...</span><span class="p">,</span> <span class="o">*...</span><span class="p">,</span> <span class="o">**...</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Errors raised by the event will also be raised by the <a class="reference internal" href="../apireference.html#royalnet.serf.Serf.call_herald_event" title="royalnet.serf.Serf.call_herald_event"><code class="xref py py-meth docutils literal notranslate"><span class="pre">Serf.call_herald_event()</span></code></a> method as one of the exceptions described in the <span class="xref std std-ref">Raising errors</span> section.</p>
|
||
</div>
|
||
<div class="section" id="distinguish-between-platforms">
|
||
<h2>Distinguish between platforms<a class="headerlink" href="#distinguish-between-platforms" title="Permalink to this headline">¶</a></h2>
|
||
<p>To see if a command is being run on a specific platform, you can check the type of the <code class="docutils literal notranslate"><span class="pre">self.serf</span></code> object:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">royalnet.serf.telegram</span> <span class="k">as</span> <span class="nn">rst</span>
|
||
<span class="kn">import</span> <span class="nn">royalnet.serf.discord</span> <span class="k">as</span> <span class="nn">rsd</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">serf</span><span class="p">,</span> <span class="n">rst</span><span class="o">.</span><span class="n">TelegramSerf</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">"This command is being run on Telegram."</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">serf</span><span class="p">,</span> <span class="n">rsd</span><span class="o">.</span><span class="n">DiscordSerf</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">"This command is being run on Discord."</span><span class="p">)</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="displaying-keyboards">
|
||
<h2>Displaying Keyboards<a class="headerlink" href="#displaying-keyboards" title="Permalink to this headline">¶</a></h2>
|
||
<p>A keyboard is a message with multiple buttons (“keys”) attached which can be pressed by an user viewing the message.</p>
|
||
<p>Once a button is pressed, a callback function is run, which has its own <a class="reference internal" href="../apireference.html#royalnet.commands.CommandData" title="royalnet.commands.CommandData"><code class="xref py py-class docutils literal notranslate"><span class="pre">CommandData</span></code></a> context and can do everything a regular comment call could.</p>
|
||
<p>The callback function is a coroutine accepting a single <code class="docutils literal notranslate"><span class="pre">data:</span> <span class="pre">CommandData</span></code> argument:</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">answer</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">CommandData</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</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">"Spaghetti were ejected from your floppy drive!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To create a new key, you can use the <a class="reference internal" href="../apireference.html#royalnet.commands.KeyboardKey" title="royalnet.commands.KeyboardKey"><code class="xref py py-class docutils literal notranslate"><span class="pre">KeyboardKey</span></code></a> class:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">key</span> <span class="o">=</span> <span class="n">KeyboardKey</span><span class="p">(</span>
|
||
<span class="n">short</span><span class="o">=</span><span class="s2">"⏏️"</span><span class="p">,</span> <span class="c1"># An emoji representing the key on platforms the full message cannot be displayed</span>
|
||
<span class="n">text</span><span class="o">=</span><span class="s2">"Eject spaghetti from the floppy drive"</span><span class="p">,</span> <span class="c1"># The text displayed on the key</span>
|
||
<span class="n">callback</span><span class="o">=</span><span class="n">answer</span> <span class="c1"># The coroutine to call when the key is pressed.</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To display a keyboard and wait for a keyboard press, you can use the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandData.keyboard" title="royalnet.commands.CommandData.keyboard"><code class="xref py py-meth docutils literal notranslate"><span class="pre">keyboard()</span></code></a> asynccontextmanager.
|
||
While the contextmanager is in scope, the keyboard will be valid and it will be possible to interact with it.
|
||
Any further key pressed will be answered with an error message.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">with</span> <span class="n">data</span><span class="o">.</span><span class="n">keyboard</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s2">"What kind of spaghetti would you want to order?"</span><span class="p">,</span> <span class="n">keys</span><span class="o">=</span><span class="n">keyboard</span><span class="p">):</span>
|
||
<span class="c1"># This will keep the keyboard valid for 10 seconds</span>
|
||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="replies-in-callbacks">
|
||
<h3>Replies in callbacks<a class="headerlink" href="#replies-in-callbacks" title="Permalink to this headline">¶</a></h3>
|
||
<p>Calls to <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">reply()</span></code></a> made with the <a class="reference internal" href="../apireference.html#royalnet.commands.CommandData" title="royalnet.commands.CommandData"><code class="xref py py-class docutils literal notranslate"><span class="pre">CommandData</span></code></a> of a keyboard callback won’t always result in a message being sent: for example, on Telegram, replies will result in a small message being displayed on the top of the screen.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="reading-data-from-the-configuration-file">
|
||
<h2>Reading data from the configuration file<a class="headerlink" href="#reading-data-from-the-configuration-file" title="Permalink to this headline">¶</a></h2>
|
||
<p>You can read data from your pack’s configuration section through the <code class="xref py py-attr docutils literal notranslate"><span class="pre">config</span></code> attribute:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">Packs</span><span class="o">.</span><span class="s2">"spaghettipack"</span><span class="p">]</span>
|
||
<span class="n">spaghetti</span> <span class="o">=</span> <span class="p">{</span> <span class="n">mode</span><span class="o">=</span><span class="s2">"al_dente"</span><span class="p">,</span> <span class="n">two</span><span class="o">=</span><span class="n">true</span> <span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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">"Here's your spaghetti </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'spaghetti'</span><span class="p">][</span><span class="s1">'mode'</span><span class="p">]</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="running-code-on-serf-start">
|
||
<h2>Running code on Serf start<a class="headerlink" href="#running-code-on-serf-start" title="Permalink to this headline">¶</a></h2>
|
||
<p>The code inside <code class="docutils literal notranslate"><span class="pre">__init__</span></code> is run only once, during the initialization step of the bot:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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">serf</span><span class="p">:</span> <span class="s2">"Serf"</span><span class="p">,</span> <span class="n">config</span><span class="p">):</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="n">serf</span><span class="o">=</span><span class="n">serf</span><span class="p">,</span> <span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">)</span>
|
||
|
||
<span class="c1"># The contents of this variable will be persisted across command calls</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">persistent_variable</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="c1"># The text will be printed only if the config flag is set to something</span>
|
||
<span class="k">if</span> <span class="n">config</span><span class="p">[</span><span class="s2">"spaghetti"</span><span class="p">][</span><span class="s2">"two"</span><span class="p">]:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Famme due spaghi!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">Note</p>
|
||
<p>Some methods may be unavailable during the initialization of the Serf.</p>
|
||
</div>
|
||
<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>To run a job independently from the rest of the command, you can schedule the execution of a coroutine inside <code class="docutils literal notranslate"><span class="pre">__init__</span></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">mycoroutine</span><span class="p">():</span>
|
||
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Free spaghetti every 60 seconds!"</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</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">serf</span><span class="p">:</span> <span class="s2">"Serf"</span><span class="p">,</span> <span class="n">config</span><span class="p">):</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="n">serf</span><span class="o">=</span><span class="n">serf</span><span class="p">,</span> <span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">loop</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">mycoroutine</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As it will be executed once for every platform Royalnet is running on, you may want to run the task only on a single platform:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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">serf</span><span class="p">:</span> <span class="s2">"Serf"</span><span class="p">,</span> <span class="n">config</span><span class="p">):</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="n">serf</span><span class="o">=</span><span class="n">serf</span><span class="p">,</span> <span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">serf</span><span class="p">,</span> <span class="n">rst</span><span class="o">.</span><span class="n">TelegramSerf</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">loop</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">mycoroutine</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
</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="newpack.html" class="btn btn-neutral float-left" title="Creating a new Pack" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||
|
||
</div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<div role="contentinfo">
|
||
<p>
|
||
© Copyright 2020, 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> |