diff --git a/docs/doctrees/creatingacommand.doctree b/docs/doctrees/creatingacommand.doctree
index 1a465175..59245b29 100644
Binary files a/docs/doctrees/creatingacommand.doctree and b/docs/doctrees/creatingacommand.doctree differ
diff --git a/docs/doctrees/environment.pickle b/docs/doctrees/environment.pickle
index b9053252..8a19806d 100644
Binary files a/docs/doctrees/environment.pickle and b/docs/doctrees/environment.pickle differ
diff --git a/docs/html/_sources/creatingacommand.rst.txt b/docs/html/_sources/creatingacommand.rst.txt
index 7034ea1e..a89d363f 100644
--- a/docs/html/_sources/creatingacommand.rst.txt
+++ b/docs/html/_sources/creatingacommand.rst.txt
@@ -67,6 +67,86 @@ And... it's done! The command is ready to be added to a bot!
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 :py:class:`str` `"carbonara al-dente"` to the command code.
+
+These arguments can be accessed in multiple ways through the ``args`` parameter passed to the :py:meth:`Command.run`
+method.
+
+Direct access
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can consider arguments as if they were separated by spaces.
+
+You can then access command arguments directly by number as if the args object was a list of :py:class:`str`.
+
+If you request an argument with a certain number, but the argument does not exist, an
+:py:exc:`royalnet.error.InvalidInputError` is raised, making the arguments accessed in this way **required**. ::
+
+ args[0]
+ # "carbonara"
+
+ args[1]
+ # "al-dente"
+
+ args[2]
+ # InvalidInputError() is raised
+
+Optional access
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't want arguments to be required, you can access them through the :py:meth:`CommandArgs.optional` method: it
+will return :py:const:`None` if the argument wasn't passed, making it **optional**. ::
+
+ args.optional(0)
+ # "carbonara"
+
+ args.optional(1)
+ # "al-dente"
+
+ args.optional(2)
+ # None
+
+You can specify a default result too, so that the method will return it instead of returning :py:const:`None`: ::
+
+ args.optional(2, default="banana")
+ # "banana"
+
+Full string
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want the full argument string, you can use the :py:meth:`CommandArgs.joined` method. ::
+
+ args.joined()
+ # "carbonara al-dente"
+
+You can specify a minimum number of arguments too, so that an :py:exc:`royalnet.error.InvalidInputError` will be
+raised if not enough arguments are present: ::
+
+ args.joined(require_at_least=3)
+ # InvalidInputError() is raised
+
+Regular expressions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For more complex commands, you may want to get arguments through `regular expressions
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()
+method.
+
+Direct access¶
+You can consider arguments as if they were separated by spaces.
+You can then access command arguments directly by number as if the args object was a list of str
.
+If you request an argument with a certain number, but the argument does not exist, an
+royalnet.error.InvalidInputError
is raised, making the arguments accessed in this way required.
+args[0]
+# "carbonara"
+
+args[1]
+# "al-dente"
+
+args[2]
+# InvalidInputError() is raised
+
+
+
+
+Optional access¶
+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"
+
+args.optional(1)
+# "al-dente"
+
+args.optional(2)
+# None
+
+
+You can specify a default result too, so that the method will return it instead of returning None
:
+args.optional(2, default="banana")
+# "banana"
+
+
+
+
+Full string¶
+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 royalnet.error.InvalidInputError
will be
+raised if not enough arguments are present:
+args.joined(require_at_least=3)
+# InvalidInputError() is raised
+
+
+
+
+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 royalnet.error.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",)
+
+args.match(r"(al-\w+)")
+# InvalidInputError() is raised
+
+args.match(r"\s*(al-\w+)")
+# ("al-dente",)
+
+args.match(r"\s*(carb\w+)\s*(al-\w+)")
+# ("carbonara", "al-dente")
+
+
+
Coroutines and slow operations¶
@@ -231,7 +312,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!
diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js
index baf6842e..88b0cc09 100644
--- a/docs/html/searchindex.js
+++ b/docs/html/searchindex.js
@@ -1 +1 @@
-Search.setIndex({docnames:["apireference","creatingacommand","index"],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"],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_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,""],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,""],DiscordConfig:[0,1,1,""],GenericBot:[0,1,1,""],TelegramBot:[0,1,1,""],TelegramConfig:[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,""],_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_royalnet:[0,2,1,""],_interface_factory:[0,2,1,""],_network_handler:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""]},"royalnet.bots.TelegramBot":{_data_factory:[0,2,1,""],_handle_update:[0,2,1,""],_init_client:[0,2,1,""],_interface_factory:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""]},"royalnet.commands":{Command:[0,1,1,""],CommandArgs:[0,1,1,""],CommandData:[0,1,1,""],CommandInterface:[0,1,1,""]},"royalnet.commands.Command":{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":{get_author:[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_net_handler:[0,2,1,""],unregister_net_handler:[0,2,1,""]},"royalnet.database":{Alchemy:[0,1,1,""],DatabaseConfig:[0,1,1,""],relationshiplinkchain:[0,4,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":{CurrentlyDisabledError:[0,5,1,""],ExternalError:[0,5,1,""],FileTooBigError:[0,5,1,""],InvalidConfigError:[0,5,1,""],InvalidInputError:[0,5,1,""],NoneFoundError:[0,5,1,""],RoyalnetRequestError:[0,5,1,""],RoyalnetResponseError:[0,5,1,""],TooManyFoundError:[0,5,1,""],UnregisteredError:[0,5,1,""],UnsupportedError:[0,5,1,""]},"royalnet.network":{ConnectionClosedError:[0,5,1,""],NetworkError:[0,5,1,""],NotConnectedError:[0,5,1,""],NotIdentifiedError:[0,5,1,""],Package:[0,1,1,""],Request:[0,1,1,""],Response:[0,1,1,""],ResponseError:[0,1,1,""],ResponseSuccess:[0,1,1,""],RoyalnetConfig:[0,1,1,""],RoyalnetLink:[0,1,1,""],RoyalnetServer:[0,1,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.network.RoyalnetLink":{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.RoyalnetServer":{find_client:[0,2,1,""],find_destination:[0,2,1,""],listener:[0,2,1,""],route_package:[0,2,1,""],serve:[0,2,1,""],start:[0,2,1,""]},"royalnet.utils":{NetworkHandler:[0,1,1,""],andformat:[0,4,1,""],asyncify:[0,4,1,""],cdj:[0,4,1,""],discord_escape:[0,4,1,""],fileformat:[0,4,1,""],numberemojiformat:[0,4,1,""],ordinalformat:[0,4,1,""],parse_5etools_entry:[0,4,1,""],plusformat:[0,4,1,""],safeformat:[0,4,1,""],sleep_until:[0,4,1,""],splitstring:[0,4,1,""],telegram_escape:[0,4,1,""],ytdldateformat:[0,4,1,""]},"royalnet.utils.NetworkHandler":{message_type:[0,3,1,""]},"royalnet.web":{Royalprint:[0,1,1,""],create_app:[0,4,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","function","Python function"],"5":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:function","5":"py:exception"},terms:{"20m":0,"byte":0,"class":[0,1],"default":0,"final":0,"function":[0,1],"import":1,"int":0,"new":[0,2],"null":0,"return":0,"short":1,"static":0,"super":1,"true":0,"try":[0,1],"while":[0,1],And:1,That:0,The:[0,1],Then:[0,1],These:0,Use:0,__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_upd:0,_init_cli:0,_init_command:0,_init_databas:0,_init_royalnet:0,_init_voic:0,_interface_factori:0,_network_handl:0,abl:0,about:0,abstracteventloop:0,access:[0,2],accur:0,add:0,add_to_music_data:0,added:[0,1],adding:[0,1],addit:0,address:0,advance_music_data:0,akin:0,alchemi:0,all:0,allow:[0,1],also:0,altern:0,alwai:0,amount:0,andformat:0,ani:[0,1],anoth:0,anymor:0,anystr:0,api:2,app:0,applic:0,arg:[0,1],argument:[0,2],around:0,arrai:0,async:[0,1],asyncifi:[0,1],asyncio:[0,1],asyncron:0,attempt:0,attribut:[0,1],audio:2,audiosourc:0,author:0,autocomplet:0,automat:0,avail:0,avoid:1,await:1,base:0,becaus:1,been:0,befor:1,being:[0,1],between:0,big:0,block:0,blockingli:0,blueprint:0,bool:0,bot:[1,2],both:0,bufferediobas:0,bug:1,call:[0,1],callabl:0,can:[0,1],cancel:0,cannot:0,caus:1,caution:0,cdj:0,chain:0,chang:0,change_pres:0,channel:0,charact:0,chat:[0,1],check:0,class_:0,classmethod:0,client:0,close:0,clue:0,code:[0,1],command:2,commandarg:0,commanddata:[0,1],commandinterfac:0,commun:0,complet:0,compon:0,comun:2,config:0,config_obj:0,configur:0,connect:0,connectedcli:0,connectionclosederror:0,consid:1,consum:0,contain:0,context:0,convers:0,convert:0,convert_to_mp3:0,convert_to_pcm:0,core:0,coroutin:[0,2],correctli:0,correspond:0,creat:[0,2],create_and_ready_from_url:0,create_app:0,create_from_url:0,current:0,currentlydisablederror:0,custom:0,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,describ:0,descript:[0,1],destin:0,destination_conv_id:0,detail:0,dfile:0,dict:0,dictionari:0,disabl:0,discord:0,discord_config:0,discord_escap:0,discordbot:0,discordcli:0,discordconfig:0,displai:0,doc:0,docstr:0,document:2,doe:[0,1],doesn:0,don:[0,1],done:1,download:0,download_1_terabyte_of_spaghetti:1,download_fil:0,download_from_url:0,dynam:0,eas:0,edit:0,effect:1,either:0,element:0,emb:0,emoji:1,empti:0,encod:0,end:0,ending_class:0,engin:0,enough:1,ensur:0,entri:0,envvar:0,epoch:0,error:2,error_data:0,error_if_non:0,escap:0,even:0,event:0,everi:[0,1],everyth:0,exact:1,exampl:[0,1],except:0,execut:[0,1],exist:0,expect:0,ext:0,externalerror:0,extra_info:0,extract:0,extract_info:0,fals:0,featur:0,fetch:0,file:[0,1],fileaudiosourc:0,fileformat:0,filenam:0,filetoobigerror:0,find:0,find_client:0,find_destin:0,finish:1,first:1,flask:0,follow:0,format:0,found:0,from:[0,1],from_dict:0,from_json_byt:0,from_json_str:0,from_object:0,fullfil:0,gener:0,genericbot:0,get:0,get_author:0,github:2,greater:0,group:0,guild:0,handl:0,handler:0,has:[0,1],has_info:0,have:[0,1],how:0,html:0,http:0,identifi:0,identity_column_nam:0,identity_t:0,ifi:0,ignor:0,ignoreerror:0,imag:1,import_nam:0,incom:0,incomplet:0,index:[0,2],inf:0,info:0,inform:0,inherit:[0,1],initi:0,input:0,insid:1,instanc:0,instead:[0,1],interfac:[0,1],interface_nam:0,invalid:0,invalidconfigerror:0,invalidinputerror:0,is_download:0,is_opu:0,isn:[0,1],itali:1,item:0,join:0,json:0,jsonabl:0,just:[0,1],keep:1,kei:0,keyword:1,kind:1,kwarg:0,last:0,less:0,like:[0,1],link:0,link_typ:0,list:0,listen:0,login:0,look:[0,1],loop:0,made:0,mai:1,main:0,mainli:0,maintain:0,make:0,manag:0,markup:0,master_secret:0,master_t:0,master_uri:0,match:0,max:0,mean:0,meantim:1,memori:0,mention:1,messag:[0,1],message_typ:0,method:[0,1],middl:0,might:0,minimum:0,miscellan:0,miss:0,month:0,more:0,multipl:0,music_data:0,must:0,name:[0,1],need:0,net_request:0,network:2,network_handl:0,networkerror:0,networkhandl:0,next:[0,1],nid:0,no_warn:0,nobodi:0,node:0,non:0,none:0,nonefounderror:0,noplaylist:0,normal:1,notat:0,notconnectederror:0,noth:0,notic:1,notidentifiederror:0,notimpl:0,now:[0,1],number:0,numberemojiformat:0,object:0,offset:0,onc:[0,1],one:0,ones:0,onli:0,open:0,oper:2,option:0,optional_arg:0,opu:0,ordinalformat:0,org:0,other:[0,1],otherwis:0,outdat:0,output:0,outtmpl:0,overrid:1,packag:0,packet:0,page:0,paramet:0,parse_5etools_entri:0,part:1,pass:0,path:0,pattern:0,pcm:0,pcm_avail:0,pickl:0,ping:1,pingcommand:1,plai:[0,1],plusformat:0,pong:1,port:0,possibl:[0,1],prefix:0,prepar:0,prepend:0,presenc:0,previou:1,previous:1,probabl:0,properti:0,python:1,quiet:0,rais:0,raise_on_error:0,read:0,readi:1,ready_up:0,real:0,realli:0,receiv:[0,1],reciev:0,recreat:0,refer:2,regex:0,regist:0,register_net_handl:0,relat:0,relationshiplinkchain:0,remov:0,replac:0,repli:[0,1],request:0,request_dict:0,request_handl:0,requir:0,require_alchemy_t:0,require_at_least:0,required_arg:0,required_secret:0,required_t:0,respect:1,respons:0,responseerror:0,responsesuccess:0,rest:1,result:0,retriev:0,retrieve_for_url:0,right:0,right_now:1,root_path:0,rout:0,route_packag:0,row:[0,1],royalcod:0,royalnet:0,royalnet_config:0,royalnetconfig:0,royalnetlink:0,royalnetrequesterror:0,royalnetresponseerror:0,royalnetserv:0,royalprint:0,run:[0,1],safeformat:0,same:1,script:1,second:0,secret:0,secret_kei:0,select:0,self:[0,1],send:[0,1],sent:[0,1],sentry_dsn:0,separ:[0,1],sequenc:0,serv:0,server:0,session_acm:0,session_cm:0,set:0,should:[0,1],side:1,signal:0,simpl:1,singl:0,skip:0,sleep:1,sleep_until:0,slow:2,small:[0,1],someth:0,somewher:0,song:0,sourc:0,source_conv_id:0,space:0,spaghetti:1,spaghetticommand:1,spawn_audiosourc:0,special:1,specif:[0,1],specifi:0,splitstr:0,sqlalchemi:0,stai:1,start:[0,1],starting_class:0,statement:0,static_fold:0,static_url_path:0,statu:0,stop:1,store:0,str:0,stream:0,string:0,subdomain:0,submodul:0,success:0,support:0,syntax:0,tabl:0,task:0,telegram:0,telegram_config:0,telegram_escap:0,telegrambot:0,telegramconfig:0,tell:1,template_fold:0,temporar:0,text:0,than:0,thei:[0,1],them:0,therefor:0,thi:[0,1],thing:1,think:1,thought:1,through:0,time:[0,1],titl:0,to_dict:0,to_discord_emb:0,to_json_byt:0,to_json_str:0,token:0,too:0,toomanyfounderror:0,tupl:0,two:0,type:[0,1],underscor:0,undescrib:0,unexpect:0,unexpectedli:0,union:0,unregister_net_handl:0,unregisterederror:0,unsupportederror:0,until:[0,1],updat:0,update_activity_with_source_titl:0,update_info:0,uri:0,url:0,url_default:0,url_prefix:0,use:[0,1],used:0,useful:0,user:0,uses:0,using:[0,1],usual:0,utf8:0,util:[1,2],uuid:0,valid:0,valu:0,variabl:0,variou:0,via:2,video:0,voic:0,wai:0,wait:0,want:[0,1],web:2,websocket:0,websocketserverprotocol:0,weird:0,welcom:2,went:0,were:0,what:1,when:[0,1],whenev:1,where:0,which:0,why:0,without:0,word:0,work:[0,1],worth:0,would:0,wrap:1,wrapper:0,written:0,wrong:0,wrote:1,year:0,yet:0,you:[0,1],your:1,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"],titleterms:{"new":1,access:1,api:0,argument:1,audio:0,bot:0,command:[0,1],comun:1,coroutin:1,creat:1,databas:[0,1],error:0,link:2,network:0,oper:1,refer:0,royalnet:[1,2],slow:1,some:2,useful:2,util:0,via:1,web:0}})
\ No newline at end of file
+Search.setIndex({docnames:["apireference","creatingacommand","index"],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"],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_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,""],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,""],DiscordConfig:[0,1,1,""],GenericBot:[0,1,1,""],TelegramBot:[0,1,1,""],TelegramConfig:[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,""],_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_royalnet:[0,2,1,""],_interface_factory:[0,2,1,""],_network_handler:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""]},"royalnet.bots.TelegramBot":{_data_factory:[0,2,1,""],_handle_update:[0,2,1,""],_init_client:[0,2,1,""],_interface_factory:[0,2,1,""],interface_name:[0,3,1,""],run:[0,2,1,""]},"royalnet.commands":{Command:[0,1,1,""],CommandArgs:[0,1,1,""],CommandData:[0,1,1,""],CommandInterface:[0,1,1,""]},"royalnet.commands.Command":{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":{get_author:[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_net_handler:[0,2,1,""],unregister_net_handler:[0,2,1,""]},"royalnet.database":{Alchemy:[0,1,1,""],DatabaseConfig:[0,1,1,""],relationshiplinkchain:[0,4,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":{CurrentlyDisabledError:[0,5,1,""],ExternalError:[0,5,1,""],FileTooBigError:[0,5,1,""],InvalidConfigError:[0,5,1,""],InvalidInputError:[0,5,1,""],NoneFoundError:[0,5,1,""],RoyalnetRequestError:[0,5,1,""],RoyalnetResponseError:[0,5,1,""],TooManyFoundError:[0,5,1,""],UnregisteredError:[0,5,1,""],UnsupportedError:[0,5,1,""]},"royalnet.network":{ConnectionClosedError:[0,5,1,""],NetworkError:[0,5,1,""],NotConnectedError:[0,5,1,""],NotIdentifiedError:[0,5,1,""],Package:[0,1,1,""],Request:[0,1,1,""],Response:[0,1,1,""],ResponseError:[0,1,1,""],ResponseSuccess:[0,1,1,""],RoyalnetConfig:[0,1,1,""],RoyalnetLink:[0,1,1,""],RoyalnetServer:[0,1,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.network.RoyalnetLink":{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.RoyalnetServer":{find_client:[0,2,1,""],find_destination:[0,2,1,""],listener:[0,2,1,""],route_package:[0,2,1,""],serve:[0,2,1,""],start:[0,2,1,""]},"royalnet.utils":{NetworkHandler:[0,1,1,""],andformat:[0,4,1,""],asyncify:[0,4,1,""],cdj:[0,4,1,""],discord_escape:[0,4,1,""],fileformat:[0,4,1,""],numberemojiformat:[0,4,1,""],ordinalformat:[0,4,1,""],parse_5etools_entry:[0,4,1,""],plusformat:[0,4,1,""],safeformat:[0,4,1,""],sleep_until:[0,4,1,""],splitstring:[0,4,1,""],telegram_escape:[0,4,1,""],ytdldateformat:[0,4,1,""]},"royalnet.utils.NetworkHandler":{message_type:[0,3,1,""]},"royalnet.web":{Royalprint:[0,1,1,""],create_app:[0,4,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","function","Python function"],"5":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:function","5":"py:exception"},terms:{"20m":0,"byte":0,"class":[0,1],"default":[0,1],"final":0,"function":[0,1],"import":1,"int":0,"new":[0,2],"null":0,"return":[0,1],"short":1,"static":0,"super":1,"true":0,"try":[0,1],"while":[0,1],And:1,For:1,That:0,The:[0,1],Then:[0,1],These:[0,1],Use:0,__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_upd:0,_init_cli:0,_init_command:0,_init_databas:0,_init_royalnet:0,_init_voic:0,_interface_factori:0,_network_handl:0,abl:0,about:0,abstracteventloop:0,access:[0,2],accur:0,add:0,add_to_music_data:0,added:[0,1],adding:[0,1],addit:0,address:0,advance_music_data:0,akin:0,alchemi:0,all:0,allow:[0,1],also:0,altern:0,alwai:0,amount:0,andformat:0,ani:[0,1],anoth:0,anymor:0,anystr:0,api:2,app:0,applic:0,arg:[0,1],argument:[0,2],around:0,arrai:0,async:[0,1],asyncifi:[0,1],asyncio:[0,1],asyncron:0,attempt:0,attribut:[0,1],audio:2,audiosourc:0,author:0,autocomplet:0,automat:0,avail:0,avoid:1,await:1,banana:1,base:0,becaus:1,been:0,befor:1,begin:1,being:[0,1],between:0,big:0,block:0,blockingli:0,blueprint:0,bool:0,bot:[1,2],both:0,bufferediobas:0,bug:1,call:[0,1],callabl:0,can:[0,1],cancel:0,cannot:0,carb:1,carbonara:1,caus:1,caution:0,cdj:0,certain:1,chain:0,chang:0,change_pres:0,channel:0,charact:0,chat:[0,1],check:0,class_:0,classmethod:0,client:0,close:0,clue:0,code:[0,1],command:2,commandarg:[0,1],commanddata:[0,1],commandinterfac:0,commun:0,complet:0,complex:1,compon:0,comun:2,config:0,config_obj:0,configur:0,connect:0,connectedcli:0,connectionclosederror:0,consid:1,consum:0,contain:0,context:0,convers:0,convert:0,convert_to_mp3:0,convert_to_pcm:0,core:0,coroutin:[0,2],correctli:0,correspond:0,creat:[0,2],create_and_ready_from_url:0,create_app:0,create_from_url:0,current:0,currentlydisablederror:0,custom:0,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,dent:1,describ:0,descript:[0,1],destin:0,destination_conv_id:0,detail:0,dfile:0,dict:0,dictionari:0,directli:1,disabl:0,discord:0,discord_config:0,discord_escap:0,discordbot:0,discordcli:0,discordconfig:0,displai:0,doc:0,docstr:0,document:2,doe:[0,1],doesn:0,don:[0,1],done:1,download:0,download_1_terabyte_of_spaghetti:1,download_fil:0,download_from_url:0,dynam:0,eas:0,edit:0,effect:1,either:0,element:0,emb:0,emoji:1,empti:0,encod:0,end:0,ending_class:0,engin:0,enough:1,ensur:0,entri:0,envvar:0,epoch:0,error:[1,2],error_data:0,error_if_non:0,escap:0,even:0,event:0,everi:[0,1],everyth:0,exact:1,exampl:[0,1],except:0,execut:[0,1],exist:[0,1],expect:0,ext:0,externalerror:0,extra_info:0,extract:0,extract_info:0,fals:0,featur:0,fetch:0,file:[0,1],fileaudiosourc:0,fileformat:0,filenam:0,filetoobigerror:0,find:0,find_client:0,find_destin:0,finish:1,first:1,flask:0,follow:0,format:0,found:0,from:[0,1],from_dict:0,from_json_byt:0,from_json_str:0,from_object:0,fullfil:0,gener:0,genericbot:0,get:[0,1],get_author:0,github:2,greater:0,group:[0,1],guild:0,handl:0,handler:0,has:[0,1],has_info:0,have:[0,1],how:0,html:0,http:0,identifi:0,identity_column_nam:0,identity_t:0,ifi:0,ignor:0,ignoreerror:0,imag:1,import_nam:0,incom:0,incomplet:0,index:[0,2],inf:0,info:0,inform:0,inherit:[0,1],initi:0,input:0,insid:1,instanc:0,instead:[0,1],interfac:[0,1],interface_nam:0,invalid:0,invalidconfigerror:0,invalidinputerror:[0,1],is_download:0,is_opu:0,isn:[0,1],itali:1,item:0,join:[0,1],json:0,jsonabl:0,just:[0,1],keep:1,kei:0,keyword:1,kind:1,kwarg:0,last:0,less:0,like:[0,1],link:0,link_typ:0,list:[0,1],listen:0,login:0,look:[0,1],loop:0,made:0,mai:1,main:0,mainli:0,maintain:0,make:[0,1],manag:0,markup:0,master_secret:0,master_t:0,master_uri:0,match:[0,1],max:0,mean:[0,1],meantim:1,memori:0,mention:1,messag:[0,1],message_typ:0,method:[0,1],middl:0,might:0,minimum:[0,1],miscellan:0,miss:0,month:0,more:[0,1],multipl:[0,1],music_data:0,must:0,name:[0,1],need:0,net_request:0,network:2,network_handl:0,networkerror:0,networkhandl:0,next:[0,1],nid:0,no_warn:0,nobodi:0,node:0,non:0,none:[0,1],nonefounderror:0,noplaylist:0,normal:1,notat:0,notconnectederror:0,noth:0,notic:1,notidentifiederror:0,notimpl:0,now:[0,1],number:[0,1],numberemojiformat:0,object:[0,1],offset:0,onc:[0,1],one:0,ones:0,onli:[0,1],open:0,oper:2,option:0,optional_arg:0,opu:0,ordinalformat:0,org:0,other:[0,1],otherwis:0,outdat:0,output:0,outtmpl:0,overrid:1,packag:0,packet:0,page:0,paramet:[0,1],parse_5etools_entri:0,part:1,pass:[0,1],path:0,pattern:[0,1],pcm:0,pcm_avail:0,pickl:0,ping:1,pingcommand:1,plai:[0,1],plusformat:0,pong:1,port:0,possibl:[0,1],prefix:0,prepar:0,prepend:0,presenc:0,present:1,previou:1,previous:1,probabl:0,properti:0,python:1,quiet:0,rais:[0,1],raise_on_error:0,read:0,readi:1,ready_up:0,real:0,realli:0,receiv:[0,1],reciev:0,recreat:0,refer:2,regex:0,regist:0,register_net_handl:0,relat:0,relationshiplinkchain:0,remov:0,replac:0,repli:[0,1],request:[0,1],request_dict:0,request_handl:0,requir:[0,1],require_alchemy_t:0,require_at_least:[0,1],required_arg:0,required_secret:0,required_t:0,respect:1,respons:0,responseerror:0,responsesuccess:0,rest:1,result:[0,1],retriev:0,retrieve_for_url:0,right:0,right_now:1,root_path:0,rout:0,route_packag:0,row:[0,1],royalcod:0,royalnet:0,royalnet_config:0,royalnetconfig:0,royalnetlink:0,royalnetrequesterror:0,royalnetresponseerror:0,royalnetserv:0,royalprint:0,run:[0,1],safeformat:0,same:1,script:1,second:0,secret:0,secret_kei:0,select:0,self:[0,1],send:[0,1],sent:[0,1],sentry_dsn:0,separ:[0,1],sequenc:0,serv:0,server:0,session_acm:0,session_cm:0,set:0,should:[0,1],side:1,signal:0,simpl:1,singl:0,skip:0,sleep:1,sleep_until:0,slow:2,small:[0,1],some:1,someth:0,somewher:0,song:0,sourc:0,source_conv_id:0,space:[0,1],spaghetti:1,spaghetticommand:1,spawn_audiosourc:0,special:1,specif:[0,1],specifi:[0,1],splitstr:0,sqlalchemi:0,stai:1,start:[0,1],starting_class:0,statement:0,static_fold:0,static_url_path:0,statu:0,stop:1,store:0,str:[0,1],stream:0,string:0,subdomain:0,submodul:0,success:0,support:0,syntax:0,tabl:0,task:0,telegram:[0,1],telegram_config:0,telegram_escap:0,telegrambot:0,telegramconfig:0,tell:1,template_fold:0,temporar:0,text:0,than:0,thei:[0,1],them:[0,1],therefor:0,thi:[0,1],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,token:0,too:[0,1],toomanyfounderror:0,tri:1,tupl:[0,1],two:0,type:[0,1],underscor:0,undescrib:0,unexpect:0,unexpectedli:0,union:0,unregister_net_handl:0,unregisterederror:0,unsupportederror:0,until:[0,1],updat:0,update_activity_with_source_titl:0,update_info:0,uri:0,url:0,url_default:0,url_prefix:0,use:[0,1],used:[0,1],useful:0,user:[0,1],uses:0,using:[0,1],usual:0,utf8:0,util:[1,2],uuid:0,valid:0,valu:0,variabl:0,variou:0,via:2,video:0,voic:0,wai:[0,1],wait:0,want:[0,1],wasn:1,web:2,websocket:0,websocketserverprotocol:0,weird:0,welcom:2,went:0,were:[0,1],what:1,when:[0,1],whenev:1,where:0,which:[0,1],why:0,without:0,word:0,work:[0,1],worth:0,would:0,wrap:1,wrapper:0,written:0,wrong:0,wrote:1,year:0,yet:0,you:[0,1],your:1,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"],titleterms:{"new":1,access:1,api:0,argument:1,audio:0,bot:0,command:[0,1],comun:1,coroutin:1,creat:1,databas:[0,1],direct:1,error:0,express:1,full:1,link:2,network:0,oper:1,option:1,refer:0,regular:1,royalnet:[1,2],slow:1,some:2,string:1,useful:2,util:0,via:1,web:0}})
\ No newline at end of file
diff --git a/docs_source/creatingacommand.rst b/docs_source/creatingacommand.rst
index 7034ea1e..a89d363f 100644
--- a/docs_source/creatingacommand.rst
+++ b/docs_source/creatingacommand.rst
@@ -67,6 +67,86 @@ And... it's done! The command is ready to be added to a bot!
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 :py:class:`str` `"carbonara al-dente"` to the command code.
+
+These arguments can be accessed in multiple ways through the ``args`` parameter passed to the :py:meth:`Command.run`
+method.
+
+Direct access
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can consider arguments as if they were separated by spaces.
+
+You can then access command arguments directly by number as if the args object was a list of :py:class:`str`.
+
+If you request an argument with a certain number, but the argument does not exist, an
+:py:exc:`royalnet.error.InvalidInputError` is raised, making the arguments accessed in this way **required**. ::
+
+ args[0]
+ # "carbonara"
+
+ args[1]
+ # "al-dente"
+
+ args[2]
+ # InvalidInputError() is raised
+
+Optional access
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't want arguments to be required, you can access them through the :py:meth:`CommandArgs.optional` method: it
+will return :py:const:`None` if the argument wasn't passed, making it **optional**. ::
+
+ args.optional(0)
+ # "carbonara"
+
+ args.optional(1)
+ # "al-dente"
+
+ args.optional(2)
+ # None
+
+You can specify a default result too, so that the method will return it instead of returning :py:const:`None`: ::
+
+ args.optional(2, default="banana")
+ # "banana"
+
+Full string
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want the full argument string, you can use the :py:meth:`CommandArgs.joined` method. ::
+
+ args.joined()
+ # "carbonara al-dente"
+
+You can specify a minimum number of arguments too, so that an :py:exc:`royalnet.error.InvalidInputError` will be
+raised if not enough arguments are present: ::
+
+ args.joined(require_at_least=3)
+ # InvalidInputError() is raised
+
+Regular expressions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For more complex commands, you may want to get arguments through `regular expressions `_.
+
+You can then use the :py:meth:`CommandArgs.match` method, which tries to match a pattern to the command argument string,
+which returns a tuple of the matched groups and raises an :py:exc:`royalnet.error.InvalidInputError` if there is no match.
+
+To match a pattern, :py:func:`re.match` is used, meaning that Python will try to match only at the beginning of the string. ::
+
+ args.match(r"(carb\w+)")
+ # ("carbonara",)
+
+ args.match(r"(al-\w+)")
+ # InvalidInputError() is raised
+
+ args.match(r"\s*(al-\w+)")
+ # ("al-dente",)
+
+ args.match(r"\s*(carb\w+)\s*(al-\w+)")
+ # ("carbonara", "al-dente")
Coroutines and slow operations
------------------------------------
@@ -87,7 +167,7 @@ are finished and may cause bugs in other parts of the code! ::
image = download_1_terabyte_of_spaghetti("right_now", from="italy")
...
-If the slow function you want does not cause any side effect, you can wrap it with the :ref:`royalnet.utils.asyncify`
+If the slow function you want does not cause any side effect, you can wrap it with the :py:func:`royalnet.utils.asyncify`
function: ::
async def run(self, args, data):
diff --git a/royalnet/bots/generic.py b/royalnet/bots/generic.py
index 78188f96..4de3288f 100644
--- a/royalnet/bots/generic.py
+++ b/royalnet/bots/generic.py
@@ -15,7 +15,7 @@ log = logging.getLogger(__name__)
class GenericBot:
"""A generic bot class, to be used as base for the other more specific classes, such as
- :ref:`royalnet.bots.TelegramBot` and :ref:`royalnet.bots.DiscordBot`. """
+ :py:class:`royalnet.bots.TelegramBot` and :py:class:`royalnet.bots.DiscordBot`. """
interface_name = NotImplemented
def _init_commands(self, commands: typing.List[typing.Type[Command]]) -> None:
diff --git a/royalnet/database/databaseconfig.py b/royalnet/database/databaseconfig.py
index 3cfae6c4..acb689d7 100644
--- a/royalnet/database/databaseconfig.py
+++ b/royalnet/database/databaseconfig.py
@@ -2,7 +2,7 @@ import typing
class DatabaseConfig:
- """The configuration to be used for the :ref:`royalnet.database.Alchemy` component of :ref:`royalnet.bots.GenericBot`."""
+ """The configuration to be used for the :py:class:`royalnet.database.Alchemy` component of :py:class:`royalnet.bots.GenericBot`."""
def __init__(self,
database_uri: str,