root/trunk/Phergie/Driver/Streams.php @ 60

Revision 60, 12.7 KB (checked in by tobias382, 5 years ago)

Fixes #1, #8, #14, #10, #13
* Fixed some parameter parsing issues in the streams driver and Command plugin
* Fixed an issue in the streams driver where quitting would not cause the

connection to be terminated on the client side

* Added automated directory creation to the event handler class
* Added isInChannel() and TYPE_KICK to request event class
* Modified the bootstrap file to use a full path when referencing the Plugin

directory and to include plugin loading debugging messages

* Fixed a bug in the bootstrap file where it would not produce an error when

required configuration settings existed, but were empty

* Cleared default values and unused settings out of the config file
* Made modifications to the fromAdmin method in the AdminCommand? plugin to

optimize performance

* Fixed a bug in the parsing logic in the Acronym plugin and added a check for

instances where the per-IP bandwidth limit has been executed

* Added a check for cases where a server has no MOTD to the Autojoin plugin
* Modified the Drink plugin to filter coffee drinks when scraping data for the

coke table and to remove the profanity filter array from memory once the
init method is done using it

* Added an avogadro alias for the mole fixed karma and added support for

configurable fixed karma for the bot

* Modified the Logging plugin to extend the Command plugin and to use PDO in

place of the standalone SQLite driver

* Modified the Php plugin to use full URLs to function pages rather than the

shorthand version, which does not always resolve to the expected URL

* Renamed the Say plugin to Puppet and added support emulation of CTCP ACTION

commands

* Modified the Url plugin to use the MIME type of the resource in place of

the title in cases where it is not HTML-based

* Fixed an issue with the parsing logic in the onMode handler of the Users

plugin

* Uncommented the onPrivmsg handler and modified it to use the new debug

configuration setting

* Added alternate nick support to the Nickserv plugin
* Removed the CONTRIBUTE file, as its fairly out of date

Line 
1<?php
2
3/**
4* @see Phergie_Driver_Abstract
5*/
6require_once 'Phergie/Driver/Abstract.php';
7
8/**
9* Provides a socket-based client driver.
10*/
11class Phergie_Driver_Streams extends Phergie_Driver_Abstract
12{
13    /**
14    * Names of commands that can be issued by callbacks ordered by
15    * priority of execution
16    *
17    * @var array
18    */
19    protected $priority = array(
20        'pass',
21        'user',
22        'pong',
23        'notice',
24        'join',
25        'list',
26        'names',
27        'version',
28        'stats',
29        'links',
30        'time',
31        'trace',
32        'admin',
33        'info',
34        'who',
35        'whois',
36        'whowas',
37        'mode',
38        'privmsg',
39        'nick',
40        'topic',
41        'invite',
42        'kill',
43        'part'
44    );
45
46    /**
47    * Socket handler
48    *
49    * @var resource
50    */
51    protected $socket;
52
53    /**
54    * List of event handler instances
55    *
56    * @var array
57    */
58    protected $handlers;
59
60    /**
61    * Flag to indicate whether or not the callbacks are currently being
62    * queued for execution rather than executed outright
63    *
64    * @var bool
65    */
66    protected $queueing;
67
68    /**
69    * Associative array mapping command names to queued sets of arguments
70    * from commands queued by callbacks
71    *
72    * @var array
73    */
74    protected $queue;
75
76    /**
77    * Constructor to initialize instance properties.
78    */
79    public function __construct()
80    {
81        $this->handlers = array();
82        $this->queueing = false;
83        $this->queue = array();
84    }
85
86    /**
87    * Adds a set of callbacks for events received from the server.
88    *
89    * @param Phergie_Event_Handler $handler
90    */
91    public function addEventHandler(Phergie_Event_Handler $handler)
92    {
93        $handler->init();
94
95        $this->handlers[] = $handler;
96    }
97
98    /**
99    * Executes a continuous loop in which the client listens for events from
100    * the server and processes them until the connection is terminated.
101    *
102    * @return void
103    */
104    public function run()
105    {
106        $server = $this->getIni('server');
107        $port = $this->getIni('port');
108        if (!$port) {
109            $port = 6667;
110        }
111
112        $this->socket = @stream_socket_client(
113            'tcp://' . $server . ':' . $port,
114            $errno,
115            $errstr
116        );
117
118        if (!$this->socket) {
119            $this->debug(rtrim('Unable to connect to server: socket error ' . $errno . ' ' . $errstr));
120            return;
121        }
122
123        $password = $this->getIni('password');
124        if ($password) {
125            $this->send('PASS', array($password));
126        }
127
128        $this->send('USER', array($this->getIni('username'), $server, $server, $this->getIni('realname')));
129        $this->doNick($this->getIni('nick'));
130
131        unset($server, $port, $password, $errno, $errstr);
132
133        while(true) {
134            $this->queue = array();
135            $this->queueing = true;
136
137            $buffer = '';
138            while (empty ($buffer)) {
139                $buffer = fgets($this->socket, 512);
140            }
141            $buffer = rtrim($buffer);
142            $this->debug('server -> ' . $buffer);
143
144            if ($buffer[0] == ':') {
145                list ($prefix, $cmd, $args) = explode(' ', substr($buffer, 1), 3);
146                $this->parseHostmask($prefix, $nick, $user, $host);
147            } else {
148                list ($cmd, $args) = explode(' ', $buffer, 2);
149            }
150
151            $cmd = strtolower($cmd);
152
153            switch ($cmd) {
154                case 'names':
155                case 'nick':
156                case 'quit':
157                case 'ping':
158                case 'join':
159                    $args = array(ltrim($args, ':'));
160                    break;
161                case 'notice':
162                    $args = explode(' :', $args);
163                    break;
164                case 'oper':
165                case 'topic':
166                case 'mode':
167                    $args = preg_split('/ :?/', $args);
168                    break;
169                case 'part':
170                    $args = preg_split('/ :?/', $args, 2);
171                    break;
172                case 'kick':
173                    $args = preg_split('/ :?/', $args, 3);
174                    break;
175                case 'privmsg':
176                    if ($args[0] == chr(1)) {
177                        /**
178                        * This will likely need to be changed if support is added
179                        * for CTCP commands other than ACTION.
180                        */
181                        list ($target, $args) = explode(' ', $args, 2);
182                        $args = explode(' ', substr($args, 1, -1), 2);
183                        $cmd = strtolower(array_shift($args));
184                        array_unshift($args, $target);
185                    } else {
186                        $args = explode(' :', $args);
187                    }
188                    break;
189            }
190
191            if (preg_match('/^[0-9]+$/', $cmd) > 0) {
192                $event = new Phergie_Event_Response();
193                $event->setCode($cmd);
194                $event->setDescription($args);
195            } else {
196                $event = new Phergie_Event_Request();
197                $event->setType($cmd);
198                $event->setArguments($args);
199                if (isset ($user)) {
200                    $event->setHost($host);
201                    $event->setUsername($user);
202                    $event->setNick($nick);
203                }
204            }
205
206            foreach ($this->handlers as $handler) {
207                $handler->setEvent($event);
208                if ($event instanceof Phergie_Event_Response) {
209                    $handler->onResponse();
210                } else {
211                    call_user_func(array($handler, 'on' . ucfirst($event->getType())));
212                }
213            }
214            $this->queueing = false;
215            foreach ($this->priority as $command) {
216                if (isset($this->queue[$command])) {
217                    foreach ($this->queue[$command] as $arguments) {
218                        $this->send($command, $arguments);
219                    }
220                }
221            }
222            if (isset ($this->queue['quit'])) {
223                if (count($this->queue['quit'][0]) > 0) {
224                    $reason = $this->queue['quit'][0][0];
225                } else {
226                    $reason = null;
227                }
228                foreach ($this->handlers as $handler) {
229                    $handler->shutdown();
230                }
231                $this->doQuit($reason);
232                break;
233            }
234            unset($this->queue, $event, $command, $arguments);
235        }
236
237        fclose($this->socket);
238    }
239
240    /**
241    * Sends a client command with accompanying arguments to the server.
242    *
243    * @param string $command Command to send
244    * @param array $arguments Ordered array of arguments to include
245    *                         (optional)
246    */
247    public function send($command, array $arguments = array())
248    {
249        if ($this->queueing) {
250            if (!isset($this->queue[$command])) {
251                $this->queue[$command] = array();
252            }
253            $this->queue[$command][] = $arguments;
254        } else {
255            $buffer = strtoupper($command);
256            if (count($arguments) > 0) {
257                $end = count($arguments) - 1;
258                $arguments[$end] = ':' . $arguments[$end];
259                $buffer .= ' ' . implode(' ', $arguments);
260            }
261            fwrite($this->socket, $buffer . "\r\n");
262            $this->debug('client -> ' . $buffer);
263        }
264    }
265
266    /**
267    * Terminates the connection with the server.
268    *
269    * @param string $reason Reason for connection termination (optional)
270    */
271    public function doQuit($reason = null)
272    {
273        $this->send(Phergie_Event_Request::TYPE_QUIT, array($reason));
274    }
275
276    /**
277    * Joins a channel.
278    *
279    * @param string $channel Name of the channel to join
280    * @param string $keys Channel key if needed (optional)
281    */
282    public function doJoin($channel, $key = null)
283    {
284        $arguments = array($channel);
285
286        if ($key !== null) {
287            $arguments[] = $key;
288        }
289
290        $this->send(Phergie_Event_Request::TYPE_JOIN, $arguments);
291    }
292
293    /**
294    * Leaves a channel.
295    *
296    * @param string $channel Name of the channel to leave
297    */
298    public function doPart($channel)
299    {
300        $this->send(Phergie_Event_Request::TYPE_PART, array($channel));
301    }
302
303    /**
304    * Invites a user to an invite-only channel.
305    *
306    * @param string $nick Nick of the user to invite
307    * @param string $channel Name of the channel
308    */
309    public function doInvite($nick, $channel)
310    {
311        $this->send(Phergie_Event_Request::TYPE_INVITE, array($nick, $channel));
312    }
313
314    /**
315    * Obtains a list of nicks of usrs in currently joined channels.
316    *
317    * @param string $channels Comma-delimited list of one or more channels
318    */
319    public function doNames($channels)
320    {
321        $this->send(Phergie_Event_Request::TYPE_NAMES, array($channels));
322    }
323
324    /**
325    * Obtains a list of channel names and topics.
326    *
327    * @param string $channels Comma-delimited list of one or more channels
328    *                         to which the response should be restricted
329    *                         (optional)
330    */
331    public function doList($channels = null)
332    {
333        $arguments = array();
334
335        if ($channels !== null) {
336            $arguments[] = $channels;
337        }
338
339        $this->send(Phergie_Event_Request::TYPE_LIST, $arguments);
340    }
341
342    /**
343    * Retrieves or changes a channel topic.
344    *
345    * @param string $channel Name of the channel
346    * @param string $topic New topic to assign (optional)
347    */
348    public function doTopic($channel, $topic = null)
349    {
350        $arguments = array($channel);
351
352        if ($topic !== null) {
353            $arguments[] = $topic;
354        }
355
356        $this->send(Phergie_Event_Request::TYPE_TOPIC, $arguments);
357    }
358
359    /**
360    * Retrieves or changes a channel or user mode.
361    *
362    * @param string $target Channel name or user nick
363    * @param string $mode New mode to assign (optional)
364    */
365    public function doMode($target, $mode = null)
366    {
367        $arguments = array($target);
368
369        if ($mode !== null) {
370            $arguments[] = $mode;
371        }
372
373        $this->send(Phergie_Event_Request::TYPE_MODE, $arguments);
374    }
375
376    /**
377    * Changes the client nick.
378    *
379    * @param string $nick New nick to assign
380    */
381    public function doNick($nick)
382    {
383        $this->send(Phergie_Event_Request::TYPE_NICK, array($nick));
384    }
385
386    /**
387    * Retrieves information about a nick.
388    *
389    * @param string $nick
390    */
391    public function doWhois($nick)
392    {
393        $this->send(Phergie_Event_Requset::TYPE_WHOIS, array($nick));
394    }
395
396    /**
397    * Sends a message to a nick or channel.
398    *
399    * @param string $target Channel name or user nick
400    * @param string $text Text of the message to send
401    */
402    public function doPrivmsg($target, $text)
403    {
404        $this->send(Phergie_Event_Request::TYPE_PRIVMSG, array($target, $text));
405    }
406
407    /**
408    * Sends a notice to a nick or channel.
409    *
410    * @param string $target Channel name or user nick
411    * @param string $text Text of the notice to send
412    */
413    public function doNotice($target, $text)
414    {
415        $this->send(Phergie_Event_Request::TYPE_NOTICE, array($target, $text));
416    }
417
418    /**
419    * Kicks a user from a channel.
420    *
421    * @param string $nick Nick of the user
422    * @param string $channel Channel name
423    * @param string $reason Reason for the kick (optional)
424    */
425    public function doKick($nick, $channel, $reason = null)
426    {
427        $arguments = array($nick, $channel);
428
429        if ($reason !== null) {
430            $arguments[] = $reason;
431        }
432
433        $this->send(Phergie_Event_Request::TYPE_KICK, $arguments);
434    }
435
436    /**
437    * Responds to a server test of client responsiveness.
438    *
439    * @param string $daemon Daemon from which the original request originates
440    */
441    public function doPong($daemon)
442    {
443        $this->send(Phergie_Event_Request::TYPE_PONG, array($daemon));
444    }
445
446    /**
447    * Sends a CTCP command to a nick or channel.
448    *
449    * @param string $target Channel name or user nick
450    * @param string $command Command to send
451    * @param array $arguments Ordered array of arguments to include
452    *                         (optional)
453    */
454    protected function sendCtcp($target, $command, array $arguments = array())
455    {
456        $this->doPrivmsg($target, chr(1) . strtoupper($command) . ' ' . implode(' ', $arguments) . chr(1));
457    }
458
459    /**
460    * Sends a /me action to a nick or channel.
461    *
462    * @param string $target Channel name or user nick
463    * @param string $text Text of the action to perform
464    */
465    public function doAction($target, $text)
466    {
467        $this->sendCtcp($target, Phergie_Event_Request::TYPE_ACTION, array($text));
468    }
469}
Note: See TracBrowser for help on using the browser.