Modules:Tutorials:Basics

From Anope Wiki

Jump to: navigation, search

Contents

Anope Modules Coding Tutorial: Basics

Introduction

Welcome to this tutorial on the basics of Anope module coding. You're reading this tutorial because you want to get familiar with coding modules for Anope, or you're bored enough to care about it ;) Anyways, before we start on this tutorial i assume you already know how to code simple stuff in C. If you can't write the classic "Hello World" example without looking in your C reference you probably should first practice a bit more with coding C in general, as module coding isn't the most simple thing to do. I agree it is fairly simple, but some errornous code might have a crash of the services as a result, and you don't want that to happen, do you?

Header

Now you're here i can safely assume you can code C, so that allows us to start on the real code :) We're going to code a module which makes HelpServ meow as example. First we need to include the required header-files for our module:

#include "module.h"

The module.h file contains references to all functions and variables we are allowed to use inside our module. Without it you'd have a bare piece of code where you would have to write all this functions yourself, which is, trust me, a lot of work. Next it's nice to define who made the module and what version of the module it is, so we can use this defines later to let Anope know it too. And if you don't comment on your code people can easily see who made it or what version they have if you add these defines.

#define AUTHOR "Anope"
#define VERSION "1.0.0"

Function Prototypes

In our module we're going to have a single command which can be executed, which will be called MEOW. Our function in C which will handle this command will be called my_he_meow. Of course this can be a different name if you prefer, but it's my style of naming my functions. The default return type of functions for command-handlers is an int, which allows us to return whether Anope should continue processing this command or should stop, but i'll get back on that later. Also we have one parameter we pass to our functions, which indicates which user triggered the function, so we can respond to the correct user. This user is passed as a struct called User. So now we know what's in the function prototype we can write it down safely:

int my_he_meow(User *u);

Module Initialization

Ok now it's time to let Anope know who we are, and when we expect Anope to give control to us. In the AnopeInit function we're going to add the function we just made the prototype for to the command hash for HelpServ in Anope, which holds all commands HelpServ knows. Also we're going to let Anope know we made this module, and what version it is, using the defines above. First we need to discuss the AnopeInit prototype though. It's return type is also an int, which tells us if the load succeeded, or not. It gets passed 2 arguments, an array of strings (char **argv), and a number which indicates how many strings are in the array (int argc). Currently, newer Anope versions (1.7.6 and newer) send the name of the person loading the module as the first argument. If argc is 0, you have an older anope version; if argc is 1 and argv[0] is NULL, it is automatically loaded; if argc is 1 and argv[0] is non-NULL it's a char * containing the nick of the person loading the module.

int AnopeInit(int argc, char **argv) {

The Command

It's time to create the command for our function. This is done using a Command type of variable. It's actually a struct containing the information on the command, but we don't need to access it as a struct. The command is then created using the createCommand function. This function gets passed a number of variables, starting with the name of the command triggering it. This is given as a literal string, in our module "meow". The second paramenter is the function which has to be called, in our module this is my_he_meow. The other paramenters can be ignored for now, just fill them with NULL, -1, -1, -1, -1, -1.

    Command *c;
    c = createCommand("meow", my_he_meow, NULL, -1, -1, -1, -1, -1);

You might notice we didn't specify the service (HelpServ in our module) which should handle it yet. This is because there are different command hashes for every service. Now it's time to add the function we just created to the command hash of HelpServ, so HelpServ knows it should call our function when we ask HelpServ to meow. This is done using the moduleAddCommand function. It returns an int which tells us the command was succesfully added, or not. The first paramenter you should pass to it is the command hash it should be put it. This is basically the service name you want the function to be for written in CAPS, so that would be HELPSERV for us. The second paramenter is the command we just created, c in our module. And as the last paramenter we specify the place of our new command in the command hash. This can either be on the start (MOD_HEAD) or on the end (MOD_TAIL) of the list. MOD_HEAD and MOD_TAIL both are defined ints, so we can use them freely instead of putting down 1 or 2. When you place your command at the end of the command hash, it loops through all other added commands first. If there's a match in there and it tells Anope to stop processing further, our function won't be called. Because of that you'll be adding your command to the start of the command hash very often. This way your module can also override default commands builtin in the services if you need it.

    moduleAddCommand(HELPSERV, c, MOD_HEAD);

Anope Logs

What if the command doesn't get added to the list properly? The moduleAddCommand nicely returns a number indicating an error, but we don't handle it. The most easy solution for this is to send the status of the loading to the log files of Anope. This is done using the alog function. It works just like printf with the difference it sends the text to the Anope logs instead of to the screen. This would change the code to add the command to this:

    alog("he_meow: Add command 'meow' status: %d", moduleAddCommand(HELPSERV, c, MOD_HEAD));

Who made which version of the module?

Now it's finally time to let Anope know who made this module, and what version of the module this is. This is done using the moduleAddAuthor and moduleAddVersion commands. They take one argument: a literal string containg the author or the version. I guess you can figure out which one takes what ;) Remember we made defines for the author and version at the start of the file, they come in handy here!

    moduleAddAuthor(AUTHOR);
    moduleAddVersion(VERSION);

We loaded (un)succesfully

We've come at the end of the AnopeInit function. Remember this function is an int so we need to return something. Here we can let Anope know the loading didn't go as expected and the module can't operate ok. This is done by returning a MOD_STOP. Anope will then unload the module again, so there won't be malicious traces hanging around in Anope anymore. If the module did load succesfully we return a MOD_CONT and Anope tells the user who loaded the module that the loading succeeded. MOD_CONT and MOD_STOP are also defined as ints so you can use them freely as ints.

    return MOD_CONT;
}

Cleaning Up

We've initialized the module, but some modules also need cleaning up when they're unloaded. This is done using the AnopeFini function. This function is a void which takes no paramenters. It's usefull to, for example, delete services clients you created or let the network know how sad it is this module is unloaded (this is generally not a good idea though, as most people will only be annoyed by it ;)). This module doesn't need this function so we can leave it empty.

void AnopeFini(void) {
}

The Function

After all the hassle to let Anope know the stuff it needs to make our module function correctly, it's finally time to go on with the real business. Our function, called my_he_meow as defined by our prototype above, will, when triggered, meow at the user who triggered it. To send out output to an user there is the function called notice. This function will use the correct way, using notice or message according to the user's preferences, to output the message given to the specified service. The service is the first paramenter given to this function, and is basically constructed of s_ followed by the correctly capitalized name of the server. For our module this would be s_HelpServ. The second paramenter is the nick we're sending the message to. This nick is stored in the User *u we defined in the prototype. It's a struct containing a member called nick which contains the nick of the user. Thus we should notice to u->nick. After these 2 paramenters we can add what we want to message to the user, constructed in a way similair to printf (remember alog? ;)).

int my_he_meow(User *u) {
    notice(s_HelpServ, u->nick, "MEOW! Specially for you \002%s\002 ;)", u->nick);

Note the \002 i used around the %s in the format string. It makes the contents of %s (in this case u->nick, the nick of the user) bold. Use \037 for underline. Note that you should also close these; Anope doesn't check them for you!

Continue or Halt?

We can now instruct Anope to stop processing the other functions which can handle this command and are after us in the command hash. This can be done by returning a MOD_STOP. If we return a MOD_CONT however, Anope continues to match the rest of the command in the command hash to find matches. In this case we don't want further processing of any commands, so we let Anope stop processing. Our function also ends here, as we've done what we should do: meow at the user! :)

    return MOD_STOP;
}

End of File

Our module is now fully functional with the functions implemented we want, so we can end our file here. This is often done using an EOF comment, so people who read the file know they received the complete file.

/* EOF */

And this comment is the end of this tutorial. If all went ok you should now know the basics of module coding for Anope, altough there's still a lot to learn. Anope offers support for things like callbacks (timers), raw message handling, and even creating your own services. It ends where your imagination ends ;) Below this text is a summary of the module we just made (with added comments). Also look around a bit in other modules if you're interested so you can learn more tricks which module coders use. Good luck!


Final Code

he_meow.c:

#include "module.h"

#define AUTHOR "Anope"
#define VERSION "1.0.0"

/* Our function which meow's at the user */
int my_he_meow(User *u);

int AnopeInit(int argc, char **argv) {
    /* We create the command we're going to use */
    Command *c;
    c = createCommand("meow", my_he_meow, NULL, -1, -1, -1, -1, -1);
    
    /* We add the command to HelpServ and log it */
    alog("he_meow: Add command 'meow' status: %d", moduleAddCommand(HELPSERV, c, MOD_HEAD));
    
    /* We tell Anope who we are and what version this module is */
    moduleAddAuthor(AUTHOR);
    moduleAddVersion(VERSION);
    
    /* Loading succeeded */
    return MOD_CONT;
}

void AnopeFini(void) {
    /* Nothing to clean up */
}

int my_he_meow(User *u) {
    /* Meow at the user, (s)he wanted it so badly */
    notice(s_HelpServ, u->nick, "MEOW! Specially for you \002%s\002 ;)", u->nick);
    
    /* Halt processing */
    return MOD_STOP;
}

/* EOF */
Personal tools