View Javadoc

1   /*------------------------------------------------------------------------------
2    * The contents of this file are subject to the Mozilla Public License Version
3    * 1.1 (the "License"); you may not use this file except in compliance with
4    * the License. You may obtain a copy of the License at
5    * http://www.mozilla.org/MPL/
6    * Software distributed under the License is distributed on an "AS IS" basis,
7    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
8    * the specific language governing rights and limitations under the License.
9    *
10   * The Original Code is levelonelabs.com code.
11   * The Initial Developer of the Original Code is Level One Labs. Portions
12   * created by the Initial Developer are Copyright (C) 2001 the Initial
13   * Developer. All Rights Reserved.
14   *
15   *         Contributor(s):
16   *             Scott Oster      (ostersc@alum.rpi.edu)
17   *             Steve Zingelwicz (sez@po.cwru.edu)
18   *             William Gorman   (willgorman@hotmail.com)
19   *
20   * Alternatively, the contents of this file may be used under the terms of
21   * either the GNU General Public License Version 2 or later (the "GPL"), or
22   * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23   * in which case the provisions of the GPL or the LGPL are applicable
24   * instead of those above. If you wish to allow use of your version of this
25   * file only under the terms of either the GPL or the LGPL, and not to allow
26   * others to use your version of this file under the terms of the NPL, indicate
27   * your decision by deleting the provisions above and replace them with the
28   * notice and other provisions required by the GPL or the LGPL. If you do not
29   * delete the provisions above, a recipient may use your version of this file
30   * under the terms of any one of the NPL, the GPL or the LGPL.
31   *----------------------------------------------------------------------------*/
32  
33  package com.levelonelabs.aimbot.modules;
34  
35  import java.text.SimpleDateFormat;
36  import java.util.ArrayList;
37  import java.util.Calendar;
38  import java.util.Date;
39  import java.util.HashMap;
40  import java.util.Iterator;
41  import java.util.StringTokenizer;
42  import java.util.Timer;
43  import java.util.TimerTask;
44  import java.util.logging.Logger;
45  
46  import org.w3c.dom.CDATASection;
47  import org.w3c.dom.Document;
48  import org.w3c.dom.Element;
49  
50  import com.levelonelabs.aim.AIMBuddy;
51  import com.levelonelabs.aim.XMLizable;
52  import com.levelonelabs.aimbot.AIMBot;
53  import com.levelonelabs.aimbot.BotModule;
54  
55  
56  /***
57   * A class to handle scheduling commands to happen at certain times
58   * 
59   * @author Steve Zingelewicz
60   * @created May 7th, 2003
61   */
62  public class ScheduleModule extends BotModule implements XMLizable {
63      private static ArrayList services;
64      private static Logger logger = Logger.getLogger(ScheduleModule.class.getName());
65      private static HashMap events = new HashMap();
66      private static Timer timer;
67  
68  
69      private class SchedulerTask extends TimerTask {
70          AIMBuddy buddy;
71          String cmd;
72          String triggerTime;
73          Date createDate;
74  
75  
76          SchedulerTask(AIMBuddy _buddy, String _cmd) {
77              buddy = _buddy;
78              cmd = _cmd;
79              createDate = new Date(); // Record the initial time when the
80                                          // event
81              // was requested
82          }
83  
84  
85          public void run() {
86              System.err.println("Timeout for: " + buddy.getName() + " cmd:" + cmd);
87              bot.handleMessage(buddy, cmd);
88  
89              removeEvent(this);
90          }
91  
92  
93          public AIMBuddy getBuddy() {
94              return (buddy);
95          }
96  
97  
98          public String getCommand() {
99              return (cmd);
100         }
101 
102 
103         public String getCreateDate() {
104             return (createDate.toString());
105         }
106     }
107 
108     /***
109      * Initialize the service commands.
110      */
111     static {
112         services = new ArrayList();
113         services.add("schedule");
114         services.add("cancel");
115         services.add("events");
116         // Create the timer to handle timed reminding events
117         timer = new Timer();
118     }
119 
120 
121     /***
122      * Constructor for the ReminderModule object
123      * 
124      * @param bot
125      */
126     public ScheduleModule(AIMBot bot) {
127         super(bot);
128     }
129 
130 
131     /***
132      * Gets the name attribute of the SchedulerModule object
133      * 
134      * @return The name value
135      */
136     public String getName() {
137         return "Scheduler Module";
138     }
139 
140 
141     /***
142      * Gets the services attribute of the SchedulerModule object
143      * 
144      * @return The services value
145      */
146     public ArrayList getServices() {
147         return services;
148     }
149 
150 
151     /***
152      * Describes the usage of the module
153      * 
154      * @return the usage of the module
155      */
156     public String help() {
157         StringBuffer sb = new StringBuffer();
158         sb.append("<B>schedule <i>TEXT</i></B> (stores text in the system for retrieval later)\n");
159         sb.append("<B>cancel <i>All or #</i></B> (removes all items or a specific item)\n");
160         sb.append("<B>events</B> (lists all reminders)\n");
161         sb
162             .append("* If the preference \"reminderatsignon\" is set to true, you will automatically get a list when you login to aim.\n");
163 
164         return sb.toString();
165     }
166 
167 
168     /***
169      * Grabs and sends any stored reminders for a buddy
170      * 
171      * @param buddy
172      * @return whether any messages where found
173      */
174     public boolean retrieveEvents(AIMBuddy buddy) {
175         // check for messages
176         ArrayList br = (ArrayList) events.get(buddy.getName());
177         if (br != null) {
178             String eventlist = "Events:<BR>";
179 
180             if (br.size() != 0) {
181                 // collect the messages
182                 for (int i = 0; i < br.size(); i++) {
183                     SchedulerTask st = (SchedulerTask) br.get(i);
184                     eventlist += ((i + 1) + ")" + (st.getCommand() + " scheduled " + st.getCreateDate() + "<BR>"));
185                 }
186             } else
187                 eventlist = "No events scheduled.";
188 
189             // send the list
190             super.sendMessage(buddy, eventlist);
191             return true;
192         }
193         return false;
194     }
195 
196 
197     /***
198      * Handle a messaging query
199      * 
200      * @param buddy
201      * @param query
202      */
203     public void performService(AIMBuddy buddy, String query) {
204         if (query.toLowerCase().startsWith("schedule")) {
205             handleSchedule(buddy, query);
206         } else if (query.toLowerCase().startsWith("cancel")) {
207             handleCancel(buddy, query);
208         } else if (query.toLowerCase().startsWith("events")) {
209             if (!retrieveEvents(buddy)) {
210                 sendMessage(buddy, "No events scheduled.");
211             }
212         }
213     }
214 
215 
216     /***
217      * Schedule time parsing function This function is getting huge and
218      * unwieldy. Once I get it all worked out, I'll figure out how to make it
219      * more manageable.
220      * 
221      * @param buddy
222      * @param query
223      */
224     private void handleSchedule(AIMBuddy buddy, String query) {
225         String token;
226         StringTokenizer st = new StringTokenizer(query, " ");
227         int state = 0;
228         int stop = 0;
229         char ch;
230         long period = 60000; // Default to 1 minute
231         String command = "";
232         SimpleDateFormat sdf = new SimpleDateFormat();
233         Date eventDate = null;
234         int sched_type = 0; // Schedule type
235 
236         try {
237 
238             while (st.hasMoreTokens() && stop == 0) {
239                 token = st.nextToken();
240                 System.err.println("token: " + token + "state:" + String.valueOf(state));
241 
242                 switch (state) {
243                     case 0 :
244                         // If you don't have schedule here, something went
245                         // horribly wrong
246                         if (token.equals("schedule"))
247                             state = 1;
248                         else
249                             state = 99;
250                         break;
251                     case 1 :
252                         // Determine the flavor of the schedule command
253                         if (token.equals("at")) {
254                             sched_type = 1;
255                             state = 2;
256                         } else if (token.equals("in")) {
257                             sched_type = 2;
258                             state = 3;
259                         } else if (token.equals("every")) {
260                             sched_type = 3;
261                             state = 4;
262                         } else
263                             state = 99;
264                         break;
265                     case 2 :
266                         // At syntax
267                         // Find the "at" and the "-" delimiter and give date
268                         // format everything in there
269                         int at_idx = query.indexOf("at");
270                         int delim_idx = query.indexOf("-");
271                         if (at_idx != -1 && delim_idx != -1) {
272 
273                             System.err.println("Schedtoken:" + query.substring(at_idx + 2, delim_idx).trim());
274                             try {
275                                 eventDate = sdf.parse(query.substring(at_idx + 2, delim_idx).trim());
276                                 state = 51;
277                             } catch (Exception e) {
278                                 System.err.println("Can't parse date & time, trying just time..");
279                                 state = 99;
280                             }
281 
282                             try {
283                                 // Just getting a time. Assume today's date
284                                 sdf = new SimpleDateFormat("h:mm a");
285                                 eventDate = sdf.parse(query.substring(at_idx + 2, delim_idx).trim());
286                                 Calendar today = Calendar.getInstance();
287                                 today.set(Calendar.HOUR, 0);
288                                 today.set(Calendar.MINUTE, 0);
289                                 today.set(Calendar.SECOND, 0);
290                                 Date todaysDate = today.getTime();
291                                 System.err.println("event date:" + eventDate.toString());
292                                 System.err.println("today's date:" + todaysDate.toString());
293                                 System.err.println(eventDate.getTime());
294                                 System.err.println(todaysDate.getTime());
295                                 // For the moment, peel off the 5 hours EST is
296                                 // off from GMT
297                                 eventDate.setTime(eventDate.getTime() + todaysDate.getTime() - 18000000);
298                                 state = 51;
299                             } catch (Exception e) {
300                                 System.err.println("Can't parse date or time, bailing");
301                                 state = 99;
302                             }
303 
304                         } else
305                             state = 99;
306                         break;
307                     case 3 :
308                         // In syntax
309                         // read the last character (s=seconds, m = minutes, h =
310                         // hours, d=days)
311                         // Strip the letter and get the number
312                         ch = token.charAt(token.length() - 1);
313                         period = Long.parseLong(token.substring(0, token.length() - 1));
314                         state = 50;
315 
316                         if (ch == 's') { // seconds
317                             period = period * 1000;
318                         } else if (ch == 'm') { // minutes
319                             period = period * 60000;
320                         } else if (ch == 'h') { // hours
321                             period = period * 3600000;
322                         } else if (ch == 'd') { // days
323                             period = period * 86400000;
324                         } else
325                             state = 99;
326                         break;
327                     case 4 :
328                         // Every syntax
329                         break;
330                     case 50 :
331                         // There should be a delimiter now.
332                         if (token.equals("-")) {
333                             stop = 1;
334                             command = query.substring(query.indexOf('-') + 1).trim();
335                         } else
336                             state = 99;
337                         break;
338                     case 51 :
339                         // Loop, looking for the delimiter now.
340                         if (token.equals("-")) {
341                             stop = 1;
342                             command = query.substring(query.indexOf('-') + 1).trim();
343                         }
344                         break;
345                     case 99 :
346                         // Error state, stop the machine
347                         stop = 1;
348                         break;
349                 }
350             }
351 
352             if (state == 1) // Machine ended with only a "Schedule" found. Just
353             // list the events
354             {
355                 if (!retrieveEvents(buddy)) {
356                     sendMessage(buddy, "No events available.");
357                 }
358             } else if (state == 50) { // Final state, all is well, schedule
359                                         // "IN"
360                 // type
361                 System.err.println("Scheduling " + command + " in " + String.valueOf(period));
362                 addEvent(buddy, period, command);
363                 sendMessage(buddy, "Event scheduled.");
364             } else if (state == 51) { // Final state, all is well, schedule
365                                         // "AT"
366                 // type
367                 System.err.println("Scheduling " + command + " at " + eventDate.toString());
368                 addEvent(buddy, eventDate, command);
369                 sendMessage(buddy, "Event scheduled.");
370             } else if (state == 99) {
371                 sendMessage(buddy, "Unable to understand schedule command");
372             } else
373                 sendMessage(buddy, "That schedule command is not implemented yet");
374         } catch (Exception e) {
375             sendMessage(buddy, "Unable to understand schedule command");
376             e.printStackTrace();
377         }
378     }
379 
380 
381     /***
382      * Forget functionality
383      * 
384      * @param buddy
385      * @param query
386      */
387     private void handleCancel(AIMBuddy buddy, String query) {
388         // handle offline request
389         StringTokenizer st = new StringTokenizer(query, " ");
390 
391         // check for proper params
392         if (st.countTokens() != 2) {
393             sendMessage(buddy, "ERROR:\n" + help());
394             return;
395         }
396         st.nextToken();
397         String tok = st.nextToken();
398         if (tok.equalsIgnoreCase("all")) {
399             clearEvents(buddy);
400             sendMessage(buddy, "All events removed.");
401         } else {
402             try {
403                 int loc = Integer.parseInt(tok) - 1;
404                 if (removeEvent(buddy, loc)) {
405                     sendMessage(buddy, "Event cancelled.");
406                 } else {
407                     sendMessage(buddy, "ERROR: number " + tok + " not valid.");
408                 }
409             } catch (Exception e) {
410                 e.printStackTrace();
411                 sendMessage(buddy, "ERROR: number invalid: " + tok);
412             }
413         }
414     }
415 
416 
417     /***
418      * Remove Specific Reminder
419      * 
420      * @param buddy
421      *            buddy
422      * @param i
423      *            list item number
424      * @return true if remove took place
425      */
426     public boolean removeEvent(AIMBuddy buddy, int i) {
427         ArrayList br = (ArrayList) events.get(buddy.getName());
428         if ((br != null) && (i >= 0) && (i < br.size())) {
429             br.remove(i);
430             return true;
431         }
432         return false;
433     }
434 
435 
436     /***
437      * @brief Removes an event given the event's SchedulerTask reference
438      * @param sTask
439      *            The reference to the item to remove
440      */
441     void removeEvent(SchedulerTask sTask) {
442         // Find the event to remove
443         ArrayList al;
444         al = (ArrayList) events.get(sTask.buddy.getName());
445         System.err.println("Removing events - " + String.valueOf(al.size()));
446         if (al != null) {
447             for (int i = 0; i < al.size(); i++) {
448 
449                 if ((SchedulerTask) al.get(i) == sTask) {
450                     al.remove(i);
451                     System.err.println("Removing " + String.valueOf(i));
452                     // removeEvent(sTask.buddy, i);
453                     break;
454                 }
455             }
456         }
457     }
458 
459 
460     /***
461      * clear reminders for a buddy
462      * 
463      * @param buddy
464      *            buddy
465      */
466     public void clearEvents(AIMBuddy buddy) {
467         ArrayList br = (ArrayList) events.get(buddy.getName());
468         if (br != null) {
469             br.clear();
470         }
471     }
472 
473 
474     /***
475      * Add a reminder for a buddy
476      * 
477      * @param buddy
478      *            buddy
479      * @param period
480      *            Delay for timer
481      */
482     public void addEvent(AIMBuddy buddy, long period, String command) {
483         ArrayList br = (ArrayList) events.get(buddy.getName());
484         if (br == null) {
485             br = new ArrayList();
486             events.put(buddy.getName(), br);
487         }
488 
489         SchedulerTask st = new SchedulerTask(buddy, command);
490         timer.schedule(st, period);
491 
492         br.add(st);
493     }
494 
495 
496     /***
497      * Add a reminder for a buddy
498      * 
499      * @param buddy
500      *            buddy
501      * @param eventDate
502      *            Date of scheduled event
503      */
504     public void addEvent(AIMBuddy buddy, Date eventDate, String command) {
505         ArrayList br = (ArrayList) events.get(buddy.getName());
506         if (br == null) {
507             br = new ArrayList();
508             events.put(buddy.getName(), br);
509         }
510 
511         SchedulerTask st = new SchedulerTask(buddy, command);
512         timer.schedule(st, eventDate);
513 
514         br.add(st);
515     }
516 
517 
518     /***
519      * @see com.levelonelabs.aim.XMLizable#writeState(Element)
520      */
521     public void writeState(Element emptyStateElement) {
522         Document doc = emptyStateElement.getOwnerDocument();
523         Iterator iter = events.keySet().iterator();
524 
525         while (iter.hasNext()) {
526             String name = (String) iter.next();
527             ArrayList br = (ArrayList) events.get(name);
528             if (br.size() > 0) {
529                 Element buddyElem = doc.createElement("buddy");
530                 buddyElem.setAttribute("name", name);
531                 for (int i = 0; i < br.size(); i++) {
532                     Element remElem = doc.createElement("event");
533                     SchedulerTask st = (SchedulerTask) br.get(i);
534 
535                     Element buddy = doc.createElement("buddy");
536                     CDATASection buddy_data = doc.createCDATASection(st.getBuddy().getName());
537                     buddy.appendChild(buddy_data);
538 
539                     Element cmd = doc.createElement("command");
540                     CDATASection cmd_data = doc.createCDATASection(st.getCommand());
541                     cmd.appendChild(cmd_data);
542 
543                     Element createDate = doc.createElement("createDate");
544                     CDATASection cd_data = doc.createCDATASection(st.getCreateDate());
545                     createDate.appendChild(cd_data);
546 
547                     remElem.appendChild(buddy);
548                     remElem.appendChild(cmd);
549                     remElem.appendChild(createDate);
550                     buddyElem.appendChild(remElem);
551                 }
552                 emptyStateElement.appendChild(buddyElem);
553             }
554         }
555     }
556 
557 
558     /***
559      * @see com.levelonelabs.aim.XMLizable#readState(Element)
560      */
561 
562     // This will be much harder to fix. Will have to diff the create date + the
563     // period
564     // with the current date to see if this reminder has been in cold storage
565     // for too
566     // long. If it hasn't, then figure out how long is left until the desired
567     // time and
568     // reschedule it with the new period.
569     public void readState(Element fullStateElement) {
570         /*
571          * //parse reminders events=new HashMap(); NodeList
572          * list=fullStateElement.getElementsByTagName("buddy"); for(int i=0; i <
573          * list.getLength(); i++) { Element buddyElem=(Element) list.item(i);
574          * String name=buddyElem.getAttribute("name"); AIMBuddy
575          * buddy=getBuddy(name); if(buddy != null) { NodeList
576          * rs=buddyElem.getElementsByTagName("event"); for(int j=0; j <
577          * rs.getLength(); j++) { String cmd; Element remindElem=(Element)
578          * rs.item(j); NodeList elems=remindElem.getChildNodes(); for(int k=0; k <
579          * elems.getLength(); k++) { =cdatas.item(k); node.
580          * if(node.getNodeType() == Node.CDATA_SECTION_NODE) { String
581          * event=node.getNodeValue(); addEvent(buddy,10, event); break; } } } } }
582          */
583     }
584 }