1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 package com.levelonelabs.aimbot;
34
35 import java.io.File;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.util.ArrayList;
39 import java.util.Enumeration;
40 import java.util.Hashtable;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Properties;
44 import java.util.StringTokenizer;
45 import java.util.logging.FileHandler;
46 import java.util.logging.Handler;
47 import java.util.logging.Level;
48 import java.util.logging.Logger;
49
50 import javax.xml.parsers.DocumentBuilder;
51 import javax.xml.parsers.DocumentBuilderFactory;
52 import javax.xml.parsers.ParserConfigurationException;
53 import javax.xml.transform.Result;
54 import javax.xml.transform.Source;
55 import javax.xml.transform.Transformer;
56 import javax.xml.transform.TransformerFactory;
57 import javax.xml.transform.dom.DOMSource;
58 import javax.xml.transform.stream.StreamResult;
59
60 import org.w3c.dom.Document;
61 import org.w3c.dom.Element;
62 import org.w3c.dom.NodeList;
63
64 import com.levelonelabs.aim.AIMAdapter;
65 import com.levelonelabs.aim.AIMBuddy;
66 import com.levelonelabs.aim.AIMClient;
67 import com.levelonelabs.aim.AIMGroup;
68 import com.levelonelabs.aim.AIMSender;
69 import com.levelonelabs.aim.XMLizable;
70
71
72 /***
73 * The main Aimbot that uses the aim package to communicate with AIM and
74 * provides services via AIM through various bot modules that perform action
75 * when clients make specific queries to the screename registered by this bot.
76 *
77 * @author Scott Oster (ostersc@alumn.rpi.edu)
78 *
79 * @created January 10, 2002
80 */
81 public class AIMBot extends AIMAdapter {
82 public static final String ROLE_USER = "User";
83 public static final String ROLE_ENEMY = "Enemy";
84 public static final String ROLE_ADMINISTRATOR = "Administrator";
85
86 private static final String PERSISTANCE_FILENAME = "persistance.xml";
87 private static Logger logger = Logger.getLogger(AIMBot.class.getName());
88 /*** A handle to the outgoing AIM connection */
89 protected AIMSender aim;
90 private String username;
91 private String password;
92 private boolean autoAdd = false;
93 private Hashtable services;
94 private Hashtable groups;
95 private List modules;
96 private boolean enforceUser;
97 private String nonUserResponse;
98
99
100 /***
101 * Constructor for the AIMBot object
102 */
103 public AIMBot() {
104 services = new Hashtable();
105 groups = new Hashtable();
106 modules = new ArrayList();
107 }
108
109
110 /***
111 * Add the specified username as an admin of the bot, if it is not already
112 *
113 * @param admin
114 */
115 private void verifyAdmin(String admin) {
116 if (admin.equals("")) {
117 return;
118 }
119 AIMBuddy adminBuddy = aim.getBuddy(admin);
120 if (adminBuddy == null) {
121 adminBuddy = new AIMBuddy(admin);
122 }
123 if (!adminBuddy.hasRole(AIMBot.ROLE_USER)) {
124 adminBuddy.addRole(AIMBot.ROLE_USER);
125 logger.info("Adding " + AIMBot.ROLE_USER + " role to " + admin);
126 }
127 if (!adminBuddy.hasRole(AIMBot.ROLE_ADMINISTRATOR)) {
128 adminBuddy.addRole(AIMBot.ROLE_ADMINISTRATOR);
129 logger.info("Adding " + AIMBot.ROLE_ADMINISTRATOR + " role to " + admin);
130 }
131 if (aim.getBuddy(adminBuddy.getName()) == null) {
132 aim.addBuddy(adminBuddy);
133 logger.info("Adding " + admin + " as an admin.");
134 }
135 }
136
137
138 /***
139 * Start up the bot.
140 *
141 * @param args
142 * The command line arguments
143 */
144 public static void main(String[] args) {
145 String propsFile = System.getProperty("config.file", "bot.properties");
146
147 AIMBot bot = new AIMBot();
148 bot.init(propsFile);
149 }
150
151
152 /***
153 * Read in the properties file and configure the bot from it.
154 *
155 * @param propertiesFileName
156 * the name of the properties file to look for with the
157 * classloader.
158 */
159 public void init(String propertiesFileName) {
160
161
162
163
164 Properties props = null;
165 try {
166 InputStream is = ClassLoader.getSystemResourceAsStream(propertiesFileName);
167 props = new Properties();
168 props.load(is);
169 } catch (Exception e) {
170 logger.severe("Failed to load props file (" + propertiesFileName + ")!");
171 logger
172 .severe("You must configure bot.properties, or add -Dconfig.file=<CONFIGFILE> to use a different one(must be on classpath).");
173 e.printStackTrace();
174 System.exit(-1);
175 }
176
177
178 String userP = props.getProperty("aim.username");
179
180
181 userP = userP.replaceAll(" ", "");
182 String passP = props.getProperty("aim.password");
183 String autoaddP = props.getProperty("bot.autoadd", "false").trim();
184 String enforceUserP = props.getProperty("bot.enforceUser", "true").trim();
185 String profileP = props.getProperty("bot.profile", "I'm running code from:\nhttp://jaimbot.sourceforge.net/")
186 .trim();
187 String nonUserResponseP = props.getProperty("bot.nonuser.response",
188 "Sorry, you must be a user of this system to send requests.").trim();
189
190 setupAIM(userP, passP, profileP, nonUserResponseP, autoaddP, enforceUserP);
191
192
193 String loglevel = props.getProperty("logger.level");
194 String logFile = props.getProperty("logger.file", username + "_aimbot.log.xml");
195 setupLogger(loglevel, logFile);
196
197 String autoPersist = props.getProperty("bot.autopersist", "true").trim();
198
199 if (autoPersist.equalsIgnoreCase("true")) {
200 Runtime.getRuntime().addShutdownHook(new Thread() {
201 public void run() {
202 persist();
203 }
204 });
205 }
206
207
208 List modNames = new ArrayList();
209 for (int i = 0; i < props.size(); i++) {
210 if (props.containsKey("mod." + i)) {
211 String modName = props.getProperty("mod." + i);
212 modNames.add(modName);
213 }
214 }
215 loadModules(modNames);
216
217 depersist();
218 String admin = props.getProperty("bot.admin", "").trim();
219
220
221 admin = admin.replaceAll(" ", "");
222 verifyAdmin(admin);
223 aim.signOn();
224 }
225
226
227 private void setupAIM(String user, String pass, String profile, String nonUserResponse, String autoAddUsers,
228 String enforceUser) {
229 this.nonUserResponse = nonUserResponse;
230 if ((user == null) || (pass == null) || user.trim().equals("") || pass.trim().equals("")) {
231 logger.severe("ERROR: invalid username or password.");
232 System.exit(-1);
233 } else {
234 this.username = user;
235 this.password = pass;
236 }
237
238
239 this.autoAdd = false;
240 if (autoAddUsers.equalsIgnoreCase("true")) {
241 this.autoAdd = true;
242 }
243
244
245 this.enforceUser = true;
246 if (enforceUser.equalsIgnoreCase("false")) {
247 this.enforceUser = false;
248 }
249
250 aim = new AIMClient(username, password, profile, nonUserResponse, this.autoAdd);
251 aim.addAIMListener(this);
252 }
253
254
255 private void setupLogger(String logLevel, String logFilename) {
256 Level level = null;
257 try {
258 level = Level.parse(logLevel);
259 } catch (Exception e) {
260 System.err.println("ERROR parsing log level, defaulting to INFO");
261 level = Level.INFO;
262 }
263
264 try {
265 Handler fh = new FileHandler(logFilename);
266 Logger.getLogger("").addHandler(fh);
267
268 Logger.getLogger("").setLevel(level);
269 Handler[] handlers = Logger.getLogger("").getHandlers();
270 for (int i = 0; i < handlers.length; i++) {
271 handlers[i].setLevel(level);
272 }
273 } catch (Exception e) {
274 logger.severe("ERROR: unable to attach FileHandler to logger!");
275 e.printStackTrace();
276 }
277 }
278
279
280 /***
281 * Returns the AIM username of the bot.
282 *
283 * @return the AIM username of the bot.
284 */
285 public String getUsername() {
286 return username;
287 }
288
289
290 /***
291 * Gets the specified group
292 *
293 * @param groupName
294 *
295 * @return The group value
296 */
297 public AIMGroup getGroup(String groupName) {
298 return (AIMGroup) this.groups.get(groupName);
299 }
300
301
302 /***
303 * Gets an enum of the groups of the AIMBot
304 *
305 * @return The groups enumeration
306 */
307 public Enumeration getGroups() {
308 return this.groups.elements();
309 }
310
311
312 /***
313 * Will call the appropriate bot module to service the request.
314 *
315 * @param buddy
316 * the buddy making the request
317 * @param request
318 * the text of the request
319 */
320 public void handleMessage(AIMBuddy buddy, String request) {
321 if (buddy != null) {
322 logger.info(buddy.getName() + " said: " + request);
323 } else {
324 logger.info("Ignoring request:" + request + " from null buddy.");
325 return;
326 }
327
328 if (buddy.hasRole(ROLE_ENEMY)) {
329 logger.info("Ignoring request:" + request + " from Enemy:" + buddy.getName() + " and attempting to warn");
330 retaliate(buddy);
331 return;
332 }
333
334 if (this.enforceUser && !buddy.hasRole(ROLE_USER)) {
335
336
337 logger.info("Ignoring request:" + request + " from buddy that isn't a user, sending non user response.");
338 if (this.nonUserResponse != null && !this.nonUserResponse.trim().equals("")) {
339 aim.sendMessage(buddy, this.nonUserResponse);
340 }
341 return;
342 }
343
344 StringTokenizer stok = new StringTokenizer(request, " ");
345 String keyword = "";
346 if (stok.hasMoreTokens()) {
347 keyword = stok.nextToken().toLowerCase().trim();
348 }
349
350
351 if (keyword.equals("help")) {
352 StringBuffer sb = new StringBuffer();
353 if (stok.hasMoreTokens()) {
354 try {
355 int ind = Integer.parseInt(stok.nextToken());
356 BotModule bm = (BotModule) modules.get(ind);
357 sb.append("Help for " + bm.getName() + ":\n" + bm.help());
358 aim.sendMessage(buddy, sb.toString());
359 return;
360 } catch (Exception e) {
361 }
362 }
363 sb.append("For information on a specific service, type <I>help</I>");
364 sb.append(" followed by the service number listed below.\n");
365 sb.append("<B>Current Services:</B>\n");
366 for (int i = 0; i < modules.size(); i++) {
367 sb.append(i + ") " + ((BotModule) modules.get(i)).getName() + " ");
368 }
369 sb.append("\n<b>EXAMPLE: help 1</b>");
370
371 aim.sendMessage(buddy, sb.toString());
372 return;
373 } else if (keyword.equals("persist")) {
374 if (buddy.hasRole(AIMBot.ROLE_ADMINISTRATOR)) {
375 boolean success = this.persist();
376 if (success) {
377 aim.sendMessage(buddy, "Persistance Succeeded.");
378 } else {
379 aim.sendMessage(buddy, "Persistance Failed!");
380 }
381 }
382
383 return;
384 }
385
386 BotModule mod = (BotModule) services.get(keyword);
387 if (mod != null) {
388 logger.info("Request(" + keyword + ") being serviced by: " + mod.getName());
389 mod.performService(buddy, request);
390 } else {
391 mod = (BotModule) modules.get(0);
392 if (mod != null) {
393 logger.info("Default Request (" + keyword + ") being serviced by: " + mod.getName());
394 mod.performService(buddy, request);
395 } else {
396 logger.severe("Couldn't find mod to service request(" + keyword + ").");
397 }
398 }
399 }
400
401
402 /***
403 * Called when AIM encounters an error
404 *
405 * @param error
406 * the error code
407 * @param message
408 * decsriptive text describing the error
409 */
410 public void handleError(String error, String message) {
411 logger.severe("ERROR(" + error + "): " + message);
412 }
413
414
415 /***
416 * Handle being warned from others. Mark them with the Enemy role and warn
417 * them back after a witty response.
418 *
419 * @param enemy
420 * the buddy that warned us
421 * @param amount
422 * the current warning level
423 */
424 public void handleWarning(AIMBuddy enemy, int amount) {
425 if ((enemy == null) || (enemy.getName().equals("anonymous"))) {
426 logger.info("AIMBot UNDER ATTACK!: anonymous raised warning to " + amount + " ***");
427 return;
428 }
429 logger.info("AIMBot UNDER ATTACK!: " + enemy.getName() + " raised warning to " + amount + " ***");
430
431 retaliate(enemy);
432 }
433
434
435 /***
436 * Warn a user, and mark them as an Enemy. If they were already an Enemy,
437 * ban them.
438 *
439 * @param enemy
440 */
441 private void retaliate(AIMBuddy enemy) {
442 if (!enemy.hasRole(ROLE_ENEMY)) {
443 enemy.addRole(ROLE_ENEMY);
444 aim.sendMessage(enemy,
445 "In the End, we will remember not the words of our enemies, but the silence of our friends");
446 } else {
447 aim.banBuddy(enemy);
448 }
449 aim.sendWarning(enemy);
450 }
451
452
453 /***
454 * Adds a Group
455 *
456 * @param group
457 * The feature to be added to the Group attribute
458 */
459 public void addGroup(AIMGroup group) {
460 this.groups.put(group.getName(), group);
461 }
462
463
464 /***
465 * Removes the specified group
466 *
467 * @param group
468 */
469 public void removeGroup(AIMGroup group) {
470 this.groups.remove(group.getName());
471 }
472
473
474 /***
475 * All bot modules will call this and pass a reference to themselves and an
476 * ArrayList containing the keywords they want to listen for
477 *
478 * @param mod
479 * the module
480 */
481 protected void registerModule(BotModule mod) {
482 this.modules.add(mod);
483 ArrayList servicesList = mod.getServices();
484 if (servicesList != null) {
485 for (int i = 0; i < servicesList.size(); i++) {
486 registerService((String) servicesList.get(i), mod);
487 }
488 }
489 }
490
491
492 /***
493 * Load BotModules and register them
494 *
495 * @param modNames
496 * List of names to load
497 */
498 private void loadModules(List modNames) {
499
500 services = new Hashtable();
501 modules = new ArrayList();
502
503
504 for (int i = 0; i < modNames.size(); i++) {
505 try {
506
507 String modName = (String) modNames.get(i);
508 Class modclass = Class.forName(modName);
509 java.lang.reflect.Constructor constructor;
510 Class[] carr = {this.getClass()};
511 Object[] oarr = {this};
512
513
514 constructor = modclass.getConstructor(carr);
515
516 BotModule mod = (BotModule) constructor.newInstance(oarr);
517
518
519 registerModule(mod);
520 logger.info("Loading mod (" + mod.getName() + ")");
521 } catch (Exception e) {
522 e.printStackTrace();
523 logger.severe("Unable to load mod=" + modNames.get(i));
524 }
525 }
526 }
527
528
529 /***
530 * Method to persist all state to XML. Saves username, password, all
531 * buddies, all groups. Modules and services will be reloaded on
532 * depersistance so we dont need to save them; just give them a chance to
533 * persist and then depersist them next time loadmoduels is called.
534 *
535 * @return returns true iff the persistance succeeded.
536 */
537 private boolean persist() {
538 Document doc = createDomDocument();
539 Element root = doc.createElement("AIMBot_Persistance");
540 root.setAttribute("username", this.username);
541
542 doc.appendChild(root);
543
544
545 Element buddiesElem = doc.createElement("buddies");
546 for (Iterator iter = aim.getBuddyNames(); iter.hasNext();) {
547 String name = (String) iter.next();
548 AIMBuddy bud = aim.getBuddy(name);
549 if (bud == null) {
550 continue;
551 }
552 Element buddyElem = doc.createElement("buddy");
553 bud.writeState(buddyElem);
554 buddiesElem.appendChild(buddyElem);
555 }
556 root.appendChild(buddiesElem);
557
558
559 Element groupsElem = doc.createElement("groups");
560 for (Iterator iter = groups.keySet().iterator(); iter.hasNext();) {
561 String name = (String) iter.next();
562 AIMGroup grp = (AIMGroup) groups.get(name);
563 if (grp == null) {
564 continue;
565 }
566 Element groupElem = doc.createElement("group");
567 grp.writeState(groupElem);
568 groupsElem.appendChild(groupElem);
569 }
570 root.appendChild(groupsElem);
571
572
573 Element modsElem = doc.createElement("modules");
574 for (int i = 0; i < modules.size(); i++) {
575 BotModule mod = (BotModule) modules.get(i);
576 if (mod instanceof XMLizable) {
577
578 Element modElem = doc.createElement("module");
579 modElem.setAttribute("name", mod.getName());
580 try {
581 ((XMLizable) mod).writeState(modElem);
582 modsElem.appendChild(modElem);
583 } catch (Throwable t) {
584 logger.severe("Problem persisting mod +[" + mod.getName() + "], ignoring it.");
585 }
586 }
587 }
588 root.appendChild(modsElem);
589
590 try {
591
592 Source source = new DOMSource(doc);
593
594
595 File file = new File(this.username + "_" + PERSISTANCE_FILENAME);
596 Result result = new StreamResult(file);
597
598
599 Transformer xformer = TransformerFactory.newInstance().newTransformer();
600 xformer.transform(source, result);
601
602 } catch (Exception e) {
603 logger.severe("ERROR: failed to persist!");
604 e.printStackTrace();
605 return false;
606 }
607
608 logger.info("Successfully persisted AIMBot to file:" + this.username + "_" + PERSISTANCE_FILENAME);
609
610 return true;
611 }
612
613
614 /***
615 * Method depersist.
616 *
617 * @return returns true iff the depersistance succeeded.
618 */
619 private boolean depersist() {
620
621 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
622
623
624 Document d;
625 try {
626 d = factory.newDocumentBuilder().parse(new File(this.username + "_" + PERSISTANCE_FILENAME));
627 } catch (IOException e) {
628 logger.info("Couldn't locate persistance file; starting fresh.");
629 return false;
630 } catch (Exception e) {
631 logger.severe("Error reading persistance file; aborting depersitance.");
632 e.printStackTrace();
633 return false;
634 }
635
636
637 Element root = d.getDocumentElement();
638
639 if ((root == null) || !root.getTagName().equalsIgnoreCase("AIMBot_Persistance")) {
640 logger.severe("Error parsing persistance file; aborting depersitance.");
641 return false;
642 }
643
644
645 Element buddiesTag = (Element) root.getElementsByTagName("buddies").item(0);
646 NodeList list = buddiesTag.getElementsByTagName("buddy");
647 for (int i = 0; i < list.getLength(); i++) {
648 Element buddyElem = (Element) list.item(i);
649 String name = buddyElem.getAttribute("name");
650
651 AIMBuddy buddy = new AIMBuddy(name);
652 buddy.readState(buddyElem);
653 aim.addBuddy(buddy);
654 }
655
656
657 Element groupsTag = (Element) root.getElementsByTagName("groups").item(0);
658 list = groupsTag.getElementsByTagName("group");
659 for (int i = 0; i < list.getLength(); i++) {
660 Element groupElem = (Element) list.item(i);
661 String name = groupElem.getAttribute("name");
662
663 AIMGroup group = new AIMGroup(name);
664 group.readState(groupElem);
665 addGroup(group);
666 }
667
668
669 Element modsTag = (Element) root.getElementsByTagName("modules").item(0);
670 list = modsTag.getElementsByTagName("module");
671 for (int i = 0; i < list.getLength(); i++) {
672 Element modElem = (Element) list.item(i);
673 String name = modElem.getAttribute("name");
674 for (int j = 0; j < modules.size(); j++) {
675 BotModule mod = (BotModule) modules.get(j);
676 if ((mod.getName().equals(name)) && (mod instanceof XMLizable)) {
677 ((XMLizable) mod).readState(modElem);
678 }
679 }
680 }
681
682 logger.info("Successfully depersisted AIMBot from file:" + this.username + "_" + PERSISTANCE_FILENAME);
683
684 return true;
685 }
686
687
688 /***
689 * createDomDocument - creates an empty document
690 *
691 * @return Document - returns an empty document
692 */
693 protected static Document createDomDocument() {
694 try {
695 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
696 Document doc = builder.newDocument();
697 return doc;
698 } catch (ParserConfigurationException e) {
699 e.printStackTrace();
700 }
701
702 return null;
703 }
704
705
706 /***
707 * Registers a specific module to handle requests that start with a specific
708 * string
709 *
710 * @param keyword
711 * a word to look for in client text that identifies a bot module
712 * to be called
713 * @param mod
714 * the bot module that will handle this request
715 */
716 private void registerService(String keyword, BotModule mod) {
717 services.put(keyword, mod);
718 }
719 }