Skip to content

Writing Plugins

Seth Hendrick edited this page Feb 2, 2019 · 11 revisions

Chaskis is completely plugin powered. Plugins can be as simple as yelling at a user when they type in all caps to as complex of grabbing an RSS Feed and posting updates to the IRC channel. This page shows the steps of how to create your own plugin.

0. Does your plugin already exist?

The first thing to do before writing a plugin is take a peek and see if the plugin you want to write does not already exist. Take a peek at the Plugins included with Chaskis first here.

Even if it doesn't appear as though a plugin you want exists, you could retrofit one of the default Plugins to do your desired task for you without writing any code. For example, if you want to write a bot that will always give a canned response when a user types a specific command, XmlBot would probably get the job done. Meanwhile, if you want to post something to the channel at an interval, you could use RssBot and post to an RSS feed you control to have the bot post for you.

1. Install ChaskisCore

The easiest way to get started on writing a plugin is to create a Visual Studio or Mono Develop C# Class Library project. Then, use NuGet to add a reference to the ChaskisCore library. ChaskisCore contains all the handler (see below) classes and the IPlugin Interface.

Chaskis uses .NET 4.7.1. You should set your Class Library to use .NET Standard 2.0, so if we ever swap to .NET Core, it will continue to keep working.

2. Implement the IPlugin interface.

You must implement the IPlugin interface for one class in your Class Library project. You could implement the IPlugin interface on multiple classes if you wanted, but that means you have multiple plugins in one assembly, which may not be desired.

The IPlugin interface has a few things you need to implement.

  • SourceCodeLocation

The location of your source code (e.g. GitHub). When someone asks the bot !botname source yourplugin, it will return this string.

  • Version

A string representation of the version of your plugin. When someone asks the bot !botname version yourplugin, it will return this string.

  • About

A brief description of your plugin. When someone asks the bot !botname about yourplugin, it will return this string.

  • Init()

Init initializes the plugin. The Chaskis plugin loader will pass in a class called PluginInitor The first being the absolute path to the .dll it loaded (including the filename of the dll). This class contains classes and information your plugin may which to know. Take a peek at this class's comments to see what each property does.

If something bad happens during Init(), such as a config file missing or an external library missing, throw an exception. If Chaskis is running as a service, it will terminate. If running as a command-line program and the user has failOnBadPlugin set to yes, the program will terminate gracefully.

  • HandleHelp

When a users asks the bot !botname help yourplugin arg1 arg2, this function is called. You'll get a way to write to the IRC channel via the passed in IIrcWriter, and get information about the IRC message the triggered the bot. Arguments are also passed in. ```!botname help yourplugin```` are NOT passed into the args array, but everything else after (separated by whitespace) is.

  • GetHandler

Returns all the event handlers your plugin has. This gets returned as an IList. Your Init function needs to populate a list of event handlers that this function should return. See below for more information about handlers.

  • Add the ChaskisPlugin Attribute.

The last thing you'll need to do is add the ChaskisPlugin attribute to your class. Any class in your assembly with this attribute will be loaded as a plugin when your assembly is loaded. The single argument to pass in is your plugin name. This is what will show up in the IRC channel. The name is stripped of whitespace and lowercased.

[ChaskisPlugin( "myplugin" )]
public class MyPlugin : IPlugin
{
    // Your implementation.
}

3. Write handlers

For each IRC message the bot hears, it could handle that message in multiple ways as defined by you, the programmer! You don't need to worry about implementing handler classes, those already exist. You simply need to create them and add them to your plugin's internal list of them.

  • Join Handlers

Fired whenever a user (not counting your bot) joins the channel the bot is in. To create a join handler, create a void function that takes in an IIrcWriter (so you can write to the IRC channel) and an IrcResponse object (so you can get the user who joined, and the channel they joined to). See below for an example .

/// <summary>
/// Ran when someone joins the channel.  Prints the user has   joined the channel.
/// </summary>
/// <param name="writer">The means to write to an IRC   channel.</param>
/// <param name="response">A response from the server.</param>
private static void JoinMessage( IIrcWriter writer, IrcResponse   response )
{
    writer.SendCommand( response.RemoteUser + " has joined " +   response.Channel );
}
  
// Some other function somewhere else
JoinHandlerConfig config = new JoinHandler
{
    JoinAction = JoinMessage
};
JoinHandler handler = new JoinHandler( config );
  
// at some point, add handler to your internal handler list   that is returned when GetHandlers() is called.
  • Part Handlers

Fired whenever a user (not counting your bot) parts the channel the bot is in. Creating a part handler is exactly like the join handler.

/// <summary>
/// Ran when someone parts the channel.  Tells the channel that they left.
/// </summary>
/// <param name="writer">The means to write to an IRC channel.</param>
/// <param name="response">A response from the server.</param>
private static void PartMessage( IIrcWriter writer, IrcResponse response )
{
    writer.SendCommand(
       response.RemoteUser + " has left " + response.Channel
    );
}
  
// Some other function somewhere else
PartHandlerConfig config = new PartHandlerConfig
{
    PartAction = PartMessage
};
PartHandler handler = new PartHandler( config );
  
// at some point, add handler to your internal handler list   that is returned when GetHandlers() is called.

A good example of Join and Part handlers being used in a plugin is the WelcomeBot Plugin.

  • Message Handler.

This fires whenever someone sends a PRIVMSG to either your bot directly, or the channel the bot is in. This one is a tad more complicated to set up than the join or part handlers, but is still straight forward.

When constructing a message handler, you will need to pass in a MessageHandlerConfig object, which has a few properties you need to specify. The first is a regex that your bot will look for that will fire the passed-in action. For example, if you want your bot to respond with "Hello, World" when a user types "!hello", pass in "!hello" to this function. The next property is the action that will fire if the regex matches. See below for the action that will send "Hello, World" to the channel.

/// <summary>
/// Ran when someone types !hello in the channel.
/// The bot only sends "Hello, World!" to the channel.
/// </summary>
/// <param name="writer">The means to write to an IRC   channel.</param>
/// <param name="response">A response from the server.</param>
private static void HelloMessage( IIrcWriter writer,   IrcResponse response )
{
    writer.SendCommand(
        "Hello, World!"
    );
}

The next property is a cooldown. This is defaulted to zero. This is the number of seconds the bot will wait before firing the action since it received a message that previously fired the action. For example, if this is set to 5, if user A sends "!hello" the bot will respond with "Hello, World!". However, 2 seconds later, user A sends it again, the cool down hasn't passed yet, so the bot ignores the line.

The next property is ResponseOptions. Set this to RespondOnlyToPMs if you want the bot to only respond to Private Messages, not messages that appear in channels. RespondOnlyToChannel should be turned on if you want the bot to only respond to messages that appear in the channel it is in. RespondToBoth responds to both private messages and messages in the channel.

The next property is RegexOptions. These are used with LineRegex while determining if a message from the server should be fired or not.

The last property is whether or not the bot will respond to itself. This is typically set to false. If set to true, you run the risk of the bot ending up in an endless loop since it keeps responding to itself. Set to true carefully.

Note, for all handlers you can also pass in a delegate instead of a function pointer.

  • All Handler

Fired for ALL received IRC messages, regardless of structure. Useful when looking for raw IRC commands.

4. Where to install files

When you install the plugin, you'll want to create a folder for your plugin in the Chaskis plugin directory. On Windows, this is located in C:\Program Files\Chaskis\Plugins\YourPlugin\ while on Linux, this is located in /usr/lib/Chaskis/Plugins/YourPlugin/. Your Plugin Assembly and any DLLS it depends on should go in the YourPlugin folder. Do NOT copy ChaskisCore.dll into this folder, as it lives elsewhere.

If your bot requires configuration, you should put sample, default configuration files in C:\Program Files\Chaskis\SampleConfig\Plugins\YourPlugin\ on Windows, or /usr/lib/Chaskis/SampleConfig/Plugins/YourPlugin/ on Linux. What this does is when a user does a Chaskis Bootstrap to a directory to turn it into their Chaskis Root, any file that lives in the SampleConfig folder that starts with "Sample" gets copied over the Chaskis Root directory. "Sample" is removed from the start of the file name. For example, if your sample default configuration was called "SampleMyConfig.xml", it will be copied over as "MyConfig.xml". The user of your plugin will then edit MyConfig.xml in the Chaskis Root directory to configure the Plugin for their use.

5. Test

You should probably test to make sure your plugin actually works. To do this, install Chaskis by either compiling it from source or grabbing a binary from here.

Chaskis can be run as a service, but a service is annoying to get Console.out or Console.Error. Therefore, we include a handy command-line version of Chaskis.exe for debugging purposes. This is located in C:\Program Files\Chaskis\bin\Chaskis.exe on Windows or /usr/bin/chaskis on Linux. If you do not wish to load the default Chaskis Root located in AppData, you can change those over the command line. You can also set if you want to abort the process if your plugin fails to load, or try to continue.

Chaskis IRC Bot Help:
--help, -h, /?    --------  Prints this message and exits.
--version         --------  Prints this message and exits.
--chaskisroot=xxx  -------- The chaskis root, where to find the chaskis config files.
                            Default is in AppData.
                            If --bootstrap is passed in, location of where to bootstrap.
--bootstrap --------------  Puts default configuration in this area.
                            Default is in AppData if --chaskisroot is not specified.
--failOnBadPlugin=yes|no -  Whether or not to fail if a plugin load fails.
                            Defaulted to no.

You may want to Bootstrap a Chaskis Root somewhere convenient by running Chaskis.exe --bootstrap --chaskisroot=yourPath. If you do not specify --chaskisroot with the --bootstrap argument, it will end up in C:\Users\You\AppData\Chaskis on Windows or /home/you/.config/Chaskis on Linux.

Open IrcConfig.xml in your newly bootstrapped Chaskis Root and configure the IRC settings. Instructions are inside the file. Then, open PluginConfig.xml. Add a new assembly tag, and set the path attribute to the absolute path to your plugin dll. While developing your plugin, it might make sense to point this to the outputted .dll when you compile.

Now, run Chaskis.exe without the --bootstrap command to have the bot attempt to join the IRC channel with the information you provided it in IrcConfig.xml. If your Chaskis Root is not in the default location, you'll want to point to that with the --chaskisroot parameter.

Open up your favorite IRC client and see if your bot joins. Test it out by throwing commands at it and see if it responds correctly. If its successful, congratulations! You successfully wrote a Chaskis plugin!