PSchwisow/phergie-irc-plugin-react-altnick

View on GitHub
src/Plugin.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * Phergie plugin for switching to alternate nicks when primary is not available (https://github.com/PSchwisow/phergie-irc-plugin-react-altnick)
 *
 * @link https://github.com/PSchwisow/phergie-irc-plugin-react-altnick for the canonical source repository
 * @copyright Copyright (c) 2015 Patrick Schwisow (https://github.com/PSchwisow/phergie-irc-plugin-react-altnick)
 * @license http://phergie.org/license Simplified BSD License
 * @package PSchwisow\Phergie\Plugin\AltNick
 */

namespace PSchwisow\Phergie\Plugin\AltNick;

use ArrayIterator;
use Phergie\Irc\Bot\React\AbstractPlugin;
use Phergie\Irc\Bot\React\EventQueueInterface as Queue;
use Phergie\Irc\ConnectionInterface;
use Phergie\Irc\Event\EventInterface as Event;
use Phergie\Irc\Event\UserEventInterface as UserEvent;
use SplObjectStorage;

/**
 * Plugin class.
 *
 * @category PSchwisow
 * @package PSchwisow\Phergie\Plugin\AltNick
 */
class Plugin extends AbstractPlugin
{
    /**
     * Array of alternate nicks to try
     *
     * @var array
     */
    private $nicks;

    /**
     * Whether or not to recover the primary nick if it disconnects
     *
     * @var bool
     */
    private $recovery = false;

    /**
     * Primary nick to recover
     *
     * @var string|null
     */
    private $primaryNick;

    /**
     * One iterator per connection
     *
     * @var SplObjectStorage (collection of ArrayIterator)
     */
    private $iterators;

    /**
     * Accepts plugin configuration.
     *
     * Supported keys:
     *
     * nicks - an array of alternate nicks (at least one is required)
     *
     * recovery - if true, will change back to the primary nick if it disconnects
     *            (optional, default false)
     *
     * @param array $config
     * @throws \InvalidArgumentException
     */
    public function __construct(array $config = [])
    {
        if (!array_key_exists('nicks', $config)) {
            throw new \InvalidArgumentException("Missing required configuration key 'nicks'");
        }
        $this->setNicks($config['nicks']);
        if (!empty($config['recovery'])) {
            $this->recovery = true;
        }
    }

    /**
     * Set adapter
     *
     * @param array $nicks
     * @return void
     * @throws \InvalidArgumentException
     */
    public function setNicks($nicks)
    {
        if (!is_array($nicks)) {
            throw new \InvalidArgumentException('setNicks method expects an array');
        }

        $special = preg_quote('[]\`_^{|}');
        $nicks = array_filter(
            array_map(
                function ($nick) use ($special) {
                    if (is_string($nick)) {
                        $nick = trim($nick);
                        return (preg_match("/^[a-z$special][a-z0-9$special-]*$/i", $nick) ? $nick : null);
                    }
                    return null;
                },
                $nicks
            )
        );

        if (empty($nicks)) {
            throw new \InvalidArgumentException('setNicks method did not receive any valid nicks');
        }

        $this->nicks = $nicks;
    }

    /**
     * Subscribe to events
     *
     * @return array
     */
    public function getSubscribedEvents()
    {
        return [
            'irc.received.err_nicknameinuse' => 'handleEvent',
            'irc.received.quit' => 'handleQuit',
        ];
    }

    /**
     * Nick is in use, pick another.
     *
     * @param \Phergie\Irc\Event\EventInterface $event
     * @param \Phergie\Irc\Bot\React\EventQueueInterface $queue
     */
    public function handleEvent(Event $event, Queue $queue)
    {
        $iterator = $this->getIterator($event->getConnection());

        if (!$iterator->valid()) {
            $queue->ircQuit('All specified alternate nicks are in use');
            return;
        }

        if ($this->recovery && $this->primaryNick === null) {
            $params = $event->getParams();
            $primaryNick = $params[1];
            $this->logger->debug("[AltNick] Saving '$primaryNick' as primary nick");
            $this->primaryNick = $primaryNick;
        }

        $nick = $iterator->current();
        $iterator->next();

        $this->logger->debug("[AltNick] Switching nick to '$nick'");
        $queue->ircNick($nick);
    }

    /**
     * Handle primary nick recovery.
     *
     * @param \Phergie\Irc\Event\UserEventInterface $event
     * @param \Phergie\Irc\Bot\React\EventQueueInterface $queue
     */
    public function handleQuit(UserEvent $event, Queue $queue)
    {
        $nick = $event->getNick();
        if ($this->primaryNick !== null && $nick == $this->primaryNick) {
            $this->logger->debug("[AltNick] '$nick' disconnected, switching to primary nick");
            $queue->ircNick($this->primaryNick);
            $this->primaryNick = null;
        }
    }

    protected function getIterator(ConnectionInterface $connection)
    {
        if ($this->iterators === null) {
            $this->iterators = new SplObjectStorage();
        }

        if (!$this->iterators->offsetExists($connection)) {
            $this->iterators[$connection] = new ArrayIterator($this->nicks);
        }

        return $this->iterators[$connection];
    }
}