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 @@
  • Regular expressions
  • +
  • Running code at the initialization of the bot
  • Coroutines and slow operations
  • Accessing the database
  • Comunicating via Royalnet
  • @@ -225,6 +226,22 @@ The previously mentioned β€œspaghetti” command should have a file called 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 ------------------------------------