-
Notifications
You must be signed in to change notification settings - Fork 27
Development
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.
- 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:
-
IRC client commands, e.g.
'privmsg'
or'notice'
; -
IRC server reply names, e.g.
'err_nosuchnick'
or'rpl_listend'
; and -
CTCP client commands prefixed with the string
'ctcp.'
, e.g.'ctcp.action'
.
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:
-
$event
, an object that contains data about the event and implementsEventInterface
or subinterfaces of it such asUserEventInterface
,ServerEventInterface
, andCtcpEventInterface
; and -
$queue
, an object used to send events back to the server that sent the original event and implementsEventQueueInterface
, a subinterface ofGeneratorInterface
where most of its methods are found.
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.
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).
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.
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.
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.
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 implementsClientInterface
and is used for low-level IRC client-server interactions and emission of custom events, e.g.Client
-
'parser'
- an object that implementsParserInterface
and is used to parse data from streams of IRC interactions, e.g.Parser
-
'converter'
- an object that implementsParserConverterInterface
and is used convert parsed IRC interaction data into event objects, e.g.ParserConverter
-
'eventQueue'
- an object that implementsEventQueueInterface
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
);
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:
-
EventEmitterInjector
injects the plugin with the bot's client if the plugin implementsEventEmitterAwareInterface
. If so, the plugin can use the client to emit events to which other plugins can subscribe. -
LoggerInjector
injects the plugin with the same logger used by the bot if the plugin implementsLoggerAwareInterface
. -
LoopInjector
injects the plugin with the event loop used by the bot's client if the client implementsLoopAccessorInterface
and the plugin implementsLoopAwareInterface
. If so, the plugin can use the event loop to execute stream and timed operations.
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