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

# 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     * @version $Id: IRC__Alerter.java,v 1.30 2002/01/17 17:58:12 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.1 public final String REVISION = "$Revision: 1.30 $";
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     String response = handleInput(message);
318     if(response != null) {
319     sendMessage(channel, response);
320 tdb 1.11 }
321 tdb 1.30.2.1 }
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 tdb 1.11 String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
375 tdb 1.30.2.1 return "I last sent an alert "+uptimeText+ " ago";
376 tdb 1.18 }
377 tdb 1.30.2.1 else {
378     return "I've never sent an alert!";
379 tdb 1.9 }
380 tdb 1.30.2.1 }
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 tdb 1.9 }
386     else {
387 tdb 1.30.2.1 return "I've never sent an alert!";
388 tdb 1.9 }
389 tdb 1.30.2.1
390 tdb 1.9 }
391 tdb 1.30.2.1 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 tdb 1.16 try {
459 tdb 1.30.2.1 rejectMessage = cp.getProperty(_name, "Alerter.IRC.rejectMessage");
460 tdb 1.16 } catch(PropertyNotFoundException e) {
461     _logger.write(this.toString(), Logger.ERROR, "Configuration error: "+e);
462     }
463 tdb 1.30.2.1 return rejectMessage;
464 tdb 1.9 }
465     }
466    
467 tdb 1.30.2.1 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 tdb 1.9 }
479     }
480    
481 tdb 1.30.2.1 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 tdb 1.9 }
492 tdb 1.30.2.1 _logger.write(this.toString(), Logger.ERROR, "Waiting "+delayTime+" seconds for reconnect...");
493     try {
494     Thread.sleep(delayTime * 1000);
495     } catch (InterruptedException e) {}
496 tdb 1.9 }
497    
498     /**
499 tdb 1.30.2.1 * 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 tdb 1.1 */
507 tdb 1.30.2.1 public String toString() {
508     return FormatName.getName(
509     _name,
510     getClass().getName(),
511     REVISION);
512     }
513 tdb 1.1
514 tdb 1.7 /**
515     * Just a reminder to what channel we're on...
516     * this can't be dynamic :)
517     */
518     private String _channel;
519 tdb 1.9
520     /**
521     * A reminder of our current nickname...
522     */
523     private String _nickname;
524    
525 tdb 1.1 }
526    
527     }