Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Development

Matthew Turland edited this page May 12, 2014 · 41 revisions

Developing Plugins

What You Need

The phergie-scaffold tool was created to automate the creation of files commonly included by plugin repositories. See its GitHub repository for more information on installing and using it.

Alternatively, create a directory containing a composer.json file with the contents shown below, then run composer install. This will install the bot and all its dependencies. See Composer documentation for further information.

{
  "minimum-stability": "dev",
  "require": {
    "phergie/phergie-irc-bot-react": "dev-master"
  }
}

Plugins Defined

Plugins are classes that implement the PluginInterface interface. This interface contains a single method, getSubscribedEvents(), which returns an associative array in which the keys are event names and the values are either valid callbacks or names of instance methods in the plugin class to handle those events (i.e. 'methodName' as a convenient shorthand for array($this, 'methodName')).

use Phergie\Irc\Event\EventInterface as Event;
use Phergie\Irc\Bot\React\EventQueueInterface as Queue;
use Phergie\Irc\Bot\React\PluginInterface;

class ExamplePlugin implements PluginInterface
{
    public function getSubscribedEvents()
    {
        return array(
            'irc.received.privmsg' => 'onPrivmsg'
        );
    }

    public function onPrivmsg(Event $event, Queue $queue)
    {
        // ...
    }
}

In the above example, 'irc.received.privmsg' is an event name and 'onPrivmsg' is the name of a method in the ExamplePlugin class to handle that event.

Supported Events

IRC Events

  • irc.received.each - occurs when any type of event is received from a server
  • irc.received.TYPE - occurs when an event of type TYPE (e.g. privmsg) is received from a server
  • irc.sending.all - occurs after an event has been processed by all plugins, at which point all synchronous responses have been enqueued
  • irc.sending.each - occurs before any type of event is sent to a server
  • irc.sending.TYPE - occurs before an event of type TYPE (e.g. privmsg) is sent to a server
  • irc.sent.each - occurs after any type of event is sent to a server
  • irc.sent.TYPE - occurs when an event of type TYPE (e.g. privmsg) is sent to a server

Valid values for TYPE are lowercase strings that include the following:

Note that the bot handles sending connection registration events, so there's no need for a plugin to do so, but plugins can still subscribe to these events.

IRC event handler methods typically accept two parameters:

One exception to this is the 'irc.sending.all' event, which takes only the $queue parameter.

Asynchronous and Timed Events

To have a plugin execute a method on a short (normally sub-second) interval, have it subscribe to the 'irc.tick' event. To have more control over the interval, your plugin must access the event loop of the bot's client. For this to happen, the plugin must implement LoopAwareInterface.

use Phergie\Irc\Client\React\LoopAwareInterface;
use Phergie\Irc\Bot\React\PluginInterface;
use React\EventLoop\LoopInterface;

class Plugin implements PluginInterface, LoopAwareInterface
{
    public function setLoop(LoopInterface $loop)
    {
        $loop->addPeriodicTimer(
            5,                            // Every 5 seconds...
            array($this, 'timedCallback') // ... execute this callback
        );
    }

    public function timedCallback()
    {
        // ...
    }
}

Custom Events

In addition to the core supported events that plugins can send and receive, they can also communicate with each other by sending and receiving custom events. To do this, they must implement EventEmitterAwareInterface. Though this is relatively trivial to do, as the interface only contains a single setEventEmitter() method, a shortcut to doing so is to extend AbstractPlugin, which provides an implementation of the interface.

Once obtained via setEventEmitter(), the event emitter object (which implements EventEmitterInterface) has an emit() method that can be used to emit an event that any plugins subscribed to it will receive.

$eventEmitter->emit('namespace.event.subevent', $parameters);

Event names are specified as strings. They are conventionally namespaced to avoid naming collisions with other plugins, with name segments delimited using periods.

$parameters is an array of parameter values received by event handler methods of subscribed plugins.

Logging

Plugins can gain access to the same logger instance used by core logic by implementing LoggerAwareInterface. Though this is relatively trivial to do, as the interface only contains a single setLogger() method, a shortcut to doing so is to extend AbstractPlugin, which provides an implementation of the interface.

Once obtained via setLogger(), the logger object can be used to log whatever events may be relevant to monitoring or debugging the plugin. In particular, one noteworthy shortcoming of Phergie's use of event callbacks is that there's no way to accurately attribute events sent by plugins in log messages (for debugging purposes) that isn't extremely hacky. As such, logging a message when a plugin sends an event is an advisable practice.

Installation

Plugins are conventionally installed using composer. To support this, a composer.json file should be included with the plugin source code that provides information about the plugin and any dependencies it has on other plugins or libraries. See the composer.json files included with existing plugins for examples.

Customization

Dependencies

The bot is represented by the Bot class, which is used by the bot runner. In addition to the logger, this class supports replacing other dependencies via configuration.

  • 'client' - an object that implements ClientInterface and is used for low-level IRC client-server interactions and emission of custom events, e.g. Client
  • 'parser' - an object that implements ParserInterface and is used to parse data from streams of IRC interactions, e.g. Parser
  • 'converter' - an object that implements ParserConverterInterface and is used convert parsed IRC interaction data into event objects, e.g. ParserConverter
  • 'eventQueue' - an object that implements EventQueueInterface and is used to queue and send events to IRC servers, e.g. EventQueue

Here's an example configuration file that implements overrides of these dependencies:

return array(
  'connections' => array(
    // ...
  ),
  'plugins' => array(
    // ...
  ),
  'client' => new My\Client,
  'parser' => new My\Parser,
  'converter' => new My\Converter,
  'eventQueue' => new My\EventQueue
);

Plugin Processors

Plugins sometimes require some common form of dependency injection or other modification after they're loaded. This is handled by plugin processors, which can be set via the 'pluginProcessors' configuration key as an array of objects implementing PluginProcessorInterface. If no value is set, by default, the bot will use these plugin processors:

Core Development

Running Tests

To run the phergie-irc-bot-react unit test suite:

curl -s https://getcomposer.org/installer | php
php composer.phar install
cd tests
../vendor/bin/phpunit
Clone this wiki locally