diff --git a/.gitignore b/.gitignore index a37f50c8..2ea4e4db 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ royalnet.egg-info/ .pytest_cache/ dist/ build/ +venv/ diff --git a/README.md b/README.md index 1fced6e8..845658bd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# `royalnet` **alpha** +# `royalnet` [![PyPI](https://img.shields.io/pypi/v/royalnet.svg)](https://pypi.org/project/royalnet/) -The fifth rewrite of [Royalnet](https://github.com/Steffo99/royalnet/)! +The fifth rewrite of the Royal Network! -It includes a lot of miscellaneous useful stuff. +It has a lot of submodules, many of which may be used in other bots. -[Documentation available here.](https://steffo99.github.io/royalnet/html/) +[Documentation available here](https://royal-games.github.io/royalnet/html/index.html). diff --git a/docs/doctrees/audio.doctree b/docs/doctrees/audio.doctree index 21512809..e21ec3d8 100644 Binary files a/docs/doctrees/audio.doctree and b/docs/doctrees/audio.doctree differ diff --git a/docs/doctrees/bots.doctree b/docs/doctrees/bots.doctree index 75d987ae..9017f84a 100644 Binary files a/docs/doctrees/bots.doctree and b/docs/doctrees/bots.doctree differ diff --git a/docs/doctrees/commands.doctree b/docs/doctrees/commands.doctree index 6b3acb45..7c00ca48 100644 Binary files a/docs/doctrees/commands.doctree and b/docs/doctrees/commands.doctree differ diff --git a/docs/doctrees/database.doctree b/docs/doctrees/database.doctree index 86a47859..545747aa 100644 Binary files a/docs/doctrees/database.doctree and b/docs/doctrees/database.doctree differ diff --git a/docs/doctrees/environment.pickle b/docs/doctrees/environment.pickle index 763c1a29..024a7379 100644 Binary files a/docs/doctrees/environment.pickle and b/docs/doctrees/environment.pickle differ diff --git a/docs/doctrees/error.doctree b/docs/doctrees/error.doctree index 002e9782..06acf09c 100644 Binary files a/docs/doctrees/error.doctree and b/docs/doctrees/error.doctree differ diff --git a/docs/doctrees/index.doctree b/docs/doctrees/index.doctree index ae0de6cd..bcf926bb 100644 Binary files a/docs/doctrees/index.doctree and b/docs/doctrees/index.doctree differ diff --git a/docs/doctrees/network.doctree b/docs/doctrees/network.doctree index b113ac0f..1fc35e4b 100644 Binary files a/docs/doctrees/network.doctree and b/docs/doctrees/network.doctree differ diff --git a/docs/doctrees/utils.doctree b/docs/doctrees/utils.doctree index 2a66abfe..48552498 100644 Binary files a/docs/doctrees/utils.doctree and b/docs/doctrees/utils.doctree differ diff --git a/docs/html/audio.html b/docs/html/audio.html index 3450f376..d783b180 100644 --- a/docs/html/audio.html +++ b/docs/html/audio.html @@ -340,17 +340,41 @@
_stop_download()
-
+

I have no clue of what this does, or why is it here. Possibly remove it?

+
+
Raises
+

InterruptDownload – …uhhh, always?

+
+
+
static create_from_url(url, outtmpl='%(title)s-%(id)s.%(ext)s', **ytdl_args) → List[royalnet.audio.youtubedl.YtdlFile]
-
+

Download the videos at the specified url.

+
+
Parameters
+
    +
  • url – The url to download the videos from.

  • +
  • outtmpl – The filename that the downloaded videos are going to have. The name can be formatted according to the outtmpl documentation.

  • +
  • ytdl_args – Other arguments to be passed to the YoutubeDL object.

  • +
+
+
Returns
+

A list of YtdlFiles.

+
+
+
delete_video_file()
-
+

Delete the file located at self.video_filename.

+
+

Note

+

No checks are done when deleting, so it may try to delete a non-existing file and raise an exception or do some other weird stuff with weird filenames.

+
+
@@ -383,6 +407,11 @@ download(outtmpl='%(title)s-%(id)s.%(ext)s', **ytdl_args) → royalnet.audio.youtubedl.YtdlFile
+
+
+to_discord_embed() → discord.embeds.Embed
+
+
@@ -460,7 +489,7 @@ The name of the downloaded and PCM-converted audio file.

class royalnet.audio.RoyalPCMAudio(rpf: royalnet.audio.royalpcmfile.RoyalPCMFile)
-

A discord-compatible discord.AudioSource that keeps data in a file instead of in memory.

+

A discord.AudioSource that keeps data in a file instead of in memory.

__init__(rpf: royalnet.audio.royalpcmfile.RoyalPCMFile)
@@ -511,29 +540,19 @@ The name of the downloaded and PCM-converted audio file.

is_opus()
-

Checks if the audio source is already encoded in Opus.

-

Defaults to False.

+

This audio file isn’t Opus-encoded, but PCM-encoded.

+
+
Returns
+

False.

+
+
read()

Reads 20ms worth of audio.

-

Subclasses must implement this.

-

If the audio is complete, then returning an empty -py:bytes-like object to signal this is the way to do so.

-

If is_opus() method returns True, then it must return -20ms worth of Opus encoded audio. Otherwise, it must be 20ms -worth of 16-bit 48KHz stereo PCM, which is about 3,840 bytes -per frame (20ms worth of audio).

-
-
Returns
-

A bytes like object that represents the PCM or Opus data.

-
-
Return type
-

bytes

-
-
+

If the audio is complete, then returning an empty bytes-like object to signal this is the way to do so.

diff --git a/docs/html/bots.html b/docs/html/bots.html index a31963d6..ae36fe0b 100644 --- a/docs/html/bots.html +++ b/docs/html/bots.html @@ -188,7 +188,8 @@
botfather_command_string
-
+

Generate a string to be pasted in the “Edit Commands” BotFather prompt.

+
diff --git a/docs/html/commands.html b/docs/html/commands.html index 4da0b945..fe3ccc48 100644 --- a/docs/html/commands.html +++ b/docs/html/commands.html @@ -158,7 +158,8 @@

royalnet.commands

-

Commands that can be used in bots. These probably won’t suit your needs, as they are tailored for the bots of the Royal Games gaming community, but you can check them for reference.

+

Commands that can be used in bots.

+

These probably won’t suit your needs, as they are tailored for the bots of the Royal Games gaming community, but they may be useful to develop new ones.

class royalnet.commands.NullCommand
diff --git a/docs/html/database.html b/docs/html/database.html index 7972185f..a90e7edf 100644 --- a/docs/html/database.html +++ b/docs/html/database.html @@ -261,7 +261,7 @@
-royal = <RelationshipProperty at 0x7a5f588; no key>
+royal = <RelationshipProperty at 0x7f81332fcbc8; no key>
@@ -291,7 +291,7 @@
-creator = <RelationshipProperty at 0x7a5f150; no key>
+creator = <RelationshipProperty at 0x7f81333176c8; no key>
@@ -316,7 +316,7 @@
-quoted_account = <RelationshipProperty at 0x7a5fa98; no key>
+quoted_account = <RelationshipProperty at 0x7f8133317948; no key>
@@ -351,7 +351,7 @@
-royal = <RelationshipProperty at 0x7a5f738; no key>
+royal = <RelationshipProperty at 0x7f8133317ec8; no key>
@@ -366,7 +366,7 @@ class royalnet.database.tables.ActiveKvGroup
-group = <RelationshipProperty at 0x7a5f738; no key>
+group = <RelationshipProperty at 0x7f8133317a48; no key>
@@ -376,7 +376,7 @@
-royal = <RelationshipProperty at 0x7a5fc48; no key>
+royal = <RelationshipProperty at 0x7f81333171c8; no key>
@@ -391,7 +391,7 @@ class royalnet.database.tables.Keyvalue
-group = <RelationshipProperty at 0x7a5f978; no key>
+group = <RelationshipProperty at 0x7f81333173c8; no key>
@@ -446,7 +446,7 @@
-royal = <RelationshipProperty at 0x7a5fae0; no key>
+royal = <RelationshipProperty at 0x7f8133317948; no key>
diff --git a/docs/html/genindex.html b/docs/html/genindex.html index ed14650c..7fe725cd 100644 --- a/docs/html/genindex.html +++ b/docs/html/genindex.html @@ -267,7 +267,7 @@
  • Alchemy (class in royalnet.database)
  • -
  • alchemy (royalnet.utils.Call attribute) +
  • alchemy (royalnet.utils.Call attribute), [1]
  • +
  • to_discord_embed() (royalnet.audio.YtdlInfo method) +
  • to_json_bytes() (royalnet.network.Package method)
  • to_json_string() (royalnet.network.Package method) @@ -865,9 +867,11 @@
  • (royalnet.audio.YtdlFile attribute)
  • +
  • ytdl_filename (royalnet.audio.RoyalPCMFile attribute) +
    • -
    • ytdl_filename (royalnet.audio.RoyalPCMFile attribute) +
    • ytdldateformat() (in module royalnet.utils)
    • YtdlFile (class in royalnet.audio)
    • diff --git a/docs/html/network.html b/docs/html/network.html index d043df08..ef989940 100644 --- a/docs/html/network.html +++ b/docs/html/network.html @@ -158,10 +158,10 @@

      royalnet.network

      -

      Royalnet realated classes.

      +

      Royalnet (websocket) related classes.

      +class royalnet.network.RoyalnetLink(master_uri: str, secret: str, link_type: str, request_handler, *, loop: asyncio.events.AbstractEventLoop = <_UnixSelectorEventLoop running=False closed=False debug=False>)
      connect()
      @@ -295,7 +295,7 @@ Contains info about the source and the destination.

      -class royalnet.network.RoyalnetServer(address: str, port: int, required_secret: str, *, loop: asyncio.events.AbstractEventLoop = <_WindowsSelectorEventLoop running=False closed=False debug=False>)
      +class royalnet.network.RoyalnetServer(address: str, port: int, required_secret: str, *, loop: asyncio.events.AbstractEventLoop = <_UnixSelectorEventLoop running=False closed=False debug=False>)
      find_client(*, nid: str = None, link_type: str = None) → List[royalnet.network.royalnetserver.ConnectedClient]
      diff --git a/docs/html/objects.inv b/docs/html/objects.inv index 47f0b6ae..a85d3b11 100644 Binary files a/docs/html/objects.inv and b/docs/html/objects.inv differ diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js index c86e25eb..25ed4af2 100644 --- a/docs/html/searchindex.js +++ b/docs/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["audio","bots","commands","database","error","index","network","utils"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":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:["audio.rst","bots.rst","commands.rst","database.rst","error.rst","index.rst","network.rst","utils.rst"],objects:{"royalnet.audio":{PlayMode:[0,1,1,""],Playlist:[0,1,1,""],Pool:[0,1,1,""],RoyalPCMAudio:[0,1,1,""],RoyalPCMFile:[0,1,1,""],YtdlFile:[0,1,1,""],YtdlInfo:[0,1,1,""]},"royalnet.audio.PlayMode":{"delete":[0,2,1,""],__init__:[0,2,1,""],_generate_generator:[0,2,1,""],add:[0,2,1,""],next:[0,2,1,""],videos_left:[0,2,1,""]},"royalnet.audio.Playlist":{"delete":[0,2,1,""],__init__:[0,2,1,""],_generate_generator:[0,2,1,""],add:[0,2,1,""],videos_left:[0,2,1,""]},"royalnet.audio.Pool":{"delete":[0,2,1,""],__init__:[0,2,1,""],_generate_generator:[0,2,1,""],add:[0,2,1,""],videos_left:[0,2,1,""]},"royalnet.audio.RoyalPCMAudio":{"delete":[0,2,1,""],__init__:[0,2,1,""],create_from_url:[0,3,1,""],create_from_ytsearch:[0,3,1,""],is_opus:[0,2,1,""],read:[0,2,1,""]},"royalnet.audio.RoyalPCMFile":{audio_filename:[0,4,1,""],create_from_url:[0,3,1,""],create_from_ytsearch:[0,3,1,""],delete_audio_file:[0,2,1,""],ytdl_args:[0,4,1,""],ytdl_filename:[0,4,1,""]},"royalnet.audio.YtdlFile":{_stop_download:[0,2,1,""],create_from_url:[0,3,1,""],delete_video_file:[0,2,1,""],ytdl_args:[0,4,1,""]},"royalnet.audio.YtdlInfo":{__init__:[0,2,1,""],create_from_url:[0,3,1,""],download:[0,2,1,""]},"royalnet.bots":{DiscordBot:[1,1,1,""],DiscordConfig:[1,1,1,""],GenericBot:[1,1,1,""],TelegramBot:[1,1,1,""],TelegramConfig:[1,1,1,""]},"royalnet.bots.DiscordBot":{_bot_factory:[1,2,1,""],_call_factory:[1,2,1,""],_init_client:[1,2,1,""],_init_voice:[1,2,1,""],add_to_music_data:[1,2,1,""],advance_music_data:[1,2,1,""],interface_name:[1,4,1,""],run:[1,2,1,""],update_activity_with_source_title:[1,2,1,""]},"royalnet.bots.GenericBot":{_call_factory:[1,2,1,""],_init_commands:[1,2,1,""],_init_database:[1,2,1,""],_init_royalnet:[1,2,1,""],_network_handler:[1,2,1,""],call:[1,2,1,""],interface_name:[1,4,1,""],run:[1,2,1,""]},"royalnet.bots.TelegramBot":{_call_factory:[1,2,1,""],_handle_update:[1,2,1,""],_init_client:[1,2,1,""],botfather_command_string:[1,4,1,""],interface_name:[1,4,1,""],run:[1,2,1,""]},"royalnet.commands":{AuthorCommand:[2,1,1,""],CiaoruoziCommand:[2,1,1,""],ColorCommand:[2,1,1,""],CvCommand:[2,1,1,""],DateparserCommand:[2,1,1,""],DiarioCommand:[2,1,1,""],KvCommand:[2,1,1,""],KvactiveCommand:[2,1,1,""],KvrollCommand:[2,1,1,""],MissingCommand:[2,1,1,""],NullCommand:[2,1,1,""],PingCommand:[2,1,1,""],PlayCommand:[2,1,1,""],PlaymodeCommand:[2,1,1,""],RageCommand:[2,1,1,""],ReminderCommand:[2,1,1,""],ShipCommand:[2,1,1,""],SkipCommand:[2,1,1,""],SmecdsCommand:[2,1,1,""],SummonCommand:[2,1,1,""],SyncCommand:[2,1,1,""],VideochannelCommand:[2,1,1,""],VideoinfoCommand:[2,1,1,""]},"royalnet.database":{Alchemy:[3,1,1,""],DatabaseConfig:[3,1,1,""],relationshiplinkchain:[3,5,1,""],tables:[3,0,0,"-"]},"royalnet.database.Alchemy":{__init__:[3,2,1,""],_create_tables:[3,2,1,""],session_acm:[3,2,1,""],session_cm:[3,2,1,""]},"royalnet.database.tables":{ActiveKvGroup:[3,1,1,""],Alias:[3,1,1,""],Diario:[3,1,1,""],Discord:[3,1,1,""],Keygroup:[3,1,1,""],Keyvalue:[3,1,1,""],Royal:[3,1,1,""],Telegram:[3,1,1,""]},"royalnet.database.tables.ActiveKvGroup":{group:[3,4,1,""],group_name:[3,4,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""]},"royalnet.database.tables.Alias":{alias:[3,4,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""]},"royalnet.database.tables.Diario":{context:[3,4,1,""],creator:[3,4,1,""],creator_id:[3,4,1,""],diario_id:[3,4,1,""],media_url:[3,4,1,""],quoted:[3,4,1,""],quoted_account:[3,4,1,""],quoted_account_id:[3,4,1,""],spoiler:[3,4,1,""],text:[3,4,1,""],timestamp:[3,4,1,""]},"royalnet.database.tables.Discord":{avatar_hash:[3,4,1,""],discord_id:[3,4,1,""],discriminator:[3,4,1,""],full_username:[3,2,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""],username:[3,4,1,""]},"royalnet.database.tables.Keygroup":{group_name:[3,4,1,""]},"royalnet.database.tables.Keyvalue":{group:[3,4,1,""],group_name:[3,4,1,""],key:[3,4,1,""],value:[3,4,1,""]},"royalnet.database.tables.Royal":{avatar:[3,4,1,""],password:[3,4,1,""],role:[3,4,1,""],uid:[3,4,1,""],username:[3,4,1,""]},"royalnet.database.tables.Telegram":{first_name:[3,4,1,""],last_name:[3,4,1,""],mention:[3,2,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""],tg_id:[3,4,1,""],username:[3,4,1,""]},"royalnet.error":{ExternalError:[4,6,1,""],InvalidConfigError:[4,6,1,""],InvalidInputError:[4,6,1,""],NoneFoundError:[4,6,1,""],RoyalnetRequestError:[4,6,1,""],RoyalnetResponseError:[4,6,1,""],TooManyFoundError:[4,6,1,""],UnregisteredError:[4,6,1,""],UnsupportedError:[4,6,1,""]},"royalnet.network":{ConnectionClosedError:[6,6,1,""],NetworkError:[6,6,1,""],NotConnectedError:[6,6,1,""],NotIdentifiedError:[6,6,1,""],Package:[6,1,1,""],Request:[6,1,1,""],Response:[6,1,1,""],ResponseError:[6,1,1,""],ResponseSuccess:[6,1,1,""],RoyalnetConfig:[6,1,1,""],RoyalnetLink:[6,1,1,""],RoyalnetServer:[6,1,1,""]},"royalnet.network.Package":{__init__:[6,2,1,""],from_dict:[6,3,1,""],from_json_bytes:[6,3,1,""],from_json_string:[6,3,1,""],reply:[6,2,1,""],to_dict:[6,2,1,""],to_json_bytes:[6,2,1,""],to_json_string:[6,2,1,""]},"royalnet.network.Request":{from_dict:[6,3,1,""],to_dict:[6,2,1,""]},"royalnet.network.Response":{from_dict:[6,7,1,""],raise_on_error:[6,2,1,""],to_dict:[6,2,1,""]},"royalnet.network.ResponseError":{raise_on_error:[6,2,1,""]},"royalnet.network.ResponseSuccess":{raise_on_error:[6,2,1,""]},"royalnet.network.RoyalnetLink":{connect:[6,2,1,""],identify:[6,2,1,""],receive:[6,2,1,""],request:[6,2,1,""],run:[6,2,1,""],send:[6,2,1,""]},"royalnet.network.RoyalnetServer":{find_client:[6,2,1,""],find_destination:[6,2,1,""],listener:[6,2,1,""],route_package:[6,2,1,""],serve:[6,2,1,""],start:[6,2,1,""]},"royalnet.utils":{Call:[7,1,1,""],Command:[7,1,1,""],CommandArgs:[7,1,1,""],NetworkHandler:[7,1,1,""],andformat:[7,5,1,""],asyncify:[7,5,1,""],cdj:[7,5,1,""],fileformat:[7,5,1,""],plusformat:[7,5,1,""],safeformat:[7,5,1,""],sleep_until:[7,5,1,""]},"royalnet.utils.Call":{__init__:[7,2,1,""],_session_init:[7,2,1,""],alchemy:[7,4,1,""],get_author:[7,2,1,""],interface_name:[7,4,1,""],interface_obj:[7,4,1,""],interface_prefix:[7,4,1,""],net_request:[7,2,1,""],reply:[7,2,1,""],run:[7,2,1,""],session_end:[7,2,1,""]},"royalnet.utils.Command":{command_description:[7,4,1,""],command_name:[7,4,1,""],command_syntax:[7,4,1,""],common:[7,7,1,""],network_handler_dict:[7,7,1,""],network_handlers:[7,4,1,""],require_alchemy_tables:[7,4,1,""]},"royalnet.utils.CommandArgs":{__getitem__:[7,2,1,""],joined:[7,2,1,""],match:[7,2,1,""],optional:[7,2,1,""]},"royalnet.utils.NetworkHandler":{message_type:[7,4,1,""]},royalnet:{audio:[0,0,0,"-"],bots:[1,0,0,"-"],commands:[2,0,0,"-"],database:[3,0,0,"-"],error:[4,0,0,"-"],network:[6,0,0,"-"],utils:[7,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","staticmethod","Python static method"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["py","exception","Python exception"],"7":["py","classmethod","Python class method"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:staticmethod","4":"py:attribute","5":"py:function","6":"py:exception","7":"py:classmethod"},terms:{"0x7a5f150":3,"0x7a5f588":3,"0x7a5f738":3,"0x7a5f978":3,"0x7a5fa98":3,"0x7a5fae0":3,"0x7a5fc48":3,"20m":0,"48khz":0,"abstract":7,"boolean":3,"byte":[0,6],"class":[0,1,2,3,6,7],"default":[0,3,7],"final":7,"float":0,"function":[0,4,7],"int":[0,6,7],"new":[0,1,3,7],"null":[1,6],"return":[0,1,4,6,7],"static":[0,6],"true":[0,3,7],"try":[1,7],"while":[0,1,3,4,7],Not:0,That:7,The:[0,1,3,4,6,7],Then:1,These:2,Use:3,__dict__:7,__doc__:7,__getitem__:7,__init__:[0,3,6,7],__module__:7,__weakref__:7,_bot_factori:1,_call_factori:1,_create_t:3,_generate_gener:0,_handle_upd:1,_init_cli:1,_init_command:1,_init_databas:1,_init_royalnet:1,_init_voic:1,_network_handl:1,_session_init:7,_stop_download:0,_windowsselectoreventloop:6,abl:7,about:[0,6],abstracteventloop:6,access:7,accur:7,activekvgroup:3,add:[0,1],add_to_music_data:1,added:7,adding:7,addit:[6,7],address:6,advanc:0,advance_music_data:1,akin:6,alchemi:[1,3,7],alia:3,all:[0,6,7],allow:3,alreadi:0,also:[0,6],altern:6,alwai:1,amount:[0,7],andformat:7,ani:[0,7],anoth:[1,6],anymor:6,anystr:7,arg:[6,7],argument:[0,7],around:[0,3],arrai:7,async:[0,3],asyncifi:7,asyncio:[1,6],asyncron:3,attempt:7,attribut:7,audio:[1,5],audio_filenam:0,audio_sourc:1,audiosourc:0,author:7,authorcommand:2,avatar:3,avatar_hash:3,base:[0,1,6,7],been:[0,4],being:4,bestaudio:0,between:7,biginteg:3,bit:0,block:[1,7],blockingli:6,bot:[0,2,3,4,5,7],botfather_command_str:1,both:3,call:[1,7],callabl:7,can:[2,4,6,7],cancel:7,cannot:[4,7],cdj:7,chain:1,chang:[0,1],change_pres:1,channel:[1,7],charact:7,chat:1,check:[0,2,3],ciaoruozicommand:2,class_:7,classmethod:[6,7],client:1,close:[6,7],colorcommand:2,column:3,columndefault:3,command:[1,4,5,7],command_arg:7,command_descript:7,command_nam:[1,7],command_prefix:1,command_syntax:7,commandarg:7,common:7,commun:[2,6],compat:0,complet:[0,4],compon:[3,4],configur:[1,3,4],connect:[1,6],connectedcli:6,connectionclosederror:6,contain:[0,4,6],context:3,convers:6,convert:[0,6,7],core:3,coroutin:[1,7],correctli:4,correspond:[0,1],creat:[0,1,3,6,7],create_from_url:0,create_from_ytsearch:0,creator:3,creator_id:3,ct_co:3,current:1,custom:[0,1],cvcommand:2,data:[0,6,7],databas:[1,5,7],database_config:1,database_uri:3,databaseconfig:[1,3],dateparsercommand:2,datetim:[3,7],debug:6,declar:[3,7],delet:0,delete_audio_fil:0,delete_video_fil:0,describ:3,descript:6,destin:[6,7],destination_conv_id:6,detail:3,diario:3,diario_id:3,diariocommand:2,dict:[0,1,6,7],dictionari:[1,6],discord:[0,1,3],discord_config:1,discord_id:3,discordbot:1,discordcli:1,discordconfig:1,discrimin:3,doc:3,doe:0,doesn:1,don:7,download:0,dure:1,dynam:7,each:0,either:[0,7],element:[4,7],empti:[0,1],encod:[0,6],end:3,ending_class:3,engin:3,ensur:7,error:[5,6,7],error_command:1,error_data:6,error_if_non:7,event:6,everi:[0,6],except:[1,4,6],execut:[1,4,6,7],exist:1,expect:4,ext:0,externalerror:4,extra:0,extra_info:6,extract:0,extract_info:0,factori:0,fals:[0,3,6,7],file:[0,1],fileformat:7,filenam:7,find:[1,3,6,7],find_client:6,find_destin:6,first:0,first_nam:3,follow:3,foreignkei:3,format:[0,1,7],found:[4,7],frame:0,from:[0,1,3,6,7],from_dict:6,from_json_byt:6,from_json_str:6,full_usernam:3,fullfil:7,game:2,gener:[0,1,7],genericbot:[1,3],get:[0,3,7],get_author:7,going:0,greater:7,group:[3,7],group_nam:3,guild:1,handl:[1,4],handler:6,has:[0,4,6,7],have:1,html:3,http:3,identifi:[6,7],identity_column_nam:3,identity_t:[1,3],ignor:7,implement:0,incom:1,index:[5,7],inf:[0,6],infinit:0,info:[0,6],inherit:[0,1,6],initi:[0,1],input:[4,7],insid:0,instanc:1,instead:[0,7],integ:3,interfac:[1,4],interface_nam:[1,7],interface_obj:7,interface_prefix:7,invalid:[4,6],invalidconfigerror:4,invalidinputerror:[4,7],is_opu:0,item:[0,7],join:7,json:6,jsonabl:6,just:1,keep:0,kei:[3,7],keygroup:3,keyvalu:3,keyword:7,kvactivecommand:2,kvcommand:2,kvrollcommand:2,kwarg:[1,7],largebinari:3,last:7,last_nam:3,least:0,left:0,like:0,link:[1,4,6],link_typ:6,list:[0,1,6,7],listen:[1,6],logger:0,login:1,look:4,loop:6,made:7,mai:[0,7],mainli:0,maintain:3,make:1,manag:3,markup:7,master_secret:6,master_t:[1,3],master_uri:6,match:[1,4,7],math:0,mean:7,media_url:3,memori:0,mention:3,messag:[1,6,7],message_typ:7,method:[0,3],middl:7,minimum:7,miscellan:7,miss:7,missing_command:1,missingcommand:2,modul:5,more:[1,3],multipl:[1,3,4],music_data:1,must:[0,6,7],name:[0,1,6],need:[1,2],net_request:7,network:[1,4,5,7],network_handl:[1,7],network_handler_dict:7,networkerror:6,networkhandl:7,next:[0,1],nid:6,no_warn:0,nobodi:6,node:6,non:[4,7],none:[0,1,3,6,7],nonefounderror:4,noplaylist:0,notat:7,notconnectederror:6,noth:[6,7],notidentifiederror:6,notimpl:[1,7],now_plai:0,nullabl:3,nullcommand:[1,2],number:[0,6],object:[0,1,3,7],offset:1,onc:[0,3],one:4,ones:1,onli:[4,7],option:[0,1,3,6,7],opu:0,order:0,org:3,other:[1,4,6,7],otherwis:[0,1,6,7],output:7,outtmpl:0,packag:6,packet:6,page:5,paramet:[0,1,3,6,7],pass:[0,7],password:3,path:[3,6],pattern:7,pcm:0,per:0,perman:0,pickl:7,pingcommand:2,plai:[0,1],playcommand:2,playlist:0,playmod:0,playmodecommand:2,plusformat:7,pool:0,port:6,possibl:7,prepar:6,presenc:1,previous:7,primary_kei:3,probabl:[0,2,7],properti:0,quiet:0,quot:3,quoted_account:3,quoted_account_id:3,ragecommand:2,rais:[1,4,6,7],raise_on_error:6,random:0,read:0,real:6,realat:6,receiv:[1,4,6,7],reciev:6,recommend:0,recreat:6,refer:2,regex:7,regist:4,relat:[0,3],relationshiplinkchain:3,relationshipproperti:3,remindercommand:2,remov:0,repeat:0,replac:7,repli:[6,7],repres:[0,1],request:[1,4,6],request_dict:1,request_handl:6,requir:[1,4,7],require_alchemy_t:7,require_at_least:7,required_secret:6,respons:[1,4,6],responseerror:[4,6],responsesuccess:6,result:7,retriev:7,role:3,rout:6,route_packag:6,row:7,royal:[2,3],royal_id:3,royalnet_config:1,royalnetconfig:[1,6],royalnetlink:[1,6,7],royalnetrequesterror:4,royalnetresponseerror:4,royalnetserv:6,royalpcmaudio:[0,1],royalpcmfil:0,rpf:0,run:[1,6,7],safeformat:7,search:[0,5],second:7,secret:6,select:[0,3],self:[1,6],send:[6,7],sent:[6,7],sequenc:[6,7],serv:6,server:6,session:7,session_acm:3,session_cm:3,session_end:7,set:[1,3],shipcommand:2,should:[1,6,7],signal:0,singl:[1,3],skipcommand:2,sleep_until:7,smecdscommand:2,someth:[4,7],somewher:6,song:[0,1],soon:0,sourc:[0,6,7],source_conv_id:6,space:7,specif:[1,7],specifi:[1,4,7],spoiler:3,sqlalchemi:[3,7],start:[1,3,6],starting_class:3,starting_list:0,starting_pool:0,statement:3,statu:1,stereo:0,str:[0,1,3,6,7],string:[0,3,6,7],sub:7,subclass:0,submodul:3,success:6,suit:2,summoncommand:2,support:4,synccommand:2,tabl:[1,7],tailor:2,task:1,telegram:[1,3],telegram_config:1,telegrambot:1,telegramcal:1,telegramconfig:1,text:[3,7],tg_id:3,than:7,thei:[2,7],them:[2,3],therefor:4,thi:[0,4,6,7],those:0,through:7,time:6,timestamp:3,titl:0,to_dict:6,to_json_byt:6,to_json_str:6,token:1,toomanyfounderror:4,tupl:3,two:7,type:[0,1,3,6,7],uid:3,underscor:7,undescrib:7,unexpect:7,unexpectedli:6,union:0,univers:7,unregisterederror:[4,7],unsupportederror:4,until:[0,7],updat:1,update_activity_with_source_titl:1,uri:3,url:0,use:[0,3],used:[0,1,2,3,6,7],useful:[0,7],user:[4,7],usernam:3,using:[1,7],usual:[0,6],utf8:6,util:[1,5],valu:[0,3,6,7],variabl:1,variou:1,veri:0,video:0,videochannelcommand:2,videoinfocommand:2,videos_left:0,voic:[0,1],wai:0,wait:7,want:[0,7],warn:0,websocket:6,websocketserverprotocol:6,weird:7,went:4,were:4,when:7,where:7,which:[0,6],without:7,won:2,word:7,worth:0,wrapper:[0,3],wrong:4,yet:6,yield:0,you:[0,2,7],your:2,youtub:0,youtube_dl:0,youtubedl:0,ytdl_arg:0,ytdl_filenam:0,ytdlfile:0,ytdlinfo:0},titles:["royalnet.audio","royalnet.bots","royalnet.commands","royalnet.database","royalnet.error","royalnet","royalnet.network","royalnet.utils"],titleterms:{audio:0,bot:1,command:2,databas:3,error:4,indic:5,network:6,royalnet:[0,1,2,3,4,5,6,7],tabl:[3,5],util:7}}) \ No newline at end of file +Search.setIndex({docnames:["audio","bots","commands","database","error","index","network","utils"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":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:["audio.rst","bots.rst","commands.rst","database.rst","error.rst","index.rst","network.rst","utils.rst"],objects:{"royalnet.audio":{PlayMode:[0,1,1,""],Playlist:[0,1,1,""],Pool:[0,1,1,""],RoyalPCMAudio:[0,1,1,""],RoyalPCMFile:[0,1,1,""],YtdlFile:[0,1,1,""],YtdlInfo:[0,1,1,""]},"royalnet.audio.PlayMode":{"delete":[0,2,1,""],__init__:[0,2,1,""],_generate_generator:[0,2,1,""],add:[0,2,1,""],next:[0,2,1,""],videos_left:[0,2,1,""]},"royalnet.audio.Playlist":{"delete":[0,2,1,""],__init__:[0,2,1,""],_generate_generator:[0,2,1,""],add:[0,2,1,""],videos_left:[0,2,1,""]},"royalnet.audio.Pool":{"delete":[0,2,1,""],__init__:[0,2,1,""],_generate_generator:[0,2,1,""],add:[0,2,1,""],videos_left:[0,2,1,""]},"royalnet.audio.RoyalPCMAudio":{"delete":[0,2,1,""],__init__:[0,2,1,""],create_from_url:[0,3,1,""],create_from_ytsearch:[0,3,1,""],is_opus:[0,2,1,""],read:[0,2,1,""]},"royalnet.audio.RoyalPCMFile":{audio_filename:[0,4,1,""],create_from_url:[0,3,1,""],create_from_ytsearch:[0,3,1,""],delete_audio_file:[0,2,1,""],ytdl_args:[0,4,1,""],ytdl_filename:[0,4,1,""]},"royalnet.audio.YtdlFile":{_stop_download:[0,2,1,""],create_from_url:[0,3,1,""],delete_video_file:[0,2,1,""],ytdl_args:[0,4,1,""]},"royalnet.audio.YtdlInfo":{__init__:[0,2,1,""],create_from_url:[0,3,1,""],download:[0,2,1,""],to_discord_embed:[0,2,1,""]},"royalnet.bots":{DiscordBot:[1,1,1,""],DiscordConfig:[1,1,1,""],GenericBot:[1,1,1,""],TelegramBot:[1,1,1,""],TelegramConfig:[1,1,1,""]},"royalnet.bots.DiscordBot":{_bot_factory:[1,2,1,""],_call_factory:[1,2,1,""],_init_client:[1,2,1,""],_init_voice:[1,2,1,""],add_to_music_data:[1,2,1,""],advance_music_data:[1,2,1,""],interface_name:[1,4,1,""],run:[1,2,1,""],update_activity_with_source_title:[1,2,1,""]},"royalnet.bots.GenericBot":{_call_factory:[1,2,1,""],_init_commands:[1,2,1,""],_init_database:[1,2,1,""],_init_royalnet:[1,2,1,""],_network_handler:[1,2,1,""],call:[1,2,1,""],interface_name:[1,4,1,""],run:[1,2,1,""]},"royalnet.bots.TelegramBot":{_call_factory:[1,2,1,""],_handle_update:[1,2,1,""],_init_client:[1,2,1,""],botfather_command_string:[1,4,1,""],interface_name:[1,4,1,""],run:[1,2,1,""]},"royalnet.commands":{AuthorCommand:[2,1,1,""],CiaoruoziCommand:[2,1,1,""],ColorCommand:[2,1,1,""],CvCommand:[2,1,1,""],DateparserCommand:[2,1,1,""],DiarioCommand:[2,1,1,""],KvCommand:[2,1,1,""],KvactiveCommand:[2,1,1,""],KvrollCommand:[2,1,1,""],MissingCommand:[2,1,1,""],NullCommand:[2,1,1,""],PingCommand:[2,1,1,""],PlayCommand:[2,1,1,""],PlaymodeCommand:[2,1,1,""],RageCommand:[2,1,1,""],ReminderCommand:[2,1,1,""],ShipCommand:[2,1,1,""],SkipCommand:[2,1,1,""],SmecdsCommand:[2,1,1,""],SummonCommand:[2,1,1,""],SyncCommand:[2,1,1,""],VideochannelCommand:[2,1,1,""],VideoinfoCommand:[2,1,1,""]},"royalnet.database":{Alchemy:[3,1,1,""],DatabaseConfig:[3,1,1,""],relationshiplinkchain:[3,5,1,""],tables:[3,0,0,"-"]},"royalnet.database.Alchemy":{__init__:[3,2,1,""],_create_tables:[3,2,1,""],session_acm:[3,2,1,""],session_cm:[3,2,1,""]},"royalnet.database.tables":{ActiveKvGroup:[3,1,1,""],Alias:[3,1,1,""],Diario:[3,1,1,""],Discord:[3,1,1,""],Keygroup:[3,1,1,""],Keyvalue:[3,1,1,""],Royal:[3,1,1,""],Telegram:[3,1,1,""]},"royalnet.database.tables.ActiveKvGroup":{group:[3,4,1,""],group_name:[3,4,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""]},"royalnet.database.tables.Alias":{alias:[3,4,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""]},"royalnet.database.tables.Diario":{context:[3,4,1,""],creator:[3,4,1,""],creator_id:[3,4,1,""],diario_id:[3,4,1,""],media_url:[3,4,1,""],quoted:[3,4,1,""],quoted_account:[3,4,1,""],quoted_account_id:[3,4,1,""],spoiler:[3,4,1,""],text:[3,4,1,""],timestamp:[3,4,1,""]},"royalnet.database.tables.Discord":{avatar_hash:[3,4,1,""],discord_id:[3,4,1,""],discriminator:[3,4,1,""],full_username:[3,2,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""],username:[3,4,1,""]},"royalnet.database.tables.Keygroup":{group_name:[3,4,1,""]},"royalnet.database.tables.Keyvalue":{group:[3,4,1,""],group_name:[3,4,1,""],key:[3,4,1,""],value:[3,4,1,""]},"royalnet.database.tables.Royal":{avatar:[3,4,1,""],password:[3,4,1,""],role:[3,4,1,""],uid:[3,4,1,""],username:[3,4,1,""]},"royalnet.database.tables.Telegram":{first_name:[3,4,1,""],last_name:[3,4,1,""],mention:[3,2,1,""],royal:[3,4,1,""],royal_id:[3,4,1,""],tg_id:[3,4,1,""],username:[3,4,1,""]},"royalnet.error":{ExternalError:[4,6,1,""],InvalidConfigError:[4,6,1,""],InvalidInputError:[4,6,1,""],NoneFoundError:[4,6,1,""],RoyalnetRequestError:[4,6,1,""],RoyalnetResponseError:[4,6,1,""],TooManyFoundError:[4,6,1,""],UnregisteredError:[4,6,1,""],UnsupportedError:[4,6,1,""]},"royalnet.network":{ConnectionClosedError:[6,6,1,""],NetworkError:[6,6,1,""],NotConnectedError:[6,6,1,""],NotIdentifiedError:[6,6,1,""],Package:[6,1,1,""],Request:[6,1,1,""],Response:[6,1,1,""],ResponseError:[6,1,1,""],ResponseSuccess:[6,1,1,""],RoyalnetConfig:[6,1,1,""],RoyalnetLink:[6,1,1,""],RoyalnetServer:[6,1,1,""]},"royalnet.network.Package":{__init__:[6,2,1,""],from_dict:[6,3,1,""],from_json_bytes:[6,3,1,""],from_json_string:[6,3,1,""],reply:[6,2,1,""],to_dict:[6,2,1,""],to_json_bytes:[6,2,1,""],to_json_string:[6,2,1,""]},"royalnet.network.Request":{from_dict:[6,3,1,""],to_dict:[6,2,1,""]},"royalnet.network.Response":{from_dict:[6,7,1,""],raise_on_error:[6,2,1,""],to_dict:[6,2,1,""]},"royalnet.network.ResponseError":{raise_on_error:[6,2,1,""]},"royalnet.network.ResponseSuccess":{raise_on_error:[6,2,1,""]},"royalnet.network.RoyalnetLink":{connect:[6,2,1,""],identify:[6,2,1,""],receive:[6,2,1,""],request:[6,2,1,""],run:[6,2,1,""],send:[6,2,1,""]},"royalnet.network.RoyalnetServer":{find_client:[6,2,1,""],find_destination:[6,2,1,""],listener:[6,2,1,""],route_package:[6,2,1,""],serve:[6,2,1,""],start:[6,2,1,""]},"royalnet.utils":{Call:[7,1,1,""],Command:[7,1,1,""],CommandArgs:[7,1,1,""],NetworkHandler:[7,1,1,""],andformat:[7,5,1,""],asyncify:[7,5,1,""],cdj:[7,5,1,""],fileformat:[7,5,1,""],plusformat:[7,5,1,""],safeformat:[7,5,1,""],sleep_until:[7,5,1,""],ytdldateformat:[7,5,1,""]},"royalnet.utils.Call":{__init__:[7,2,1,""],_session_init:[7,2,1,""],alchemy:[7,4,1,""],get_author:[7,2,1,""],interface_name:[7,4,1,""],interface_obj:[7,4,1,""],interface_prefix:[7,4,1,""],net_request:[7,2,1,""],reply:[7,2,1,""],run:[7,2,1,""],session_end:[7,2,1,""]},"royalnet.utils.Command":{command_description:[7,4,1,""],command_name:[7,4,1,""],command_syntax:[7,4,1,""],common:[7,7,1,""],network_handler_dict:[7,7,1,""],network_handlers:[7,4,1,""],require_alchemy_tables:[7,4,1,""]},"royalnet.utils.CommandArgs":{__getitem__:[7,2,1,""],joined:[7,2,1,""],match:[7,2,1,""],optional:[7,2,1,""]},"royalnet.utils.NetworkHandler":{message_type:[7,4,1,""]},royalnet:{audio:[0,0,0,"-"],bots:[1,0,0,"-"],commands:[2,0,0,"-"],database:[3,0,0,"-"],error:[4,0,0,"-"],network:[6,0,0,"-"],utils:[7,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","staticmethod","Python static method"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["py","exception","Python exception"],"7":["py","classmethod","Python class method"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:staticmethod","4":"py:attribute","5":"py:function","6":"py:exception","7":"py:classmethod"},terms:{"0x7f81332fcbc8":3,"0x7f81333171c8":3,"0x7f81333173c8":3,"0x7f81333176c8":3,"0x7f8133317948":3,"0x7f8133317a48":3,"0x7f8133317ec8":3,"20m":0,"abstract":7,"boolean":3,"byte":[0,6],"class":[0,1,2,3,6,7],"default":[3,7],"final":7,"float":0,"function":[0,4,7],"int":[0,6,7],"new":[0,1,2,3,7],"null":[1,6],"return":[0,1,4,6,7],"static":[0,6],"true":[0,3,7],"try":[0,1,7],"while":[0,1,3,4,7],For:7,Not:0,That:7,The:[0,1,3,4,6,7],Then:1,These:2,Use:3,__dict__:7,__doc__:7,__getitem__:7,__init__:[0,3,6,7],__module__:7,__slots__:7,__weakref__:7,_bot_factori:1,_call_factori:1,_create_t:3,_generate_gener:0,_handle_upd:1,_init_cli:1,_init_command:1,_init_databas:1,_init_royalnet:1,_init_voic:1,_network_handl:1,_session_init:7,_stop_download:0,_unixselectoreventloop:6,abl:7,about:6,abstracteventloop:6,access:7,accord:0,accur:7,activekvgroup:3,add:[0,1,7],add_to_music_data:1,added:7,adding:7,addit:[6,7],address:6,advanc:0,advance_music_data:1,akin:6,alchemi:[1,3,7],alia:3,all:[0,6,7],allow:3,also:[0,6],altern:6,alwai:[0,1],amount:[0,7],andformat:7,ani:[0,7],anoth:[1,6],anymor:6,anystr:7,arg:[6,7],argument:[0,7],around:[0,3],arrai:7,associ:7,async:[0,3],asyncifi:7,asyncio:[1,6],asyncron:3,attempt:7,attribut:7,audio:[1,5],audio_filenam:0,audio_sourc:1,audiosourc:0,author:7,authorcommand:2,autocomplet:7,avatar:3,avatar_hash:3,base:[0,1,6,7],been:[0,4],being:[4,7],bestaudio:0,between:7,biginteg:3,block:[1,7],blockingli:6,bot:[0,2,3,4,5,7],botfath:1,botfather_command_str:1,both:3,call:[1,7],callabl:7,can:[0,2,4,6,7],cancel:7,cannot:[4,7],cdj:7,chain:1,chang:[0,1],change_pres:1,channel:[1,7],charact:7,chat:1,check:[0,3],ciaoruozicommand:2,class_:7,classmethod:[6,7],classyalnet:7,client:1,close:[6,7],clue:0,colorcommand:2,column:3,columndefault:3,command:[1,4,5,7],command_arg:7,command_descript:7,command_nam:[1,7],command_prefix:1,command_syntax:7,commandarg:7,common:7,commun:[2,6],complet:[0,4],compon:[3,4],configur:[1,3,4],connect:[1,6,7],connectedcli:6,connectionclosederror:6,contain:[0,4,6],context:3,convers:6,convert:[0,6,7],core:3,coroutin:[1,7],correctli:4,correspond:[0,1],creat:[0,1,3,6,7],create_from_url:0,create_from_ytsearch:0,creator:3,creator_id:3,ct_co:3,current:1,custom:[0,1],cvcommand:2,dai:7,data:[0,6,7],databas:[1,5,7],database_config:1,database_uri:3,databaseconfig:[1,3],date:7,dateparsercommand:2,datetim:[3,7],debug:6,declar:[3,7],delet:0,delete_audio_fil:0,delete_video_fil:0,describ:3,descript:[6,7],destin:[6,7],destination_conv_id:6,detail:3,develop:2,diario:3,diario_id:3,diariocommand:2,dict:[0,1,6,7],dictionari:[1,6],discord:[0,1,3,7],discord_config:1,discord_id:3,discordbot:1,discordcli:1,discordconfig:1,discrimin:3,displai:7,doc:3,document:0,doe:[0,7],doesn:[1,7],don:7,done:0,download:0,dure:1,dynam:7,each:0,eas:7,edit:1,either:[0,7],element:[4,7],emb:0,empti:[0,1],encod:[0,6],end:3,ending_class:3,engin:3,ensur:7,error:[5,6,7],error_command:1,error_data:6,error_if_non:7,event:6,everi:[0,6],exampl:7,except:[0,1,4,6],execut:[1,4,6,7],exist:[0,1,7],expect:4,ext:0,externalerror:4,extra:0,extra_info:6,extract:0,extract_info:0,factori:0,fals:[0,3,6,7],file:[0,1],fileformat:7,filenam:[0,7],find:[1,3,6,7],find_client:6,find_destin:6,first:0,first_nam:3,follow:3,foreignkei:3,format:[0,1,7],found:[4,7],from:[0,1,3,6,7],from_dict:6,from_json_byt:6,from_json_str:6,full_usernam:3,fullfil:7,game:2,gener:[0,1,7],genericbot:[1,3],get:[0,3,7],get_author:7,going:0,greater:7,group:[3,7],group_nam:3,guild:1,handl:[1,4],handler:6,has:[0,4,6,7],have:[0,1,7],here:0,html:3,http:3,identifi:[6,7],identity_column_nam:3,identity_t:[1,3],ifi:7,ignor:7,incom:1,index:[5,7],inf:[0,6],infinit:0,info:[0,6],inherit:[0,1,6,7],initi:[0,1],input:[4,7],insid:0,instanc:1,instead:[0,7],integ:3,interfac:[1,4,7],interface_nam:[1,7],interface_obj:7,interface_prefix:7,interruptdownload:0,invalid:[4,6],invalidconfigerror:4,invalidinputerror:[4,7],is_opu:0,isn:0,item:[0,7],join:7,json:6,jsonabl:6,just:1,keep:0,kei:[3,7],keygroup:3,keyvalu:3,keyword:7,kvactivecommand:2,kvcommand:2,kvrollcommand:2,kwarg:[1,7],largebinari:3,last:7,last_nam:3,least:0,left:0,less:7,like:0,link:[1,4,6],link_typ:6,list:[0,1,6,7],listen:[1,6],locat:0,logger:0,login:1,look:4,loop:6,made:7,mai:[0,2,7],main:7,mainli:0,maintain:3,make:1,manag:3,markup:7,master_secret:6,master_t:[1,3],master_uri:6,match:[1,4,7],math:0,mean:7,media_url:3,memori:0,mention:3,messag:[1,6,7],message_typ:7,method:3,middl:7,minimum:7,miscellan:7,miss:7,missing_command:1,missingcommand:2,modul:5,month:7,more:[1,3],multipl:[1,3,4],music_data:1,must:[6,7],name:[0,1,6,7],need:[1,2],net_request:7,network:[1,4,5,7],network_handl:[1,7],network_handler_dict:7,networkerror:6,networkhandl:7,next:[0,1],nid:6,no_warn:0,nobodi:6,node:[6,7],non:[0,4,7],none:[0,1,3,6,7],nonefounderror:4,noplaylist:0,notat:7,notconnectederror:6,noth:[6,7],notidentifiederror:6,notimpl:[1,7],now_plai:0,nullabl:3,nullcommand:[1,2],number:[0,6],object:[0,1,3,7],offset:1,onc:[0,3],one:4,ones:[1,2],onli:[4,7],option:[0,1,3,6,7],optional_arg:7,opu:0,order:0,org:3,other:[0,1,4,6,7],otherwis:[1,6,7],output:7,outtmpl:0,packag:6,packet:6,page:5,paramet:[0,1,3,6,7],pass:[0,7],password:3,past:1,path:[3,6],pattern:7,pcm:0,perman:0,pickl:7,pingcommand:2,plai:[0,1],playcommand:2,playlist:0,playmod:0,playmodecommand:2,plusformat:7,pool:0,port:6,possibl:[0,7],prefix:7,prepar:6,prepend:7,presenc:1,previous:7,primary_kei:3,probabl:[0,2,7],prompt:1,properti:0,quiet:0,quot:3,quoted_account:3,quoted_account_id:3,ragecommand:2,rais:[0,1,4,6,7],raise_on_error:6,random:0,read:0,real:6,receiv:[1,4,6,7],reciev:6,recommend:0,recreat:6,regex:7,regist:4,relat:[0,3,6],relationshiplinkchain:3,relationshipproperti:3,remindercommand:2,remov:0,repeat:0,replac:7,repli:[6,7],repres:1,request:[1,4,6,7],request_dict:1,request_handl:6,requir:[1,4,7],require_alchemy_t:7,require_at_least:7,required_arg:7,required_secret:6,respons:[1,4,6],responseerror:[4,6],responsesuccess:6,result:7,retriev:7,role:3,rout:6,route_packag:6,row:7,royal:[2,3],royal_id:3,royalnet_config:1,royalnetconfig:[1,6],royalnetlink:[1,6,7],royalnetrequesterror:4,royalnetresponseerror:4,royalnetserv:6,royalpcmaudio:[0,1],royalpcmfil:0,rpf:0,run:[1,6,7],safeformat:7,search:[0,5],second:7,secret:6,select:[0,3],self:[0,1,6],send:[6,7],sent:[6,7],separ:7,sequenc:[6,7],serv:6,server:6,session:7,session_acm:3,session_cm:3,session_end:7,set:[1,3,7],shipcommand:2,should:[1,6,7],signal:0,singl:[1,3],skipcommand:2,sleep_until:7,small:7,smecdscommand:2,some:0,someth:[4,7],somewher:6,song:[0,1],soon:0,sourc:6,source_conv_id:6,space:7,specif:[1,7],specifi:[0,1,4,7],spoiler:3,sqlalchemi:[3,7],start:[1,3,6],starting_class:3,starting_list:0,starting_pool:0,statement:3,statu:1,str:[0,1,3,6,7],string:[0,1,3,6,7],stuff:0,sub:7,submodul:3,success:6,suit:2,summoncommand:2,support:4,synccommand:2,syntax:7,tabl:[1,7],tailor:2,task:1,telegram:[1,3,7],telegram_config:1,telegrambot:[1,7],telegramcal:1,telegramconfig:1,text:[3,7],tg_id:3,than:7,thei:2,them:3,therefor:4,thi:[0,4,6,7],those:0,through:7,time:6,timestamp:3,titl:0,to_dict:6,to_discord_emb:0,to_json_byt:6,to_json_str:6,token:1,toomanyfounderror:4,tupl:3,two:7,type:[1,3,6,7],uhhh:0,uid:3,underscor:7,undescrib:7,unexpect:7,unexpectedli:6,union:0,univers:7,unregisterederror:[4,7],unsupportederror:4,until:[0,7],updat:1,update_activity_with_source_titl:1,uri:3,url:0,use:[0,3],used:[0,1,2,3,6,7],useful:[0,2,7],user:[4,7],usernam:3,using:[1,7],usual:[0,6],utf8:6,util:[1,5],uuid:7,valid:7,valu:[0,3,6,7],variabl:1,variou:1,veri:0,video:0,video_filenam:0,videochannelcommand:2,videoinfocommand:2,videos_left:0,voic:[0,1],wai:0,wait:7,want:[0,7],warn:0,websocket:6,websocketserverprotocol:6,weird:[0,7],went:4,were:4,what:0,when:[0,7],where:7,which:[0,6,7],why:0,without:7,won:2,word:7,work:7,worth:0,wrapper:[0,3],wrong:4,year:7,yet:6,yield:0,you:[0,7],your:2,youtub:[0,7],youtube_dl:0,youtubedl:0,ytdl_arg:0,ytdl_filenam:0,ytdldateformat:7,ytdlfile:0,ytdlinfo:0,yyyi:7,yyyymmdd:7},titles:["royalnet.audio","royalnet.bots","royalnet.commands","royalnet.database","royalnet.error","royalnet","royalnet.network","royalnet.utils"],titleterms:{audio:0,bot:1,command:2,databas:3,error:4,indic:5,network:6,royalnet:[0,1,2,3,4,5,6,7],tabl:[3,5],util:7}}) \ No newline at end of file diff --git a/docs/html/utils.html b/docs/html/utils.html index e15f0ff6..914ce373 100644 --- a/docs/html/utils.html +++ b/docs/html/utils.html @@ -173,6 +173,30 @@
      class royalnet.utils.Call(channel, command: Type[royalnet.utils.command.Command], command_args: List[str] = None, **kwargs)

      A command call. An abstract class, sub-bots should create a new call class from this.

      +
      +
      +interface_name
      +

      The name of the interface that is calling the command. For example, telegram, or discord.

      +
      + +
      +
      +interface_obj
      +

      The main object of the interface that is calling the command. For example, the royalnet.bots.TelegramBot object.

      +
      + +
      +
      +interface_prefix
      +

      The command prefix used by the interface. For example, /, or !.

      +
      + +
      +
      +alchemy
      +

      The royalnet.database.Alchemy object associated to this interface. May be None if the interface is not connected to any database.

      +
      +
      __init__(channel, command: Type[royalnet.utils.command.Command], command_args: List[str] = None, **kwargs)
      @@ -196,8 +220,8 @@
      -
      -alchemy = NotImplemented
      +
      +alchemy = NotImplemented
      @@ -216,18 +240,18 @@ That probably means, the database row identifying the user.

      -
      -interface_name = NotImplemented
      +
      +interface_name = NotImplemented
      -
      -interface_obj = NotImplemented
      +
      +interface_obj = NotImplemented
      -
      -interface_prefix = NotImplemented
      +
      +interface_prefix = NotImplemented
      @@ -238,7 +262,7 @@ That probably means, the database row identifying the user.

      Parameters
      • message – The data to be sent. Must be pickle-able.

      • -
      • destination

      • +
      • destination – The destination of the request, either in UUID format or node name.

      @@ -272,20 +296,50 @@ That probably means, the database row identifying the user.

      class royalnet.utils.Command
      -

      A generic command, called from any source.

      -
      -
      -command_description = NotImplemented
      -
      - +

      The base class from which all commands should inherit.

      -command_name = NotImplemented
      -
      +command_name +

      The name of the command. To have /example on Telegram, the name should be example.

      +
      + +
      +
      +command_description
      +

      A small description of the command, to be displayed when the command is being autocompleted.

      +
      -command_syntax = NotImplemented
      +command_syntax +

      The syntax of the command, to be displayed when a royalnet.error.InvalidInputError is raised, in the format (required_arg) [optional_arg].

      +
      + +
      +
      +require_alchemy_tables
      +

      A set of royalnet.database tables, that must exist for this command to work.

      +
      + +
      +
      +network_handlers
      +

      A list of :py:class:`classyalnet.utils.NetworkHandler`s that must exist for this command to work.

      +
      + +
      +
      +command_description = NotImplemented
      +
      + +
      +
      +command_name = NotImplemented
      +
      + +
      +
      +command_syntax = NotImplemented
      @@ -299,13 +353,13 @@ That probably means, the database row identifying the user.

      -
      -network_handlers = {}
      +
      +network_handlers = {}
      -
      -require_alchemy_tables = {}
      +
      +require_alchemy_tables = {}
      @@ -329,8 +383,20 @@ That probably means, the database row identifying the user.

      -royalnet.utils.cdj(class_) → dict
      +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.

      +
      +
      Parameters
      +

      class – The object that you want to dict-ify.

      +
      +
      Returns
      +

      The class dict.

      +
      +
      +
      +

      Warning

      +

      You can’t dict-ify classes with __slots__!

      +
      @@ -346,7 +412,7 @@ That probably means, the database row identifying the user.

      royalnet.utils.plusformat(i: int) → str
      -

      Convert an int to a str, adding a + if they are greater than 0.

      +

      Convert an int to a str, prepending a + if it’s greater than 0.

      Parameters

      i – the int to convert.

      @@ -360,14 +426,14 @@ That probably means, the database row identifying the user.

      class royalnet.utils.CommandArgs
      -

      The arguments of a command.

      +

      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].

      Raises
      -

      InvalidInputError

      +

      royalnet.error.InvalidInputError – if the requested argument does not exist.

      @@ -381,7 +447,7 @@ That probably means, the database row identifying the user.

      require_at_least – the minimum amount of arguments required, will raise royalnet.error.InvalidInputError if the requirement is not fullfilled.

      Raises
      -

      royalnet.error.InvalidInputError

      +

      royalnet.error.InvalidInputError – if there are less than require_at_least arguments.

      Returns

      The space-joined string.

      @@ -397,8 +463,11 @@ That probably means, the database row identifying the user.

      Parameters

      pattern – The regex pattern to be passed to re.match().

      -
      Returns
      -

      The matched groups, as returned by re.Match.groups().

      +
      Raises
      +

      royalnet.error.InvalidInputError – if the pattern doesn’t match.

      +
      +
      Returns
      +

      The matched groups, as returned by re.Match.groups().

      @@ -454,7 +523,7 @@ That probably means, the database row identifying the user.

      royalnet.utils.plusformat(i: int) → str
      -

      Convert an int to a str, adding a + if they are greater than 0.

      +

      Convert an int to a str, prepending a + if it’s greater than 0.

      Parameters

      i – the int to convert.

      @@ -469,6 +538,31 @@ That probably means, the database row identifying the user.

      royalnet.utils.fileformat(string: str) → str

      Ensure a string can be used as a filename by replacing all non-word characters with underscores.

      +
      +
      Parameters
      +

      string – the input string.

      +
      +
      Returns
      +

      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.

      +
      +
      Parameters
      +
        +
      • string – the input string, in the YYYYMMDD format.

      • +
      • separator – the string to add between the years, the months and the days. Defaults to -.

      • +
      +
      +
      Returns
      +

      The resulting string, in the format YYYY-MM-DD format.

      +
      +
      diff --git a/royalnet/__init__.py b/royalnet/__init__.py index cdd20349..32461c59 100644 --- a/royalnet/__init__.py +++ b/royalnet/__init__.py @@ -1,5 +1,5 @@ from . import audio, bots, commands, database, network, utils, error -version = "5.0a11" +version = "5.0a12" __all__ = ["audio", "bots", "commands", "database", "network", "utils", "error"] diff --git a/royalnet/audio/playmodes.py b/royalnet/audio/playmodes.py index 7b8d59c3..d93ad243 100644 --- a/royalnet/audio/playmodes.py +++ b/royalnet/audio/playmodes.py @@ -47,6 +47,18 @@ class PlayMode: """Delete all :py:class:`royalnet.audio.RoyalPCMAudio` contained inside this PlayMode.""" raise NotImplementedError() + def queue_preview(self) -> typing.List[RoyalPCMAudio]: + """Display all the videos in the PlayMode as a list, if possible. + + To be used with `queue` commands, for example. + + Raises: + NotImplementedError: If a preview can't be generated. + + Returns: + A list of videos contained in the queue.""" + raise NotImplementedError() + class Playlist(PlayMode): """A video list. :py:class:`royalnet.audio.RoyalPCMAudio` played are removed from the list.""" @@ -80,9 +92,13 @@ class Playlist(PlayMode): self.list.append(item) def delete(self) -> None: + if self.now_playing is not None: + self.now_playing.delete() while self.list: self.list.pop(0).delete() - self.now_playing.delete() + + def queue_preview(self) -> typing.List[RoyalPCMAudio]: + return self.list class Pool(PlayMode): @@ -125,3 +141,8 @@ class Pool(PlayMode): item.delete() self.pool = None self._pool_copy = None + + def queue_preview(self) -> typing.List[RoyalPCMAudio]: + preview_pool = self.pool.copy() + random.shuffle(preview_pool) + return preview_pool \ No newline at end of file diff --git a/royalnet/audio/royalpcmaudio.py b/royalnet/audio/royalpcmaudio.py index 3a4ed425..76ddc7e9 100644 --- a/royalnet/audio/royalpcmaudio.py +++ b/royalnet/audio/royalpcmaudio.py @@ -5,7 +5,7 @@ from .royalpcmfile import RoyalPCMFile class RoyalPCMAudio(AudioSource): - """A discord-compatible :py:class:`discord.AudioSource` that keeps data in a file instead of in memory.""" + """A :py:class:`discord.AudioSource` that keeps data in a file instead of in memory.""" def __init__(self, rpf: "RoyalPCMFile"): """Create a :py:class:`discord.audio.RoyalPCMAudio` from a :py:class:`royalnet.audio.RoyalPCMFile`. @@ -41,9 +41,16 @@ class RoyalPCMAudio(AudioSource): return [RoyalPCMAudio(rpf) for rpf in rpf_list] def is_opus(self): + """This audio file isn't Opus-encoded, but PCM-encoded. + + Returns: + ``False``.""" return False def read(self): + """Reads 20ms worth of audio. + + If the audio is complete, then returning an empty :py:class:`bytes`-like object to signal this is the way to do so.""" data: bytes = self._file.read(OpusEncoder.FRAME_SIZE) # If the file was externally closed, it means it was deleted if self._file.closed: diff --git a/royalnet/audio/youtubedl.py b/royalnet/audio/youtubedl.py index cc9a3289..bfce5976 100644 --- a/royalnet/audio/youtubedl.py +++ b/royalnet/audio/youtubedl.py @@ -48,14 +48,30 @@ class YtdlFile: @staticmethod def create_from_url(url, outtmpl="%(title)s-%(id)s.%(ext)s", **ytdl_args) -> typing.List["YtdlFile"]: + """Download the videos at the specified url. + + Parameters: + url: The url to download the videos from. + outtmpl: The filename that the downloaded videos are going to have. The name can be formatted according to the `outtmpl documentation `_. + ytdl_args: Other arguments to be passed to the YoutubeDL object. + + Returns: + A :py:class:`list` of YtdlFiles.""" info_list = YtdlInfo.create_from_url(url) return [info.download(outtmpl, **ytdl_args) for info in info_list] def _stop_download(self): + """I have no clue of what this does, or why is it here. Possibly remove it? + + Raises: + InterruptDownload: ...uhhh, always?""" raise InterruptDownload() def delete_video_file(self): - # TODO: _might_ be unsafe, test this + """Delete the file located at ``self.video_filename``. + + Note: + No checks are done when deleting, so it may try to delete a non-existing file and raise an exception or do some other weird stuff with weird filenames.""" os.remove(self.video_filename) @@ -150,9 +166,9 @@ class YtdlInfo: embed.set_thumbnail( url=self.thumbnail) embed.set_author(name=self.uploader, url=self.uploader_url) - embed.set_footer(text="Fonte: youtube-dl", icon_url="https://i.imgur.com/TSvSRYn.png") + embed.set_footer(text="Source: youtube-dl", icon_url="https://i.imgur.com/TSvSRYn.png") embed.add_field(name="Duration", value=str(self.duration), inline=True) - embed.add_field(name="Published on", value=str(self.upload_date), inline=True) + embed.add_field(name="Published on", value=self.upload_date.strftime("%d %b %Y"), inline=True) return embed def __repr__(self): diff --git a/royalnet/bots/telegram.py b/royalnet/bots/telegram.py index 874576c0..430f979d 100644 --- a/royalnet/bots/telegram.py +++ b/royalnet/bots/telegram.py @@ -130,6 +130,7 @@ class TelegramBot(GenericBot): @property def botfather_command_string(self) -> str: + """Generate a string to be pasted in the "Edit Commands" BotFather prompt.""" string = "" for command_key in self.commands: command = self.commands[command_key] diff --git a/royalnet/commands/__init__.py b/royalnet/commands/__init__.py index e034cfdd..4dcb2dd3 100644 --- a/royalnet/commands/__init__.py +++ b/royalnet/commands/__init__.py @@ -1,4 +1,6 @@ -"""Commands that can be used in bots. These probably won't suit your needs, as they are tailored for the bots of the `Royal Games `_ gaming community, but you can check them for reference.""" +"""Commands that can be used in bots. + +These probably won't suit your needs, as they are tailored for the bots of the Royal Games gaming community, but they may be useful to develop new ones.""" from .null import NullCommand from .ping import PingCommand @@ -23,9 +25,12 @@ from .playmode import PlaymodeCommand from .videochannel import VideochannelCommand from .missing import MissingCommand from .cv import CvCommand +from .pause import PauseCommand +from .queue import QueueCommand __all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand", "SyncCommand", "DiarioCommand", "RageCommand", "DateparserCommand", "AuthorCommand", "ReminderCommand", "KvactiveCommand", "KvCommand", "KvrollCommand", "VideoinfoCommand", "SummonCommand", "PlayCommand", - "SkipCommand", "PlaymodeCommand", "VideochannelCommand", "MissingCommand", "CvCommand"] + "SkipCommand", "PlaymodeCommand", "VideochannelCommand", "MissingCommand", "CvCommand", "PauseCommand", + "QueueCommand"] diff --git a/royalnet/commands/pause.py b/royalnet/commands/pause.py new file mode 100644 index 00000000..95437ff4 --- /dev/null +++ b/royalnet/commands/pause.py @@ -0,0 +1,53 @@ +import typing +import discord +from ..network import Request, ResponseSuccess +from ..utils import Command, Call, NetworkHandler +from ..error import TooManyFoundError, NoneFoundError +if typing.TYPE_CHECKING: + from ..bots import DiscordBot + + +class PauseNH(NetworkHandler): + message_type = "music_pause" + + # noinspection PyProtectedMember + @classmethod + 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"]) + 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] + # 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") + # Toggle pause + resume = voice_client._player.is_paused() + if resume: + voice_client._player.resume() + else: + voice_client._player.pause() + return ResponseSuccess({"resume": resume}) + + +class PauseCommand(Command): + + command_name = "pause" + command_description = "Mette in pausa o riprende la riproduzione della canzone attuale." + command_syntax = "[ [guild] ]" + + network_handlers = [PauseNH] + + @classmethod + async def common(cls, call: Call): + guild, = call.args.match(r"(?:\[(.+)])?") + response = await call.net_request(Request("music_pause", {"guild_name": guild}), "discord") + if response["resume"]: + await call.reply(f"▶️ Riproduzione ripresa.") + else: + await call.reply(f"⏸ Riproduzione messa in pausa.") diff --git a/royalnet/commands/queue.py b/royalnet/commands/queue.py new file mode 100644 index 00000000..b6acf99d --- /dev/null +++ b/royalnet/commands/queue.py @@ -0,0 +1,76 @@ +import typing +import pickle +from ..network import Request, ResponseSuccess +from ..utils import Command, Call, NetworkHandler, numberemojiformat +from ..error import TooManyFoundError, NoneFoundError +if typing.TYPE_CHECKING: + from ..bots import DiscordBot + + +class QueueNH(NetworkHandler): + message_type = "music_queue" + + @classmethod + 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"]) + 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] + # Check if the guild has a PlayMode + playmode = bot.music_data.get(guild) + if not playmode: + return ResponseSuccess({ + "type": None + }) + try: + queue = playmode.queue_preview() + except NotImplementedError: + return ResponseSuccess({ + "type": playmode.__class__.__name__ + }) + return ResponseSuccess({ + "type": playmode.__class__.__name__, + "queue": + { + "strings": [str(element.rpf.info) for element in queue], + "pickled_embeds": str(pickle.dumps([element.rpf.info.to_discord_embed() for element in queue])) + } + }) + + +class QueueCommand(Command): + + command_name = "queue" + command_description = "Visualizza un'anteprima della coda di riproduzione attuale." + command_syntax = "[ [guild] ]" + + network_handlers = [QueueNH] + + @classmethod + async def common(cls, call: Call): + guild, = call.args.match(r"(?:\[(.+)])?") + data = await call.net_request(Request("music_queue", {"guild_name": guild}), "discord") + if data["type"] is None: + await call.reply("ℹ️ Non c'è nessuna coda di riproduzione attiva al momento.") + return + elif "queue" not in data: + await call.reply(f"ℹ️ La coda di riproduzione attuale ([c]{data['type']}[/c]) non permette l'anteprima.") + return + if data["type"] == "Playlist": + message = f"ℹ️ Questa [c]Playlist[/c] contiene {len(data['queue'])} elementi, e i prossimi saranno:\n" + elif data["type"] == "Pool": + message = f"ℹ️ Questo [c]Pool[/c] contiene {len(data['queue'])} elementi, tra cui:\n" + else: + message = f"ℹ️ Il PlayMode attuale, [c]{data['type']}[/c], contiene {len(data['queue'])} elementi:\n" + if call.interface_name == "discord": + await call.reply(message) + for embed in pickle.loads(eval(data["queue"]["pickled_embeds"]))[:5]: + await call.channel.send(embed=embed) + else: + message += numberemojiformat(data["queue"]["strings"][:10]) + await call.reply(message) diff --git a/royalnet/commands/skip.py b/royalnet/commands/skip.py index 238cf885..a91a02b4 100644 --- a/royalnet/commands/skip.py +++ b/royalnet/commands/skip.py @@ -23,7 +23,7 @@ class SkipNH(NetworkHandler): guild = list(bot.music_data)[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(): + if not (voice_client.is_playing() or voice_client.is_paused()): raise NoneFoundError("Nothing to skip") # noinspection PyProtectedMember voice_client._player.stop() diff --git a/royalnet/network/__init__.py b/royalnet/network/__init__.py index 1737f835..eee8ebc4 100644 --- a/royalnet/network/__init__.py +++ b/royalnet/network/__init__.py @@ -1,4 +1,4 @@ -"""Royalnet realated classes.""" +"""Royalnet (websocket) related classes.""" from .request import Request from .response import Response, ResponseSuccess, ResponseError from .package import Package diff --git a/royalnet/royalgames.py b/royalnet/royalgames.py index 867a66e7..ba387c1c 100644 --- a/royalnet/royalgames.py +++ b/royalnet/royalgames.py @@ -20,7 +20,7 @@ log.setLevel(logging.WARNING) commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand, AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand, KvrollCommand, VideoinfoCommand, SummonCommand, PlayCommand, SkipCommand, PlaymodeCommand, - VideochannelCommand, CvCommand] + VideochannelCommand, CvCommand, PauseCommand, QueueCommand] address, port = "127.0.0.1", 1234 diff --git a/royalnet/utils/__init__.py b/royalnet/utils/__init__.py index cd1860b0..770b7196 100644 --- a/royalnet/utils/__init__.py +++ b/royalnet/utils/__init__.py @@ -9,7 +9,7 @@ from .safeformat import safeformat from .classdictjanitor import cdj from .sleepuntil import sleep_until from .networkhandler import NetworkHandler -from .formatters import andformat, plusformat, fileformat, ytdldateformat +from .formatters import andformat, plusformat, fileformat, ytdldateformat, numberemojiformat __all__ = ["asyncify", "Call", "Command", "safeformat", "cdj", "sleep_until", "plusformat", "CommandArgs", - "NetworkHandler", "andformat", "plusformat", "fileformat", "ytdldateformat"] + "NetworkHandler", "andformat", "plusformat", "fileformat", "ytdldateformat", "numberemojiformat"] diff --git a/royalnet/utils/call.py b/royalnet/utils/call.py index ea8799d2..f45c8c82 100644 --- a/royalnet/utils/call.py +++ b/royalnet/utils/call.py @@ -10,7 +10,13 @@ loop = asyncio.get_event_loop() class Call: - """A command call. An abstract class, sub-bots should create a new call class from this.""" + """A command call. An abstract class, sub-bots should create a new call class from this. + + Attributes: + interface_name: The name of the interface that is calling the command. For example, ``telegram``, or ``discord``. + interface_obj: The main object of the interface that is calling the command. For example, the :py:class:`royalnet.bots.TelegramBot` object. + interface_prefix: The command prefix used by the interface. For example, ``/``, or ``!``. + alchemy: The :py:class:`royalnet.database.Alchemy` object associated to this interface. May be None if the interface is not connected to any database.""" # These parameters / methods should be overridden interface_name = NotImplemented @@ -30,7 +36,7 @@ class Call: Parameters: message: The data to be sent. Must be :py:mod:`pickle`-able. - destination: """ + destination: The destination of the request, either in UUID format or node name.""" raise NotImplementedError() async def get_author(self, error_if_none=False): diff --git a/royalnet/utils/classdictjanitor.py b/royalnet/utils/classdictjanitor.py index d4b97065..3897f8dd 100644 --- a/royalnet/utils/classdictjanitor.py +++ b/royalnet/utils/classdictjanitor.py @@ -1,5 +1,16 @@ -def cdj(class_) -> 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.""" +import typing + +def cdj(class_: typing.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. + + Parameters: + class_: The object that you want to dict-ify. + + Returns: + The class dict. + + Warning: + You can't dict-ify classes with ``__slots__``!""" d = dict(class_.__dict__) del d["__module__"] del d["__dict__"] diff --git a/royalnet/utils/command.py b/royalnet/utils/command.py index 5c88f33c..f9e515f8 100644 --- a/royalnet/utils/command.py +++ b/royalnet/utils/command.py @@ -6,7 +6,14 @@ if typing.TYPE_CHECKING: class Command: - """A generic command, called from any source.""" + """The base class from which all commands should inherit. + + Attributes: + command_name: The name of the command. To have ``/example`` on Telegram, the name should be ``example``. + command_description: A small description of the command, to be displayed when the command is being autocompleted. + command_syntax: The syntax of the command, to be displayed when a :py:exc:`royalnet.error.InvalidInputError` is raised, in the format ``(required_arg) [optional_arg]``. + require_alchemy_tables: A set of :py:class:`royalnet.database` tables, that must exist for this command to work. + network_handlers: A list of :py:class:`classyalnet.utils.NetworkHandler`s that must exist for this command to work.""" command_name: str = NotImplemented command_description: str = NotImplemented diff --git a/royalnet/utils/commandargs.py b/royalnet/utils/commandargs.py index e87f4924..575f2682 100644 --- a/royalnet/utils/commandargs.py +++ b/royalnet/utils/commandargs.py @@ -4,13 +4,13 @@ from royalnet.error import InvalidInputError class CommandArgs(list): - """The arguments of a command.""" + """An interface to access the arguments of a command with ease.""" def __getitem__(self, item): """Arguments can be accessed with an array notation, such as ``args[0]``. Raises: - :py:exc:`InvalidInputError` if the requested argument does not exist.""" + royalnet.error.InvalidInputError: if the requested argument does not exist.""" if isinstance(item, int): try: return super().__getitem__(item) @@ -30,7 +30,7 @@ class CommandArgs(list): require_at_least: the minimum amount of arguments required, will raise :py:exc:`royalnet.error.InvalidInputError` if the requirement is not fullfilled. Raises: - :py:exc:`royalnet.error.InvalidInputError` if the ``require_at_least`` requirement is not fullfilled. + royalnet.error.InvalidInputError: if there are less than ``require_at_least`` arguments. Returns: The space-joined string.""" @@ -44,6 +44,9 @@ class CommandArgs(list): Parameters: pattern: The regex pattern to be passed to :py:func:`re.match`. + Raises: + royalnet.error.InvalidInputError: if the pattern doesn't match. + Returns: The matched groups, as returned by :py:func:`re.Match.groups`.""" text = self.joined() diff --git a/royalnet/utils/escaping.py b/royalnet/utils/escaping.py index dc59bfde..31a99ec7 100644 --- a/royalnet/utils/escaping.py +++ b/royalnet/utils/escaping.py @@ -1,4 +1,8 @@ def 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.""" return string.replace("*", "\\*") \ .replace("_", "\\_") \ .replace("`", "\\`") \ @@ -15,6 +19,10 @@ def discord_escape(string: str) -> str: def 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.""" return string.replace("<", "<") \ .replace(">", ">") \ .replace("[b]", "") \ diff --git a/royalnet/utils/formatters.py b/royalnet/utils/formatters.py index 9e519fb4..1e4f92ee 100644 --- a/royalnet/utils/formatters.py +++ b/royalnet/utils/formatters.py @@ -23,7 +23,7 @@ def andformat(l: typing.List[str], middle=", ", final=" and ") -> str: def plusformat(i: int) -> str: - """Convert an :py:class:`int` to a :py:class:`str`, adding a ``+`` if they are greater than 0. + """Convert an :py:class:`int` to a :py:class:`str`, prepending a ``+`` if it's greater than 0. Parameters: i: the :py:class:`int` to convert. @@ -36,11 +36,37 @@ def plusformat(i: int) -> str: def fileformat(string: str) -> str: - """Ensure a string can be used as a filename by replacing all non-word characters with underscores.""" + """Ensure a string can be used as a filename by replacing all non-word characters with underscores. + + Parameters: + string: the input string. + + Returns: + A valid filename string.""" return re.sub(r"\W", "_", string) def ytdldateformat(string: typing.Optional[str], separator: str = "-") -> str: + """Convert the weird date string returned by ``youtube-dl`` into the ``YYYY-MM-DD`` format. + + Parameters: + string: the input string, in the ``YYYYMMDD`` format. + separator: the string to add between the years, the months and the days. Defaults to ``-``. + + Returns: + The resulting string, in the format ``YYYY-MM-DD`` format.""" if string is None: return "" - return f"{string[0:4]}-{string[4:6]}-{string[6:8]}" + return f"{string[0:4]}{separator}{string[4:6]}{separator}{string[6:8]}" + + +def numberemojiformat(l: typing.List[str]) -> str: + number_emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"] + extra_emoji = "*️⃣" + result = "" + for index, element in enumerate(l): + try: + result += f"{number_emojis[index]} {element}\n" + except IndexError: + result += f"{extra_emoji} {element}\n" + return result diff --git a/royalnet/web/blueprints/helloworld.py b/royalnet/web/blueprints/helloworld.py new file mode 100644 index 00000000..cd8e77aa --- /dev/null +++ b/royalnet/web/blueprints/helloworld.py @@ -0,0 +1,11 @@ +import flask as f +from .. import Royalprint + + +bp = Royalprint("helloworld", __name__, url_prefix="/helloworld") + + +@bp.route("/") +def helloworld(): + return "Hello world!" + diff --git a/royalnet/web/blueprints/testing.py b/royalnet/web/blueprints/testing.py new file mode 100644 index 00000000..989b95af --- /dev/null +++ b/royalnet/web/blueprints/testing.py @@ -0,0 +1,13 @@ +import flask as f +from .. import Royalprint +from ...database.tables import Royal + + +bp = Royalprint("testing", __name__, url_prefix="/testing", required_tables={Royal}) + + +@bp.route("/listroyals") +def listroyals(): + from ..alchemyhandler import alchemy, alchemy_session + royals = alchemy_session.query(alchemy.Royal).all() + return f''