-
Notifications
You must be signed in to change notification settings - Fork 0
/
IRCBot.class.php
235 lines (173 loc) · 5.54 KB
/
IRCBot.class.php
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<?php
class IRCBot {
// Internal vars
private static $instance;
public $memcache;
// Internal storage
private $data = array(); // bot config and variables
private $modules = array(); // modules and module methods
private $socket; // IRC connection
private function __construct($_CONFIG) {
$this->debug('IRCBot __construct running, loading vars and config');
// Overloading all config into class.
foreach($_CONFIG as $name => $value) {
$this->$name = $value;
}
// Connect to memcached server
$this->debug('Connecting to memcached server.');
$this->memcache = new Memcached('ircbot');
$this->memcache->addServer('127.0.0.1', 11211);
}
public static function instance($_CONFIG = array()) {
if(!self::$instance) {
self::$instance = new IRCBot($_CONFIG);
}
return self::$instance;
}
public function connect() {
$this->debug('Connecting to IRC server: '.$this->server.':'.$this->port);
// Init socket etc.
$this->socket = fsockopen($this->server, $this->port, $err_num, $err_msg, 30);
if(!$this->socket) {
$this->debug('Connection to server failed: '.$err_msg);
}
// Sending NICK/PASS/USER to server
$this->send_raw(sprintf('NICK %s', $this->nick));
$this->send_raw(sprintf('PASS %s', $this->nick));
$this->send_raw(sprintf('USER %s %s %s :%s', $this->ident, $this->ident, $this->server, $this->realname));
// .. and wait for ping
$this->debug('Wait for first PING to arrive.');
while($data = fgets($this->socket, 4096)) {
if(substr($data, 0, 4) == 'PING') {
$this->pingpong($data);
break;
}
}
// Wait and flush socket to be able to connect
fflush($this->socket);
sleep(10);
}
public function is_connected() {
if($this->socket) {
return true;
}
return false;
}
public function load_module($module, $args = array()) {
$this->debug('Loading module: '.$module.' with args: '.str_replace("\n", '', var_export($args, true)));
// Load module and add to $modules etc
if(is_file(MODPATH.'/'.$module.'.php')) {
include_once(MODPATH.'/'.$module.'.php');
}
if (class_exists($module)) {
$this->modules[$module] = new $module;
} else {
$this->debug('Couldn\'t find class for module: '.$module);
}
}
function send_raw($string) {
$this->debug('Sending '.$string.' to IRC server.');
// Send to server
if(!fwrite($this->socket, $string.PHP_EOL)) {
$this->debug('Couldn\'t write '.$string.' to connection, exiting...');
die();
}
}
// Special function to handle ping/pongs
function pingpong($data) {
// Got PING, sending PONG
$this->debug('Got '.$data);
// Send PONG
$ping = explode(' ', $data);
$this->send_raw('PONG '.trim($ping[1]));
}
// Shortcut to send PRIVMSG
function privmsg($who, $what) {
$this->send_raw(sprintf('PRIVMSG %s :%s', $who, $what));
}
// Shortcut to send NOTICES
function notice($who, $what) {
$this->send_raw(sprintf('NOTICE %s :%s', $who, $what));
}
// Shortcut to kick people
function kick($who, $from, $reason) {
$this->send_raw(sprintf('KICK %s %s :%s', $from, $who, $reason));
}
/**************************
** Main loop begins here! *
***************************/
public function loop() {
$this->debug('Waiting for commands in loop()');
// Join channels
$this->send_raw('JOIN '.$this->channels);
// Build a method tree using the loaded classes.
$tree = array();
foreach($this->modules as $module) {
$methods = get_class_methods($module);
foreach($methods as $method) {
// Ignore constructor
if($method == '__construct') continue;
$tree[$method][] = get_class($module);
}
}
// Ennnnnnndless loop!
while(true) {
while($data = fgets($this->socket, 4096)) {
// Remove : if it's the first char
$data = itrim($data);
// Special case for ping/pong
if(substr($data, 0, 4) == 'PING') {
$this->pingpong($data);
}
// Set som vars to reflect the input string
$junk = $action = '';
list($junk, $action) = explode(' ', $data);
// Check if the action exists (privmsg, notice etc)
if(isset($tree[$action])) {
// Loop through all classes in the the tree for that action
foreach($tree[$action] as $class) {
// Run the action in the module class
$this->modules[$class]->$action($data);
}
}
}
// Sleep for 100ms
usleep(100000);
}
}
/***************************
* Logging and debug methods
****************************/
public function debug($message) {
// Trim a bit...
$message = trim($message);
// Always log everything
$this->__log($message);
// If debug is turned on, echo message
if($this->debug) {
echo '['.date('Y-m-d H:i:s').'] '.$message.PHP_EOL;
}
}
private function __log($message) {
$log = '['.date('Y-m-d H:i:s').'] '.$message.PHP_EOL;
file_put_contents(BASEPATH.'/bot.log', $log, FILE_APPEND | LOCK_EX);
}
/**********************
* Magic methods below
**********************/
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}