mirror of
https://github.com/RYGhub/royalnet.git
synced 2025-03-23 16:47:14 +00:00
A lot of new stuff is coming!
This commit is contained in:
parent
22c3cd6351
commit
9cbcce32da
37 changed files with 608 additions and 273 deletions
docs
doctrees
html
docs_source
royalnet
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -137,7 +137,7 @@ If you want the full argument string, you can use the :py:meth:`CommandArgs.join
|
|||
args.joined()
|
||||
# "carbonara al-dente"
|
||||
|
||||
You can specify a minimum number of arguments too, so that an :py:exc:`royalnet.error.InvalidInputError` will be
|
||||
You can specify a minimum number of arguments too, so that an :py:exc:`InvalidInputError` will be
|
||||
raised if not enough arguments are present: ::
|
||||
|
||||
args.joined(require_at_least=3)
|
||||
|
@ -149,7 +149,7 @@ Regular expressions
|
|||
For more complex commands, you may want to get arguments through `regular expressions <https://regexr.com/>`_.
|
||||
|
||||
You can then use the :py:meth:`CommandArgs.match` method, which tries to match a pattern to the command argument string,
|
||||
which returns a tuple of the matched groups and raises an :py:exc:`royalnet.error.InvalidInputError` if there is no match.
|
||||
which returns a tuple of the matched groups and raises an :py:exc:`InvalidInputError` if there is no match.
|
||||
|
||||
To match a pattern, :py:func:`re.match` is used, meaning that Python will try to match only at the beginning of the string. ::
|
||||
|
||||
|
@ -165,6 +165,25 @@ To match a pattern, :py:func:`re.match` is used, meaning that Python will try to
|
|||
args.match(r"\s*(carb\w+)\s*(al-\w+)")
|
||||
# ("carbonara", "al-dente")
|
||||
|
||||
Raising errors
|
||||
---------------------------------------------
|
||||
|
||||
If you want to display an error message to the user, you can raise a :py:exc:`CommandError` using the error message as argument: ::
|
||||
|
||||
if not kitchen.is_open():
|
||||
raise CommandError("The kitchen is closed. Come back later!")
|
||||
|
||||
You can also manually raise :py:exc:`InvalidInputError` to redisplay the command syntax, along with your error message: ::
|
||||
|
||||
if args[0] not in allowed_pasta:
|
||||
raise InvalidInputError("The specified pasta type is invalid.")
|
||||
|
||||
If you need a Royalnet feature that's not available on the current interface, you can raise an
|
||||
:py:exc:`UnsupportedError` with a brief description of what's missing: ::
|
||||
|
||||
if interface.name != "telegram":
|
||||
raise UnsupportedError("This command can only be run on Telegram interfaces.")
|
||||
|
||||
Running code at the initialization of the bot
|
||||
---------------------------------------------
|
||||
|
||||
|
|
|
@ -3,4 +3,60 @@
|
|||
Running Royalnet
|
||||
====================================
|
||||
|
||||
This documentation page hasn't been written yet, please refer to the README until then.
|
||||
To run a ``royalnet`` instance, you have first to download the package from ``pip``:
|
||||
|
||||
The Keyring
|
||||
------------------------------------
|
||||
::
|
||||
|
||||
pip install royalnet
|
||||
|
||||
|
||||
To run ``royalnet``, you'll have to setup the system keyring.
|
||||
|
||||
On Windows and desktop Linux, this is already configured;
|
||||
on a headless Linux instance, you'll need to `manually start and unlock the keyring daemon
|
||||
<https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems>`_.
|
||||
|
||||
Now you have to create a new ``royalnet`` configuration. Start the configuration wizard: ::
|
||||
|
||||
python -m royalnet.configurator
|
||||
|
||||
You'll be prompted to enter a "secrets name": this is the name of the group of API keys that will be associated with
|
||||
your bot. Enter a name that you'll be able to remember. ::
|
||||
|
||||
Desired secrets name [__default__]: royalgames
|
||||
|
||||
You'll then be asked for a network password.
|
||||
|
||||
This password is used to connect to the rest of the :py:mod:`royalnet.network`, or, if you're hosting a local Network,
|
||||
it will be the necessary password to connect to it: ::
|
||||
|
||||
Network password []: cosafaunapesuunafoglia
|
||||
|
||||
Then you'll be asked for a Telegram Bot API token.
|
||||
You can get one from `@BotFather <https://t.me/BotFather>`_. ::
|
||||
|
||||
Telegram Bot API token []: 000000000:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
||||
The next prompt will ask for a Discord Bot API token.
|
||||
You can get one at the `Discord Developers Portal <https://discordapp.com/developers/applications/>`_. ::
|
||||
|
||||
Discord Bot API token []: AAAAAAAAAAAAAAAAAAAAAAAA.AAAAAA.AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
||||
Now the configurator will ask you for a Imgur API token.
|
||||
`Register an application <https://api.imgur.com/oauth2/addclient>`_ on Imgur to be supplied one.
|
||||
The token should be of type "anonymous usage without user authorization". ::
|
||||
|
||||
Imgur API token []: aaaaaaaaaaaaaaa
|
||||
|
||||
Next, you'll be asked for a Sentry DSN. You probably won't have one, so just ignore it and press enter. ::
|
||||
|
||||
Sentry DSN []:
|
||||
|
||||
Now that all tokens are configured, you're ready to launch the bot!
|
||||
|
||||
Running the bots
|
||||
------------------------------------
|
||||
|
||||
TODO
|
|
@ -286,11 +286,6 @@
|
|||
<code class="sig-name descname">convert_to_pcm</code><span class="sig-paren">(</span><span class="sig-paren">)</span> → None<a class="headerlink" href="#royalnet.audio.YtdlDiscord.convert_to_pcm" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.audio.YtdlDiscord.create_and_ready_from_url">
|
||||
<em class="property">classmethod </em><code class="sig-name descname">create_and_ready_from_url</code><span class="sig-paren">(</span><em class="sig-param">url</em>, <em class="sig-param">**ytdl_args</em><span class="sig-paren">)</span> → List[royalnet.audio.ytdldiscord.YtdlDiscord]<a class="headerlink" href="#royalnet.audio.YtdlDiscord.create_and_ready_from_url" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.audio.YtdlDiscord.create_from_url">
|
||||
<em class="property">classmethod </em><code class="sig-name descname">create_from_url</code><span class="sig-paren">(</span><em class="sig-param">url</em>, <em class="sig-param">**ytdl_args</em><span class="sig-paren">)</span> → List[royalnet.audio.ytdldiscord.YtdlDiscord]<a class="headerlink" href="#royalnet.audio.YtdlDiscord.create_from_url" title="Permalink to this definition">¶</a></dt>
|
||||
|
@ -573,7 +568,7 @@ find the chain that links the <code class="docutils literal notranslate"><span c
|
|||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.bots.GenericBot.run_blocking">
|
||||
<code class="sig-name descname">run_blocking</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.bots.GenericBot.run_blocking" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">run_blocking</code><span class="sig-paren">(</span><em class="sig-param">verbose=False</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.bots.GenericBot.run_blocking" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
|
@ -657,6 +652,13 @@ find the chain that links the <code class="docutils literal notranslate"><span c
|
|||
<dt id="royalnet.commands.Command">
|
||||
<em class="property">class </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">Command</code><span class="sig-paren">(</span><em class="sig-param">interface: royalnet.commands.commandinterface.CommandInterface</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.Command" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="attribute">
|
||||
<dt id="royalnet.commands.Command.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = NotImplemented</em><a class="headerlink" href="#royalnet.commands.Command.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A list of possible aliases for a command.
|
||||
To have <code class="docutils literal notranslate"><span class="pre">/e</span></code> as alias for <code class="docutils literal notranslate"><span class="pre">/example</span></code>, one should set aliases to <code class="docutils literal notranslate"><span class="pre">["e"]</span></code>.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="attribute">
|
||||
<dt id="royalnet.commands.Command.description">
|
||||
<code class="sig-name descname">description</code><em class="property"> = NotImplemented</em><a class="headerlink" href="#royalnet.commands.Command.description" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A small description of the command, to be displayed when the command is being autocompleted.</p>
|
||||
|
@ -683,7 +685,7 @@ To have <code class="docutils literal notranslate"><span class="pre">/example</s
|
|||
<dl class="attribute">
|
||||
<dt id="royalnet.commands.Command.syntax">
|
||||
<code class="sig-name descname">syntax</code><em class="property"> = ''</em><a class="headerlink" href="#royalnet.commands.Command.syntax" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The syntax of the command, to be displayed when a <a class="reference internal" href="#royalnet.error.InvalidInputError" title="royalnet.error.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code></a> is raised,
|
||||
<dd><p>The syntax of the command, to be displayed when a <code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code> is raised,
|
||||
in the format <code class="docutils literal notranslate"><span class="pre">(required_arg)</span> <span class="pre">[optional_arg]</span></code>.</p>
|
||||
</dd></dl>
|
||||
|
||||
|
@ -694,12 +696,12 @@ in the format <code class="docutils literal notranslate"><span class="pre">(requ
|
|||
<em class="property">class </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">CommandData</code><a class="headerlink" href="#royalnet.commands.CommandData" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="method">
|
||||
<dt id="royalnet.commands.CommandData.delete_invoking">
|
||||
<em class="property">async </em><code class="sig-name descname">delete_invoking</code><span class="sig-paren">(</span><em class="sig-param">error_if_unavailable=False</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.CommandData.delete_invoking" title="Permalink to this definition">¶</a></dt>
|
||||
<em class="property">async </em><code class="sig-name descname">delete_invoking</code><span class="sig-paren">(</span><em class="sig-param">error_if_unavailable=False</em><span class="sig-paren">)</span> → None<a class="headerlink" href="#royalnet.commands.CommandData.delete_invoking" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Delete the invoking message, if supported by the interface.</p>
|
||||
<p>The invoking message is the message send by the user that contains the command.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>error_if_unavailable</strong> – if True, raise NotImplementedError() if the message cannot been deleted</p>
|
||||
<dd class="field-odd"><p><strong>error_if_unavailable</strong> – if True, raise an exception if the message cannot been deleted.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
@ -711,10 +713,7 @@ in the format <code class="docutils literal notranslate"><span class="pre">(requ
|
|||
That probably means, the database row identifying the user.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>error_if_none</strong> – Raise a <a class="reference internal" href="#royalnet.error.UnregisteredError" title="royalnet.error.UnregisteredError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.UnregisteredError</span></code></a> if this is True and the call has no author.</p>
|
||||
</dd>
|
||||
<dt class="field-even">Raises</dt>
|
||||
<dd class="field-even"><p><a class="reference internal" href="#royalnet.error.UnregisteredError" title="royalnet.error.UnregisteredError"><strong>royalnet.error.UnregisteredError</strong></a> – </p>
|
||||
<dd class="field-odd"><p><strong>error_if_none</strong> – Raise an exception if this is True and the call has no author.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
@ -749,7 +748,7 @@ That probably means, the database row identifying the user.</p>
|
|||
<dd><p>Arguments can be accessed with an array notation, such as <code class="docutils literal notranslate"><span class="pre">args[0]</span></code>.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Raises</dt>
|
||||
<dd class="field-odd"><p><a class="reference internal" href="#royalnet.error.InvalidInputError" title="royalnet.error.InvalidInputError"><strong>royalnet.error.InvalidInputError</strong></a> – if the requested argument does not exist.</p>
|
||||
<dd class="field-odd"><p><strong>royalnet.error.InvalidInputError</strong> – if the requested argument does not exist.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
@ -760,10 +759,10 @@ That probably means, the database row identifying the user.</p>
|
|||
<dd><p>Get the arguments as a space-joined string.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>require_at_least</strong> – the minimum amount of arguments required, will raise <a class="reference internal" href="#royalnet.error.InvalidInputError" title="royalnet.error.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code></a> if the requirement is not fullfilled.</p>
|
||||
<dd class="field-odd"><p><strong>require_at_least</strong> – the minimum amount of arguments required, will raise <code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code> if the requirement is not fullfilled.</p>
|
||||
</dd>
|
||||
<dt class="field-even">Raises</dt>
|
||||
<dd class="field-even"><p><a class="reference internal" href="#royalnet.error.InvalidInputError" title="royalnet.error.InvalidInputError"><strong>royalnet.error.InvalidInputError</strong></a> – if there are less than <code class="docutils literal notranslate"><span class="pre">require_at_least</span></code> arguments.</p>
|
||||
<dd class="field-even"><p><strong>royalnet.error.InvalidInputError</strong> – if there are less than <code class="docutils literal notranslate"><span class="pre">require_at_least</span></code> arguments.</p>
|
||||
</dd>
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The space-joined string.</p>
|
||||
|
@ -780,7 +779,7 @@ That probably means, the database row identifying the user.</p>
|
|||
<dd class="field-odd"><p><strong>pattern</strong> – The regex pattern to be passed to <a class="reference external" href="https://docs.python.org/3.7/library/re.html#re.match" title="(in Python v3.7)"><code class="xref py py-func docutils literal notranslate"><span class="pre">re.match()</span></code></a>.</p>
|
||||
</dd>
|
||||
<dt class="field-even">Raises</dt>
|
||||
<dd class="field-even"><p><a class="reference internal" href="#royalnet.error.InvalidInputError" title="royalnet.error.InvalidInputError"><strong>royalnet.error.InvalidInputError</strong></a> – if the pattern doesn’t match.</p>
|
||||
<dd class="field-even"><p><strong>royalnet.error.InvalidInputError</strong> – if the pattern doesn’t match.</p>
|
||||
</dd>
|
||||
<dt class="field-odd">Returns</dt>
|
||||
<dd class="field-odd"><p>The matched groups, as returned by <code class="xref py py-func docutils literal notranslate"><span class="pre">re.Match.groups()</span></code>.</p>
|
||||
|
@ -807,6 +806,33 @@ That probably means, the database row identifying the user.</p>
|
|||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.commands.CommandError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">CommandError</code><span class="sig-paren">(</span><em class="sig-param">message=''</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.CommandError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Something went wrong during the execution of this command.</p>
|
||||
<p>Display an error message to the user, explaining what went wrong.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.commands.InvalidInputError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">InvalidInputError</code><span class="sig-paren">(</span><em class="sig-param">message=''</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.InvalidInputError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The command has received invalid input and cannot complete.</p>
|
||||
<p>Display an error message to the user, along with the correct syntax for the command.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.commands.UnsupportedError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">UnsupportedError</code><span class="sig-paren">(</span><em class="sig-param">message=''</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.UnsupportedError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A requested feature is not available on this interface.</p>
|
||||
<p>Display an error message to the user, telling them to use another interface.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.commands.KeyboardExpiredError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.commands.</code><code class="sig-name descname">KeyboardExpiredError</code><span class="sig-paren">(</span><em class="sig-param">message=''</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.commands.KeyboardExpiredError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A special type of exception that can be raised in keyboard handlers to mark a specific keyboard as expired.</p>
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
<div class="section" id="module-royalnet.database">
|
||||
<span id="database"></span><h2>Database<a class="headerlink" href="#module-royalnet.database" title="Permalink to this headline">¶</a></h2>
|
||||
|
@ -896,7 +922,7 @@ That probably means, the database row identifying the user.</p>
|
|||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.network.NetworkLink.run">
|
||||
<em class="property">async </em><code class="sig-name descname">run</code><span class="sig-paren">(</span><em class="sig-param">loops: numbers.Real = inf</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkLink.run" title="Permalink to this definition">¶</a></dt>
|
||||
<em class="property">async </em><code class="sig-name descname">run</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkLink.run" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Blockingly run the Link.</p>
|
||||
</dd></dl>
|
||||
|
||||
|
@ -1031,19 +1057,14 @@ Contains info about the source and the destination.</p>
|
|||
<dd><p>Executed every time a package is received and must be routed somewhere.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.network.NetworkServer.run">
|
||||
<em class="property">async </em><code class="sig-name descname">run</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkServer.run" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.network.NetworkServer.run_blocking">
|
||||
<code class="sig-name descname">run_blocking</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkServer.run_blocking" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">run_blocking</code><span class="sig-paren">(</span><em class="sig-param">verbose=False</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkServer.run_blocking" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="royalnet.network.NetworkServer.serve">
|
||||
<em class="property">async </em><code class="sig-name descname">serve</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkServer.serve" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">serve</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.network.NetworkServer.serve" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
@ -1347,48 +1368,6 @@ Use with caution?</p>
|
|||
</div>
|
||||
<div class="section" id="module-royalnet.error">
|
||||
<span id="error"></span><h2>Error<a class="headerlink" href="#module-royalnet.error" title="Permalink to this headline">¶</a></h2>
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.CurrentlyDisabledError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">CurrentlyDisabledError</code><a class="headerlink" href="#royalnet.error.CurrentlyDisabledError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>This feature is temporarely disabled and is not available right now.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.ExternalError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">ExternalError</code><a class="headerlink" href="#royalnet.error.ExternalError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Something went wrong in a non-Royalnet component and the command execution cannot be completed.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.FileTooBigError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">FileTooBigError</code><a class="headerlink" href="#royalnet.error.FileTooBigError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The file to be downloaded would be too big to store; therefore, it has been skipped.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.InvalidConfigError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">InvalidConfigError</code><a class="headerlink" href="#royalnet.error.InvalidConfigError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The bot has not been configured correctly, therefore the command can not function.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.InvalidInputError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">InvalidInputError</code><a class="headerlink" href="#royalnet.error.InvalidInputError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The command has received invalid input and cannot complete.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.KeyboardExpiredError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">KeyboardExpiredError</code><a class="headerlink" href="#royalnet.error.KeyboardExpiredError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A special type of exception that can be raised in keyboard handlers to mark a specific keyboard as expired.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.NoneFoundError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">NoneFoundError</code><a class="headerlink" href="#royalnet.error.NoneFoundError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The element that was being looked for was not found.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.RoyalnetRequestError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">RoyalnetRequestError</code><span class="sig-paren">(</span><em class="sig-param">error: ResponseError</em><span class="sig-paren">)</span><a class="headerlink" href="#royalnet.error.RoyalnetRequestError" title="Permalink to this definition">¶</a></dt>
|
||||
|
@ -1407,24 +1386,6 @@ Use with caution?</p>
|
|||
<dd><p>The <a class="reference internal" href="#royalnet.network.Response" title="royalnet.network.Response"><code class="xref py py-class docutils literal notranslate"><span class="pre">royalnet.network.Response</span></code></a> that was received is invalid.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.TooManyFoundError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">TooManyFoundError</code><a class="headerlink" href="#royalnet.error.TooManyFoundError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Multiple elements matching the request were found, and only one was expected.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.UnregisteredError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">UnregisteredError</code><a class="headerlink" href="#royalnet.error.UnregisteredError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The command required a registered user, and the user was not registered.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="exception">
|
||||
<dt id="royalnet.error.UnsupportedError">
|
||||
<em class="property">exception </em><code class="sig-prename descclassname">royalnet.error.</code><code class="sig-name descname">UnsupportedError</code><a class="headerlink" href="#royalnet.error.UnsupportedError" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The command is not supported for the specified interface.</p>
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
<li class="toctree-l3"><a class="reference internal" href="#regular-expressions">Regular expressions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#raising-errors">Raising errors</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#running-code-at-the-initialization-of-the-bot">Running code at the initialization of the bot</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#coroutines-and-slow-operations">Coroutines and slow operations</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#delete-the-invoking-message">Delete the invoking message</a></li>
|
||||
|
@ -256,7 +257,7 @@ ones.</p>
|
|||
<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.7/library/stdtypes.html#str" title="(in Python v3.7)"><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.error.InvalidInputError" title="royalnet.error.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code></a> is raised, making the arguments accessed in this way <strong>required</strong>.</p>
|
||||
<code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code> 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>
|
||||
|
||||
|
@ -295,7 +296,7 @@ will return <code class="xref py py-const docutils literal notranslate"><span cl
|
|||
<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.error.InvalidInputError" title="royalnet.error.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code></a> will be
|
||||
<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"># InvalidInputError() is raised</span>
|
||||
|
@ -306,7 +307,7 @@ raised if not enough arguments are present:</p>
|
|||
<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.error.InvalidInputError" title="royalnet.error.InvalidInputError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">royalnet.error.InvalidInputError</span></code></a> if there is no match.</p>
|
||||
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.7/library/re.html#re.match" title="(in Python v3.7)"><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>
|
||||
|
@ -323,6 +324,25 @@ which returns a tuple of the matched groups and raises an <a class="reference in
|
|||
</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>You can also manually raise <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> to redisplay the command syntax, along with your error message:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_pasta</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="n">InvalidInputError</span><span class="p">(</span><span class="s2">"The specified pasta type is invalid."</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you need a Royalnet feature that’s not available on the current interface, you can raise an
|
||||
<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> with a brief description of what’s missing:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">interface</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"telegram"</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="n">UnsupportedError</span><span class="p">(</span><span class="s2">"This command can only be run on Telegram interfaces."</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</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>You can run code while the bot is starting by overriding the <code class="xref py py-meth docutils literal notranslate"><span class="pre">Command.__init__()</span></code> function.</p>
|
||||
|
|
|
@ -155,7 +155,6 @@
|
|||
| <a href="#B"><strong>B</strong></a>
|
||||
| <a href="#C"><strong>C</strong></a>
|
||||
| <a href="#D"><strong>D</strong></a>
|
||||
| <a href="#E"><strong>E</strong></a>
|
||||
| <a href="#F"><strong>F</strong></a>
|
||||
| <a href="#G"><strong>G</strong></a>
|
||||
| <a href="#H"><strong>H</strong></a>
|
||||
|
@ -260,10 +259,12 @@
|
|||
<li><a href="apireference.html#royalnet.bots.DiscordBot.advance_music_data">advance_music_data() (royalnet.bots.DiscordBot method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.database.Alchemy">Alchemy (class in royalnet.database)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.commands.CommandInterface.alchemy">alchemy (royalnet.commands.CommandInterface attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.commands.CommandInterface.alchemy">alchemy (royalnet.commands.CommandInterface attribute)</a>
|
||||
<li><a href="apireference.html#royalnet.commands.Command.aliases">aliases (royalnet.commands.Command attribute)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.utils.andformat">andformat() (in module royalnet.utils)</a>
|
||||
</li>
|
||||
|
@ -292,25 +293,23 @@
|
|||
<li><a href="apireference.html#royalnet.commands.CommandArgs">CommandArgs (class in royalnet.commands)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.commands.CommandData">CommandData (class in royalnet.commands)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.commands.CommandError">CommandError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.commands.CommandInterface">CommandInterface (class in royalnet.commands)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkLink.connect">connect() (royalnet.network.NetworkLink method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.ConnectionClosedError">ConnectionClosedError</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.network.ConnectionClosedError">ConnectionClosedError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlMp3.convert_to_mp3">convert_to_mp3() (royalnet.audio.YtdlMp3 method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlDiscord.convert_to_pcm">convert_to_pcm() (royalnet.audio.YtdlDiscord method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlDiscord.create_and_ready_from_url">create_and_ready_from_url() (royalnet.audio.YtdlDiscord class method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlMp3.create_and_ready_from_url">(royalnet.audio.YtdlMp3 class method)</a>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlMp3.create_and_ready_from_url">create_and_ready_from_url() (royalnet.audio.YtdlMp3 class method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="apireference.html#royalnet.web.create_app">create_app() (in module royalnet.web)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlDiscord.create_from_url">create_from_url() (royalnet.audio.YtdlDiscord class method)</a>
|
||||
|
@ -319,8 +318,6 @@
|
|||
<li><a href="apireference.html#royalnet.audio.YtdlMp3.create_from_url">(royalnet.audio.YtdlMp3 class method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="apireference.html#royalnet.error.CurrentlyDisabledError">CurrentlyDisabledError</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
|
@ -354,22 +351,12 @@
|
|||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
<h2 id="E">E</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.error.ExternalError">ExternalError</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
<h2 id="F">F</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.audio.FileAudioSource">FileAudioSource (class in royalnet.audio)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.utils.fileformat">fileformat() (in module royalnet.utils)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.error.FileTooBigError">FileTooBigError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkServer.find_client">find_client() (royalnet.network.NetworkServer method)</a>
|
||||
</li>
|
||||
|
@ -435,9 +422,7 @@
|
|||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.error.InvalidConfigError">InvalidConfigError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.error.InvalidInputError">InvalidInputError</a>
|
||||
<li><a href="apireference.html#royalnet.commands.InvalidInputError">InvalidInputError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.audio.YtdlFile.is_downloaded">is_downloaded() (royalnet.audio.YtdlFile method)</a>
|
||||
</li>
|
||||
|
@ -461,7 +446,7 @@
|
|||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.error.KeyboardExpiredError">KeyboardExpiredError</a>
|
||||
<li><a href="apireference.html#royalnet.commands.KeyboardExpiredError">KeyboardExpiredError</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
@ -504,16 +489,14 @@
|
|||
<li><a href="apireference.html#royalnet.network.NetworkConfig">NetworkConfig (class in royalnet.network)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkError">NetworkError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.utils.NetworkHandler">NetworkHandler (class in royalnet.utils)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.utils.NetworkHandler">NetworkHandler (class in royalnet.utils)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkLink">NetworkLink (class in royalnet.network)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkServer">NetworkServer (class in royalnet.network)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.error.NoneFoundError">NoneFoundError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NotConnectedError">NotConnectedError</a>
|
||||
</li>
|
||||
|
@ -643,8 +626,6 @@
|
|||
<li><a href="apireference.html#royalnet.commands.Command.run">(royalnet.commands.Command method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkLink.run">(royalnet.network.NetworkLink method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.NetworkServer.run">(royalnet.network.NetworkServer method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="apireference.html#royalnet.bots.GenericBot.run_blocking">run_blocking() (royalnet.bots.GenericBot method)</a>
|
||||
|
@ -708,8 +689,6 @@
|
|||
<li><a href="apireference.html#royalnet.network.Package.to_json_bytes">to_json_bytes() (royalnet.network.Package method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.network.Package.to_json_string">to_json_string() (royalnet.network.Package method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.error.TooManyFoundError">TooManyFoundError</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
@ -720,12 +699,10 @@
|
|||
<li><a href="apireference.html#royalnet.commands.CommandInterface.unregister_keyboard_key">unregister_keyboard_key() (royalnet.commands.CommandInterface method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.commands.CommandInterface.unregister_net_handler">unregister_net_handler() (royalnet.commands.CommandInterface method)</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.error.UnregisteredError">UnregisteredError</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="apireference.html#royalnet.error.UnsupportedError">UnsupportedError</a>
|
||||
<li><a href="apireference.html#royalnet.commands.UnsupportedError">UnsupportedError</a>
|
||||
</li>
|
||||
<li><a href="apireference.html#royalnet.bots.DiscordBot.update_activity_with_source_title">update_activity_with_source_title() (royalnet.bots.DiscordBot method)</a>
|
||||
</li>
|
||||
|
|
|
@ -153,7 +153,11 @@
|
|||
<p>Welcome to the documentation of Royalnet!</p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="runningroyalnet.html">Running Royalnet</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="runningroyalnet.html">Running Royalnet</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="runningroyalnet.html#the-keyring">The Keyring</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="runningroyalnet.html#running-the-bots">Running the bots</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="creatingacommand.html">Royalnet Commands</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="creatingacommand.html#creating-a-new-command">Creating a new Command</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="creatingacommand.html#command-arguments">Command arguments</a><ul>
|
||||
|
@ -163,6 +167,7 @@
|
|||
<li class="toctree-l3"><a class="reference internal" href="creatingacommand.html#regular-expressions">Regular expressions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="creatingacommand.html#raising-errors">Raising errors</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="creatingacommand.html#running-code-at-the-initialization-of-the-bot">Running code at the initialization of the bot</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="creatingacommand.html#coroutines-and-slow-operations">Coroutines and slow operations</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="creatingacommand.html#delete-the-invoking-message">Delete the invoking message</a></li>
|
||||
|
|
Binary file not shown.
|
@ -82,7 +82,11 @@
|
|||
|
||||
|
||||
<ul class="current">
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Running Royalnet</a></li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Running Royalnet</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-keyring">The Keyring</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#running-the-bots">Running the bots</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="creatingacommand.html">Royalnet Commands</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="apireference.html">API Reference</a></li>
|
||||
</ul>
|
||||
|
@ -151,7 +155,56 @@
|
|||
|
||||
<div class="section" id="running-royalnet">
|
||||
<h1>Running Royalnet<a class="headerlink" href="#running-royalnet" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This documentation page hasn’t been written yet, please refer to the README until then.</p>
|
||||
<p>To run a <code class="docutils literal notranslate"><span class="pre">royalnet</span></code> instance, you have first to download the package from <code class="docutils literal notranslate"><span class="pre">pip</span></code>:</p>
|
||||
<div class="section" id="the-keyring">
|
||||
<h2>The Keyring<a class="headerlink" href="#the-keyring" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">royalnet</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To run <code class="docutils literal notranslate"><span class="pre">royalnet</span></code>, you’ll have to setup the system keyring.</p>
|
||||
<p>On Windows and desktop Linux, this is already configured;
|
||||
on a headless Linux instance, you’ll need to <a class="reference external" href="https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems">manually start and unlock the keyring daemon</a>.</p>
|
||||
<p>Now you have to create a new <code class="docutils literal notranslate"><span class="pre">royalnet</span></code> configuration. Start the configuration wizard:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">royalnet</span><span class="o">.</span><span class="n">configurator</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You’ll be prompted to enter a “secrets name”: this is the name of the group of API keys that will be associated with
|
||||
your bot. Enter a name that you’ll be able to remember.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Desired</span> <span class="n">secrets</span> <span class="n">name</span> <span class="p">[</span><span class="n">__default__</span><span class="p">]:</span> <span class="n">royalgames</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You’ll then be asked for a network password.</p>
|
||||
<p>This password is used to connect to the rest of the <a class="reference internal" href="apireference.html#module-royalnet.network" title="royalnet.network"><code class="xref py py-mod docutils literal notranslate"><span class="pre">royalnet.network</span></code></a>, or, if you’re hosting a local Network,
|
||||
it will be the necessary password to connect to it:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Network</span> <span class="n">password</span> <span class="p">[]:</span> <span class="n">cosafaunapesuunafoglia</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Then you’ll be asked for a Telegram Bot API token.
|
||||
You can get one from <a class="reference external" href="https://t.me/BotFather">@BotFather</a>.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Telegram</span> <span class="n">Bot</span> <span class="n">API</span> <span class="n">token</span> <span class="p">[]:</span> <span class="mi">000000000</span><span class="p">:</span><span class="n">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The next prompt will ask for a Discord Bot API token.
|
||||
You can get one at the <a class="reference external" href="https://discordapp.com/developers/applications/">Discord Developers Portal</a>.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Discord</span> <span class="n">Bot</span> <span class="n">API</span> <span class="n">token</span> <span class="p">[]:</span> <span class="n">AAAAAAAAAAAAAAAAAAAAAAAA</span><span class="o">.</span><span class="n">AAAAAA</span><span class="o">.</span><span class="n">AAAAAAAAAAAAAAAAAAAAAAAAAAA</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now the configurator will ask you for a Imgur API token.
|
||||
<a class="reference external" href="https://api.imgur.com/oauth2/addclient">Register an application</a> on Imgur to be supplied one.
|
||||
The token should be of type “anonymous usage without user authorization”.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Imgur</span> <span class="n">API</span> <span class="n">token</span> <span class="p">[]:</span> <span class="n">aaaaaaaaaaaaaaa</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Next, you’ll be asked for a Sentry DSN. You probably won’t have one, so just ignore it and press enter.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Sentry</span> <span class="n">DSN</span> <span class="p">[]:</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now that all tokens are configured, you’re ready to launch the bot!</p>
|
||||
</div>
|
||||
<div class="section" id="running-the-bots">
|
||||
<h2>Running the bots<a class="headerlink" href="#running-the-bots" title="Permalink to this headline">¶</a></h2>
|
||||
<p>TODO</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -137,7 +137,7 @@ If you want the full argument string, you can use the :py:meth:`CommandArgs.join
|
|||
args.joined()
|
||||
# "carbonara al-dente"
|
||||
|
||||
You can specify a minimum number of arguments too, so that an :py:exc:`royalnet.error.InvalidInputError` will be
|
||||
You can specify a minimum number of arguments too, so that an :py:exc:`InvalidInputError` will be
|
||||
raised if not enough arguments are present: ::
|
||||
|
||||
args.joined(require_at_least=3)
|
||||
|
@ -149,7 +149,7 @@ Regular expressions
|
|||
For more complex commands, you may want to get arguments through `regular expressions <https://regexr.com/>`_.
|
||||
|
||||
You can then use the :py:meth:`CommandArgs.match` method, which tries to match a pattern to the command argument string,
|
||||
which returns a tuple of the matched groups and raises an :py:exc:`royalnet.error.InvalidInputError` if there is no match.
|
||||
which returns a tuple of the matched groups and raises an :py:exc:`InvalidInputError` if there is no match.
|
||||
|
||||
To match a pattern, :py:func:`re.match` is used, meaning that Python will try to match only at the beginning of the string. ::
|
||||
|
||||
|
@ -165,6 +165,25 @@ To match a pattern, :py:func:`re.match` is used, meaning that Python will try to
|
|||
args.match(r"\s*(carb\w+)\s*(al-\w+)")
|
||||
# ("carbonara", "al-dente")
|
||||
|
||||
Raising errors
|
||||
---------------------------------------------
|
||||
|
||||
If you want to display an error message to the user, you can raise a :py:exc:`CommandError` using the error message as argument: ::
|
||||
|
||||
if not kitchen.is_open():
|
||||
raise CommandError("The kitchen is closed. Come back later!")
|
||||
|
||||
You can also manually raise :py:exc:`InvalidInputError` to redisplay the command syntax, along with your error message: ::
|
||||
|
||||
if args[0] not in allowed_pasta:
|
||||
raise InvalidInputError("The specified pasta type is invalid.")
|
||||
|
||||
If you need a Royalnet feature that's not available on the current interface, you can raise an
|
||||
:py:exc:`UnsupportedError` with a brief description of what's missing: ::
|
||||
|
||||
if interface.name != "telegram":
|
||||
raise UnsupportedError("This command can only be run on Telegram interfaces.")
|
||||
|
||||
Running code at the initialization of the bot
|
||||
---------------------------------------------
|
||||
|
||||
|
|
|
@ -3,4 +3,60 @@
|
|||
Running Royalnet
|
||||
====================================
|
||||
|
||||
This documentation page hasn't been written yet, please refer to the README until then.
|
||||
To run a ``royalnet`` instance, you have first to download the package from ``pip``:
|
||||
|
||||
The Keyring
|
||||
------------------------------------
|
||||
::
|
||||
|
||||
pip install royalnet
|
||||
|
||||
|
||||
To run ``royalnet``, you'll have to setup the system keyring.
|
||||
|
||||
On Windows and desktop Linux, this is already configured;
|
||||
on a headless Linux instance, you'll need to `manually start and unlock the keyring daemon
|
||||
<https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems>`_.
|
||||
|
||||
Now you have to create a new ``royalnet`` configuration. Start the configuration wizard: ::
|
||||
|
||||
python -m royalnet.configurator
|
||||
|
||||
You'll be prompted to enter a "secrets name": this is the name of the group of API keys that will be associated with
|
||||
your bot. Enter a name that you'll be able to remember. ::
|
||||
|
||||
Desired secrets name [__default__]: royalgames
|
||||
|
||||
You'll then be asked for a network password.
|
||||
|
||||
This password is used to connect to the rest of the :py:mod:`royalnet.network`, or, if you're hosting a local Network,
|
||||
it will be the necessary password to connect to it: ::
|
||||
|
||||
Network password []: cosafaunapesuunafoglia
|
||||
|
||||
Then you'll be asked for a Telegram Bot API token.
|
||||
You can get one from `@BotFather <https://t.me/BotFather>`_. ::
|
||||
|
||||
Telegram Bot API token []: 000000000:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
||||
The next prompt will ask for a Discord Bot API token.
|
||||
You can get one at the `Discord Developers Portal <https://discordapp.com/developers/applications/>`_. ::
|
||||
|
||||
Discord Bot API token []: AAAAAAAAAAAAAAAAAAAAAAAA.AAAAAA.AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
||||
Now the configurator will ask you for a Imgur API token.
|
||||
`Register an application <https://api.imgur.com/oauth2/addclient>`_ on Imgur to be supplied one.
|
||||
The token should be of type "anonymous usage without user authorization". ::
|
||||
|
||||
Imgur API token []: aaaaaaaaaaaaaaa
|
||||
|
||||
Next, you'll be asked for a Sentry DSN. You probably won't have one, so just ignore it and press enter. ::
|
||||
|
||||
Sentry DSN []:
|
||||
|
||||
Now that all tokens are configured, you're ready to launch the bot!
|
||||
|
||||
Running the bots
|
||||
------------------------------------
|
||||
|
||||
TODO
|
|
@ -52,7 +52,7 @@ class DiscordBot(GenericBot):
|
|||
query = query.filter(self.identity_column == user.id)
|
||||
result = await asyncify(query.one_or_none)
|
||||
if result is None and error_if_none:
|
||||
raise UnregisteredError("Author is not registered")
|
||||
raise CommandError("You must be registered to use this command.")
|
||||
return result
|
||||
|
||||
async def delete_invoking(data, error_if_unavailable=False):
|
||||
|
@ -115,52 +115,42 @@ class DiscordBot(GenericBot):
|
|||
# Call the command
|
||||
log.debug(f"Calling command '{command.name}'")
|
||||
with message.channel.typing():
|
||||
# Run the command
|
||||
try:
|
||||
await command.run(CommandArgs(parameters), data=data)
|
||||
await command.run(CommandArgs(parameters), data)
|
||||
except InvalidInputError as e:
|
||||
await data.reply(f":warning: {' '.join(e.args)}\n"
|
||||
f"Syntax: [c]!{command.name} {command.syntax}[/c]")
|
||||
await data.reply(f"⚠️ {e.message}\n"
|
||||
f"Syntax: [c]/{command.name} {command.syntax}[/c]")
|
||||
except UnsupportedError as e:
|
||||
await data.reply(f"⚠️ {e.message}")
|
||||
except CommandError as e:
|
||||
await data.reply(f"⚠️ {e.message}")
|
||||
except Exception as e:
|
||||
sentry_sdk.capture_exception(e)
|
||||
error_message = f"🦀 {e.__class__.__name__} 🦀\n"
|
||||
error_message = f"🦀 [b]{e.__class__.__name__}[/b] 🦀\n"
|
||||
error_message += '\n'.join(e.args)
|
||||
log.error(f"Error in {command.name}: {error_message}")
|
||||
await data.reply(f"{error_message}")
|
||||
if __debug__:
|
||||
raise
|
||||
await data.reply(error_message)
|
||||
|
||||
async def on_ready(cli):
|
||||
async def on_ready(cli) -> None:
|
||||
log.debug("Connection successful, client is ready")
|
||||
await cli.change_presence(status=discord.Status.online)
|
||||
|
||||
def find_guild_by_name(cli, name: str) -> discord.Guild:
|
||||
"""Find the :py:class:`discord.Guild` with the specified name. Case-insensitive.
|
||||
|
||||
Raises:
|
||||
:py:exc:`NoneFoundError` if no channels are found.
|
||||
:py:exc:`TooManyFoundError` if more than one is found."""
|
||||
def find_guild_by_name(cli, name: str) -> typing.List[discord.Guild]:
|
||||
"""Find the :py:class:`discord.Guild` with the specified name (case insensitive)."""
|
||||
all_guilds: typing.List[discord.Guild] = cli.guilds
|
||||
matching_channels: typing.List[discord.Guild] = []
|
||||
for guild in all_guilds:
|
||||
if guild.name.lower() == name.lower():
|
||||
matching_channels.append(guild)
|
||||
if len(matching_channels) == 0:
|
||||
raise NoneFoundError("No channels were found")
|
||||
elif len(matching_channels) > 1:
|
||||
raise TooManyFoundError("Too many channels were found")
|
||||
return matching_channels[0]
|
||||
return matching_channels
|
||||
|
||||
def find_channel_by_name(cli,
|
||||
name: str,
|
||||
guild: typing.Optional[discord.Guild] = None) -> discord.abc.GuildChannel:
|
||||
guild: typing.Optional[discord.Guild] = None) -> typing.List[discord.abc.GuildChannel]:
|
||||
"""Find the :py:class:`TextChannel`, :py:class:`VoiceChannel` or :py:class:`CategoryChannel` with the
|
||||
specified name.
|
||||
specified name (case insensitive).
|
||||
|
||||
Case-insensitive.
|
||||
|
||||
Guild is optional, but the method will raise a :py:exc:`TooManyFoundError` if none is specified and
|
||||
there is more than one channel with the same name. Will also raise a :py:exc:`NoneFoundError` if no
|
||||
channels are found. """
|
||||
You can specify a guild to only find channels in that specific guild."""
|
||||
if guild is not None:
|
||||
all_channels = guild.channels
|
||||
else:
|
||||
|
@ -173,21 +163,14 @@ class DiscordBot(GenericBot):
|
|||
continue
|
||||
if channel.name.lower() == name.lower():
|
||||
matching_channels.append(channel)
|
||||
if len(matching_channels) == 0:
|
||||
raise NoneFoundError("No channels were found")
|
||||
elif len(matching_channels) > 1:
|
||||
raise TooManyFoundError("Too many channels were found")
|
||||
return matching_channels[0]
|
||||
return matching_channels
|
||||
|
||||
def find_voice_client_by_guild(cli, guild: discord.Guild):
|
||||
"""Find the :py:class:`discord.VoiceClient` belonging to a specific :py:class:`discord.Guild`.
|
||||
|
||||
Raises:
|
||||
:py:exc:`NoneFoundError` if the :py:class:`discord.Guild` currently has no :py:class:`discord.VoiceClient`."""
|
||||
def find_voice_client_by_guild(cli, guild: discord.Guild) -> typing.Optional[discord.VoiceClient]:
|
||||
"""Find the :py:class:`discord.VoiceClient` belonging to a specific :py:class:`discord.Guild`."""
|
||||
for voice_client in cli.voice_clients:
|
||||
if voice_client.guild == guild:
|
||||
return voice_client
|
||||
raise NoneFoundError("No voice clients found")
|
||||
return None
|
||||
|
||||
return DiscordClient
|
||||
|
||||
|
|
|
@ -103,18 +103,15 @@ class GenericBot:
|
|||
log.debug(f"Using {network_handler} as handler for {request.handler}")
|
||||
response: Response = await getattr(network_handler, self.interface_name)(self, request.data)
|
||||
return response.to_dict()
|
||||
except Exception:
|
||||
if __debug__:
|
||||
raise
|
||||
exit(1)
|
||||
_, exc, _ = sys.exc_info()
|
||||
log.debug(f"Exception {exc} in {network_handler}")
|
||||
except Exception as e:
|
||||
sentry_sdk.capture_exception(e)
|
||||
log.debug(f"Exception {e} in {network_handler}")
|
||||
return ResponseError("exception_in_handler",
|
||||
f"An exception was raised in {network_handler} for {request.handler}. Check "
|
||||
f"extra_info for details.",
|
||||
extra_info={
|
||||
"type": exc.__class__.__name__,
|
||||
"str": str(exc)
|
||||
"type": e.__class__.__name__,
|
||||
"str": str(e)
|
||||
}).to_dict()
|
||||
|
||||
def _init_database(self):
|
||||
|
@ -127,8 +124,8 @@ class GenericBot:
|
|||
required_tables = required_tables.union(command.require_alchemy_tables)
|
||||
log.debug(f"Found {len(required_tables)} required tables")
|
||||
self.alchemy = Alchemy(self.uninitialized_database_config.database_uri, required_tables)
|
||||
self.master_table = self.alchemy.__getattribute__(self.uninitialized_database_config.master_table.__name__)
|
||||
self.identity_table = self.alchemy.__getattribute__(self.uninitialized_database_config.identity_table.__name__)
|
||||
self.master_table = self.alchemy.__getattribute__(self.uninitialized_database_config.master_table.__qualname__)
|
||||
self.identity_table = self.alchemy.__getattribute__(self.uninitialized_database_config.identity_table.__qualname__)
|
||||
self.identity_column = self.identity_table.__getattribute__(self.identity_table,
|
||||
self.uninitialized_database_config.identity_column_name)
|
||||
self.identity_chain = relationshiplinkchain(self.master_table, self.identity_table)
|
||||
|
|
|
@ -166,15 +166,17 @@ class TelegramBot(GenericBot):
|
|||
try:
|
||||
await command.run(CommandArgs(parameters), data)
|
||||
except InvalidInputError as e:
|
||||
await data.reply(f"⚠️ {' '.join(e.args)}\n"
|
||||
await data.reply(f"⚠️ {e.message}\n"
|
||||
f"Syntax: [c]/{command.name} {command.syntax}[/c]")
|
||||
except UnsupportedError as e:
|
||||
await data.reply(f"⚠️ {e.message}")
|
||||
except CommandError as e:
|
||||
await data.reply(f"⚠️ {e.message}")
|
||||
except Exception as e:
|
||||
sentry_sdk.capture_exception(e)
|
||||
error_message = f"🦀 [b]{e.__class__.__name__}[/b] 🦀\n"
|
||||
error_message += '\n'.join(e.args)
|
||||
await data.reply(error_message)
|
||||
if __debug__:
|
||||
raise
|
||||
|
||||
async def _handle_callback_query(self, update: telegram.Update):
|
||||
query: telegram.CallbackQuery = update.callback_query
|
||||
|
|
|
@ -8,17 +8,11 @@ from .color import ColorCommand
|
|||
from .cv import CvCommand
|
||||
from .diario import DiarioCommand
|
||||
from .mp3 import Mp3Command
|
||||
from .pause import PauseCommand
|
||||
from .ping import PingCommand
|
||||
from .play import PlayCommand
|
||||
from .playmode import PlaymodeCommand
|
||||
from .queue import QueueCommand
|
||||
from .rage import RageCommand
|
||||
from .reminder import ReminderCommand
|
||||
from .ship import ShipCommand
|
||||
from .skip import SkipCommand
|
||||
from .smecds import SmecdsCommand
|
||||
from .summon import SummonCommand
|
||||
from .videochannel import VideochannelCommand
|
||||
from .dnditem import DnditemCommand
|
||||
from .dndspell import DndspellCommand
|
||||
|
@ -33,17 +27,11 @@ commands = [
|
|||
CvCommand,
|
||||
DiarioCommand,
|
||||
Mp3Command,
|
||||
PauseCommand,
|
||||
PingCommand,
|
||||
PlayCommand,
|
||||
PlaymodeCommand,
|
||||
QueueCommand,
|
||||
RageCommand,
|
||||
ReminderCommand,
|
||||
ShipCommand,
|
||||
SkipCommand,
|
||||
SmecdsCommand,
|
||||
SummonCommand,
|
||||
VideochannelCommand,
|
||||
DnditemCommand,
|
||||
DndspellCommand,
|
||||
|
|
|
@ -7,7 +7,7 @@ from ..commanddata import CommandData
|
|||
from ...network import Request, ResponseSuccess
|
||||
from ...utils import NetworkHandler, andformat
|
||||
from ...bots import DiscordBot
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError
|
||||
|
||||
|
||||
class CvNH(NetworkHandler):
|
||||
|
@ -17,13 +17,14 @@ class CvNH(NetworkHandler):
|
|||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild: discord.Guild = bot.client.find_guild_by_name(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.client.guilds) == 0:
|
||||
raise NoneFoundError("No guilds found")
|
||||
if len(bot.client.guilds) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Edit the message, sorted by channel
|
||||
discord_members = list(guild.members)
|
||||
channels = {0: None}
|
||||
|
@ -119,7 +120,7 @@ class CvCommand(Command):
|
|||
|
||||
description: str = "Elenca le persone attualmente connesse alla chat vocale."
|
||||
|
||||
syntax: str = "[guildname] "
|
||||
syntax: str = "[guildname] ['all']"
|
||||
|
||||
def __init__(self, interface: CommandInterface):
|
||||
super().__init__(interface)
|
||||
|
|
|
@ -8,7 +8,7 @@ from ..commandargs import CommandArgs
|
|||
from ..commanddata import CommandData
|
||||
from ...database.tables import User, Diario, Alias
|
||||
from ...utils import asyncify
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError, InvalidInputError
|
||||
|
||||
|
||||
async def to_imgur(imgur_api_key, photosizes: typing.List[telegram.PhotoSize], caption="") -> str:
|
||||
|
@ -27,7 +27,7 @@ async def to_imgur(imgur_api_key, photosizes: typing.List[telegram.PhotoSize], c
|
|||
}) as request:
|
||||
response = await request.json()
|
||||
if not response["success"]:
|
||||
raise ExternalError("imgur returned an error in the image upload.")
|
||||
raise CommandError("Imgur returned an error in the image upload.")
|
||||
return response["data"]["link"]
|
||||
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@ import telegram
|
|||
import asyncio
|
||||
import re
|
||||
import logging
|
||||
import typing
|
||||
from ..command import Command
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...database.tables import MMEvent, MMDecision, MMResponse
|
||||
from ...error import *
|
||||
from ..commanderrors import InvalidInputError, UnsupportedError
|
||||
from ...utils import asyncify, telegram_escape, sleep_until
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -11,8 +11,7 @@ from ..commandinterface import CommandInterface
|
|||
from ..commanddata import CommandData
|
||||
from ...utils import sleep_until, asyncify, telegram_escape, discord_escape
|
||||
from ...database.tables import Reminder
|
||||
from ...error import *
|
||||
|
||||
from ..commanderrors import InvalidInputError, UnsupportedError
|
||||
|
||||
class ReminderCommand(Command):
|
||||
name: str = "reminder"
|
||||
|
@ -76,7 +75,7 @@ class ReminderCommand(Command):
|
|||
elif self.interface.name == "discord":
|
||||
interface_data = pickle.dumps(data.message.channel.id)
|
||||
else:
|
||||
raise UnsupportedError("Interface not supported")
|
||||
raise UnsupportedError("This command does not support the current interface.")
|
||||
creator = await data.get_author()
|
||||
reminder = self.interface.alchemy.Reminder(creator=creator,
|
||||
interface_name=self.interface.name,
|
||||
|
|
|
@ -9,7 +9,7 @@ from ..commandargs import CommandArgs
|
|||
from ..commanddata import CommandData
|
||||
from ..commandinterface import CommandInterface
|
||||
from ...utils import asyncify
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError, KeyboardExpiredError
|
||||
from ...database.tables import TriviaScore
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ class TriviaCommand(Command):
|
|||
j = await response.json()
|
||||
# Parse the question
|
||||
if j["response_code"] != 0:
|
||||
raise ExternalError(f"OpenTDB returned {j['response_code']} response_code")
|
||||
raise CommandError(f"OpenTDB returned an error response_code ({j['response_code']}).")
|
||||
question = j["results"][0]
|
||||
text = f'❓ [b]{question["category"]} - {question["difficulty"].capitalize()}[/b]\n' \
|
||||
f'{html.unescape(question["question"])}'
|
||||
|
|
|
@ -3,7 +3,7 @@ import discord
|
|||
from ..command import Command
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError, UnsupportedError
|
||||
|
||||
|
||||
class VideochannelCommand(Command):
|
||||
|
@ -30,18 +30,15 @@ class VideochannelCommand(Command):
|
|||
if channel.name == channel_name:
|
||||
matching_channels.append(channel)
|
||||
if len(matching_channels) == 0:
|
||||
await data.reply("⚠️ Non esiste alcun canale vocale con il nome specificato.")
|
||||
return
|
||||
raise CommandError("Non esiste alcun canale vocale con il nome specificato.")
|
||||
elif len(matching_channels) > 1:
|
||||
await data.reply("⚠️ Esiste più di un canale vocale con il nome specificato.")
|
||||
return
|
||||
raise CommandError("Esiste più di un canale vocale con il nome specificato.")
|
||||
channel = matching_channels[0]
|
||||
else:
|
||||
author: discord.Member = message.author
|
||||
voice: typing.Optional[discord.VoiceState] = author.voice
|
||||
if voice is None:
|
||||
await data.reply("⚠️ Non sei connesso a nessun canale vocale!")
|
||||
return
|
||||
raise CommandError("Non sei connesso a nessun canale vocale.")
|
||||
channel = voice.channel
|
||||
if author.is_on_mobile():
|
||||
await data.reply(f"📹 Per entrare in modalità video, clicca qui: <https://discordapp.com/channels/{channel.guild.id}/{channel.id}>\n[b]Attenzione: la modalità video non funziona su Discord per Android e iOS![/b]")
|
||||
|
|
|
@ -8,7 +8,7 @@ from ..commandargs import CommandArgs
|
|||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler, asyncify
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError, InvalidInputError, UnsupportedError, KeyboardExpiredError
|
||||
from ...audio import YtdlDiscord
|
||||
from ...audio.playmodes import Playlist
|
||||
if typing.TYPE_CHECKING:
|
||||
|
@ -27,13 +27,14 @@ class ZawarudoNH(NetworkHandler):
|
|||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild = bot.client.find_guild(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.music_data) == 0:
|
||||
raise NoneFoundError("No voice clients active")
|
||||
if len(bot.music_data) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.music_data)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Ensure the guild has a PlayMode before adding the file to it
|
||||
if not bot.music_data.get(guild):
|
||||
# TODO: change Exception
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
"""Commands that can be used in bots.
|
||||
|
||||
These probably won't suit your needs, as they are tailored for the bots of the User Games gaming community, but they
|
||||
may be useful to develop new ones."""
|
||||
|
||||
from .pause import PauseCommand
|
||||
from .play import PlayCommand
|
||||
from .playmode import PlaymodeCommand
|
||||
from .queue import QueueCommand
|
||||
from .skip import SkipCommand
|
||||
from .summon import SummonCommand
|
||||
|
||||
|
||||
commands = [
|
||||
PauseCommand,
|
||||
PlayCommand,
|
||||
PlaymodeCommand,
|
||||
QueueCommand,
|
||||
SkipCommand,
|
||||
SummonCommand,
|
||||
]
|
||||
|
||||
|
||||
__all__ = [command.__name__ for command in commands]
|
|
@ -6,7 +6,7 @@ from ..commandargs import CommandArgs
|
|||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import NoneFoundError
|
||||
from ..commanderrors import CommandError
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
||||
|
@ -19,17 +19,18 @@ class PauseNH(NetworkHandler):
|
|||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild = bot.client.find_guild_by_name(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.music_data) == 0:
|
||||
raise NoneFoundError("No voice clients active")
|
||||
if len(bot.music_data) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.music_data)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Set the currently playing source as ended
|
||||
voice_client: discord.VoiceClient = bot.client.find_voice_client_by_guild(guild)
|
||||
if not (voice_client.is_playing() or voice_client.is_paused()):
|
||||
raise NoneFoundError("Nothing to pause")
|
||||
raise CommandError("There is nothing to pause.")
|
||||
# Toggle pause
|
||||
resume = voice_client._player.is_paused()
|
||||
if resume:
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import typing
|
||||
import pickle
|
||||
import datetime
|
||||
import discord
|
||||
from ..command import Command
|
||||
from ..commandinterface import CommandInterface
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler, asyncify
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError
|
||||
from ...audio import YtdlDiscord
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
@ -21,13 +22,14 @@ class PlayNH(NetworkHandler):
|
|||
"""Handle a play Royalnet request. That is, add audio to a PlayMode."""
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild = bot.client.find_guild(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.music_data) == 0:
|
||||
raise NoneFoundError("No voice clients active")
|
||||
if len(bot.music_data) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.music_data)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Ensure the guild has a PlayMode before adding the file to it
|
||||
if not bot.music_data.get(guild):
|
||||
# TODO: change Exception
|
||||
|
@ -53,7 +55,7 @@ class PlayNH(NetworkHandler):
|
|||
class PlayCommand(Command):
|
||||
name: str = "play"
|
||||
|
||||
description: str = "Aggiunge una canzone alla coda della chat vocale."
|
||||
description: str = "Aggiunge un url alla coda della chat vocale."
|
||||
|
||||
syntax = "[ [guild] ] (url)"
|
||||
|
||||
|
@ -64,7 +66,9 @@ class PlayCommand(Command):
|
|||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
guild_name, url = args.match(r"(?:\[(.+)])?\s*<?(.+)>?")
|
||||
if not (url.startswith("http://") or url.startswith("https://")):
|
||||
raise
|
||||
raise CommandError("PlayCommand only accepts URLs.\n"
|
||||
"If you want to search a song on YouTube or Soundcloud, please use YoutubeCommand"
|
||||
" or SoundcloudCommand!")
|
||||
response = await self.interface.net_request(Request("music_play", {"url": url, "guild_name": guild_name}), "discord")
|
||||
if len(response["videos"]) == 0:
|
||||
await data.reply(f"⚠️ Nessun video trovato.")
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import typing
|
||||
import pickle
|
||||
import discord
|
||||
from ..command import Command
|
||||
from ..commandinterface import CommandInterface
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError
|
||||
from ...audio.playmodes import Playlist, Pool, Layers
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
@ -20,13 +21,14 @@ class PlaymodeNH(NetworkHandler):
|
|||
"""Handle a playmode Royalnet request. That is, change current PlayMode."""
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild = bot.client.find_guild(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.music_data) == 0:
|
||||
raise NoneFoundError("No voice clients active")
|
||||
if len(bot.music_data) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.music_data)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Delete the previous PlayMode, if it exists
|
||||
if bot.music_data[guild] is not None:
|
||||
bot.music_data[guild].delete()
|
||||
|
@ -38,7 +40,7 @@ class PlaymodeNH(NetworkHandler):
|
|||
elif data["mode_name"] == "layers":
|
||||
bot.music_data[guild] = Layers()
|
||||
else:
|
||||
raise ValueError("No such PlayMode")
|
||||
raise CommandError("Unknown PlayMode specified.")
|
||||
return ResponseSuccess()
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import typing
|
||||
import pickle
|
||||
import discord
|
||||
from ..command import Command
|
||||
from ..commandinterface import CommandInterface
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler, numberemojiformat
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
||||
|
@ -18,13 +19,14 @@ class QueueNH(NetworkHandler):
|
|||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild = bot.client.find_guild_by_name(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.music_data) == 0:
|
||||
raise NoneFoundError("No voice clients active")
|
||||
if len(bot.music_data) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.music_data)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Check if the guild has a PlayMode
|
||||
playmode = bot.music_data.get(guild)
|
||||
if not playmode:
|
||||
|
|
|
@ -7,7 +7,7 @@ from ..commandargs import CommandArgs
|
|||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler, asyncify
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import *
|
||||
from ..commanderrors import CommandError
|
||||
from ...audio import YtdlDiscord
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
@ -20,17 +20,18 @@ class SkipNH(NetworkHandler):
|
|||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guild = bot.client.find_guild_by_name(data["guild_name"])
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
if len(bot.music_data) == 0:
|
||||
raise NoneFoundError("No voice clients active")
|
||||
if len(bot.music_data) > 1:
|
||||
raise TooManyFoundError("Multiple guilds found")
|
||||
guild = list(bot.music_data)[0]
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Set the currently playing source as ended
|
||||
voice_client: discord.VoiceClient = bot.client.find_voice_client_by_guild(guild)
|
||||
if not (voice_client.is_playing() or voice_client.is_paused()):
|
||||
raise NoneFoundError("Nothing to skip")
|
||||
raise CommandError("Nothing to skip")
|
||||
# noinspection PyProtectedMember
|
||||
voice_client._player.stop()
|
||||
return ResponseSuccess()
|
||||
|
|
84
royalnet/commands/royalmusic/soundcloud.py
Normal file
84
royalnet/commands/royalmusic/soundcloud.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
import typing
|
||||
import pickle
|
||||
import datetime
|
||||
import discord
|
||||
from ..command import Command
|
||||
from ..commandinterface import CommandInterface
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler, asyncify
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ..commanderrors import CommandError
|
||||
from ...audio import YtdlDiscord
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
||||
|
||||
class SoundcloudNH(NetworkHandler):
|
||||
message_type = "music_soundcloud"
|
||||
|
||||
@classmethod
|
||||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
"""Handle a play Royalnet request. That is, add audio to a PlayMode."""
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Ensure the guild has a PlayMode before adding the file to it
|
||||
if not bot.music_data.get(guild):
|
||||
raise KeyError("No music data available for this guild.")
|
||||
# Create url
|
||||
ytdl_args = {
|
||||
"format": "bestaudio",
|
||||
"outtmpl": f"./downloads/{datetime.datetime.now().timestamp()}_%(title)s.%(ext)s"
|
||||
}
|
||||
# Start downloading
|
||||
dfiles: typing. List[YtdlDiscord] = await asyncify(YtdlDiscord.create_from_url, f'scsearch:{data["search"]}',
|
||||
**ytdl_args)
|
||||
await bot.add_to_music_data(dfiles, guild)
|
||||
# Create response dictionary
|
||||
response = {
|
||||
"videos": [{
|
||||
"title": dfile.info.title,
|
||||
"discord_embed_pickle": str(pickle.dumps(dfile.info.to_discord_embed()))
|
||||
} for dfile in dfiles]
|
||||
}
|
||||
return ResponseSuccess(response)
|
||||
|
||||
|
||||
class SoundcloudCommand(Command):
|
||||
name: str = "soundcloud"
|
||||
|
||||
aliases = ["sc"]
|
||||
|
||||
description: str = "Cerca una canzone su Soundcloud e la aggiunge alla coda della chat vocale."
|
||||
|
||||
syntax = "[ [guild] ] (url)"
|
||||
|
||||
def __init__(self, interface: CommandInterface):
|
||||
super().__init__(interface)
|
||||
interface.register_net_handler(SoundcloudNH.message_type, SoundcloudNH)
|
||||
|
||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
guild_name, search = args.match(r"(?:\[(.+)])?\s*<?(.+)>?")
|
||||
if search.startswith("http://") or search.startswith("https://"):
|
||||
raise CommandError("YoutubeCommand only accepts search queries, and you've sent an URL.\n"
|
||||
"If you want to add a song from an url, please use PlayCommand!")
|
||||
response = await self.interface.net_request(Request("music_soundcloud", {"search": search,
|
||||
"guild_name": guild_name}),
|
||||
"discord")
|
||||
if len(response["videos"]) == 0:
|
||||
await data.reply(f"⚠️ Nessun video trovato.")
|
||||
for video in response["videos"]:
|
||||
if self.interface.name == "discord":
|
||||
# This is one of the unsafest things ever
|
||||
embed = pickle.loads(eval(video["discord_embed_pickle"]))
|
||||
await data.message.channel.send(content="▶️ Aggiunto alla coda:", embed=embed)
|
||||
else:
|
||||
await data.reply(f"▶️ Aggiunto alla coda: [i]{video['title']}[/i]")
|
|
@ -6,7 +6,7 @@ from ..commandargs import CommandArgs
|
|||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ...error import NoneFoundError
|
||||
from ..commanderrors import CommandError
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
||||
|
@ -18,9 +18,10 @@ class SummonNH(NetworkHandler):
|
|||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
"""Handle a summon Royalnet request.
|
||||
That is, join a voice channel, or move to a different one if that is not possible."""
|
||||
channel = bot.client.find_channel_by_name(data["channel_name"])
|
||||
channels = bot.client.find_channel_by_name(data["channel_name"])
|
||||
channel = channels[0]
|
||||
if not isinstance(channel, discord.VoiceChannel):
|
||||
raise NoneFoundError("Channel is not a voice channel")
|
||||
raise CommandError("Channel is not a voice channel")
|
||||
bot.loop.create_task(bot.client.vc_connect_or_move(channel))
|
||||
return ResponseSuccess()
|
||||
|
||||
|
|
83
royalnet/commands/royalmusic/youtube.py
Normal file
83
royalnet/commands/royalmusic/youtube.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
import typing
|
||||
import pickle
|
||||
import datetime
|
||||
import discord
|
||||
from ..command import Command
|
||||
from ..commandinterface import CommandInterface
|
||||
from ..commandargs import CommandArgs
|
||||
from ..commanddata import CommandData
|
||||
from ...utils import NetworkHandler, asyncify
|
||||
from ...network import Request, ResponseSuccess
|
||||
from ..commanderrors import CommandError
|
||||
from ...audio import YtdlDiscord
|
||||
if typing.TYPE_CHECKING:
|
||||
from ...bots import DiscordBot
|
||||
|
||||
|
||||
class YoutubeNH(NetworkHandler):
|
||||
message_type = "music_youtube"
|
||||
|
||||
@classmethod
|
||||
async def discord(cls, bot: "DiscordBot", data: dict):
|
||||
"""Handle a play Royalnet request. That is, add audio to a PlayMode."""
|
||||
# Find the matching guild
|
||||
if data["guild_name"]:
|
||||
guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(data["guild_name"])
|
||||
else:
|
||||
guilds = bot.client.guilds
|
||||
if len(guilds) == 0:
|
||||
raise CommandError("No guilds with the specified name found.")
|
||||
if len(guilds) > 1:
|
||||
raise CommandError("Multiple guilds with the specified name found.")
|
||||
guild = list(bot.client.guilds)[0]
|
||||
# Ensure the guild has a PlayMode before adding the file to it
|
||||
if not bot.music_data.get(guild):
|
||||
raise KeyError("No music data available for this guild.")
|
||||
# Create url
|
||||
ytdl_args = {
|
||||
"format": "bestaudio",
|
||||
"outtmpl": f"./downloads/{datetime.datetime.now().timestamp()}_%(title)s.%(ext)s"
|
||||
}
|
||||
# Start downloading
|
||||
dfiles: typing. List[YtdlDiscord] = await asyncify(YtdlDiscord.create_from_url, f'ytsearch:{data["search"]}', **ytdl_args)
|
||||
await bot.add_to_music_data(dfiles, guild)
|
||||
# Create response dictionary
|
||||
response = {
|
||||
"videos": [{
|
||||
"title": dfile.info.title,
|
||||
"discord_embed_pickle": str(pickle.dumps(dfile.info.to_discord_embed()))
|
||||
} for dfile in dfiles]
|
||||
}
|
||||
return ResponseSuccess(response)
|
||||
|
||||
|
||||
class YoutubeCommand(Command):
|
||||
name: str = "youtube"
|
||||
|
||||
aliases = ["yt"]
|
||||
|
||||
description: str = "Cerca un video su YouTube e lo aggiunge alla coda della chat vocale."
|
||||
|
||||
syntax = "[ [guild] ] (url)"
|
||||
|
||||
def __init__(self, interface: CommandInterface):
|
||||
super().__init__(interface)
|
||||
interface.register_net_handler(YoutubeNH.message_type, YoutubeNH)
|
||||
|
||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
guild_name, search = args.match(r"(?:\[(.+)])?\s*<?(.+)>?")
|
||||
if search.startswith("http://") or search.startswith("https://"):
|
||||
raise CommandError("YoutubeCommand only accepts search queries, and you've sent an URL.\n"
|
||||
"If you want to add a song from an url, please use PlayCommand!")
|
||||
response = await self.interface.net_request(Request("music_youtube", {"search": search,
|
||||
"guild_name": guild_name}),
|
||||
"discord")
|
||||
if len(response["videos"]) == 0:
|
||||
await data.reply(f"⚠️ Nessun video trovato.")
|
||||
for video in response["videos"]:
|
||||
if self.interface.name == "discord":
|
||||
# This is one of the unsafest things ever
|
||||
embed = pickle.loads(eval(video["discord_embed_pickle"]))
|
||||
await data.message.channel.send(content="▶️ Aggiunto alla coda:", embed=embed)
|
||||
else:
|
||||
await data.reply(f"▶️ Aggiunto alla coda: [i]{video['title']}[/i]")
|
|
@ -4,8 +4,6 @@ from sqlalchemy.ext.declarative import declarative_base
|
|||
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||
from contextlib import contextmanager, asynccontextmanager
|
||||
from ..utils import asyncify
|
||||
# noinspection PyUnresolvedReferences
|
||||
from ..error import InvalidConfigError
|
||||
|
||||
|
||||
class Alchemy:
|
||||
|
|
Loading…
Add table
Reference in a new issue