/
bot.smartirc.inc
162 lines (143 loc) · 7.95 KB
/
bot.smartirc.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?php
/**
* @file
* Classes and functions that wrap Drupal up for Net_SmartIRC.
*/
/**
* Start the Net_SmartIRC bot.
*/
function bot_smartirc_start() {
require_once 'Net/SmartIRC.php';
require_once 'Net/SmartIRC/defines.php';
variable_set('bot_status', BOT_STATUS_CONNECTING);
if (db_driver() === 'mysql') {
// prevent timeouts on slow channels.
db_query('SET SESSION wait_timeout = 86400');
}
// initialize the bot with some sane defaults.
global $irc; // allow it to be slurped by Drupal modules if need be.
$irc = new Net_SmartIRC(); // MmmmmmM. The IRC object itself. Magick happens here.
$irc->nreplycodes = $SMARTIRC_nreplycodes; // unreliably set from $_GLOBALS, so force it here.
$irc->setDebug( variable_get('bot_debugging', 0) ? SMARTIRC_DEBUG_ALL : SMARTIRC_DEBUG_NONE );
// the (boolean) here is required, as Net_SmartIRC doesn't respect a FAPI checkbox value of 1, only TRUE.
$irc->setAutoReconnect((boolean) $config->get()('bot_auto_reconnect', 1)); // reconnect to the server if disconnected.
$irc->setAutoRetry((boolean) $config->get()('bot_auto_retry', 1)); // retry if a server connection fails.
$irc->setUseSockets((boolean) $config->get()('bot_real_sockets', 1)); // socket_connect or fsockopen?
$irc->setChannelSyncing(TRUE); // keep a list of joined users per channel.
// send every message type the library supports to our wrapper class.
// we can automate the creation of these actionhandlers, but not the
// class methods below (only PHP 5 supports default methods easily).
$irc_message_types = array(
'UNKNOWN', 'CHANNEL', 'QUERY', 'CTCP', 'NOTICE', 'WHO',
'JOIN', 'INVITE', 'ACTION', 'TOPICCHANGE', 'NICKCHANGE', 'KICK',
'QUIT', 'LOGIN', 'INFO', 'LIST', 'NAME', 'MOTD',
'MODECHANGE', 'PART', 'ERROR', 'BANLIST', 'TOPIC', 'NONRELEVANT',
'WHOIS', 'WHOWAS', 'USERMODE', 'CHANNELMODE', 'CTCP_REQUEST', 'CTCP_REPLY',
);
foreach ($irc_message_types as $irc_message_type) {
$class = 'bot_irc_msg_' . drupal_strtolower($irc_message_type);
$irc->registerActionhandler(constant('SMARTIRC_TYPE_' . $irc_message_type), '.*', new $class(), 'invoke');
}
// set up a timers similar to Drupal's hook_cron(), multiple types. I would have
// liked to just pass a parameter to a single function, but SmartIRC can't do that.
$irc->registerTimehandler(300000, new bot_irc_bot_cron(), 'invoke'); // 5 minutes.
$irc->registerTimehandler(60000, new bot_irc_bot_cron_faster(), 'invoke'); // 1 minute.
$irc->registerTimehandler(15000, new bot_irc_bot_cron_fastest(), 'invoke'); // 15 seconds.
// connect and begin listening.
$irc->connect($config->get()('bot_server', 'irc.freenode.net'), variable_get('bot_server_port', 6667));
$irc->login($config->get()('bot_nickname', 'bot_module'), $config->get()('bot_nickname', 'bot_module') . ' :http://drupal.org/project/bot', 8, $config->get()('bot_nickname', 'bot_module'), $config->get()('bot_password'));
variable_set('bot_status', BOT_STATUS_CONNECTED);
// channel joining has moved to bot_irc_bot_cron_fastest().
// read that function for the rationale, and what we gain from it.
$irc->listen(); // go into the forever loop - no code after this is run.
$irc->disconnect(); // if we stop listening, disconnect properly.
// see bot_irc_bot_cron_fastest() for another disconnect() call.
variable_set('bot_status', BOT_STATUS_DISCONNECTED);
}
/**
* A Drupal wrapper for the various Net_SmartIRC handlers.
*/
class bot_smartirc_wrapper {
/**
* Handle all Net_SmartIRC callbacks.
*
* @param &$irc
* The Net_SmartIRC $irc object.
* @param &$data
* The Net_SmartIRC data object for this message.
*/
function invoke(&$irc, &$data = NULL) {
if (isset($data)) {
// Net_SmartIRC passes data through in the encoding received from the
// IRC server. The IRC server, in turn, passes it through as received
// from the remote client. If the remote client is using a single byte
// encoding (ISO-8559-1 aka Latin-1), this will result in mangled
// characters or may raise errors for 8-bit characters. Under Drupal 7
// and PHP 5.3, this caused an "invalid multibyte sequence" warning
// from htmlspecialchars() and a PDOException that crashed the bot.
// (To reproduce: use XChat to connect to a server as latin1, but then
// send an 8-bit Latin-1 character like a Euro [€]). Since we cannot
// control the encoding of IRC messages like we can control the encoding
// of web form submissions, we need to force UTF-8 for all messages.
if (function_exists('mb_detect_encoding')) {
$encoding = mb_detect_encoding($data->message, 'UTF-8, ISO-8859-1');
// we convert every string, even those already labeled as UTF-8, as
// there were still related crashes when we were only doing non-UTF-8s.
// we also only convert over message related elements and leave the
// existing parses (nick, ident, host, type, channel, etc.) alone.
$data->message = mb_convert_encoding($data->message, 'UTF-8', $encoding);
$data->rawmessage = mb_convert_encoding($data->rawmessage, 'UTF-8', $encoding);
$data->messageex = explode(' ', $data->message);
$data->rawmessageex = explode(' ', $data->rawmessage);
}
}
// The name of the hook is a substring of the class name.
$hook = drupal_substr(get_class($this), drupal_strlen('bot_'));
// Give modules an opportunity to stop message processing.
foreach (module_implements('irc_access') as $module) {
$function = "{$module}_irc_access";
$result = $function($hook, $data);
// Stop handling this request if FALSE.
if (isset($result) && $result === FALSE) {
return;
}
}
// Proceed normally.
module_invoke_all($hook, $data);
}
}
// Bot cron sub-classes.
class bot_irc_bot_cron extends bot_smartirc_wrapper {}
class bot_irc_bot_cron_faster extends bot_smartirc_wrapper {}
class bot_irc_bot_cron_fastest extends bot_smartirc_wrapper {}
// IRC message sub-classes.
class bot_irc_msg_unknown extends bot_smartirc_wrapper {}
class bot_irc_msg_channel extends bot_smartirc_wrapper {}
class bot_irc_msg_query extends bot_smartirc_wrapper {}
class bot_irc_msg_ctcp extends bot_smartirc_wrapper {}
class bot_irc_msg_notice extends bot_smartirc_wrapper {}
class bot_irc_msg_who extends bot_smartirc_wrapper {}
class bot_irc_msg_join extends bot_smartirc_wrapper {}
class bot_irc_msg_invite extends bot_smartirc_wrapper {}
class bot_irc_msg_action extends bot_smartirc_wrapper {}
class bot_irc_msg_topicchange extends bot_smartirc_wrapper {}
class bot_irc_msg_nickchange extends bot_smartirc_wrapper {}
class bot_irc_msg_kick extends bot_smartirc_wrapper {}
class bot_irc_msg_quit extends bot_smartirc_wrapper {}
class bot_irc_msg_login extends bot_smartirc_wrapper {}
class bot_irc_msg_info extends bot_smartirc_wrapper {}
class bot_irc_msg_list extends bot_smartirc_wrapper {}
class bot_irc_msg_name extends bot_smartirc_wrapper {}
class bot_irc_msg_motd extends bot_smartirc_wrapper {}
class bot_irc_msg_modechange extends bot_smartirc_wrapper {}
class bot_irc_msg_part extends bot_smartirc_wrapper {}
class bot_irc_msg_error extends bot_smartirc_wrapper {}
class bot_irc_msg_banlist extends bot_smartirc_wrapper {}
class bot_irc_msg_topic extends bot_smartirc_wrapper {}
class bot_irc_msg_nonrelevant extends bot_smartirc_wrapper {}
class bot_irc_msg_whois extends bot_smartirc_wrapper {}
class bot_irc_msg_whowas extends bot_smartirc_wrapper {}
class bot_irc_msg_usermode extends bot_smartirc_wrapper {}
class bot_irc_msg_channelmode extends bot_smartirc_wrapper {}
class bot_irc_msg_ctcp_request extends bot_smartirc_wrapper {}
class bot_irc_msg_ctcp_reply extends bot_smartirc_wrapper {}