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 Apr 22, 2014 · 41 revisions

Developing Plugins

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 names of instance methods in the plugin class to handle those events.

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

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

    public function onPrivmsg(EventInterface $event, EventQueueInterface $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 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:

Plugin Events

The bot does not provide any means by which plugins can directly access each other, short of injecting one into another via configuration. These events provide a more flexible means of detecting if a plugin is loaded and getting access to the corresponding object when one is.

  • plugin.each - occurs when any plugin is loaded
  • plugin.global - occurs when a plugin that is not connection-specific is loaded
  • plugin.connection - occurs when a plugin that is connection-specific is loaded

Each of these events receives the plugin object (that implements PluginInterface) that was loaded as its first parameter. The plugin.connection event receives the associated connection object (that implements ConnectionInterface) as its second parameter.

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.

Important: In order for core logic that supports connection-specific plugins to work, one of these values contained in $parameters must associate the emitted event with a connection. Supported values for this purpose include a connection object (i.e. one that implements ConnectionInterface) or an event object (i.e. one that implements EventInterface with an associated connection object set via that event object's setConnection() method).

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.

Tools

The phergie-scaffold tool was created to automate the creation of files commonly included by core plugin repositories. It was designed to be generic enough to support the creation of third-party plugins as well. See its GitHub repository for more information.

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 dependency injection or some other form of 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:

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