diff --git a/docs/doctrees/apireference.doctree b/docs/doctrees/apireference.doctree index 46fb5e7e..8c7e5075 100644 Binary files a/docs/doctrees/apireference.doctree and b/docs/doctrees/apireference.doctree differ diff --git a/docs/doctrees/environment.pickle b/docs/doctrees/environment.pickle index 308b3e3d..a7f9c07b 100644 Binary files a/docs/doctrees/environment.pickle and b/docs/doctrees/environment.pickle differ diff --git a/docs/html/_sources/apireference.rst.txt b/docs/html/_sources/apireference.rst.txt index 78f973f2..a8b13455 100644 --- a/docs/html/_sources/apireference.rst.txt +++ b/docs/html/_sources/apireference.rst.txt @@ -5,66 +5,7 @@ These pages were automatically generated from docstrings in code. They might be outdated, or incomplete. -Audio ------------------------------------- - -.. automodule:: royalnet.audio - :members: - :undoc-members: - :private-members: - -Bots ------------------------------------- - -.. automodule:: royalnet.bots - :members: - :undoc-members: - :private-members: - -Commands ------------------------------------- - -.. automodule:: royalnet.commands - :members: - :undoc-members: - :private-members: - -Database ------------------------------------- - -.. automodule:: royalnet.database - :members: - :undoc-members: - :private-members: - -Network ------------------------------------- - -.. automodule:: royalnet.network - :members: - :undoc-members: - :private-members: - -Utils ------------------------------------- - -.. automodule:: royalnet.utils - :members: - :undoc-members: - :private-members: - -Web ------------------------------------- - -.. automodule:: royalnet.web - :members: - :undoc-members: - :private-members: - -Error ------------------------------------- - -.. automodule:: royalnet.error +.. automodule:: royalnet :members: :undoc-members: :private-members: diff --git a/docs/html/_static/jquery-3.4.1.js b/docs/html/_static/jquery-3.4.1.js index 1b8f13f9..773ad95c 100644 --- a/docs/html/_static/jquery-3.4.1.js +++ b/docs/html/_static/jquery-3.4.1.js @@ -5170,7 +5170,7 @@ jQuery.event = { } } - // Remove common event handler if we removed something and no more handlers exist + // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || diff --git a/docs/html/apireference.html b/docs/html/apireference.html index fa397e6a..66d957d9 100644 --- a/docs/html/apireference.html +++ b/docs/html/apireference.html @@ -83,17 +83,7 @@
@@ -162,1231 +152,6 @@These pages were automatically generated from docstrings in code.
They might be outdated, or incomplete.
-Video and audio downloading related classes, mainly used for Discord voice bots.
-royalnet.audio.
YtdlInfo
(info: Dict[str, Any])¶A wrapper around youtube_dl extracted info.
-__init__
(info: Dict[str, Any])¶Create a YtdlInfo from the dict returned by the youtube_dl.YoutubeDL.extract_info()
function.
Warning
-Does not download the info, for that use royalnet.audio.YtdlInfo.retrieve_for_url()
.
_default_ytdl_args
= {'ignoreerrors': True, 'no_warnings': True, 'noplaylist': True, 'outtmpl': '%(title)s-%(id)s.%(ext)s', 'quiet': True}¶retrieve_for_url
(url, **ytdl_args) → List[royalnet.audio.ytdlinfo.YtdlInfo]¶Fetch the info for an url through YoutubeDL.
-A list
containing the infos for the requested videos.
to_discord_embed
() → discord.embeds.Embed¶Return this info as a discord.Embed
.
royalnet.audio.
YtdlFile
(url: str, info: Optional[royalnet.audio.ytdlinfo.YtdlInfo] = None, filename: Optional[str] = None)¶Information about a youtube-dl downloaded file.
-_default_ytdl_args
= {'ignoreerrors': True, 'no_warnings': True, 'noplaylist': True, 'outtmpl': '%(epoch)s-%(title)s-%(id)s.%(ext)s', 'quiet': True}¶delete
()¶download_file
(**ytdl_args) → None¶download_from_url
(url: str, **ytdl_args) → List[royalnet.audio.ytdlfile.YtdlFile]¶has_info
() → bool¶is_downloaded
() → bool¶open
()¶update_info
(**ytdl_args) → None¶royalnet.audio.
FileAudioSource
(file)¶A discord.AudioSource
that uses a io.BufferedIOBase
as an input instead of memory.
The stream should be in the usual PCM encoding.
-Warning
-This AudioSource will consume (and close) the passed stream.
-is_opus
()¶This audio file isn’t Opus-encoded, but PCM-encoded.
-False
.
royalnet.audio.
YtdlDiscord
(ytdl_file: royalnet.audio.ytdlfile.YtdlFile)¶convert_to_pcm
() → None¶create_from_url
(url, **ytdl_args) → List[royalnet.audio.ytdldiscord.YtdlDiscord]¶delete
() → None¶info
¶pcm_available
()¶ready_up
()¶spawn_audiosource
() → royalnet.audio.fileaudiosource.FileAudioSource¶royalnet.audio.
YtdlMp3
(ytdl_file: royalnet.audio.ytdlfile.YtdlFile)¶convert_to_mp3
() → None¶create_and_ready_from_url
(url, **ytdl_args) → List[royalnet.audio.ytdlmp3.YtdlMp3]¶create_from_url
(url, **ytdl_args) → List[royalnet.audio.ytdlmp3.YtdlMp3]¶delete
() → None¶info
¶pcm_available
()¶ready_up
()¶Various bot interfaces, and a generic class to create new ones.
-royalnet.bots.
TelegramBot
(*, network_config: Optional[royalnet.network.networkconfig.NetworkConfig] = None, database_config: Optional[royalnet.database.databaseconfig.DatabaseConfig] = None, commands: List[Type[royalnet.commands.command.Command]] = None, sentry_dsn: Optional[str] = None, loop: asyncio.events.AbstractEventLoop = None, secrets_name: str = '__default__')¶A bot that connects to Telegram.
-_data_factory
() → Type[royalnet.commands.commanddata.CommandData]¶_handle_callback_query
(update: telegram.update.Update)¶_handle_message
(update: telegram.update.Update)¶_handle_update
(update: telegram.update.Update)¶_init_client
()¶Create the telegram.Bot
, and set the starting offset.
_initialize
()¶_interface_factory
() → Type[royalnet.commands.commandinterface.CommandInterface]¶interface_name
= 'telegram'¶run
()¶A blocking coroutine that should make the bot start listening to commands and requests.
-safe_api_call
(f: Callable, *args, **kwargs) → Optional¶royalnet.bots.
DiscordBot
(*, network_config: Optional[royalnet.network.networkconfig.NetworkConfig] = None, database_config: Optional[royalnet.database.databaseconfig.DatabaseConfig] = None, commands: List[Type[royalnet.commands.command.Command]] = None, sentry_dsn: Optional[str] = None, loop: asyncio.events.AbstractEventLoop = None, secrets_name: str = '__default__')¶A bot that connects to Discord.
-_bot_factory
() → Type[discord.client.Client]¶Create a custom DiscordClient class inheriting from discord.Client
.
_data_factory
() → Type[royalnet.commands.commanddata.CommandData]¶_init_client
()¶Create an instance of the DiscordClient class created in royalnet.bots.DiscordBot._bot_factory()
.
_init_voice
()¶Initialize the variables needed for the connection to voice chat.
-_initialize
()¶_interface_factory
() → Type[royalnet.commands.commandinterface.CommandInterface]¶add_to_music_data
(dfiles: List[royalnet.audio.ytdldiscord.YtdlDiscord], guild: discord.guild.Guild)¶Add a list of royalnet.audio.YtdlDiscord
to the corresponding music_data object.
advance_music_data
(guild: discord.guild.Guild)¶Try to play the next song, while it exists. Otherwise, just return.
-interface_name
= 'discord'¶run
()¶Login to Discord, then run the bot.
-update_activity_with_source_title
()¶Change the bot’s presence (using discord.Client.change_presence()
) to match the current listening status.
If multiple guilds are using the bot, the bot will always have an empty presence.
-royalnet.bots.
GenericBot
(*, network_config: Optional[royalnet.network.networkconfig.NetworkConfig] = None, database_config: Optional[royalnet.database.databaseconfig.DatabaseConfig] = None, commands: List[Type[royalnet.commands.command.Command]] = None, sentry_dsn: Optional[str] = None, loop: asyncio.events.AbstractEventLoop = None, secrets_name: str = '__default__')¶A generic bot class, to be used as base for the other more specific classes, such as
-royalnet.bots.TelegramBot
and royalnet.bots.DiscordBot
.
_data_factory
() → Type[royalnet.commands.commanddata.CommandData]¶_init_commands
() → None¶Generate the commands
dictionary required to handle incoming messages, and the network_handlers
-dictionary required to handle incoming requests.
_init_database
()¶Create an royalnet.database.Alchemy
with the tables required by the commands. Then,
-find the chain that links the master_table
to the identity_table
.
_init_loop
()¶_init_network
()¶Create a royalnet.network.NetworkLink
, and run it as a asyncio.Task
.
_init_sentry
()¶_initialize
()¶_interface_factory
() → Type[royalnet.commands.commandinterface.CommandInterface]¶_network_handler
(request_dict: dict) → dict¶Handle a single dict
received from the royalnet.network.NetworkLink
.
Another dict
, formatted as a royalnet.network.Response
.
get_secret
(username: str)¶interface_name
= NotImplemented¶run
()¶A blocking coroutine that should make the bot start listening to commands and requests.
-run_blocking
(verbose=False)¶set_secret
(username: str, password: str)¶royalnet.commands.
CommandInterface
¶alchemy
= NotImplemented¶bot
= NotImplemented¶loop
= NotImplemented¶name
= NotImplemented¶net_request
(message, destination: str) → dict¶Send data through a royalnet.network.NetworkLink
and wait for a
-royalnet.network.Reply
.
message – The data to be sent. Must be pickle
-able.
destination – The destination of the request, either in UUID format or node name.
prefix
= NotImplemented¶register_keyboard_key
(key_name: str, callback: Callable)¶register_net_handler
(message_type: str, network_handler: Callable)¶Register a new handler for messages received through Royalnet.
-unregister_keyboard_key
(key_name: str)¶unregister_net_handler
(message_type: str)¶Remove a Royalnet handler.
-royalnet.commands.
Command
(interface: royalnet.commands.commandinterface.CommandInterface)¶aliases
= NotImplemented¶A list of possible aliases for a command.
-To have /e
as alias for /example
, one should set aliases to ["e"]
.
description
= NotImplemented¶A small description of the command, to be displayed when the command is being autocompleted.
-name
= NotImplemented¶The main name of the command.
-To have /example
on Telegram, the name should be example
.
require_alchemy_tables
= {}¶A set of royalnet.database
tables that must exist for this command to work.
run
(args: royalnet.commands.commandargs.CommandArgs, data: royalnet.commands.commanddata.CommandData) → None¶syntax
= ''¶The syntax of the command, to be displayed when a royalnet.error.InvalidInputError
is raised,
-in the format (required_arg) [optional_arg]
.
royalnet.commands.
CommandData
¶delete_invoking
(error_if_unavailable=False) → None¶Delete the invoking message, if supported by the interface.
-The invoking message is the message send by the user that contains the command.
-error_if_unavailable – if True, raise an exception if the message cannot been deleted.
-Try to find the identifier of the user that sent the message. -That probably means, the database row identifying the user.
-error_if_none – Raise an exception if this is True and the call has no author.
-keyboard
(text: str, keyboard: Dict[str, Callable]) → None¶Send a keyboard having the keys of the dict as keys and calling the correspondent values on a press.
-The function should be passed the CommandData
instance as a argument.
reply
(text: str) → None¶Send a text message to the channel where the call was made.
-text – The text to be sent, possibly formatted in the weird undescribed markup that I’m using.
-royalnet.commands.
CommandArgs
¶An interface to access the arguments of a command with ease.
-__getitem__
(item)¶Arguments can be accessed with an array notation, such as args[0]
.
royalnet.error.InvalidInputError – if the requested argument does not exist.
-joined
(*, require_at_least=0) → str¶Get the arguments as a space-joined string.
-require_at_least – the minimum amount of arguments required, will raise royalnet.error.InvalidInputError
if the requirement is not fullfilled.
royalnet.error.InvalidInputError – if there are less than require_at_least
arguments.
The space-joined string.
-match
(pattern: Union[str, Pattern], *flags) → Sequence[AnyStr]¶Match the royalnet.utils.commandargs.joined()
to a regex pattern.
pattern – The regex pattern to be passed to re.match()
.
royalnet.error.InvalidInputError – if the pattern doesn’t match.
-The matched groups, as returned by re.Match.groups()
.
optional
(index: int, default=None)¶Get the argument at a specific index, but don’t raise an error if nothing is found, instead returning the default
value.
index – The index of the argument you want to retrieve.
default – The value returned if the argument is missing.
Either the argument or the default
value, defaulting to None
.
royalnet.commands.
CommandError
(message='')¶Something went wrong during the execution of this command.
-Display an error message to the user, explaining what went wrong.
-royalnet.commands.
InvalidInputError
(message='')¶The command has received invalid input and cannot complete.
-Display an error message to the user, along with the correct syntax for the command.
-royalnet.commands.
UnsupportedError
(message='')¶A requested feature is not available on this interface.
-Display an error message to the user, telling them to use another interface.
-royalnet.commands.
KeyboardExpiredError
(message='')¶A special type of exception that can be raised in keyboard handlers to mark a specific keyboard as expired.
-Relational database classes and methods.
-royalnet.database.
Alchemy
(database_uri: str, tables: Set)¶A wrapper around SQLAlchemy declarative that allows to use multiple databases at once while maintaining a single table-class for both of them.
-__init__
(database_uri: str, tables: Set)¶Create a new Alchemy object.
-database_uri – The uri of the database, as described at https://docs.sqlalchemy.org/en/13/core/engines.html .
tables – The set of tables to be created and used in the selected database. Check the tables submodule for more details.
_create_tables
(tables: Set)¶session_acm
()¶Use Alchemy as a asyncronous context manager (to be used in async with statements).
-session_cm
()¶Use Alchemy as a context manager (to be used in with statements).
-royalnet.database.
relationshiplinkchain
(starting_class, ending_class) → Optional[tuple]¶Find the path to follow to get from the starting table to the ending table.
-royalnet.database.
DatabaseConfig
(database_uri: str, master_table: Type, identity_table: Type, identity_column_name: str)¶The configuration to be used for the royalnet.database.Alchemy
component of royalnet.bots.GenericBot
.
Royalnet (websocket) related classes.
-royalnet.network.
NetworkLink
(master_uri: str, secret: str, link_type: str, request_handler, *, loop: asyncio.events.AbstractEventLoop = None)¶connect
()¶Connect to the royalnet.network.NetworkServer
at self.master_uri
.
identify
() → None¶receive
() → royalnet.network.package.Package¶Recieve a Package
from the royalnet.network.NetworkServer
.
royalnet.network.royalnetlink.ConnectionClosedError –
-request
(message, destination)¶run
()¶Blockingly run the Link.
-send
(package: royalnet.network.package.Package)¶royalnet.network.
NetworkError
(error_data: dict, *args)¶royalnet.network.
NotConnectedError
¶The royalnet.network.NetworkLink
is not connected to a royalnet.network.NetworkServer
.
royalnet.network.
NotIdentifiedError
¶The royalnet.network.NetworkLink
has not identified yet to a royalnet.network.NetworkServer
.
royalnet.network.
Package
(data: dict, *, source: str, destination: str, source_conv_id: Optional[str] = None, destination_conv_id: Optional[str] = None)¶A Royalnet package, the data type with which a royalnet.network.NetworkLink
communicates with a royalnet.network.NetworkServer
or another link.
-Contains info about the source and the destination.
__init__
(data: dict, *, source: str, destination: str, source_conv_id: Optional[str] = None, destination_conv_id: Optional[str] = None)¶Create a Package.
-data – The data that should be sent. Usually a royalnet.network.Message
.
source – The nid
of the node that created this Package.
destination – The link_type
of the destination node, or alternatively, the nid
of the node. Can also be the NULL
value to send the message to nobody.
source_conv_id – The conversation id of the node that created this package. Akin to the sequence number on IP packets.
destination_conv_id – The conversation id of the node that this Package is a reply to.
from_dict
(d) → royalnet.network.package.Package¶Create a Package from a dictionary.
-from_json_bytes
(b: bytes) → royalnet.network.package.Package¶Create a Package from UTF8-encoded JSON bytes.
-from_json_string
(string: str) → royalnet.network.package.Package¶Create a Package from a JSON string.
-reply
(data) → royalnet.network.package.Package¶Reply to this Package with another Package.
-data – The data that should be sent. Usually a royalnet.network.Message
.
The reply Package.
-to_dict
() → dict¶Convert the Package into a dictionary.
-to_json_bytes
() → bytes¶Convert the Package into UTF8-encoded JSON bytes.
-to_json_string
() → str¶Convert the Package into a JSON string.
-royalnet.network.
NetworkServer
(address: str, port: int, required_secret: str, *, loop: asyncio.events.AbstractEventLoop = None)¶find_client
(*, nid: str = None, link_type: str = None) → List[royalnet.network.networkserver.ConnectedClient]¶find_destination
(package: royalnet.network.package.Package) → List[royalnet.network.networkserver.ConnectedClient]¶Find a list of destinations for the package.
-package – The package to find the destination of.
-A list
of ConnectedClients
to send the package to.
listener
(websocket: websockets.server.WebSocketServerProtocol, path)¶route_package
(package: royalnet.network.package.Package) → None¶Executed every time a package is received and must be routed somewhere.
-run_blocking
(verbose=False)¶serve
()¶royalnet.network.
NetworkConfig
(master_uri: str, master_secret: str)¶royalnet.network.
ConnectionClosedError
¶The royalnet.network.NetworkLink
’s connection was closed unexpectedly. The link can’t be used anymore.
royalnet.network.
Request
(handler: str, data: dict)¶A request sent from a royalnet.network.NetworkLink
to another.
It contains the name of the requested handler, in addition to the data.
-from_dict
(d: dict)¶to_dict
()¶royalnet.network.
Response
¶A base class to be inherited by all other response types.
- - - - - - -royalnet.network.
ResponseSuccess
(data: Optional[dict] = None)¶A response to a successful royalnet.network.Request
.
royalnet.network.
ResponseError
(name: str, description: str, extra_info: Optional[dict] = None)¶A response to a invalid royalnet.network.Request
.
Miscellaneous useful functions and classes.
-royalnet.utils.
asyncify
(function: Callable, *args, **kwargs)¶Convert a function into a coroutine.
-Warning
-The coroutine cannot be cancelled, and any attempts to do so will result in unexpected outputs.
-royalnet.utils.
safeformat
(string: str, **words: str) → str¶str.format()
something, but ignore missing keys instead of raising an error.
string – The base string to be formatted.
words – The words to format the string with.
The formatted string.
-royalnet.utils.
cdj
(class_: Any) → dict¶Return a dict of the class attributes without the __module__
, __dict__
, __weakref__
and __doc__
keys, to be used while generating dynamically SQLAlchemy declarative table classes.
class_ – The object that you want to dict-ify.
-The class dict.
-Warning
-You can’t dict-ify classes with __slots__
!
royalnet.utils.
sleep_until
(dt: datetime.datetime) → None¶Block the call until the specified datetime.
-Warning
-Accurate only to seconds.
-royalnet.utils.
plusformat
(i: int) → str¶Convert an int
to a str
, prepending a +
if it’s greater than 0.
royalnet.utils.
NetworkHandler
¶The NetworkHandler functions are called when a specific Message type is received.
-message_type
= NotImplemented¶royalnet.utils.
andformat
(l: List[str], middle=', ', final=' and ') → str¶Convert a list
to a str
by adding final
between the last two elements and middle
between the others.
royalnet.utils.
plusformat
(i: int) → strConvert an int
to a str
, prepending a +
if it’s greater than 0.
royalnet.utils.
fileformat
(string: str) → str¶Ensure a string can be used as a filename by replacing all non-word characters with underscores.
-string – the input string.
-A valid filename string.
-royalnet.utils.
ytdldateformat
(string: Optional[str], separator: str = '-') → str¶Convert the weird date string returned by youtube-dl
into the YYYY-MM-DD
format.
string – the input string, in the YYYYMMDD
format.
separator – the string to add between the years, the months and the days. Defaults to -
.
The resulting string, in the format YYYY-MM-DD
format.
royalnet.utils.
numberemojiformat
(l: List[str]) → str¶royalnet.utils.
telegram_escape
(string: str) → str¶Escape a string to be sent through Telegram, and format it using RoyalCode.
-Warning
-Currently escapes everything, even items in code blocks.
-royalnet.utils.
discord_escape
(string: str) → str¶Escape a string to be sent through Discord, and format it using RoyalCode.
-Warning
-Currently escapes everything, even items in code blocks.
-royalnet.utils.
splitstring
(s: str, max: int) → List[str]¶royalnet.utils.
parse_5etools_entry
(entry) → str¶royalnet.utils.
ordinalformat
(number: int)¶royalnet.web.
create_app
(config_obj: Type, blueprints: List[royalnet.web.royalprint.Royalprint])¶Create a flask.Flask
application object.
Gets the app.secret_key
from the SECRET_KEY
envvar.
Also requires a DB_PATH
key in config_obj
to initialize the database connection.
Warning
-The code for this class was written at 1 AM, and I have no clue of how and why it works or even if it really does work. -Use with caution?
-config_obj – The object to be passed to flask.Flask.config.from_object()
.
blueprints – A list of blueprints to be registered to the application.
The created flask.Flask
.
royalnet.web.
Royalprint
(name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, root_path=None, required_tables: Optional[set] = None)¶An edited flask.Blueprint
containing an additional required_tables
parameter.
royalnet.error.
RoyalnetRequestError
(error: ResponseError)¶An error was raised while handling the Royalnet request.
-This exception contains the royalnet.network.ResponseError
that was returned by the other Link.
args
¶royalnet.error.
RoyalnetResponseError
¶The royalnet.network.Response
that was received is invalid.
Next, create a new Python file with the name
you have thought of.
The previously mentioned “spaghetti” command should have a file called spaghetti.py
.
Then, in the first row of the file, import the Command
class from royalnet, and create a new class inheriting from it:
Then, in the first row of the file, import the Command
class from royalnet, and create a new class inheriting from it:
from royalnet.commands import Command
class SpaghettiCommand(Command):
@@ -215,8 +215,8 @@ The previously mentioned “spaghetti” command should have a file called description = "Send a spaghetti emoji in the chat."
Now override the Command.run()
method, adding the code you want the bot to run when the command is called.
To send a message in the chat the command was called in, you can use the CommandData.reply()
method:
Now override the Command.run()
method, adding the code you want the bot to run when the command is called.
To send a message in the chat the command was called in, you can use the CommandData.reply()
method:
from royalnet.commands import Command
class SpaghettiCommand(Command):
@@ -234,7 +234,7 @@ The previously mentioned “spaghetti” command should have a file called Command arguments¶
A command can have some arguments passed by the user: for example, on Telegram an user may type /spaghetti carbonara al-dente
to pass the str
“carbonara al-dente” to the command code.
-These arguments can be accessed in multiple ways through the args
parameter passed to the Command.run()
+
These arguments can be accessed in multiple ways through the args
parameter passed to the Command.run()
method.
If you want your command to use arguments, override the syntax
class attribute with a brief description of the
syntax of your command, possibly using (round parentheses) for required arguments and [square brackets] for optional
@@ -271,7 +271,7 @@ ones.
If you don’t want arguments to be required, you can access them through the CommandArgs.optional()
method: it
+
If you don’t want arguments to be required, you can access them through the CommandArgs.optional()
method: it
will return None
if the argument wasn’t passed, making it optional.
args.optional(0)
# "carbonara"
@@ -291,12 +291,12 @@ will return
Full string¶
-If you want the full argument string, you can use the CommandArgs.joined()
method.
+If you want the full argument string, you can use the CommandArgs.joined()
method.
args.joined()
# "carbonara al-dente"
-You can specify a minimum number of arguments too, so that an InvalidInputError
will be
+
You can specify a minimum number of arguments too, so that an InvalidInputError
will be
raised if not enough arguments are present:
args.joined(require_at_least=3)
# InvalidInputError() is raised
@@ -306,8 +306,8 @@ raised if not enough arguments are present:
Regular expressions¶
For more complex commands, you may want to get arguments through regular expressions.
-You can then use the 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 InvalidInputError
if there is no match.
+You can then use the 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 InvalidInputError
if there is no match.
To match a pattern, re.match()
is used, meaning that Python will try to match only at the beginning of the string.
args.match(r"(carb\w+)")
# ("carbonara",)
@@ -326,18 +326,18 @@ which returns a tuple of the matched groups and raises an
Raising errors¶
-If you want to display an error message to the user, you can raise a CommandError
using the error message as argument:
+If you want to display an error message to the user, you can raise a 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 InvalidInputError
to redisplay the command syntax, along with your error message:
+You can also manually raise 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
-UnsupportedError
with a brief description of what’s missing:
+UnsupportedError
with a brief description of what’s missing:
if interface.name != "telegram":
raise UnsupportedError("This command can only be run on Telegram interfaces.")
@@ -346,7 +346,7 @@ which returns a tuple of the matched groups and raises an
Running code at the initialization of the bot¶
You can run code while the bot is starting by overriding the Command.__init__()
function.
-You should keep the super().__init__(interface)
call at the start of it, so that the Command
instance is
+
You should keep the super().__init__(interface)
call at the start of it, so that the Command
instance is
initialized properly, then add your code after it.
You can add fields to the command to keep shared data between multiple command calls (but not bot restarts): it may
be useful for fetching external static data and keeping it until the bot is restarted, or to store references to all the
@@ -377,7 +377,7 @@ be useful for fetching external static data and keeping it until the bot is rest
Coroutines and slow operations¶
You may have noticed that in the previous examples we used await data.reply("🍝")
instead of just data.reply("🍝")
.
-This is because CommandData.reply()
isn’t a simple method: it is a coroutine, a special kind of function that
+
This is because CommandData.reply()
isn’t a simple method: it is a coroutine, a special kind of function that
can be executed separately from the rest of the code, allowing the bot to do other things in the meantime.
By adding the await
keyword before the data.reply("🍝")
, we tell the bot that it can do other things, like
receiving new messages, while the message is being sent.
@@ -389,7 +389,7 @@ are finished and may cause bugs in other parts of the code!
...
-If the slow function you want does not cause any side effect, you can wrap it with the royalnet.utils.asyncify()
+
If the slow function you want does not cause any side effect, you can wrap it with the royalnet.utils.asyncify()
function:
async def run(self, args, data):
# If the called function has no side effect, you can do this!
@@ -404,7 +404,7 @@ a coroutine that does the same exact thing.
Delete the invoking message¶
The invoking message of a command is the message that the user sent that the bot recognized as a command; for example,
the message /spaghetti carbonara
is the invoking message for the spaghetti
command run.
-You can have the bot delete the invoking message for a command by calling the CommandData.delete_invoking
+
You can have the bot delete the invoking message for a command by calling the CommandData.delete_invoking
method:
async def run(self, args, data):
await data.delete_invoking()
@@ -427,13 +427,13 @@ to True:
Using the database¶
Bots can be connected to a PostgreSQL database through a special SQLAlchemy interface called
-royalnet.database.Alchemy
.
+royalnet.database.Alchemy
.
If the connection is established, the self.interface.alchemy
and self.interface.session
fields will be
available for use in commands.
-self.interface.alchemy
is an instance of royalnet.database.Alchemy
, which contains the
+
self.interface.alchemy
is an instance of royalnet.database.Alchemy
, which contains the
sqlalchemy.engine.Engine
, metadata and tables, while self.interface.session
is a
sqlalchemy.orm.session.Session
, and can be interacted in the same way as one.
-If you want to use royalnet.database.Alchemy
in your command, you should override the
+
If you want to use royalnet.database.Alchemy
in your command, you should override the
require_alchemy_tables
field with the set
of Alchemy tables you need.
from royalnet.commands import Command
from royalnet.database.tables import Royal
@@ -453,7 +453,7 @@ available for use in commands.
Querying the database¶
You can sqlalchemy.orm.query.Query
the database using the SQLAlchemy ORM.
-The SQLAlchemy tables can be found inside royalnet.database.Alchemy
with the same name they were created
+
The SQLAlchemy tables can be found inside royalnet.database.Alchemy
with the same name they were created
from, if they were specified in require_alchemy_tables
.
RoyalTable = self.interface.alchemy.Royal
query = self.interface.session.query(RoyalTable)
@@ -487,7 +487,7 @@ from, if they were specified in You can fetch the query results with the sqlalchemy.orm.query.Query.all()
,
sqlalchemy.orm.query.Query.first()
, sqlalchemy.orm.query.Query.one()
and
sqlalchemy.orm.query.Query.one_or_none()
methods.
-Remember to use royalnet.utils.asyncify()
when fetching results, as it may take a while!
+Remember to use royalnet.utils.asyncify()
when fetching results, as it may take a while!
Use sqlalchemy.orm.query.Query.all()
if you want a list
of all results:
results: list = await asyncify(query.all)
diff --git a/docs/html/genindex.html b/docs/html/genindex.html
index 8574095f..44576c20 100644
--- a/docs/html/genindex.html
+++ b/docs/html/genindex.html
@@ -150,585 +150,8 @@
Index
- _
- | A
- | B
- | C
- | D
- | F
- | G
- | H
- | I
- | J
- | K
- | L
- | M
- | N
- | O
- | P
- | R
- | S
- | T
- | U
- | Y
-_
-A
-
-
-
-
-
-B
-
-
-
-
-C
-D
-
-
-
-
-
-F
-
-
-
-
-
-G
-
-
-
-
-
-H
-
-
-
-
-I
-
-
-
-
-
-J
-
-
-
-
-K
-
-
-
- - KeyboardExpiredError
-
-
-
-
-L
-
-
-
-
-
-M
-
-
-
-
-
-N
-
-
-
-
-
-O
-
-
-
-
-
-P
-
-
-
-
-
-R
-S
-T
-
-
-
-
-
-U
-
-
-
-
-
-Y
-
-
-
-
-
diff --git a/docs/html/index.html b/docs/html/index.html
index b5c0ea49..1bb04ae0 100644
--- a/docs/html/index.html
+++ b/docs/html/index.html
@@ -182,17 +182,7 @@
Comunicating via Royalnet
-API Reference
-
+API Reference
diff --git a/docs/html/objects.inv b/docs/html/objects.inv
index f9a3aae8..705aa710 100644
Binary files a/docs/html/objects.inv and b/docs/html/objects.inv differ
diff --git a/docs/html/runningroyalnet.html b/docs/html/runningroyalnet.html
index 01bccbcd..4e1840ef 100644
--- a/docs/html/runningroyalnet.html
+++ b/docs/html/runningroyalnet.html
@@ -174,7 +174,7 @@ your bot. Enter a name that you’ll be able to remember.
You’ll then be asked for a network password.
-This password is used to connect to the rest of the royalnet.network
, or, if you’re hosting a local Network,
+
This password is used to connect to the rest of the royalnet.network
, or, if you’re hosting a local Network,
it will be the necessary password to connect to it:
Network password []: cosafaunapesuunafoglia
diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js
index f72c9203..7ae9ac73 100644
--- a/docs/html/searchindex.js
+++ b/docs/html/searchindex.js
@@ -1 +1 @@
-Search.setIndex({docnames:["apireference","creatingacommand","index","runningroyalnet"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["apireference.rst","creatingacommand.rst","index.rst","runningroyalnet.rst"],objects:{"royalnet.audio":{FileAudioSource:[0,1,1,""],YtdlDiscord:[0,1,1,""],YtdlFile:[0,1,1,""],YtdlInfo:[0,1,1,""],YtdlMp3:[0,1,1,""]},"royalnet.audio.FileAudioSource":{is_opus:[0,2,1,""],read:[0,2,1,""]},"royalnet.audio.YtdlDiscord":{"delete":[0,2,1,""],convert_to_pcm:[0,2,1,""],create_from_url:[0,2,1,""],info:[0,2,1,""],pcm_available:[0,2,1,""],ready_up:[0,2,1,""],spawn_audiosource:[0,2,1,""]},"royalnet.audio.YtdlFile":{"delete":[0,2,1,""],_default_ytdl_args:[0,3,1,""],download_file:[0,2,1,""],download_from_url:[0,2,1,""],has_info:[0,2,1,""],is_downloaded:[0,2,1,""],open:[0,2,1,""],update_info:[0,2,1,""]},"royalnet.audio.YtdlInfo":{__init__:[0,2,1,""],_default_ytdl_args:[0,3,1,""],retrieve_for_url:[0,2,1,""],to_discord_embed:[0,2,1,""]},"royalnet.audio.YtdlMp3":{"delete":[0,2,1,""],convert_to_mp3:[0,2,1,""],create_and_ready_from_url:[0,2,1,""],create_from_url:[0,2,1,""],info:[0,2,1,""],pcm_available:[0,2,1,""],ready_up:[0,2,1,""]},"royalnet.bots":{DiscordBot:[0,1,1,""],GenericBot:[0,1,1,""],TelegramBot:[0,1,1,""]},"royalnet.bots.DiscordBot":{_bot_factory:[0,2,1,""],_data_factory:[0,2,1,""],_init_client:[0,2,1,""],_init_voice:[0,2,1,""],_initialize:[0,2,1,""],_interface_factory:[0,2,1,""],add_to_music_data:[0,2,1,""],advance_music_data:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""],update_activity_with_source_title:[0,2,1,""]},"royalnet.bots.GenericBot":{_data_factory:[0,2,1,""],_init_commands:[0,2,1,""],_init_database:[0,2,1,""],_init_loop:[0,2,1,""],_init_network:[0,2,1,""],_init_sentry:[0,2,1,""],_initialize:[0,2,1,""],_interface_factory:[0,2,1,""],_network_handler:[0,2,1,""],get_secret:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""],run_blocking:[0,2,1,""],set_secret:[0,2,1,""]},"royalnet.bots.TelegramBot":{_data_factory:[0,2,1,""],_handle_callback_query:[0,2,1,""],_handle_message:[0,2,1,""],_handle_update:[0,2,1,""],_init_client:[0,2,1,""],_initialize:[0,2,1,""],_interface_factory:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""],safe_api_call:[0,2,1,""]},"royalnet.commands":{Command:[0,1,1,""],CommandArgs:[0,1,1,""],CommandData:[0,1,1,""],CommandError:[0,4,1,""],CommandInterface:[0,1,1,""],InvalidInputError:[0,4,1,""],KeyboardExpiredError:[0,4,1,""],UnsupportedError:[0,4,1,""]},"royalnet.commands.Command":{aliases:[0,3,1,""],description:[0,3,1,""],name:[0,3,1,""],require_alchemy_tables:[0,3,1,""],run:[0,2,1,""],syntax:[0,3,1,""]},"royalnet.commands.CommandArgs":{__getitem__:[0,2,1,""],joined:[0,2,1,""],match:[0,2,1,""],optional:[0,2,1,""]},"royalnet.commands.CommandData":{delete_invoking:[0,2,1,""],get_author:[0,2,1,""],keyboard:[0,2,1,""],reply:[0,2,1,""]},"royalnet.commands.CommandInterface":{alchemy:[0,3,1,""],bot:[0,3,1,""],loop:[0,3,1,""],name:[0,3,1,""],net_request:[0,2,1,""],prefix:[0,3,1,""],register_keyboard_key:[0,2,1,""],register_net_handler:[0,2,1,""],unregister_keyboard_key:[0,2,1,""],unregister_net_handler:[0,2,1,""]},"royalnet.database":{Alchemy:[0,1,1,""],DatabaseConfig:[0,1,1,""],relationshiplinkchain:[0,5,1,""]},"royalnet.database.Alchemy":{__init__:[0,2,1,""],_create_tables:[0,2,1,""],session_acm:[0,2,1,""],session_cm:[0,2,1,""]},"royalnet.error":{RoyalnetRequestError:[0,4,1,""],RoyalnetResponseError:[0,4,1,""]},"royalnet.error.RoyalnetRequestError":{args:[0,2,1,""]},"royalnet.network":{ConnectionClosedError:[0,4,1,""],NetworkConfig:[0,1,1,""],NetworkError:[0,4,1,""],NetworkLink:[0,1,1,""],NetworkServer:[0,1,1,""],NotConnectedError:[0,4,1,""],NotIdentifiedError:[0,4,1,""],Package:[0,1,1,""],Request:[0,1,1,""],Response:[0,1,1,""],ResponseError:[0,1,1,""],ResponseSuccess:[0,1,1,""]},"royalnet.network.NetworkLink":{connect:[0,2,1,""],identify:[0,2,1,""],receive:[0,2,1,""],request:[0,2,1,""],run:[0,2,1,""],send:[0,2,1,""]},"royalnet.network.NetworkServer":{find_client:[0,2,1,""],find_destination:[0,2,1,""],listener:[0,2,1,""],route_package:[0,2,1,""],run_blocking:[0,2,1,""],serve:[0,2,1,""]},"royalnet.network.Package":{__init__:[0,2,1,""],from_dict:[0,2,1,""],from_json_bytes:[0,2,1,""],from_json_string:[0,2,1,""],reply:[0,2,1,""],to_dict:[0,2,1,""],to_json_bytes:[0,2,1,""],to_json_string:[0,2,1,""]},"royalnet.network.Request":{from_dict:[0,2,1,""],to_dict:[0,2,1,""]},"royalnet.network.Response":{from_dict:[0,2,1,""],raise_on_error:[0,2,1,""],to_dict:[0,2,1,""]},"royalnet.network.ResponseError":{raise_on_error:[0,2,1,""]},"royalnet.network.ResponseSuccess":{raise_on_error:[0,2,1,""]},"royalnet.utils":{NetworkHandler:[0,1,1,""],andformat:[0,5,1,""],asyncify:[0,5,1,""],cdj:[0,5,1,""],discord_escape:[0,5,1,""],fileformat:[0,5,1,""],numberemojiformat:[0,5,1,""],ordinalformat:[0,5,1,""],parse_5etools_entry:[0,5,1,""],plusformat:[0,5,1,""],safeformat:[0,5,1,""],sleep_until:[0,5,1,""],splitstring:[0,5,1,""],telegram_escape:[0,5,1,""],ytdldateformat:[0,5,1,""]},"royalnet.utils.NetworkHandler":{message_type:[0,3,1,""]},"royalnet.web":{Royalprint:[0,1,1,""],create_app:[0,5,1,""]},royalnet:{audio:[0,0,0,"-"],bots:[0,0,0,"-"],commands:[0,0,0,"-"],database:[0,0,0,"-"],error:[0,0,0,"-"],network:[0,0,0,"-"],utils:[0,0,0,"-"],web:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","exception","Python exception"],"5":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:exception","5":"py:function"},terms:{"20m":0,"byte":0,"class":[0,1],"default":[0,1],"final":0,"function":[0,1],"import":1,"int":0,"new":[0,2,3],"null":0,"return":[0,1],"short":1,"static":[0,1],"super":1,"true":[0,1],"try":[0,1],"while":[0,1],Adding:2,And:1,For:1,Not:1,That:0,The:[0,1,2],Then:[0,1,3],These:[0,1],Use:[0,1],Using:2,__default__:[0,3],__dict__:0,__doc__:0,__getitem__:0,__init__:[0,1],__module__:0,__slots__:0,__weakref__:0,_bot_factori:0,_create_t:0,_data_factori:0,_default_ytdl_arg:0,_handle_callback_queri:0,_handle_messag:0,_handle_upd:0,_init_cli:0,_init_command:0,_init_databas:0,_init_loop:0,_init_network:0,_init_sentri:0,_init_voic:0,_initi:0,_interface_factori:0,_network_handl:0,aaaaaa:3,aaaaaaaaaaaaaaa:3,aaaaaaaaaaaaaaaaaaaaaaaa:3,aaaaaaaaaaaaaaaaaaaaaaaaaaa:3,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:3,abl:[0,3],about:[0,1],abstracteventloop:0,access:[0,2],accur:0,add:[0,1],add_to_music_data:0,added:[0,1],adding:[0,1],addit:0,addition:1,address:0,advance_music_data:0,after:[1,3],akin:0,alchemi:[0,2],alia:0,alias:0,all:[0,1,3],allow:[0,1],allowed_pasta:1,along:[0,1],alreadi:[1,3],also:[0,1],altern:0,alwai:[0,1],amount:0,andformat:0,ani:[0,1],anonym:3,anoth:0,anymor:0,anystr:0,api:[2,3],app:0,append:1,applic:[0,3],arg:[0,1],argument:[0,2],around:0,arrai:0,ascend:1,ask:3,associ:3,async:[0,1],asyncifi:[0,1],asyncio:[0,1],asyncron:0,attempt:0,attribut:[0,1],audio:2,audiosourc:0,author:[0,3],autocomplet:0,automat:0,avail:[0,1,3],avoid:1,await:1,back:1,banana:1,base:0,becaus:1,been:0,befor:1,begin:1,being:[0,1],between:[0,1],block:0,blockingli:0,blueprint:0,bool:0,bot:2,botfath:3,both:0,bracket:1,brief:1,bufferediobas:0,bug:1,call:[0,1],callabl:0,callback:0,can:[0,1,3],cancel:0,cannot:0,carb:1,carbonara:1,caus:1,caution:0,cdj:0,certain:1,chain:0,chang:[0,1],change_pres:0,channel:0,charact:0,chat:[0,1],check:0,class_:0,classmethod:0,client:0,close:[0,1],clue:0,code:[0,2],column:1,come:1,command:2,commandarg:[0,1],commanddata:[0,1],commanderror:[0,1],commandinterfac:0,commun:0,comparis:1,complet:0,complex:1,compon:0,comun:2,config:0,config_obj:0,configur:[0,3],connect:[0,1,3],connectedcli:0,connectionclosederror:0,consid:1,consum:0,contain:[0,1],context:0,convers:0,convert:0,convert_to_mp3:0,convert_to_pcm:0,core:0,coroutin:[0,2],correct:0,correspond:0,cosafaunapesuunafoglia:3,could:1,creat:[0,2,3],create_and_ready_from_url:0,create_app:0,create_from_url:0,current:[0,1],custom:0,daemon:3,dai:0,data:[0,1],databas:2,database_config:0,database_uri:0,databaseconfig:0,date:0,datetim:0,db_path:0,declar:0,def:1,delet:[0,2],delete_invok:[0,1],dent:1,desc:1,descend:1,describ:0,descript:[0,1],desir:3,desktop:3,destin:0,destination_conv_id:0,detail:0,develop:3,dfile:0,dict:0,dictionari:0,differ:1,direct:2,directli:1,discord:[0,3],discord_escap:0,discordbot:0,discordcli:0,displai:[0,1],doc:0,docstr:0,document:[1,2],doe:[0,1],doesn:0,don:[0,1],done:1,download:[0,3],download_1_terabyte_of_spaghetti:1,download_fil:0,download_from_url:0,dsn:3,dure:0,dynam:0,eas:0,edit:0,effect:1,either:0,element:[0,1],els:[1,3],emb:0,emoji:1,empti:0,encod:0,end:0,ending_class:0,engin:[0,1],enough:1,ensur:0,enter:3,entri:0,envvar:0,epoch:0,error:2,error_data:0,error_if_non:0,error_if_unavail:[0,1],escap:0,establish:1,even:0,event:0,everi:[0,1],everyth:0,exact:1,exampl:[0,1],except:[0,1],execut:[0,1],exist:[0,1],expect:1,expir:0,explain:0,express:2,ext:0,extern:1,extra_info:0,extract:0,extract_info:0,fals:0,featur:[0,1],fetch:[0,2],field:1,file:[0,1],fileaudiosourc:0,fileformat:0,filenam:0,filter:2,find:0,find_client:0,find_destin:0,finish:1,first:[1,3],flag:0,flask:0,follow:0,format:0,found:[0,1],from:[0,1,3],from_dict:0,from_json_byt:0,from_json_str:0,from_object:0,full:2,fullfil:0,gener:0,genericbot:0,get:[0,1,3],get_author:0,get_secret:0,github:2,greater:[0,1],group:[0,1,3],guild:0,handl:0,handler:0,has:[0,1],has_info:0,have:[0,1,3],headless:3,help:3,here:1,host:3,how:0,html:0,http:0,identifi:0,identity_column_nam:0,identity_t:0,ifi:0,ignor:[0,1,3],ignoreerror:0,imag:1,imgur:3,import_nam:0,incom:0,incomplet:0,index:[0,2],info:0,inform:0,inherit:[0,1],initi:[0,2],input:0,insid:1,instal:3,instanc:[0,1,3],instead:[0,1],interact:1,interfac:[0,1],interface_nam:0,interpret:3,invalid:[0,1],invalidinputerror:[0,1],invok:[0,2],is_download:0,is_open:1,is_opu:0,isn:[0,1],itali:1,item:0,join:[0,1],json:0,jsonabl:0,just:[0,1,3],keep:1,kei:[0,3],key_nam:0,keyboard:0,keyboardexpirederror:0,keyr:2,keyword:1,kind:1,kitchen:1,kwarg:0,last:0,later:1,launch:3,less:0,like:[0,1],link:0,link_typ:0,linux:3,list:[0,1],listen:0,local:3,login:0,look:1,loop:0,made:0,mai:1,main:[0,3],mainli:0,maintain:0,make:[0,1],manag:0,manual:[1,3],mark:0,markup:0,master_secret:0,master_t:0,master_uri:0,match:[0,1],max:0,mean:[0,1],meantim:1,member:1,memori:0,mention:1,messag:[0,2],message_typ:0,metadata:1,method:[0,1],middl:0,might:0,minimum:[0,1],miscellan:0,miss:[0,1],month:0,more:[0,2],multipl:[0,1],music_data:0,must:0,name:[0,1,3],necessari:3,need:[0,1,3],net_request:0,network:[2,3],network_config:0,network_handl:0,networkconfig:0,networkerror:0,networkhandl:0,networklink:0,networkserv:0,next:[0,1,3],nid:0,no_warn:0,nobodi:0,node:0,non:0,none:[0,1],noplaylist:0,normal:1,notat:0,notconnectederror:0,noth:[0,1],notic:1,notidentifiederror:0,notimpl:0,now:[1,3],number:[0,1],numberemojiformat:0,object:[0,1],offset:0,onc:[0,1],one:[0,1,3],one_or_non:1,ones:[0,1],onli:[0,1],open:0,oper:2,option:[0,2,3],optional_arg:0,opu:0,order:2,order_bi:1,ordinalformat:0,org:0,orm:1,other:[0,1],otherwis:[0,1],outdat:0,output:0,outtmpl:0,overrid:1,packag:[0,3],packet:0,page:0,paramet:[0,1],parenthes:1,parse_5etools_entri:0,part:1,pass:[0,1,3],password:[0,3],pasta:1,path:0,pattern:[0,1],pcm:0,pcm_avail:0,pickl:0,ping:1,pingcommand:1,pip:3,plai:[0,1],plusformat:0,pong:1,port:0,portal:3,possibl:[0,1],postgresql:1,prefix:0,prepar:0,prepend:0,presenc:0,present:1,press:[0,3],previou:1,previous:1,probabl:[0,3],process:3,prompt:3,properli:1,properti:0,python3:3,python:[1,3],queri:2,quiet:0,rais:[0,2],raise_on_error:0,read:[0,1],readi:[1,3],ready_up:0,realli:0,receiv:[0,1],reciev:0,recogn:1,recreat:0,redisplai:1,refer:[1,2],regex:0,regist:[0,3],register_keyboard_kei:0,register_net_handl:0,regular:2,relat:0,relationshiplinkchain:0,rememb:[1,3],remov:0,replac:0,repli:[0,1],request:[0,1],request_dict:0,request_handl:0,requested_pasta:1,requestedpasta:1,requir:[0,1],require_alchemy_t:[0,1],require_at_least:[0,1],required_arg:0,required_secret:0,required_t:0,requrire_alchemy_t:1,respect:1,respons:0,responseerror:0,responsesuccess:0,rest:[1,3],restart:1,result:[0,2],retriev:0,retrieve_for_url:0,right_now:1,role:1,root_path:0,round:1,rout:0,route_packag:0,row:[0,1],royal:1,royalcod:0,royalgam:3,royalnet:0,royalnetlink:0,royalnetrequesterror:0,royalnetresponseerror:0,royalprint:0,royalt:1,run:[0,2],run_block:0,safe_api_cal:0,safeformat:0,same:1,script:1,second:0,secret:[0,3],secret_kei:0,secrets_nam:0,section:1,see:3,select:0,self:[0,1],send:[0,1],sent:[0,1],sentri:3,sentry_dsn:0,separ:[0,1],sequenc:0,serv:0,server:0,session:1,session_acm:0,session_cm:0,set:[0,1],set_secret:0,setup:3,share:1,should:[0,1,3],side:1,signal:0,simpl:1,singl:[0,1],sleep:1,sleep_until:0,slow:2,small:[0,1],some:1,someth:0,somewher:0,song:0,soon:1,sort:1,sourc:0,source_conv_id:0,space:[0,1],spaghetti:1,spaghetticommand:1,spawn_audiosourc:0,special:[0,1],specif:[0,1],specifi:[0,1,3],splitstr:0,sqlalchemi:[0,1],squar:1,stai:1,start:[0,1,3],starting_class:0,statement:0,static_fold:0,static_url_path:0,statu:0,stop:1,store:1,str:[0,1],stream:0,string:[0,2],subdomain:0,submodul:0,success:0,suppli:3,support:[0,1],syntax:[0,1],system:3,tabl:[0,1],take:1,task:[0,1],telegram:[0,1,3],telegram_escap:0,telegrambot:0,tell:[0,1],template_fold:0,text:0,than:[0,1],thei:[0,1,3],them:[0,1],thi:[0,1,3],thing:1,think:1,thought:1,through:[0,1],time:[0,1],titl:0,to_dict:0,to_discord_emb:0,to_json_byt:0,to_json_str:0,todo:[],token:3,too:1,tri:1,tupl:[0,1],two:0,type:[0,1,3],underscor:0,undescrib:0,unexpect:0,unexpectedli:0,union:[0,1],unlock:3,unregister_keyboard_kei:0,unregister_net_handl:0,unsupportederror:[0,1],until:[0,1],updat:0,update_activity_with_source_titl:0,update_info:0,uri:0,url:0,url_default:0,url_prefix:0,usag:3,use:[0,1],used:[0,1,3],useful:[0,1],user:[0,1,3],usernam:[0,1],uses:0,using:[0,1],usual:0,utf8:0,util:[1,2],uuid:0,valid:0,valu:0,variabl:0,variou:0,verbos:0,via:2,video:0,voic:0,wai:[0,1],wait:0,want:[0,1],wasn:1,web:2,websit:1,websocket:0,websocketserverprotocol:0,weird:0,welcom:2,went:0,were:[0,1],what:[0,1],when:[0,1],whenev:1,where:0,which:[0,1],why:0,window:3,without:[0,3],wizard:3,won:[1,3],word:[0,3],work:[0,1],worth:0,wrap:1,wrapper:0,written:0,wrong:0,year:0,yet:0,you:[0,1,3],your:[1,3],youtub:0,youtube_dl:0,youtubedl:0,ytdl_arg:0,ytdl_file:0,ytdldateformat:0,ytdldiscord:0,ytdlfile:0,ytdlinfo:0,ytdlmp3:0,yyyi:0,yyyymmdd:0},titles:["API Reference","Royalnet Commands","royalnet","Running Royalnet"],titleterms:{"new":1,Adding:1,The:3,Using:1,access:1,alchemi:1,api:0,argument:1,audio:0,bot:[0,1,3],code:1,command:[0,1],comun:1,coroutin:1,creat:1,databas:[0,1],delet:1,direct:1,error:[0,1],express:1,fetch:1,filter:1,full:1,initi:1,invok:1,keyr:3,link:2,messag:1,more:1,network:0,oper:1,option:1,order:1,queri:1,rais:1,refer:0,regular:1,result:1,royalnet:[1,2,3],run:[1,3],slow:1,some:2,string:1,useful:2,util:0,via:1,web:0}})
\ No newline at end of file
+Search.setIndex({docnames:["apireference","creatingacommand","index","runningroyalnet"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["apireference.rst","creatingacommand.rst","index.rst","runningroyalnet.rst"],objects:{},objnames:{},objtypes:{},terms:{"20m":[],"byte":[],"class":1,"default":1,"final":[],"function":1,"import":1,"int":[],"new":[2,3],"null":[],"return":1,"short":1,"static":1,"super":1,"true":1,"try":1,"while":1,Adding:2,And:1,For:1,Not:1,That:[],The:[1,2],Then:[1,3],These:[0,1],Use:1,Using:2,__default__:3,__dict__:[],__doc__:[],__getitem__:[],__init__:1,__module__:[],__slots__:[],__weakref__:[],_bot_factori:[],_create_t:[],_data_factori:[],_default_ytdl_arg:[],_handle_callback_queri:[],_handle_messag:[],_handle_upd:[],_init_cli:[],_init_command:[],_init_databas:[],_init_loop:[],_init_network:[],_init_sentri:[],_init_voic:[],_initi:[],_interface_factori:[],_network_handl:[],aaaaaa:3,aaaaaaaaaaaaaaa:3,aaaaaaaaaaaaaaaaaaaaaaaa:3,aaaaaaaaaaaaaaaaaaaaaaaaaaa:3,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:3,abl:3,about:1,abstracteventloop:[],access:2,accur:[],add:1,add_to_music_data:[],added:1,adding:1,addit:[],addition:1,address:[],advance_music_data:[],after:[1,3],akin:[],alchemi:2,alia:[],alias:[],all:[1,3],allow:1,allowed_pasta:1,along:1,alreadi:[1,3],also:1,altern:[],alwai:1,amount:[],andformat:[],ani:1,anonym:3,anoth:[],anymor:[],anystr:[],api:[2,3],app:[],append:1,applic:3,arg:1,argument:2,around:[],arrai:[],ascend:1,ask:3,associ:3,async:1,asyncifi:1,asyncio:1,asyncron:[],attempt:[],attribut:1,audio:[],audiosourc:[],author:3,autocomplet:[],automat:0,avail:[1,3],avoid:1,await:1,back:1,banana:1,base:[],becaus:1,been:[],befor:1,begin:1,being:1,between:1,block:[],blockingli:[],blueprint:[],bool:[],bot:2,botfath:3,both:[],bracket:1,brief:1,bufferediobas:[],bug:1,call:1,callabl:[],callback:[],can:[1,3],cancel:[],cannot:[],carb:1,carbonara:1,caus:1,caution:[],cdj:[],certain:1,chain:[],chang:1,change_pres:[],channel:[],charact:[],chat:1,check:[],class_:[],classmethod:[],client:[],close:1,clue:[],code:[0,2],column:1,come:1,command:2,commandarg:1,commanddata:1,commanderror:1,commandinterfac:[],commun:[],comparis:1,complet:[],complex:1,compon:[],comun:2,config:[],config_obj:[],configur:3,connect:[1,3],connectedcli:[],connectionclosederror:[],consid:1,consum:[],contain:1,context:[],convers:[],convert:[],convert_to_mp3:[],convert_to_pcm:[],core:[],coroutin:2,correct:[],correspond:[],cosafaunapesuunafoglia:3,could:1,creat:[2,3],create_and_ready_from_url:[],create_app:[],create_from_url:[],current:1,custom:[],daemon:3,dai:[],data:1,databas:2,database_config:[],database_uri:[],databaseconfig:[],date:[],datetim:[],db_path:[],declar:[],def:1,delet:2,delete_invok:1,dent:1,desc:1,descend:1,describ:[],descript:1,desir:3,desktop:3,destin:[],destination_conv_id:[],detail:[],develop:3,dfile:[],dict:[],dictionari:[],differ:1,direct:2,directli:1,discord:3,discord_escap:[],discordbot:[],discordcli:[],displai:1,doc:[],docstr:0,document:[1,2],doe:1,doesn:[],don:1,done:1,download:3,download_1_terabyte_of_spaghetti:1,download_fil:[],download_from_url:[],dsn:3,dure:[],dynam:[],eas:[],edit:[],effect:1,either:[],element:1,els:[1,3],emb:[],emoji:1,empti:[],encod:[],end:[],ending_class:[],engin:1,enough:1,ensur:[],enter:3,entri:[],envvar:[],epoch:[],error:2,error_data:[],error_if_non:[],error_if_unavail:1,escap:[],establish:1,even:[],event:[],everi:1,everyth:[],exact:1,exampl:1,except:1,execut:1,exist:1,expect:1,expir:[],explain:[],express:2,ext:[],extern:1,extra_info:[],extract:[],extract_info:[],fals:[],featur:1,fetch:2,field:1,file:1,fileaudiosourc:[],fileformat:[],filenam:[],filter:2,find:[],find_client:[],find_destin:[],finish:1,first:[1,3],flag:[],flask:[],follow:[],format:[],found:1,from:[0,1,3],from_dict:[],from_json_byt:[],from_json_str:[],from_object:[],full:2,fullfil:[],gener:0,genericbot:[],get:[1,3],get_author:[],get_secret:[],github:2,greater:1,group:[1,3],guild:[],handl:[],handler:[],has:1,has_info:[],have:[1,3],headless:3,help:3,here:1,host:3,how:[],html:[],http:[],identifi:[],identity_column_nam:[],identity_t:[],ifi:[],ignor:[1,3],ignoreerror:[],imag:1,imgur:3,import_nam:[],incom:[],incomplet:0,index:2,info:[],inform:[],inherit:1,initi:2,input:[],insid:1,instal:3,instanc:[1,3],instead:1,interact:1,interfac:1,interface_nam:[],interpret:3,invalid:1,invalidinputerror:1,invok:2,is_download:[],is_open:1,is_opu:[],isn:1,itali:1,item:[],join:1,json:[],jsonabl:[],just:[1,3],keep:1,kei:3,key_nam:[],keyboard:[],keyboardexpirederror:[],keyr:2,keyword:1,kind:1,kitchen:1,kwarg:[],last:[],later:1,launch:3,less:[],like:1,link:[],link_typ:[],linux:3,list:1,listen:[],local:3,login:[],look:1,loop:[],made:[],mai:1,main:3,mainli:[],maintain:[],make:1,manag:[],manual:[1,3],mark:[],markup:[],master_secret:[],master_t:[],master_uri:[],match:1,max:[],mean:1,meantim:1,member:1,memori:[],mention:1,messag:2,message_typ:[],metadata:1,method:1,middl:[],might:0,minimum:1,miscellan:[],miss:1,month:[],more:2,multipl:1,music_data:[],must:[],name:[1,3],necessari:3,need:[1,3],net_request:[],network:3,network_config:[],network_handl:[],networkconfig:[],networkerror:[],networkhandl:[],networklink:[],networkserv:[],next:[1,3],nid:[],no_warn:[],nobodi:[],node:[],non:[],none:1,noplaylist:[],normal:1,notat:[],notconnectederror:[],noth:1,notic:1,notidentifiederror:[],notimpl:[],now:[1,3],number:1,numberemojiformat:[],object:1,offset:[],onc:1,one:[1,3],one_or_non:1,ones:1,onli:1,open:[],oper:2,option:[2,3],optional_arg:[],opu:[],order:2,order_bi:1,ordinalformat:[],org:[],orm:1,other:1,otherwis:1,outdat:0,output:[],outtmpl:[],overrid:1,packag:3,packet:[],page:0,paramet:1,parenthes:1,parse_5etools_entri:[],part:1,pass:[1,3],password:3,pasta:1,path:[],pattern:1,pcm:[],pcm_avail:[],pickl:[],ping:1,pingcommand:1,pip:3,plai:1,plusformat:[],pong:1,port:[],portal:3,possibl:1,postgresql:1,prefix:[],prepar:[],prepend:[],presenc:[],present:1,press:3,previou:1,previous:1,probabl:3,process:3,prompt:3,properli:1,properti:[],python3:3,python:[1,3],queri:2,quiet:[],rais:2,raise_on_error:[],read:1,readi:[1,3],ready_up:[],realli:[],receiv:1,reciev:[],recogn:1,recreat:[],redisplai:1,refer:[1,2],regex:[],regist:3,register_keyboard_kei:[],register_net_handl:[],regular:2,relat:[],relationshiplinkchain:[],rememb:[1,3],remov:[],replac:[],repli:1,request:1,request_dict:[],request_handl:[],requested_pasta:1,requestedpasta:1,requir:1,require_alchemy_t:1,require_at_least:1,required_arg:[],required_secret:[],required_t:[],requrire_alchemy_t:1,respect:1,respons:[],responseerror:[],responsesuccess:[],rest:[1,3],restart:1,result:2,retriev:[],retrieve_for_url:[],right_now:1,role:1,root_path:[],round:1,rout:[],route_packag:[],row:1,royal:1,royalcod:[],royalgam:3,royalnet:[],royalnetlink:[],royalnetrequesterror:[],royalnetresponseerror:[],royalprint:[],royalt:1,run:2,run_block:[],safe_api_cal:[],safeformat:[],same:1,script:1,second:[],secret:3,secret_kei:[],secrets_nam:[],section:1,see:3,select:[],self:1,send:1,sent:1,sentri:3,sentry_dsn:[],separ:1,sequenc:[],serv:[],server:[],session:1,session_acm:[],session_cm:[],set:1,set_secret:[],setup:3,share:1,should:[1,3],side:1,signal:[],simpl:1,singl:1,sleep:1,sleep_until:[],slow:2,small:1,some:1,someth:[],somewher:[],song:[],soon:1,sort:1,sourc:[],source_conv_id:[],space:1,spaghetti:1,spaghetticommand:1,spawn_audiosourc:[],special:1,specif:1,specifi:[1,3],splitstr:[],sqlalchemi:1,squar:1,stai:1,start:[1,3],starting_class:[],statement:[],static_fold:[],static_url_path:[],statu:[],stop:1,store:1,str:1,stream:[],string:2,subdomain:[],submodul:[],success:[],suppli:3,support:1,syntax:1,system:3,tabl:1,take:1,task:1,telegram:[1,3],telegram_escap:[],telegrambot:[],tell:1,template_fold:[],text:[],than:1,thei:[0,1,3],them:1,thi:[1,3],thing:1,think:1,thought:1,through:1,time:1,titl:[],to_dict:[],to_discord_emb:[],to_json_byt:[],to_json_str:[],todo:[],token:3,too:1,tri:1,tupl:1,two:[],type:[1,3],underscor:[],undescrib:[],unexpect:[],unexpectedli:[],union:1,unlock:3,unregister_keyboard_kei:[],unregister_net_handl:[],unsupportederror:1,until:1,updat:[],update_activity_with_source_titl:[],update_info:[],uri:[],url:[],url_default:[],url_prefix:[],usag:3,use:1,used:[1,3],useful:1,user:[1,3],usernam:1,uses:[],using:1,usual:[],utf8:[],util:1,uuid:[],valid:[],valu:[],variabl:[],variou:[],verbos:[],via:2,video:[],voic:[],wai:1,wait:[],want:1,wasn:1,web:[],websit:1,websocket:[],websocketserverprotocol:[],weird:[],welcom:2,went:[],were:[0,1],what:1,when:1,whenev:1,where:[],which:1,why:[],window:3,without:3,wizard:3,won:[1,3],word:3,work:1,worth:[],wrap:1,wrapper:[],written:[],wrong:[],year:[],yet:[],you:[1,3],your:[1,3],youtub:[],youtube_dl:[],youtubedl:[],ytdl_arg:[],ytdl_file:[],ytdldateformat:[],ytdldiscord:[],ytdlfile:[],ytdlinfo:[],ytdlmp3:[],yyyi:[],yyyymmdd:[]},titles:["API Reference","Royalnet Commands","royalnet","Running Royalnet"],titleterms:{"new":1,Adding:1,The:3,Using:1,access:1,alchemi:1,api:0,argument:1,audio:[],bot:[1,3],code:1,command:1,comun:1,coroutin:1,creat:1,databas:1,delet:1,direct:1,error:1,express:1,fetch:1,filter:1,full:1,initi:1,invok:1,keyr:3,link:2,messag:1,more:1,network:[],oper:1,option:1,order:1,queri:1,rais:1,refer:0,regular:1,result:1,royalnet:[1,2,3],run:[1,3],slow:1,some:2,string:1,useful:2,util:[],via:1,web:[]}})
\ No newline at end of file
diff --git a/royalnet/bots/discord.py b/royalnet/bots/discord.py
index 8130e5bd..b5aa86bc 100644
--- a/royalnet/bots/discord.py
+++ b/royalnet/bots/discord.py
@@ -5,7 +5,7 @@ from .generic import GenericBot
from ..utils import *
from ..error import *
from ..audio import *
-from ..packs import *
+from ..commands import *
log = _logging.getLogger(__name__)