ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/server/uk/org/iscream/cms/server/client/alerters/IRC__Alerter.java
Revision: 1.30.2.1
Committed: Mon Feb 4 00:14:13 2002 UTC (22 years, 3 months ago) by tdb
Branch: SERVER_PIRCBOT
Changes since 1.30: +253 -378 lines
Log Message:
Integration of PircBot into i-scream as a replacement for the internal IRC
code that was put together in a hurry. PircBot makes the code much neater,
and ensures that the IRC interactivity is nicely seperated from the main
i-scream code.

PircBot is maintained by Paul, an i-scream developer also, at
http://www.jibble.org.

Changes so far are mostly to move the existing features to the new
structure provided by PircBot. It is still very much in development, and
is very messy :)

File Contents

# Content
1 //---PACKAGE DECLARATION---
2 package uk.org.iscream.cms.server.client.alerters;
3
4 //---IMPORTS---
5 import uk.org.iscream.cms.server.client.*;
6 import uk.org.iscream.cms.server.core.*;
7 import uk.org.iscream.cms.server.util.*;
8 import uk.org.iscream.cms.server.componentmanager.*;
9 import java.io.*;
10 import java.net.*;
11 import java.util.*;
12 import java.text.DateFormat;
13 import org.jibble.pircbot.*;
14
15 /**
16 * This Alert sends an IRC message.
17 *
18 * Clean shutdown could be achieved by stopping the run() method in the
19 * IRCBot inner class.
20 *
21 * @author $Author: tdb $
22 * @version $Id: IRC__Alerter.java,v 1.30 2002/01/17 17:58:12 tdb Exp $
23 */
24 public class IRC__Alerter extends AlerterSkeleton {
25
26 //---FINAL ATTRIBUTES---
27
28 /**
29 * The current CVS revision of this class
30 */
31 public final String REVISION = "$Revision: 1.30 $";
32
33 /**
34 * A description of this alerter
35 */
36 public final String DESC = "Sends alerts on an IRC channel";
37
38 //---STATIC METHODS---
39
40 //---CONSTRUCTORS---
41
42 public IRC__Alerter() {
43 super();
44 // construct and initialise the bot
45 _ircbot = new IRCBot();
46 //_ircbot.setVerbose(true);
47 Thread ircThread = new Thread(_ircbot);
48 // set it's name and start it
49 ircThread.setName("client.IRC__Alerter$IRCBot");
50 ircThread.start();
51 // log our start time
52 _startTime = System.currentTimeMillis();
53 _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started");
54 }
55
56 //---PUBLIC METHODS---
57
58 /**
59 * Implements the abstract method from the skeleton class.
60 * This method will attempt to send an alert
61 * message over the IRC channel.
62 *
63 * @param alert the alert to send
64 */
65 public void sendAlert(Alert alert) {
66 // sort out the message
67 String alertType = Alert.alertLevels[alert.getLevel()];
68 String message;
69 try {
70 message = _cp.getProperty(_name, "Alerter.IRC.message");
71 } catch (PropertyNotFoundException e) {
72 message = NOT_CONFIGURED;
73 _logger.write(toString(), Logger.WARNING, "Alerter.IRC.message value unavailable using default of " + message);
74 }
75 message = processAlertMessage(message, alert);
76 // only send alerts if we're active
77 if(_active) {
78 // send the message
79 _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
80 _ircbot.sendMsg(message);
81 // count sent alerts
82 _alertCount++;
83 } else {
84 // don't send, but keep a count that we ignored it
85 _ignoredCount++;
86 }
87 // we'll always store the "last alert", regardless
88 // of whether we actually display it or not
89 _lastAlert = message;
90 _lastAlertTime = System.currentTimeMillis();
91 }
92
93 /**
94 * Overrides the {@link java.lang.Object#toString() Object.toString()}
95 * method to provide clean logging (every class should have this).
96 *
97 * This uses the uk.org.iscream.cms.server.util.NameFormat class
98 * to format the toString()
99 *
100 * @return the name of this class and its CVS revision
101 */
102 public String toString() {
103 return FormatName.getName(
104 _name,
105 getClass().getName(),
106 REVISION);
107 }
108
109 /**
110 * Return the String representation of what the alerter does
111 *
112 * @return the description
113 */
114 public String getDescription(){
115 return DESC;
116 }
117
118 //---PRIVATE METHODS---
119
120 //---ACCESSOR/MUTATOR METHODS---
121
122 /**
123 * Returns the "friendly" name of this class. This
124 * is simply an accessor for _name, required due to
125 * inheritance issues with extending AlerterSkeleton.
126 *
127 * @return the friendly name
128 */
129 protected String getFName() {
130 return _name;
131 }
132
133 //---ATTRIBUTES---
134
135 /**
136 * A reference to the IRCBot
137 */
138 private IRCBot _ircbot;
139
140 /**
141 * Are we "active"
142 */
143 private boolean _active = false;
144
145 /**
146 * The last alert that was sent
147 */
148 private String _lastAlert = "no alerts have been sent";
149
150 /**
151 * The time of the last alert
152 */
153 private long _lastAlertTime = -1;
154
155 /**
156 * Number of alerts sent
157 */
158 private int _alertCount = 0;
159
160 /**
161 * Number of alerts ignored when in "stopped" mode
162 */
163 private int _ignoredCount = 0;
164
165 /**
166 * Time of IRCBot startup
167 */
168 private long _startTime;
169
170 /**
171 * This holds a reference to the
172 * system logger that is being used.
173 */
174 protected Logger _logger = ReferenceManager.getInstance().getLogger();
175
176 /**
177 * This is the friendly identifier of the
178 * component this class is running in.
179 * eg, a Filter may be called "filter1",
180 * If this class does not have an owning
181 * component, a name from the configuration
182 * can be placed here. This name could also
183 * be changed to null for utility classes.
184 */
185 private String _name = "IRC";
186
187 //---STATIC ATTRIBUTES---
188
189 //---INNER CLASSES---
190
191 class IRCBot extends PircBot implements Runnable {
192
193 /**
194 * The default reconnect delay in seconds
195 */
196 public final int DEFAULT_RECONNECT_DELAY = 30;
197
198 public void run() {
199 while(true) {
200 try {
201 init();
202 break;
203 }
204 catch (IOException e) {
205 _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
206 reconnectSleep();
207 }
208 }
209 //System.out.println("falling out!");
210 }
211
212 public void init() throws IOException {
213 _logger.write(this.toString(), Logger.DEBUG, "Initialising IRCBot...");
214 ConfigurationProxy cp = ConfigurationProxy.getInstance();
215 String server;
216 int port;
217 try {
218 server = cp.getProperty(_name, "Alerter.IRC.IRCServer");
219 port = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.IRCPort"));
220 } catch (PropertyNotFoundException e) {
221 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
222 throw new IOException("Can't get irc server details due to configuration error");
223 } catch (NumberFormatException e) {
224 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
225 throw new IOException("Can't get irc server details due to malformed configuration");
226 }
227 String user, comment;
228 try {
229 user = cp.getProperty(_name, "Alerter.IRC.user");
230 comment = cp.getProperty(_name, "Alerter.IRC.comment");
231 } catch (PropertyNotFoundException e) {
232 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
233 throw new IOException("Can't get user details due to configuration error");
234 }
235 this.setLogin(user);
236 this.setVersion(comment);
237 this.setFinger(comment); // ?
238 String nickList;
239 try {
240 nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
241 } catch (PropertyNotFoundException e) {
242 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
243 throw new IOException("Can't get nickname due to configuration error");
244 }
245 StringTokenizer st = new StringTokenizer(nickList, ";");
246 boolean ok = false;
247 while(!ok && st.hasMoreTokens()) {
248 String nick = st.nextToken();
249 try {
250 _logger.write(this.toString(), Logger.DEBUG, "Trying nick: "+nick);
251 this.setName(nick);
252 this.connect(server, port);
253 // must be ok if we get here
254 _nickname = nick;
255 ok = true;
256 }
257 catch(IOException e) {
258 _logger.write(this.toString(), Logger.ERROR, "IO error when connecting to server: "+e);
259 throw new IOException("IO error when connecting to server");
260 }
261 catch(IrcException e) {
262 _logger.write(this.toString(), Logger.ERROR, "IRC error when connecting to server: "+e);
263 throw new IOException("IRC error when connecting to server");
264 }
265 catch(NickAlreadyInUseException e) {
266 _logger.write(this.toString(), Logger.ERROR, "Nickname "+nick+" is already in use: "+e);
267 // just carry on around while loop
268 }
269 }
270 if(!ok) {
271 // oh dear, we couldn't get on.
272 _logger.write(this.toString(), Logger.ERROR, "All nicknames already in use");
273 throw new IOException("All nicknames already in use");
274 }
275 try {
276 _channel = cp.getProperty(_name, "Alerter.IRC.channel");
277 this.joinChannel(_channel);
278 } catch (PropertyNotFoundException e) {
279 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
280 throw new IOException("Can't get channel name due to configuration error");
281 }
282
283 String startupNotice;
284 try {
285 startupNotice = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice");
286 sendNotice(_channel, startupNotice);
287 } catch (PropertyNotFoundException e) {
288 _logger.write(this.toString(), Logger.DEBUG, "Startup notice not defined, so not sending: "+e);
289 }
290
291 // we should set this when all is ready!
292 _active = true;
293 //System.out.println("leaving init");
294 }
295
296 public void sendMsg(String msg) {
297 sendMessage(_channel, msg);
298 }
299
300 public void onDisconnect() {
301 _active = false;
302 while(true) {
303 reconnectSleep();
304 try {
305 init();
306 break;
307 }
308 catch (IOException e) {
309 _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
310 }
311 }
312 //System.out.println("falling out of disconnect!");
313 }
314
315 public void onMessage(String channel, String sender, String login, String hostname, String message) {
316 if(isForMe(message)) {
317 String response = handleInput(message);
318 if(response != null) {
319 sendMessage(channel, response);
320 }
321 }
322 }
323
324 public void onPrivateMessage(String sender, String login, String hostname, String message) {
325 String response = handleInput(message);
326 if(response != null) {
327 sendMessage(sender, response);
328 }
329 }
330
331 public void onNickChange(String oldNick, String login, String hostname, String newNick) {
332 if(oldNick.equals(_nickname)) {
333 _nickname = newNick;
334 }
335 }
336
337 private String handleInput(String message) {
338 //return "yup, gotcha!";
339 ConfigurationProxy cp = ConfigurationProxy.getInstance();
340 // setup some String's
341 String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
342 String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
343 // get the command set
344 try {
345 stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
346 startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
347 timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand");
348 lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand");
349 joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand");
350 nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
351 versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand");
352 helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand");
353 statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand");
354 uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand");
355 } catch (PropertyNotFoundException e) {
356 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
357 // lets bail from handling this line...
358 // ...it's gonna be hard without a command set!
359 return null;
360 }
361
362 if(message.indexOf(stopCommand)!=-1) {
363 _active = false;
364 return "alerts have been stopped";
365 }
366 else if(message.indexOf(startCommand)!=-1) {
367 _active = true;
368 return "alerts have been activated";
369 }
370 // this needs to go here if it contains the same words as the lastAlertCommand
371 else if(message.indexOf(timeSinceLastAlertCommand)!=-1) {
372 if(_lastAlertTime != -1) {
373 long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
374 String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
375 return "I last sent an alert "+uptimeText+ " ago";
376 }
377 else {
378 return "I've never sent an alert!";
379 }
380 }
381 else if(message.indexOf(lastAlertCommand)!=-1) {
382 if(_lastAlertTime != -1) {
383 String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
384 return "last alert was at "+date+"; "+_lastAlert;
385 }
386 else {
387 return "I've never sent an alert!";
388 }
389
390 }
391 else if(message.indexOf(joinCommand)!=-1) {
392 String joinCmd = joinCommand;
393 String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
394 int endOfChan = newChan.indexOf(" ");
395 if(endOfChan == -1) {
396 endOfChan = newChan.length();
397 }
398 newChan = newChan.substring(0, endOfChan);
399 if(newChan.equals(_channel)) {
400 return "I'm already on "+newChan+"!";
401 } else {
402 partChannel(_channel);
403 joinChannel(newChan);
404 _channel = newChan;
405 return null; // ???
406 }
407 }
408 else if(message.indexOf(nickChangeCommand)!=-1) {
409 String nickChangeCmd = nickChangeCommand;
410 String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
411 int endOfNick = newNick.indexOf(" ");
412 if(endOfNick == -1) {
413 endOfNick = newNick.length();
414 }
415 newNick = newNick.substring(0, endOfNick);
416 changeNick(newNick);
417 // should we check this worked?
418 //_nickname = newNick;
419 return null; // ???
420 }
421 else if(message.indexOf(versionCommand)!=-1) {
422 return "I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot";
423 }
424 else if(message.indexOf(helpCommand)!=-1) {
425 return "this will return some help text soon";
426 /*
427 sendPrivMsg(getMsgSender(line), "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
428 sendPrivMsg(getMsgSender(line), "I understand the following commands;");
429 sendPrivMsg(getMsgSender(line), stopCommand);
430 sendPrivMsg(getMsgSender(line), startCommand);
431 sendPrivMsg(getMsgSender(line), lastAlertCommand);
432 sendPrivMsg(getMsgSender(line), joinCommand);
433 sendPrivMsg(getMsgSender(line), nickChangeCommand);
434 sendPrivMsg(getMsgSender(line), statCommand);
435 sendPrivMsg(getMsgSender(line), uptimeCommand);
436 sendPrivMsg(getMsgSender(line), timeSinceLastAlertCommand);
437 sendPrivMsg(getMsgSender(line), helpCommand);
438 */
439 }
440 else if(message.indexOf(statCommand)!=-1) {
441 return "I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!";
442 }
443 else if(message.indexOf(uptimeCommand)!=-1) {
444 long uptime = (System.currentTimeMillis() - _startTime) / 1000;
445 String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
446 return "I have been running for "+uptimeText;
447 }
448 else if(message.indexOf("ping")!=-1) {
449 return "pong";
450 }
451 else if(message.indexOf("do a jibble dance")!=-1) {
452 // little joke :)
453 return "jives to the funky beat shouting \"ii--screeeaaammm\"";
454 }
455
456 else {
457 String rejectMessage = NOT_CONFIGURED;
458 try {
459 rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
460 } catch(PropertyNotFoundException e) {
461 _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
462 }
463 return rejectMessage;
464 }
465 }
466
467 private boolean isForMe(String message) {
468 // change this!
469 String nick = _nickname.toLowerCase();
470 String msg = message.toLowerCase();
471 if(msg.startsWith(nick + ", ") ||
472 msg.startsWith(nick + ": ") ||
473 msg.startsWith(nick + " ")) {
474 return true;
475 }
476 else {
477 return false;
478 }
479 }
480
481 private void reconnectSleep() {
482 int delayTime = 0;
483 try {
484 delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
485 } catch (NumberFormatException e) {
486 delayTime = DEFAULT_RECONNECT_DELAY;
487 _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
488 } catch (PropertyNotFoundException e) {
489 delayTime = DEFAULT_RECONNECT_DELAY;
490 _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
491 }
492 _logger.write(this.toString(), Logger.ERROR, "Waiting "+delayTime+" seconds for reconnect...");
493 try {
494 Thread.sleep(delayTime * 1000);
495 } catch (InterruptedException e) {}
496 }
497
498 /**
499 * Overrides the {@link java.lang.Object#toString() Object.toString()}
500 * method to provide clean logging (every class should have this).
501 *
502 * This uses the uk.org.iscream.cms.server.util.NameFormat class
503 * to format the toString()
504 *
505 * @return the name of this class and its CVS revision
506 */
507 public String toString() {
508 return FormatName.getName(
509 _name,
510 getClass().getName(),
511 REVISION);
512 }
513
514 /**
515 * Just a reminder to what channel we're on...
516 * this can't be dynamic :)
517 */
518 private String _channel;
519
520 /**
521 * A reminder of our current nickname...
522 */
523 private String _nickname;
524
525 }
526
527 }