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.12
Committed: Sun Mar 4 05:37:15 2001 UTC (23 years, 2 months ago) by tdb
Branch: MAIN
Changes since 1.11: +7 -4 lines
Log Message:
Added a version command.

File Contents

# User Rev Content
1 tdb 1.1 //---PACKAGE DECLARATION---
2     package uk.ac.ukc.iscream.client.alerters;
3    
4     //---IMPORTS---
5     import uk.ac.ukc.iscream.client.*;
6     import uk.ac.ukc.iscream.core.*;
7     import uk.ac.ukc.iscream.util.*;
8     import uk.ac.ukc.iscream.componentmanager.*;
9    
10     import java.io.*;
11     import java.net.*;
12 tdb 1.9 import java.util.*;
13 tdb 1.11 import java.text.*;
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.12 * @author $Author: tdb1 $
22     * @version $Id: IRC__Alerter.java,v 1.11 2001/03/04 05:26:00 tdb1 Exp $
23 tdb 1.1 */
24     public class IRC__Alerter implements PluginAlerter {
25    
26     //---FINAL ATTRIBUTES---
27    
28     /**
29     * The current CVS revision of this class
30     */
31 tdb 1.12 public final String REVISION = "$Revision: 1.11 $";
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 tdb 1.9 /**
39     * The default reconnect delay in seconds
40     */
41     public final int DEFAULT_RECONNECT_DELAY = 30;
42    
43 tdb 1.1 //---STATIC METHODS---
44    
45     //---CONSTRUCTORS---
46    
47     public IRC__Alerter() {
48 tdb 1.7
49 tdb 1.1 // connect to the IRC server
50 tdb 1.9 _ircbot = new IRCBot();
51     _ircbot.start();
52 tdb 1.11 _startTime = System.currentTimeMillis();
53 tdb 1.4
54 tdb 1.5 _logger.write(toString(), Logger.SYSINIT, "IRC Alerter started");
55 tdb 1.1 }
56    
57     //---PUBLIC METHODS---
58    
59     public void sendAlert(Alert alert) {
60 tdb 1.9 // only send alerts if we're active
61     if(_active) {
62     ConfigurationProxy cp = ConfigurationProxy.getInstance();
63     String levelName = cp.getProperty(_name, "Alerter.IRC.level");
64     int level = StringUtils.getStringPos(levelName, Alert.alertLevels);
65     // only send if it's equal (or above) our level
66     if(alert.getLevel() >= level) {
67     String alertType = Alert.alertLevels[alert.getLevel()];
68     String thresholdType = Alert.thresholdLevels[alert.getThreshold()];
69     // sort out the message
70     String message = cp.getProperty(_name, "Alerter.IRC.message");
71     message = StringUtils.replaceText(message, "%level%", alertType);
72     message = StringUtils.replaceText(message, "%threshold%", thresholdType);
73     message = StringUtils.replaceText(message, "%source%", alert.getSource());
74     message = StringUtils.replaceText(message, "%value%", alert.getValue());
75     message = StringUtils.replaceText(message, "%thresholdValue%", alert.getThresholdValue());
76     message = StringUtils.replaceText(message, "%attributeName%", alert.getAttributeName());
77     message = StringUtils.replaceText(message, "%timeTillNextAlert%", getTimeString(Long.parseLong(alert.getTimeTillNextAlert())));
78    
79     // send the message
80 ajm 1.10 _logger.write(toString(), Logger.DEBUG, "Sending " + _name + " at "+ alertType + " level");
81 tdb 1.9 _ircbot.sendMsg(message);
82     _lastAlert = message;
83 tdb 1.11 _lastAlertTime = System.currentTimeMillis();
84     _alertCount ++;
85 tdb 1.9 }
86 tdb 1.1 }
87     }
88    
89     /**
90     * Overrides the {@link java.lang.Object#toString() Object.toString()}
91     * method to provide clean logging (every class should have this).
92     *
93     * This uses the uk.ac.ukc.iscream.util.NameFormat class
94     * to format the toString()
95     *
96     * @return the name of this class and its CVS revision
97     */
98     public String toString() {
99     return FormatName.getName(
100     _name,
101     getClass().getName(),
102     REVISION);
103     }
104    
105     /**
106     * return the String representation of what the filter does
107     */
108     public String getDescription(){
109     return DESC;
110     }
111    
112     //---PRIVATE METHODS---
113    
114 ajm 1.8 private String getTimeString(long time) {
115     String timeString = null;
116     if (time >= 60) {
117     timeString = (time / 60) + " minute(s)";
118     } else if (time >= 3600) {
119     timeString = ((time/60) / 60) + " hour(s)";
120     } else {
121     timeString = time + " second(s)";
122     }
123     return timeString;
124     }
125    
126 tdb 1.1 //---ACCESSOR/MUTATOR METHODS---
127    
128     //---ATTRIBUTES---
129    
130 tdb 1.7 /**
131     * A reference to the IRCBot
132     */
133 tdb 1.1 private IRCBot _ircbot;
134    
135     /**
136 tdb 1.9 * Are we "active"
137     */
138     private boolean _active = false;
139    
140     /**
141     * The last alert that was sent
142     */
143     private String _lastAlert = "no alerts have been sent";
144    
145     /**
146 tdb 1.11 * The time of the last alert
147     */
148     private long _lastAlertTime = -1;
149    
150     /**
151     * Number of alerts sent
152     */
153     private long _alertCount = 0;
154    
155     /**
156     * Time of IRCBot startup
157     */
158     private long _startTime;
159    
160     /**
161 tdb 1.1 * This is the friendly identifier of the
162     * component this class is running in.
163     * eg, a Filter may be called "filter1",
164     * If this class does not have an owning
165     * component, a name from the configuration
166     * can be placed here. This name could also
167     * be changed to null for utility classes.
168     */
169 ajm 1.8 private String _name = "IRC Alert";
170 tdb 1.1
171     /**
172     * This holds a reference to the
173     * system logger that is being used.
174     */
175     private Logger _logger = ReferenceManager.getInstance().getLogger();
176    
177     //---STATIC ATTRIBUTES---
178    
179     //---INNER CLASSES---
180    
181     /**
182     * This class provides some basic IRCBot functionality. It connects
183     * to a specified server, and will remain there until told to
184     * leave. Whilst connected it can send a message or a notice to
185     * the server.
186     */
187     class IRCBot extends Thread {
188    
189     /**
190     * Main thread loop, this part of the class listens for
191     * messages from the server, and acts accordingly. At the
192     * present moment it only responds to pings.
193     */
194     public void run() {
195 tdb 1.9 // so we can stop if required
196 tdb 1.1 boolean run = true;
197     while(run) {
198 tdb 1.9 // flag so we can stop the loop
199     boolean doRead = true;
200     // connect to the IRC server
201 tdb 1.1 try {
202 tdb 1.9 connect();
203     sendNotice(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.startupNotice"));
204     } catch(IOException e) {
205     doRead=false;
206     _logger.write(this.toString(), Logger.ERROR, "Error connecting: "+e);
207     }
208     while(doRead) {
209     try {
210     // read a command
211     String cmd = _socketIn.readLine();
212     // if we have a null, we've lost contact
213     if(cmd == null) {
214     throw new IOException("End of stream reached");
215     }
216     // let another method deal with the input
217     handleInput(cmd);
218     } catch (IOException e) {
219     // comms failure, maybe our link is dead.
220     _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
221     // stop, and loop round for a reconnect.
222     doRead = false;
223 tdb 1.1 }
224 tdb 1.9 }
225     // make sure we're disconnected
226     try {
227     disconnect();
228 tdb 1.1 } catch (IOException e) {
229 tdb 1.9 _logger.write(this.toString(), Logger.ERROR, "Communication error: "+e);
230     }
231    
232     // comms have failed, so wait a while and reconnect
233     int delayTime = 0;
234     try {
235     delayTime = Integer.parseInt(ConfigurationProxy.getInstance().getProperty(_name, "Alerter.IRC.reconnectDelay"));
236     } catch (NumberFormatException e) {
237     delayTime = DEFAULT_RECONNECT_DELAY;
238     _logger.write(this.toString(), Logger.WARNING, "Erronous Alerter.IRC.reconnectDelay value in configuration using default of " + delayTime + " seconds");
239     } catch (org.omg.CORBA.MARSHAL e2) {
240     delayTime = DEFAULT_RECONNECT_DELAY;
241     _logger.write(this.toString(), Logger.WARNING, "Alerter.IRC.reconnectDelay value unavailable using default of " + delayTime + " seconds");
242 tdb 1.1 }
243 tdb 1.9 try {
244     Thread.sleep(delayTime * 1000);
245     } catch (InterruptedException e) {}
246 tdb 1.1 }
247 tdb 1.9 // maybe disconnect here ? - shutdown method not implemented yet
248     //disconnect();
249 tdb 1.1 }
250    
251     /**
252     * Sends a message to the channel.
253     *
254     * @param msg The message to send
255     */
256     public void sendMsg(String msg) {
257     _socketOut.println("PRIVMSG "+_channel+" :"+msg);
258     }
259    
260     /**
261 tdb 1.9 * Sends a message to the channel.
262     *
263     * @param user The user to send to
264     * @param msg The message to send
265     */
266     public void sendPrivMsg(String user, String msg) {
267     _socketOut.println("PRIVMSG "+user+" :"+msg);
268     }
269    
270     /**
271     * Sends an action to the channel.
272     *
273     * @param msg the action message
274     */
275     public void sendAction(String msg) {
276     char esc = 001;
277     sendMsg(esc+"ACTION "+msg+esc);
278     }
279    
280     /**
281 tdb 1.1 * Sends a notice to the channel.
282     *
283     * @param msg The notice to send
284     */
285     public void sendNotice(String msg) {
286     _socketOut.println("NOTICE "+_channel+" :"+msg);
287     }
288    
289     /**
290     * Connect to the IRC server, log in, and join the channel.
291     *
292     * @throws IOException if the connection fails
293     */
294     public void connect() throws IOException {
295 tdb 1.7 ConfigurationProxy cp = ConfigurationProxy.getInstance();
296 tdb 1.1 // setup the socket, reader and writer
297 tdb 1.7 String server = cp.getProperty(_name, "Alerter.IRC.IRCServer");
298     int port = Integer.parseInt(cp.getProperty(_name, "Alerter.IRC.IRCPort"));
299     _socket = new Socket(server, port);
300 tdb 1.1 _socketIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
301     _socketOut = new PrintWriter(_socket.getOutputStream(), true);
302 tdb 1.9 //_socketOut.println("PASS");
303     // send USER details
304     String user = cp.getProperty(_name, "Alerter.IRC.user");
305     String comment = cp.getProperty(_name, "Alerter.IRC.comment");
306     _socketOut.println("USER "+user+" 8 * :"+comment);
307     // attempt to get a nick
308     String nickList = cp.getProperty(_name, "Alerter.IRC.nickList");
309     StringTokenizer st = new StringTokenizer(nickList, ";");
310     boolean ok = false;
311     // try until we exhaust our list
312     while(!ok && st.hasMoreTokens()) {
313     String nick = st.nextToken();
314     _socketOut.println("NICK "+nick);
315     // get a "yes" or "no" response back
316     String response = "";
317     do {
318     response = _socketIn.readLine();
319     if(response==null) {
320     throw new IOException("Communication error whilst logging in");
321     }
322     } while(response.indexOf("001")==-1 && response.indexOf("433")==-1);
323     // see if it was a yes
324     if(response.indexOf("001")!=-1) {
325     // great, we're logged in !
326     ok = true;
327     // store the name we're using
328     _nickname = nick;
329     }
330     else {
331     // log we couldn't get the name
332     _logger.write(this.toString(), Logger.WARNING, "Nickname in use: "+nick);
333     }
334     }
335     if(!ok) {
336     // oh dear, we couldn't get on.
337     throw new IOException("All nicknames in use");
338     }
339 tdb 1.1 // join the channel
340 tdb 1.7 _channel = cp.getProperty(_name, "Alerter.IRC.channel");
341 tdb 1.1 _socketOut.println("JOIN "+_channel);
342 tdb 1.9 // allow alerts
343     _active = true;
344 tdb 1.1 }
345    
346     /**
347     * Disconnect "nicely" from the IRC server.
348     *
349     * @throws IOException if the disconnection fails
350     */
351     public void disconnect() throws IOException {
352 tdb 1.9 // stop alerts
353     _active = false;
354 tdb 1.6 // send proper QUIT
355 tdb 1.1 _socketOut.println("QUIT : iscreamBot component shutting down...");
356     // close the socket
357     _socketOut.close();
358     _socketIn.close();
359     _socket.close();
360     }
361 tdb 1.7
362 tdb 1.1 /**
363 tdb 1.9 * Overrides the {@link java.lang.Object#toString() Object.toString()}
364     * method to provide clean logging (every class should have this).
365     *
366     * This uses the uk.ac.ukc.iscream.util.NameFormat class
367     * to format the toString()
368     *
369     * @return the name of this class and its CVS revision
370     */
371     public String toString() {
372     return FormatName.getName(
373     _name,
374     getClass().getName(),
375     REVISION);
376     }
377    
378     /**
379     * Deals with incoming lines from the server.
380     *
381     * @param line the line to deal with
382     */
383     private void handleInput(String line) {
384     ConfigurationProxy cp = ConfigurationProxy.getInstance();
385     // if it's a PING...
386     if(line.startsWith("PING")) {
387     // ...send a PONG
388     _socketOut.println("PONG" + line.substring(4));
389     }
390     // see if it's for us
391 tdb 1.11 else if(getMsg(line).startsWith(_nickname+",")
392     || getMsg(line).startsWith(_nickname+":")
393     || getMsg(line).startsWith(_nickname+" ")) {
394 tdb 1.9 // we have a message for us
395     String message = getMsg(line).substring(_nickname.length());
396     if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.stopCommand"))!=-1) {
397     _active = false;
398     sendMsg(getMsgSender(line)+", alerts have been stopped");
399     }
400     else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.startCommand"))!=-1) {
401     _active = true;
402     sendMsg(getMsgSender(line)+", alerts have been activated");
403     }
404 tdb 1.11 // this needs to go here if it contains the same words as the lastAlertCommand
405     else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand"))!=-1) {
406     if(_lastAlertTime != -1) {
407     long uptime = (System.currentTimeMillis() - _lastAlertTime) / 1000;
408     String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
409     sendMsg(getMsgSender(line)+", I last sent an alert "+uptimeText+ " ago");
410     }
411     else {
412     sendMsg(getMsgSender(line)+", I've never sent an alert!");
413     }
414     }
415 tdb 1.9 else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.lastAlertCommand"))!=-1) {
416 tdb 1.11 if(_lastAlertTime != -1) {
417     String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.UK).format(new Date(_lastAlertTime));
418     sendMsg(getMsgSender(line)+", last alert was at "+date+"; "+_lastAlert);
419     }
420     else {
421     sendMsg(getMsgSender(line)+", I've never sent an alert!");
422     }
423    
424 tdb 1.9 }
425     else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.joinCommand"))!=-1) {
426     String joinCmd = cp.getProperty(_name, "Alerter.IRC.joinCommand");
427     String newChan = message.substring(message.indexOf(joinCmd) + joinCmd.length() + 1);
428     int endOfChan = newChan.indexOf(" ");
429     if(endOfChan == -1) {
430     endOfChan = newChan.length();
431     }
432     newChan = newChan.substring(0, endOfChan);
433     sendMsg(getMsgSender(line)+", okay, I'm off to "+newChan);
434     _socketOut.println("PART "+_channel);
435     _socketOut.println("JOIN "+newChan);
436     _channel = newChan;
437     }
438 tdb 1.11 else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.nickChangeCommand"))!=-1) {
439     String nickChangeCmd = cp.getProperty(_name, "Alerter.IRC.nickChangeCommand");
440     String newNick = message.substring(message.indexOf(nickChangeCmd) + nickChangeCmd.length() + 1);
441     int endOfNick = newNick.indexOf(" ");
442     if(endOfNick == -1) {
443     endOfNick = newNick.length();
444     }
445     newNick = newNick.substring(0, endOfNick);
446     sendMsg(getMsgSender(line)+", okay, changing my nickname to "+newNick);
447     _socketOut.println("NICK "+newNick);
448     _nickname = newNick;
449     }
450 tdb 1.12 else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.versionCommand"))!=-1) {
451     sendMsg(getMsgSender(line)+", I am version "+REVISION.substring(11, REVISION.length() -2)+" of the i-scream alerting bot");
452     }
453 tdb 1.9 else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.helpCommand"))!=-1) {
454 tdb 1.12 sendPrivMsg(getMsgSender(line), "Hello, I am the i-scream alerting bot version "+REVISION.substring(11, REVISION.length() -2));
455 tdb 1.9 sendPrivMsg(getMsgSender(line), "I understand the following commands;");
456     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.stopCommand"));
457     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.startCommand"));
458     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.lastAlertCommand"));
459 tdb 1.11 sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.joinCommand"));
460     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.nickChangeCommand"));
461     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.statCommand"));
462     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.uptimeCommand"));
463     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.timeSinceLastAlertCommand"));
464     sendPrivMsg(getMsgSender(line), cp.getProperty(_name, "Alerter.IRC.helpCommand"));
465     }
466     else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.statCommand"))!=-1) {
467     sendMsg(getMsgSender(line)+", I have sent a total of "+_alertCount+" alerts!");
468     }
469     else if(message.indexOf(cp.getProperty(_name, "Alerter.IRC.uptimeCommand"))!=-1) {
470     long uptime = (System.currentTimeMillis() - _startTime) / 1000;
471     String uptimeText = DateUtils.formatTime(uptime, "%DAYS% days, %HOURS% hours, %MINS% mins, and %SECS% secs");
472     sendMsg(getMsgSender(line)+", I have been running for "+uptimeText);
473 tdb 1.9 }
474     else if(message.indexOf("do a jibble dance")!=-1) {
475     // little joke :)
476     sendAction("jives to the funky beat shouting \"ii--screeeaaammm\"");
477     }
478     else {
479     sendMsg(getMsgSender(line)+", "+cp.getProperty(_name, "Alerter.IRC.rejectMessage"));
480     }
481     }
482     else if(line.indexOf(_nickname)!=-1 && line.indexOf(_channel)!=-1 && line.indexOf("KICK")!=-1) {
483     sendPrivMsg(getMsgSender(line), "That wasn't a nice thing to do...");
484     _channel = cp.getProperty(_name, "Alerter.IRC.channel");
485     _socketOut.println("JOIN "+_channel);
486     }
487     }
488    
489     /**
490     * Strips the header from a message line
491     *
492     * @param line the line to strip
493     * @return the message from the line
494     */
495     private String getMsg(String line) {
496     String result = "";
497     if(line.indexOf("PRIVMSG")!=-1) {
498     int firstColon = line.indexOf(":");
499     if(firstColon != -1) {
500     int secondColon = line.indexOf(":", firstColon+1);
501     if(secondColon != -1) {
502     result = line.substring(secondColon+1);
503     }
504     }
505     }
506     return result;
507     }
508    
509     /**
510     * Finds out the sender of the message
511     *
512     * @param line the line to look for a sender in
513     * @return the sender
514     */
515     private String getMsgSender(String line) {
516     String result = "";
517     int colon = line.indexOf(":");
518     int excl = line.indexOf("!");
519     if(colon!=-1 && excl!=-1) {
520     result = line.substring(colon+1, excl);
521     }
522     return result;
523     }
524    
525     /**
526 tdb 1.1 * The socket connected to the server
527     */
528     private Socket _socket;
529    
530     /**
531     * The writer
532     */
533     private PrintWriter _socketOut;
534    
535     /**
536     * The reader
537     */
538     private BufferedReader _socketIn;
539    
540 tdb 1.7 /**
541     * Just a reminder to what channel we're on...
542     * this can't be dynamic :)
543     */
544     private String _channel;
545 tdb 1.9
546     /**
547     * A reminder of our current nickname...
548     */
549     private String _nickname;
550    
551 tdb 1.1 }
552    
553     }