trigger mirror
[m6w6/ext-ircclient] / bot / bot.php
1 <?php
2
3 namespace irc\client;
4
5 /* PHP-5.3 */
6 function origin($origin, $key) {
7 $origin = parse_origin($origin);
8 return $origin[$key];
9 }
10
11 class Robot extends Session
12 {
13 protected $config;
14 protected $connected = false;
15 protected $joined = array();
16 protected $work = array();
17
18 function __construct($config) {
19 $this->configure($config);
20 parent::__construct($this->config->nick, $this->config->user, $this->config->real);
21 }
22
23 function run($watch_stdin = false) {
24 printf("Connecting to %s...\n", $this->config->host);
25 $this->doConnect($this->config->ipv6, $this->config->host, $this->config->port ?: 6667);
26
27 if ($watch_stdin) {
28 for ( stream_set_blocking(STDIN, 0), $i = 0, $x = ["–","\\","|","/"];
29 false !== ($fds = @parent::run(array(STDIN), null, 1));
30 ++$i) {
31 if (!$this->isConnected()) {
32 printf(" %s \r", $x[$i%4]);
33 }
34 if ($fds[0]) {
35 switch ($command = fgets(STDIN)) {
36 case "quit\n":
37 $this->disconnect();
38 break 2;
39 case "reload\n":
40 $this->reload();
41 break;
42 case "update\n":
43 $this->update();
44 break;
45 default:
46 $this->doRaw($command);
47 }
48 } else {
49 $this->work();
50 }
51 }
52 } else {
53 while (false !== parent::run(null, null, 1)) {
54 $this->work();
55 }
56 }
57 printf("Bye!\n");
58 }
59
60 function isConnected() {
61 return $this->connected;
62 }
63
64 function disconnect() {
65 $this->connected = false;
66 parent::disconnect();
67 }
68
69 function configure($config) {
70 $this->config = (object) (parse_ini_file($config, true) + ["config" => $config]);
71 }
72
73 function reload() {
74 printf("Reloading config...\n");
75 $this->configure($this->config->config);
76 $this->join();
77 }
78
79 function update() {
80 foreach ($this->joined as $channel) {
81 $this->doNames($channel);
82 }
83 }
84
85 function eventName($event) {
86 static $map;
87
88 if (empty($map)) {
89 $rfl = new \ReflectionExtension("ircclient");
90 $map = array_map(function($v) {
91 return substr($v, 11);
92 }, array_flip(
93 $rfl->getConstants()
94 ));
95 }
96
97 return isset($map[$event]) ? $map[$event] : "UNKNOWN_$event";
98 }
99
100 function work() {
101 if (!empty($this->work)) {
102 printf("Checking work queue (%d)\n", $this->eventName($event), count($this->work));
103 list($cb_func, $cb_args) = array_shift($this->work);
104 call_user_func_array($cb_func, $cb_args);
105 }
106 }
107
108 function onError($origin, array $args) {
109 fprintf(STDERR, "ERROR: %s %s\n", $origin, implode(" ", $args));
110 }
111
112 function onNumeric($origin, $event, array $args) {
113 static $buf = array();
114
115 switch($event) {
116 case RPL_NAMREPLY:
117 if ((list(,, $channel, $users) = $args)) {
118 $buf["names"][$channel] = array_merge((array) $buf["names"][$channel], explode(" ", $users));
119 }
120 break;
121
122 case RPL_ENDOFNAMES:
123 if ((list(, $channel) = $args)) {
124 foreach ($buf["names"][$channel] as $user) {
125 if ($user{0} === "@") {
126 $user = substr($user, 1);
127 }
128 if ($user !== $this->config->nick) {
129 printf("Adding work: WHOIS %s\n", $user);
130 $this->work[] = [
131 [$this, "doWhois"],
132 [$user]
133 ];
134 }
135 }
136 }
137 $buf["names"][$channel] = array();
138 break;
139
140 case RPL_WHOISUSER:
141 if ((list(, $nick, $user, $host, , $real) = $args)) {
142 $this->op(substr($user, 1) ."@" . $host);
143 }
144 break;
145 }
146
147 fprintf(STDERR, "DEBUG: %s %s %s\n", $origin, $this->eventName($event), "<".implode("> <", $args).">");
148 }
149
150 function onConnect($origin, array $args) {
151 list($nick) = $args;
152 $this->connected = true;
153 printf("Connected to %s as %s\n", $origin, $nick);
154 $this->join();
155 }
156
157 function join() {
158 $nopart = array();
159 foreach (array_filter((array) $this->config, "is_array") as $channel => $config) {
160 $nopart[] = $channel;
161 printf("Joining %s...\n", $channel);
162 $this->doJoin($channel, strlen($config["pass"]) ? $config["pass"] : NULL);
163 }
164 foreach (array_diff($this->joined, $nopart) as $channel) {
165 printf("Leaving %s...\n", $channel);
166 $this->doPart($channel);
167 }
168 }
169
170 function op($channel, $origin) {
171 if (preg_match($this->config->{$channel}["oper"], $origin)) {
172 $nick = origin($origin, "nick");
173 printf("Set +o %s\n", $nick);
174 $this->doChannelMode($channel, "+o $nick");
175 }
176 }
177
178 function onPart($origin, array $args) {
179 $nick = origin($origin, "nick");
180
181 if ($nick == $this->config->nick && (list($channel) = $args)) {
182 printf("Left %s\n", $channel);
183 unset($this->joined[array_search($this->joined, $channel, true)]);
184 }
185 }
186
187 function onJoin($origin, array $args) {
188 list($channel) = $args;
189 $nick = origin($origin, "nick");
190
191 if ($nick === $this->config->nick) {
192 printf("Joined %s\n", $channel);
193 $this->joined[] = $channel;
194 } else {
195 printf("%s joined %s\n", $origin, $channel);
196 $this->op($channel, $origin);
197 }
198 }
199
200 function onMode($origin, array $args) {
201 if (count($args) >= 3) {
202 list($channel, $mode, $users) = $args;
203
204 if ($mode === "+o") {
205 printf("Got +o %s\n", $users);
206
207 if (preg_match(sprintf("/\b%s\b/", preg_quote($this->config->nick)), $users)) {
208 $this->doNames($channel);
209 }
210 }
211 }
212 }
213 }