-
Notifications
You must be signed in to change notification settings - Fork 18
API
Creating a Code module is simple and fun! This document is laid out in the following format,
-
How To Get Started
-
Create A Module
-
Functions in a Module
-
Commands, Rules, Events, and Priorities
-
Documenting Your Module
-
Built-In Functions, Constants, and Variables
-
Add Global Variables
-
Using @hook()
-
Further Reading
-
Author's Comments
-
How to Get Started
Before you can get started you need to have a fork of Code on your computer. You can get Code here https://code.liamstanley.io.
Regardless of which fork you have you can create modules for either or. I won't go through the instructions of installing Code since those are already included in both Code packages.
Also I'm assuming you know the complete basics about Code, (ie: what language he is written in; how to set him up; how to config him, etc.)
- Create A Module
The very first step in creating a module is to open up your text editor of choice and save the file in your Code/modules folder. (Assuming you haven't changed the default location of where modules will be placed for Code.) As far as I am aware there really isn't a convention already in place for how to name your files. The way I recommend doing it is to summarize what the goal of the whole file will accomplish or does. Later one you will see that if you have a collection of functions you might want to name the file for what the collection collectively does or accomplishes.
Once you have saved your file you now have created a file that Code can "reload" in a chat room with the admin commands. So for example lets say you have created a file hello_world.py without restarting Code you can simply go into a chat room he is sitting in and run the command "Code: reload hello_world" but change "Code" to whatever his nick is on the server you are running him on.
NOTICE: When you reload a module in the chat room or via private message you do NOT specify the extension.
FOLLOW ALONG: Now go create a "hello_world.py" file in your modules folder and then reload it in your chat room. If it were successful you will see a message along these lines:
nick: module 'hello_world.py' from '/home/nick/Code/modules/hello_world.py' (version: 2010-02-18 02:00:00)
Now what this all means is that it has successfully loaded the module from your path where Code is located at. The version is the time and date that the file was last modify. The 'nick' mentioned above is your irc nick that you are using to administer Code.
NOTE: You can use anything that is included in your current library of Python that is locally installed on the computer in which you are running Code. Which means you can use modules, such as MATH, PICKLE, SQLITE3, etc.. if they are normally available locally in your Python PATH directories.
- Functions In A Module
Now if you have ever done any programming before, especially in python then you'll probably know what a function is, if not I would recommend you pick up a book about programming (or try several great e-books) to learn about the wonderful world of programming.
Now the way functions work in Python is like this::
def function_name(myvar1, myvar2):
"def" is how you declare that this is a function and obviously "function_name" is the name of the function. Please try to keep your function names descriptive and short so if you share your code it will be easy to understand by fellow programmers.
Also "myvar1" and "myvar2" are the incoming variables into this function. However, for most of the functions you'll be creating with Code it will look like this:
def helloworld(code, input):
Here you need to always (unless you know what you are doing) include "Code" and "input." "Code" brings into scope the variables and accessor you'll need in order for Code to do what you want him to do. The "input" variable is what listens and brings into scope certain variables that are usually used to trigger the function. You'll see in the next section on how to 'trigger' a function. I'll also show an example of a working module you can use to test.
- Commands, Rules, Priorities, and Events
This is where, in my opinion, the most fun starts. Using commands you can get Code to actually compute a given function and "say" things in the irc chat room. I'm going to display an example of a fully working module and I'll break down the pieces of what each thing does so you'll better understand what exactly is going on.
from util.hook import *
@hook(cmds=['hello'], priority='medium')
def helloworld(code, input):
code.say("Hello world!")
Figure 5-1
If you saved the above code into a hello_world.py file in your modules directory of Code and then loaded as I mentioned above then why type .hello (Assuming your prefix is ".") in the chat room he is currently sitting in your Code bot should he should say "Hello world!" in the chat room.
The first important thing (and new thing) is the code.say() function. 'code.say()' will basically take any string or python variable that can be printed natively to stdout into the chat room. You can even have him say variables that of type string, int, etc.. (eg: code.say(mystr) )
The key thing about this example is @hook(cmds=['hello'], priority='medium')
This basically tells Code to keep an eye out for anything that begins with a ".hello" (Assuming "." is your prefix) regardless of what is following it. You could try typing
.hello 125
Figure 5-2
and you'll still get your nice message of "Hello world!" I'll show later where you can actually pass that extra stuff into your functions and actually use them to manipulate data.
Another key thing to note about figure 5-1 is the priority inside the @hook() feature. This isn't required for every function but it helps Code keep things in order when a bunch of things are going on. This is really handy when he is being used by several different people at the same time. Basically inside Code's guts he runs all commands in the following order based on priority. High, Medium, then Low. All high commands will execute before all medium commands, and all medium commands will execute before all low commands. But this order is only done when there are commands being requested to be executed. Unless you build a function that is of utmost importance to the operation of Code, you'll most likely want to give the function a medium priority. If you are doing something really obscure then you can get away with a low priority.
RULES I've saved the best for last!
Rules can be tricky, because they aren't as easy as "commands". Because they differ in the way they offer more flexibility and you can monitor more messages for lines in a chatroom than with commands. Let's say you built a function that logs the current channel you are in. But you don't know how to have Code compute your function for every line in the room you can do this using rules.
A key concept to remember is that you never want to have a function have both a "command" and a "rule" defined because there could be an example where a certain phrase someone enters could trigger the function twice so your output or even data you may be manipulating may be done more times than you want it to be done.
The most powerful way to use rules are to use them in the form of regular expressions. Using regular expressions you actually have more power of what "types" of things you are looking for in each line in the room. For every message in a chat room on irc that is sent that line is checked against your rule. If your rule would apply to a line, then your function is called, but if your rule doesn't match a line in a room it silently fails. (There is no error message displayed, your function simply does not execute.)
Here is a great example of a "rule" in action.
from util.hook import *
@hook(rule=r'$nickname!', priority='high', thread=False
def interjection(code, input):
code.say(input.nick + '!')
Figure 5-3
There a few new things in this example. First off is the use of a 'rule' to call the function. The r'' means that the string of code you enter is to be interpreted as a raw string. In Python this means that the escape sequence are different. This makes it easier for you to enter regular expressions without having to double up on some of your characters. Another thing to note, the use of $nickname, is a global variable built inside Code that refers to whatever nickname that you have set for Code in your configuration file. Therefore the regular expression rule is keeping an eye out for anyone who types just 'Code!' in the channel where Code could be the nick of your clone of Code.
Another thing to point out is the use of thread=False inside of @hook. Out of the box, Code is multi-threaded so technically it can use two cores on a dual-core machine. But the best part is, you can specifically tell a given function not to be "multi-threaded." There are numerous occasions where this may be necessary.
The best tip I can give regarding rules is to take a look at the modules that come with Code out of the box.
EVENTS Events are basically 'triggers' that will execute your function when the specified event that you listed occurs. A perfect example is to have a function execute every time someone joins a channel.
from util.hook import *
@hook(rule=r'.*', event='JOIN')
def message_join(code, input):
...
Figure 5-4
You can use either JOIN, PART, NICK, QUIT, INVITE and PRIVMSG. (There are more, but most aren't used in any normal case..). JOIN watches for join commands from the IRC server. PART looks for people leaving a channel that Code is currently in. NICK looks for people in the room that have changed their nicks, QUIT looks for anyone who has quit the network, INVITE looks for invite requests to a channel and PRIVMSG is the default and it looks for any message sent to the channel or to Code.
NOTE: One thing to note here, you need to have a rule that matches anything, which is basically '.*' or the event will not match anything.
- Documenting Your Module
This is probably the most important part of creating a module. This allows you to make your module easy to understand to other people and it also allows you to create examples for other people to see how to use your modules in the chat room!
First off the first 7 lines of your document should be used to create a description based off of how the author started it. Here is an example of how to start off your brand new module!
"""
Code Copyright (C) 2012-2014 Liam Stanley
Credits: (If someone hlped you, put their name here!)
helloworld.py - Code helloworld Module
http://code.liamstanley.io/
"""
Figure 6-1
Now you don't necessarily have to do everything exactly the same as I have done but it is highly recommend, especially if you would like to create your very own fork of the Code project.
The first line identifies the copyright, Code being licensed under the General Publics License. The second line right after the three double quotes, represents the credits/contributors of the file. If you forked this from another user, i recommend adding the user to the line. The third line is the filename of the document, and a short description of the file. Then the final line before the last three double quotes, is either the location of support, location you received the file, or the URL to the original creator's site. I would highly recommend keeping this there.
DOCUMENTING YOUR FUNCTIONS
This part is in my opinion very important, especially if you have several functions in your module file. You should add a doctype to your main function. To do this you simply go to the line right below the declaration of your function and using three double quotes ( """ ) type in a brief description of what this function does.
For example::
def welcomemessage(code, input):
"""Sends a message to a user upon a JOIN event handler, configurable."""
...
Figure 6-2
A nice but not necessary thing to include in your functions that you want to present to the world is the example
property. This displays an example you set, when someone messages Code ".help command".
For example::
from util.hook import *
@hook(cmds=['join'], example='join #example [key]')
def join(code, input):
...
Figure 6-3
Here in Figure 6-3, when a user messages Code, ".help join" Code will respond saying ".join #example [key]"
Next at the very end of your document you are going to want to add the following lines of code. This is a small python hack, if you will, that prevents some weird things from happening if you tried to run the module directly inside of python and not inside Code.
if __name__ == "__main__":
print __doc__.strip()
Figure 6-4
- Built-In Functions, Constants, And Variables
Here is an incomplete and growing list of built-in functions that Code has access to that you wouldn't normally find in other Python programs.
This section is expected to grow with time.
NOTE: These only work inside a function that has the two variables, 'Code', and 'input' passed to them. For example declare a function like::
def myfunction(code, input):
And inside that function you'll have access to all variables listed below.
You can access variables from the config file by doing the follow: code.config.name_of_variable. Using this pattern if you want to access the channels you connect to upon launch you can do code.config.channels, and this will return a list of channels that Code connects to upon launching.
code.nick will return the string that represents the current nick of Code running, according to your configuration file.
code.doc Dictionary of documentation for each command/module
code.chan Dictionary of user-tracked data code.chan -- dictionary of channels code.chan[channel] -- dictionary of users/data code.chan[channel][user] -- dictionary of data for a specific user. (normal, voiced, op, etc)
code.say(myvar) will print the contents of the variable "myvar" to the chatroom
code.nick(newnick) will change the bots nickname to something that is 2-16 chars long, and doesn't contain any not-allowed chars.
code.write(['JOIN'], channel) this makes Code say a JOIN command to the IRC server to the join specified channel.
input.admin is a variable that is a boolean of whether or not the current nick is in the admin list.
input.group() This provides the text of the entire line that was caught by either a command or rule for the function.
input.nick contains the nick of the person that sent a message that triggered the function.
input.sender Contains a string of what channel/user a message was sent from This includes the # at the beginning of the channel name (if sender was a channel).
input.modules A list of currently loaded modules, that is loaded when the bot is loaded
input.voiced True or False depending on if the user is voiced or not in a given channel
input.op True or False depending on if the user is opped or not in a given channel
- Add Global Variables
If you've been wondering how to have a global variable so you don't have to save passwords or api keys in the modules themselves, then this section is for you. The way this is accomplish is we put these "sensitive" variables in the default.py file in the .code folder.
The first place we'll stop (assuming you've already started on your module) is in the default.py file.
Figure 8-1
When creating a variable in defauly.py, for example:
welcomemessage = "Hello, welcome to the Code development channel!"
Your variable is considered welcomemessage
via the default.py. When attempting to reference that variable from a module, the best practice i've found for this is to use code.config.welcomemessage
. Always remember to wrap your variable on the right type, or you'll find errors. (i.e, if it is a integer, simply do var = 9
, a list, mylist = ['var1', 'var2', 'var3']
, or simply a string, mystring = 'var1'
.
If you would like your users to be able to un-comment the variable, make sure you add an exception pipe so when your script attempts to use a variable that doesn't exist, it doesn't flip out. For example:
if hasattr(code.config, 'welcomemessage'):
# It's uncommented, safe to directly use code.config.<variable>
if code.config.welcomemessage == '1' or if code.config.welcomemessage == '2':
do work
else:
# Assume its commented out
That's pretty much it to creating global variables accessible in any module. NOTE: Again to access these 'global' variables in Code you need to pass 'code, input' to the function in order to use these.
- Using @hook()
To be able to seemlessly and cleanly hook commands, regex, events, etc, into Code, the use of the "hook" decorator is flawless.
First off, import it into your module with from util.hook import *
at the top of your file. Then, add the below to the TOP of your function, on the line before it's defined. E.g:
#!/usr/bin/env python
"""
Your documentation here...
"""
from util.hook import *
@hook(cmds=['hello'], priority='low')
def helloworld(code, input):
code.say("Hello world!")
Inside of hook you can add many things to easily allow/disallow users, and change trigger options. Those are (not all are listed yet because it's always in development) commands=['hello', 'world'] (Aliases: commands, command, cmds, cmd) example='hellow world' (Aliases: example, ex) rate=30 rule=r'.*' priority='low' thread=False event='JOIN' args=True (Non-normal trigger: responds with no arguments to the user, and returns without the function) admin=True (Non-normal trigger: Only returns the function if input.admin is True) owner=True (Non-normal trigger: Only returns the function if input.owner is True) voiced=True (Non-normal trigger: Only returns the function if input.voiced is True) op=True (Non-normal trigger: Only returns the function if input.op is True)
- Further Reading
Regardless if you know Python or are comfortable with Python you should still read up on Python. I am always learning new and fantastic things that one can do with Python. It is truly an amazing language!
- Author's Comments
If you have any comments or questions, head to my personal website, Liam Stanley http://liamstanley.io.