diff --git a/docs/doctrees/creatingacommand.doctree b/docs/doctrees/creatingacommand.doctree index 59245b29..185af670 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 cb175423..bcf19274 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 a89d363f..3cbcc681 100644 --- a/docs/html/_sources/creatingacommand.rst.txt +++ b/docs/html/_sources/creatingacommand.rst.txt @@ -73,6 +73,23 @@ 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. +If you want your command to use arguments, override the ``syntax`` class attribute with a brief description of the +syntax of your command, possibly using (round parentheses) for required arguments and [square brackets] for optional +ones. :: + + from royalnet.commands import Command + + class SpaghettiCommand(Command): + name = "spaghetti" + + description = "Send a spaghetti emoji in the chat." + + syntax = "(requestedpasta)" + + async def run(self, args, data): + await data.reply(f"π Here's your {args[0]}!") + + Direct access ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,10 +165,44 @@ To match a pattern, :py:func:`re.match` is used, meaning that Python will try to args.match(r"\s*(carb\w+)\s*(al-\w+)") # ("carbonara", "al-dente") +Running code at the initialization of the bot +------------------------------------ + +You can run code while the bot is starting by overriding the :py:meth:`Command.__init__` function. + +You should keep the ``super().__init__(interface)`` call at the start of it, so that the :py:class:`Command` instance is +initialized properly, then add your code after it. + +You can add fields to the command to keep **shared data between multiple command calls** (but not bot restarts): it may +be useful for fetching external static data and keeping it until the bot is restarted, or to store references to all the +:py:class:`asyncio.Task` started by the bot. :: + + from royalnet.commands import Command + + class SpaghettiCommand(Command): + name = "spaghetti" + + description = "Send a spaghetti emoji in the chat." + + syntax = "(pasta)" + + def __init__(self, interface): + super().__init__(interface) + self.requested_pasta = [] + + async def run(self, args, data): + pasta = args[0] + if pasta in self.requested_pasta: + await data.reply(f"β οΈ This pasta was already requested before.") + return + self.requested_pasta.append(pasta) + await data.reply(f"π Here's your {pasta}!") + + Coroutines and slow operations ------------------------------------ -You may have noticed that in the previous example I wrote ``await data.reply("π")`` instead of just ``data.reply("π")``. +You may have noticed that in the previous examples we used ``await data.reply("π")`` instead of just ``data.reply("π")``. This is because :py:meth:`CommandData.reply` isn't a simple method: it is a coroutine, a special kind of function that can be executed separately from the rest of the code, allowing the bot to do other things in the meantime. @@ -181,5 +232,11 @@ a coroutine that does the same exact thing. Accessing the database ------------------------------------ +.. Usually, bots are connected to a PostgreSQL database through a :py:class:`royalnet.database.Alchemy` interface (which is +itself a SQLAlchemy wrapper). + +.. Commands can access the connected database through the :py:class:`royalnet.database.Alchemy` available at +``self.interface.alchemy``, and can access the database session at ``self.interface.session``. + Comunicating via Royalnet ------------------------------------ diff --git a/docs/html/creatingacommand.html b/docs/html/creatingacommand.html index cce935e6..94b99b0e 100644 --- a/docs/html/creatingacommand.html +++ b/docs/html/creatingacommand.html @@ -91,6 +91,7 @@
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.
+If you want your command to use arguments, override the syntax
class attribute with a brief description of the
+syntax of your command, possibly using (round parentheses) for required arguments and [square brackets] for optional
+ones.
+from royalnet.commands import Command
+
+class SpaghettiCommand(Command):
+ name = "spaghetti"
+
+ description = "Send a spaghetti emoji in the chat."
+
+ syntax = "(requestedpasta)"
+
+ async def run(self, args, data):
+ await data.reply(f"π Here's your {args[0]}!")
+
+
Direct accessΒΆ
You can consider arguments as if they were separated by spaces.
@@ -297,9 +314,40 @@ which returns a tuple of the matched groups and raises an
+Running code at the initialization of the botΒΆ
+You can run code while the bot is starting by overriding the Command.__init__()
function.
+You should keep the super().__init__(interface)
call at the start of it, so that the Command
instance is
+initialized properly, then add your code after it.
+You can add fields to the command to keep shared data between multiple command calls (but not bot restarts): it may
+be useful for fetching external static data and keeping it until the bot is restarted, or to store references to all the
+asyncio.Task
started by the bot.
+from royalnet.commands import Command
+
+class SpaghettiCommand(Command):
+ name = "spaghetti"
+
+ description = "Send a spaghetti emoji in the chat."
+
+ syntax = "(pasta)"
+
+ def __init__(self, interface):
+ super().__init__(interface)
+ self.requested_pasta = []
+
+ async def run(self, args, data):
+ pasta = args[0]
+ if pasta in self.requested_pasta:
+ await data.reply(f"β οΈ This pasta was already requested before.")
+ return
+ self.requested_pasta.append(pasta)
+ await data.reply(f"π Here's your {pasta}!")
+
+
+
Coroutines and slow operationsΒΆ
-You may have noticed that in the previous example I wrote await data.reply("π")
instead of just data.reply("π")
.
+You may have noticed that in the previous examples we used await data.reply("π")
instead of just data.reply("π")
.
This is because CommandData.reply()
isnβt a simple method: it is a coroutine, a special kind of function that
can be executed separately from the rest of the code, allowing the bot to do other things in the meantime.
By adding the await
keyword before the data.reply("π")
, we tell the bot that it can do other things, like
@@ -325,6 +373,8 @@ a coroutine that does the same exact thing.
Accessing the databaseΒΆ
+itself a SQLAlchemy wrapper).
+self.interface.alchemy
, and can access the database session at self.interface.session
.
Comunicating via RoyalnetΒΆ
diff --git a/docs/html/index.html b/docs/html/index.html
index 53a29943..b0cb249f 100644
--- a/docs/html/index.html
+++ b/docs/html/index.html
@@ -161,6 +161,7 @@
Regular expressions
+Running code at the initialization of the bot
Coroutines and slow operations
Accessing the database
Comunicating via Royalnet
diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js
index 9f5756ea..9ac17acb 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,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,direct:2,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,express:2,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,full:2,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,2],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,regular:2,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,2],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
+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,1],"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,1],add_to_music_data:0,added:[0,1],adding:[0,1],addit:0,address:0,advance_music_data:0,after:1,akin:0,alchemi:[0,1],all:[0,1],allow:[0,1],alreadi:1,also:0,altern:0,alwai:0,amount:0,andformat:0,ani:[0,1],anoth:0,anymor:0,anystr:0,api:2,app:0,append:1,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,1],big:0,block:0,blockingli:0,blueprint:0,bool:0,bot:2,both:0,bracket:1,brief:1,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,2],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,direct:2,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,express:2,ext:0,extern:1,externalerror:0,extra_info:0,extract:0,extract_info:0,fals:0,featur:0,fetch:[0,1],field:1,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,full:2,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],here: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,2],input:0,insid:1,instanc:[0,1],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,itself:1,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,1],onli:[0,1],open:0,oper:2,option:[0,2],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],parenthes:1,parse_5etools_entri:0,part:1,pass:[0,1],pasta: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,properli:1,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:[1,2],regex:0,regist:0,register_net_handl:0,regular:2,relat:0,relationshiplinkchain:0,remov:0,replac:0,repli:[0,1],request:[0,1],request_dict:0,request_handl:0,requested_pasta:1,requestedpasta:1,requir:[0,1],require_alchemy_t:0,require_at_least:[0,1],required_arg:0,required_secret:0,required_t:0,respect:1,respons:0,responseerror:0,responsesuccess:0,rest:1,restart:1,result:[0,1],retriev:0,retrieve_for_url:0,right:0,right_now:1,root_path:0,round:1,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,2],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:1,session_acm:0,session_cm:0,set:0,share:1,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,1],squar:1,stai:1,start:[0,1],starting_class:0,statement:0,static_fold:0,static_url_path:0,statu:0,stop:1,store:[0,1],str:[0,1],stream:0,string:[0,2],subdomain:0,submodul:0,success:0,support:0,syntax:[0,1],tabl:0,task:[0,1],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,1],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,1],written:0,wrong:0,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,1],code:1,command:[0,1],comun:1,coroutin:1,creat:1,databas:[0,1],direct:1,error:0,express:1,full:1,initi:1,link:2,network:0,oper:1,option:1,refer:0,regular:1,royalnet:[1,2],run:1,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 a89d363f..3cbcc681 100644
--- a/docs_source/creatingacommand.rst
+++ b/docs_source/creatingacommand.rst
@@ -73,6 +73,23 @@ 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.
+If you want your command to use arguments, override the ``syntax`` class attribute with a brief description of the
+syntax of your command, possibly using (round parentheses) for required arguments and [square brackets] for optional
+ones. ::
+
+ from royalnet.commands import Command
+
+ class SpaghettiCommand(Command):
+ name = "spaghetti"
+
+ description = "Send a spaghetti emoji in the chat."
+
+ syntax = "(requestedpasta)"
+
+ async def run(self, args, data):
+ await data.reply(f"π Here's your {args[0]}!")
+
+
Direct access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -148,10 +165,44 @@ To match a pattern, :py:func:`re.match` is used, meaning that Python will try to
args.match(r"\s*(carb\w+)\s*(al-\w+)")
# ("carbonara", "al-dente")
+Running code at the initialization of the bot
+------------------------------------
+
+You can run code while the bot is starting by overriding the :py:meth:`Command.__init__` function.
+
+You should keep the ``super().__init__(interface)`` call at the start of it, so that the :py:class:`Command` instance is
+initialized properly, then add your code after it.
+
+You can add fields to the command to keep **shared data between multiple command calls** (but not bot restarts): it may
+be useful for fetching external static data and keeping it until the bot is restarted, or to store references to all the
+:py:class:`asyncio.Task` started by the bot. ::
+
+ from royalnet.commands import Command
+
+ class SpaghettiCommand(Command):
+ name = "spaghetti"
+
+ description = "Send a spaghetti emoji in the chat."
+
+ syntax = "(pasta)"
+
+ def __init__(self, interface):
+ super().__init__(interface)
+ self.requested_pasta = []
+
+ async def run(self, args, data):
+ pasta = args[0]
+ if pasta in self.requested_pasta:
+ await data.reply(f"β οΈ This pasta was already requested before.")
+ return
+ self.requested_pasta.append(pasta)
+ await data.reply(f"π Here's your {pasta}!")
+
+
Coroutines and slow operations
------------------------------------
-You may have noticed that in the previous example I wrote ``await data.reply("π")`` instead of just ``data.reply("π")``.
+You may have noticed that in the previous examples we used ``await data.reply("π")`` instead of just ``data.reply("π")``.
This is because :py:meth:`CommandData.reply` isn't a simple method: it is a coroutine, a special kind of function that
can be executed separately from the rest of the code, allowing the bot to do other things in the meantime.
@@ -181,5 +232,11 @@ a coroutine that does the same exact thing.
Accessing the database
------------------------------------
+.. Usually, bots are connected to a PostgreSQL database through a :py:class:`royalnet.database.Alchemy` interface (which is
+itself a SQLAlchemy wrapper).
+
+.. Commands can access the connected database through the :py:class:`royalnet.database.Alchemy` available at
+``self.interface.alchemy``, and can access the database session at ``self.interface.session``.
+
Comunicating via Royalnet
------------------------------------