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.2
Committed: Mon Feb 4 23:14:38 2002 UTC (22 years, 3 months ago) by tdb
Branch: SERVER_PIRCBOT
Changes since 1.30.2.1: +61 -54 lines
Log Message:
Some tweaks and additions. Should now have the functionality that the old
version had. Still some bugs to iron out relating to pircbot though. Also
need to tidy up and javadoc.

File Contents

# User Rev Content
1 tdb 1.1 //---PACKAGE DECLARATION---
2 tdb 1.29 package uk.org.iscream.cms.server.client.alerters;
3 tdb 1.1
4     //---IMPORTS---
5 tdb 1.29 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 tdb 1.1 import java.io.*;
10     import java.net.*;
11 tdb 1.9 import java.util.*;
12 ajm 1.25 import java.text.DateFormat;
13 tdb 1.30.2.1 import org.jibble.pircbot.*;
14 tdb 1.1
15     /**
16     * This Alert sends an IRC message.
17     *
18 tdb 1.9 * Clean shutdown could be achieved by stopping the run() method in the
19     * IRCBot inner class.
20     *
21 tdb 1.30.2.1 * @author $Author: tdb $
22 tdb 1.30.2.2 * @version $Id: IRC__Alerter.java,v 1.30.2.1 2002/02/04 00:14:13 tdb Exp $
23 tdb 1.1 */
24 ajm 1.25 public class IRC__Alerter extends AlerterSkeleton {
25 tdb 1.1
26     //---FINAL ATTRIBUTES---
27    
28     /**
29     * The current CVS revision of this class
30     */
31 tdb 1.30.2.2 public final String REVISION = "$Revision: 1.30.2.1 $";
32 tdb 1.1
33 tdb 1.9 /**
34     * A description of this alerter
35     */
36 tdb 1.1 public final String DESC = "Sends alerts on an IRC channel";
37    
38     //---STATIC METHODS---
39    
40     //---CONSTRUCTORS---
41 tdb 1.30.2.1
42 tdb 1.1 public IRC__Alerter() {
43 ajm 1.25 super();
44 tdb 1.30.2.1 // construct and initialise the bot
45 tdb 1.9 _ircbot = new IRCBot();
46 tdb 1.30.2.1 //_ircbot.setVerbose(true);
47     Thread ircThread = new Thread(_ircbot);
48 tdb 1.19 // set it's name and start it
49 tdb 1.30.2.1 ircThread.setName("client.IRC__Alerter$IRCBot");
50     ircThread.start();
51     // log our start time
52 tdb 1.11 _startTime = System.currentTimeMillis();
53 tdb 1.5 _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started");
54 tdb 1.1 }
55    
56     //---PUBLIC METHODS---
57    
58 ajm 1.25 /**
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 tdb 1.1 public void sendAlert(Alert alert) {
66 tdb 1.27 // 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 tdb 1.9 // only send alerts if we're active
77 tdb 1.27 if(_active) {
78 ajm 1.25 // send the message
79     _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
80     _ircbot.sendMsg(message);
81 tdb 1.26 // count sent alerts
82     _alertCount++;
83 ajm 1.25 } else {
84 tdb 1.26 // don't send, but keep a count that we ignored it
85     _ignoredCount++;
86 tdb 1.18 }
87 tdb 1.26 // 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 tdb 1.1 }
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 tdb 1.29 * This uses the uk.org.iscream.cms.server.util.NameFormat class
98 tdb 1.1 * 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 ajm 1.25 /**
110     * Return the String representation of what the alerter does
111     *
112     * @return the description
113 tdb 1.1 */
114     public String getDescription(){
115     return DESC;
116     }
117    
118     //---PRIVATE METHODS---
119 ajm 1.8
120 tdb 1.1 //---ACCESSOR/MUTATOR METHODS---
121 tdb 1.28
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 tdb 1.1
133     //---ATTRIBUTES---
134    
135 tdb 1.7 /**
136     * A reference to the IRCBot
137     */
138 tdb 1.1 private IRCBot _ircbot;
139    
140     /**
141 tdb 1.9 * 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 tdb 1.11 * The time of the last alert
152     */
153     private long _lastAlertTime = -1;
154    
155     /**
156     * Number of alerts sent
157     */
158 tdb 1.18 private int _alertCount = 0;
159    
160     /**
161     * Number of alerts ignored when in "stopped" mode
162     */
163     private int _ignoredCount = 0;
164 tdb 1.11
165     /**
166     * Time of IRCBot startup
167     */
168     private long _startTime;
169    
170     /**
171 tdb 1.30.2.1 * This holds a reference to the
172     * system logger that is being used.
173     */
174     protected Logger _logger = ReferenceManager.getInstance().getLogger();
175    
176     /**
177 tdb 1.1 * 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 ajm 1.25 private String _name = "IRC";
186 tdb 1.1
187     //---STATIC ATTRIBUTES---
188    
189     //---INNER CLASSES---
190    
191 tdb 1.30.2.1 class IRCBot extends PircBot implements Runnable {
192 tdb 1.16
193 tdb 1.1 /**
194 tdb 1.30.2.1 * The default reconnect delay in seconds
195 tdb 1.1 */
196 tdb 1.30.2.1 public final int DEFAULT_RECONNECT_DELAY = 30;
197    
198 tdb 1.1 public void run() {
199 tdb 1.30.2.1 while(true) {
200 tdb 1.1 try {
201 tdb 1.30.2.1 init();
202     break;
203 tdb 1.9 }
204 tdb 1.30.2.1 catch (IOException e) {
205     _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
206     reconnectSleep();
207 tdb 1.9 }
208 tdb 1.1 }
209 tdb 1.30.2.1 //System.out.println("falling out!");
210 tdb 1.9 }
211    
212 tdb 1.30.2.1 public void init() throws IOException {
213     _logger.write(this.toString(), Logger.DEBUG, "Initialising IRCBot...");
214 tdb 1.7 ConfigurationProxy cp = ConfigurationProxy.getInstance();
215 tdb 1.16 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 tdb 1.30.2.1 this.setLogin(user);
236     this.setVersion(comment);
237     this.setFinger(comment); // ?
238 tdb 1.16 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 tdb 1.9 StringTokenizer st = new StringTokenizer(nickList, ";");
246     boolean ok = false;
247     while(!ok && st.hasMoreTokens()) {
248     String nick = st.nextToken();
249 tdb 1.30.2.1 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 tdb 1.9 _nickname = nick;
255 tdb 1.30.2.1 ok = true;
256 tdb 1.9 }
257 tdb 1.30.2.1 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 tdb 1.9 }
269     }
270     if(!ok) {
271     // oh dear, we couldn't get on.
272 tdb 1.30.2.1 _logger.write(this.toString(), Logger.ERROR, "All nicknames already in use");
273     throw new IOException("All nicknames already in use");
274 tdb 1.9 }
275 tdb 1.16 try {
276     _channel = cp.getProperty(_name, "Alerter.IRC.channel");
277 tdb 1.30.2.1 this.joinChannel(_channel);
278 tdb 1.16 } 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 tdb 1.30.2.1
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 tdb 1.9 _active = true;
293 tdb 1.30.2.1 //System.out.println("leaving init");
294 tdb 1.1 }
295    
296 tdb 1.30.2.1 public void sendMsg(String msg) {
297     sendMessage(_channel, msg);
298 tdb 1.9 }
299    
300 tdb 1.30.2.1 public void onDisconnect() {
301     _active = false;
302     while(true) {
303     reconnectSleep();
304 tdb 1.16 try {
305 tdb 1.30.2.1 init();
306     break;
307 tdb 1.16 }
308 tdb 1.30.2.1 catch (IOException e) {
309     _logger.write(this.toString(), Logger.ERROR, "Error initialising IRCBot: "+e);
310 tdb 1.11 }
311 tdb 1.30.2.1 }
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 tdb 1.30.2.2 handleInput(message, channel);
318 tdb 1.30.2.1 }
319     }
320    
321     public void onPrivateMessage(String sender, String login, String hostname, String message) {
322 tdb 1.30.2.2 handleInput(message, sender);
323 tdb 1.30.2.1 }
324    
325     public void onNickChange(String oldNick, String login, String hostname, String newNick) {
326     if(oldNick.equals(_nickname)) {
327     _nickname = newNick;
328     }
329     }
330    
331 tdb 1.30.2.2 public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) {
332     if(recipientNick.equals(_nickname) && channel.equals(_channel)) {
333     sendMessage(kickerNick, "That wasn't a nice thing to do...");
334     try {
335     _channel = ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.channel");
336     } catch(PropertyNotFoundException e) {
337     _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
338     }
339     // should this be in the try?
340     joinChannel(_channel);
341     }
342     }
343    
344     private void handleInput(String message, String source) {
345     System.out.println("message: "+message);
346     System.out.println("from: "+source);
347 tdb 1.30.2.1 ConfigurationProxy cp = ConfigurationProxy.getInstance();
348     // setup some String's
349     String stopCommand, startCommand, timeSinceLastAlertCommand, lastAlertCommand, joinCommand;
350     String nickChangeCommand, versionCommand, helpCommand, statCommand, uptimeCommand;
351     // get the command set
352     try {
353     stopCommand = cp.getProperty(_name, "Alerter.IRC.stopCommand");
354     startCommand = cp.getProperty(_name, "Alerter.IRC.startCommand");
355     timeSinceLastAlertCommand = cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand");
356     lastAlertCommand = cp.getProperty(_name, "Alerter.IRC.lastAlertCommand");
357     joinCommand = cp.getProperty(_name, "Alerter.IRC.joinCommand");
358     nickChangeCommand = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
359     versionCommand = cp.getProperty(_name, "Alerter.IRC.versionCommand");
360     helpCommand = cp.getProperty(_name, "Alerter.IRC.helpCommand");
361     statCommand = cp.getProperty(_name, "Alerter.IRC.statCommand");
362     uptimeCommand = cp.getProperty(_name, "Alerter.IRC.uptimeCommand");
363     } catch (PropertyNotFoundException e) {
364     _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
365     // lets bail from handling this line...
366     // ...it's gonna be hard without a command set!
367 tdb 1.30.2.2 return;
368 tdb 1.30.2.1 }
369    
370 tdb 1.30.2.2 if(message.indexOf(stopCommand) != -1) {
371 tdb 1.30.2.1 _active = false;
372 tdb 1.30.2.2 sendMessage(source, "alerts have been stopped");
373 tdb 1.30.2.1 }
374 tdb 1.30.2.2 else if(message.indexOf(startCommand) != -1) {
375 tdb 1.30.2.1 _active = true;
376 tdb 1.30.2.2 sendMessage(source, "alerts have been activated");
377 tdb 1.30.2.1 }
378     // this needs to go here if it contains the same words as the lastAlertCommand
379 tdb 1.30.2.2 else if(message.indexOf(timeSinceLastAlertCommand) != -1) {
380 tdb 1.30.2.1 if(_lastAlertTime != -1) {
381     long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
382 tdb 1.11 String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
383 tdb 1.30.2.2 sendMessage(source, "I last sent an alert "+uptimeText+ " ago");
384 tdb 1.18 }
385 tdb 1.30.2.1 else {
386 tdb 1.30.2.2 sendMessage(source, "I've never sent an alert!");
387 tdb 1.9 }
388 tdb 1.30.2.1 }
389 tdb 1.30.2.2 else if(message.indexOf(lastAlertCommand) != -1) {
390 tdb 1.30.2.1 if(_lastAlertTime != -1) {
391     String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
392 tdb 1.30.2.2 sendMessage(source, "last alert was at "+date+"; "+_lastAlert);
393 tdb 1.9 }
394     else {
395 tdb 1.30.2.2 sendMessage(source, "I've never sent an alert!");
396 tdb 1.9 }
397 tdb 1.30.2.1
398 tdb 1.9 }
399 tdb 1.30.2.2 else if(message.indexOf(joinCommand) != -1) {
400 tdb 1.30.2.1 String joinCmd = joinCommand;
401     String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
402     int endOfChan = newChan.indexOf(" ");
403     if(endOfChan == -1) {
404     endOfChan = newChan.length();
405     }
406     newChan = newChan.substring(0, endOfChan);
407     if(newChan.equals(_channel)) {
408 tdb 1.30.2.2 sendMessage(source, "I'm already on "+newChan+"!");
409 tdb 1.30.2.1 } else {
410     partChannel(_channel);
411     joinChannel(newChan);
412     _channel = newChan;
413 tdb 1.30.2.2 //return null; // ???
414 tdb 1.30.2.1 }
415     }
416 tdb 1.30.2.2 else if(message.indexOf(nickChangeCommand) != -1) {
417 tdb 1.30.2.1 String nickChangeCmd = nickChangeCommand;
418     String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
419     int endOfNick = newNick.indexOf(" ");
420     if(endOfNick == -1) {
421     endOfNick = newNick.length();
422     }
423     newNick = newNick.substring(0, endOfNick);
424     changeNick(newNick);
425     // should we check this worked?
426     //_nickname = newNick;
427 tdb 1.30.2.2 //return null; // ???
428 tdb 1.30.2.1 }
429 tdb 1.30.2.2 else if(message.indexOf(versionCommand) != -1) {
430     sendMessage(source, "I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot");
431 tdb 1.30.2.1 }
432 tdb 1.30.2.2 else if(message.indexOf(helpCommand) != -1) {
433     //System.out.println("--starting help");
434     sendMessage(source, "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
435     sendMessage(source, "I understand the following commands;");
436     sendMessage(source, stopCommand);
437     sendMessage(source, startCommand);
438     sendMessage(source, lastAlertCommand);
439     sendMessage(source, joinCommand);
440     sendMessage(source, nickChangeCommand);
441     sendMessage(source, statCommand);
442     sendMessage(source, uptimeCommand);
443     sendMessage(source, timeSinceLastAlertCommand);
444     sendMessage(source, helpCommand);
445     //System.out.println("--ending help");
446 tdb 1.30.2.1 }
447 tdb 1.30.2.2 else if(message.indexOf(statCommand) != -1) {
448     sendMessage(source, "I have sent a total of "+_alertCount+" alerts, and ignored a total of "+_ignoredCount+"!");
449 tdb 1.30.2.1 }
450 tdb 1.30.2.2 else if(message.indexOf(uptimeCommand) != -1) {
451 tdb 1.30.2.1 long uptime = (System.currentTimeMillis() - _startTime) / 1000;
452     String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
453 tdb 1.30.2.2 sendMessage(source, "I have been running for "+uptimeText);
454 tdb 1.30.2.1 }
455 tdb 1.30.2.2 else if(message.indexOf("ping") != -1) {
456     sendMessage(source, "pong");
457 tdb 1.30.2.1 }
458 tdb 1.30.2.2 else if(message.indexOf("do a jibble dance") != -1) {
459 tdb 1.30.2.1 // little joke :)
460 tdb 1.30.2.2 sendAction(source, "jives to the funky beat shouting \"ii--screeeaaammm\"");
461 tdb 1.30.2.1 }
462    
463     else {
464     String rejectMessage = NOT_CONFIGURED;
465 tdb 1.16 try {
466 tdb 1.30.2.1 rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
467 tdb 1.16 } catch(PropertyNotFoundException e) {
468     _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
469     }
470 tdb 1.30.2.2 sendMessage(source, rejectMessage);
471 tdb 1.9 }
472     }
473    
474 tdb 1.30.2.1 private boolean isForMe(String message) {
475     // change this!
476     String nick = _nickname.toLowerCase();
477     String msg = message.toLowerCase();
478     if(msg.startsWith(nick + ", ") ||
479     msg.startsWith(nick + ": ") ||
480     msg.startsWith(nick + " ")) {
481     return true;
482     }
483     else {
484     return false;
485 tdb 1.9 }
486     }
487    
488 tdb 1.30.2.1 private void reconnectSleep() {
489     int delayTime = 0;
490     try {
491     delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
492     } catch (NumberFormatException e) {
493     delayTime = DEFAULT_RECONNECT_DELAY;
494     _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
495     } catch (PropertyNotFoundException e) {
496     delayTime = DEFAULT_RECONNECT_DELAY;
497     _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
498 tdb 1.9 }
499 tdb 1.30.2.1 _logger.write(this.toString(), Logger.ERROR, "Waiting "+delayTime+" seconds for reconnect...");
500     try {
501     Thread.sleep(delayTime * 1000);
502     } catch (InterruptedException e) {}
503 tdb 1.9 }
504    
505     /**
506 tdb 1.30.2.1 * Overrides the {@link java.lang.Object#toString() Object.toString()}
507     * method to provide clean logging (every class should have this).
508     *
509     * This uses the uk.org.iscream.cms.server.util.NameFormat class
510     * to format the toString()
511     *
512     * @return the name of this class and its CVS revision
513 tdb 1.1 */
514 tdb 1.30.2.1 public String toString() {
515     return FormatName.getName(
516     _name,
517     getClass().getName(),
518     REVISION);
519     }
520 tdb 1.1
521 tdb 1.7 /**
522     * Just a reminder to what channel we're on...
523     * this can't be dynamic :)
524     */
525     private String _channel;
526 tdb 1.9
527     /**
528     * A reminder of our current nickname...
529     */
530     private String _nickname;
531    
532 tdb 1.1 }
533    
534     }