00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 package com.rosalfred.core.ia.rivescript;
00021
00022 import java.io.BufferedReader;
00023 import java.io.DataInputStream;
00024 import java.io.File;
00025 import java.io.FileFilter;
00026 import java.io.FileInputStream;
00027 import java.io.FileNotFoundException;
00028 import java.io.IOException;
00029 import java.io.InputStreamReader;
00030 import java.text.Normalizer;
00031 import java.util.ArrayList;
00032 import java.util.Arrays;
00033 import java.util.HashMap;
00034 import java.util.List;
00035 import java.util.Random;
00036 import java.util.Vector;
00037 import java.util.regex.Matcher;
00038 import java.util.regex.Pattern;
00039
00040 import com.rivescript.Client;
00041 import com.rivescript.ClientManager;
00042 import com.rivescript.Topic;
00043 import com.rivescript.TopicManager;
00044 import com.rivescript.Trigger;
00045 import com.rivescript.Util;
00046 import com.rosalfred.core.ia.rivescript.lang.Java;
00047
00068 public class RiveScript {
00069
00070 public static final String TYPE_PYTHON = "python";
00071 public static final String TYPE_PERL = "perl";
00072 public static final String TYPE_JAVA = "java";
00073
00074
00075 private boolean debug = false;
00076 private int depth = 50;
00077 private String error = "";
00078 private static Random rand = new Random();
00079
00080
00084 public static final double VERSION = 0.02;
00085
00086
00087 private static final double RS_VERSION = 2.0;
00088 private static final String CMD_DEFINE = "!";
00089 private static final String CMD_TRIGGER = "+";
00090 private static final String CMD_PREVIOUS = "%";
00091 private static final String CMD_REPLY = "-";
00092 private static final String CMD_CONTINUE = "^";
00093 private static final String CMD_REDIRECT = "@";
00094 private static final String CMD_CONDITION = "*";
00095 private static final String CMD_LABEL = ">";
00096 private static final String CMD_ENDLABEL = "<";
00097
00098
00099 private TopicManager topics = new TopicManager();
00100
00101
00102 private ClientManager clients = new ClientManager();
00103
00104
00105 private HashMap<String, ObjectHandler<BotReply>> handlers =
00106 new HashMap<String, ObjectHandler<BotReply>>();
00107 protected HashMap<String, String> objects =
00108 new HashMap<String, String>();
00109
00110
00111
00112
00113 private HashMap<String, String> globals =
00114 new HashMap<String, String>();
00115 private HashMap<String, String> vars =
00116 new HashMap<String, String>();
00117 private HashMap<String, Vector<String>> arrays =
00118 new HashMap<String, Vector<String>>();
00119 private HashMap<String, String> subs =
00120 new HashMap<String, String>();
00121 private String[] subs_s =
00122 null;
00123 private HashMap<String, String> person =
00124 new HashMap<String, String>();
00125 private String[] person_s =
00126 null;
00127
00128
00129 private String currentUser = null;
00130
00131
00132
00133
00134
00140 public RiveScript (boolean debug) {
00141 this.debug = debug;
00142
00143
00144 Topic.setDebug(this.debug);
00145 }
00146
00150 public RiveScript () {
00151 this(false);
00152 }
00153
00154
00155
00156
00157
00161 public String error () {
00162 return this.error;
00163 }
00164
00170 protected boolean error(final String message) {
00171 this.error = message;
00172 return false;
00173 }
00174
00175 protected HashMap<String, ObjectHandler<BotReply>> getHandlers() {
00176 return this.handlers;
00177 }
00178
00179
00180
00181
00182
00190 public boolean loadDirectory (final String path, final String[] exts) {
00191 say("Load directory: " + path);
00192
00193
00194 final File dh = new File(path);
00195
00196
00197 for (int i = 0; i < exts.length; i++) {
00198
00199 this.say("Searching for files of type: " + exts[i]);
00200 final String type = exts[i];
00201 final List<File> filesRive = this.getFiles(dh, type);
00202
00203
00204 if (filesRive == null) {
00205 return error("Couldn't read any files from directory " + path);
00206 }
00207
00208
00209 for (File file : filesRive) {
00210 if (type.endsWith(TYPE_JAVA)) {
00211 loadFileJava(file.getAbsolutePath());
00212 } else {
00213 loadFileRive(file.getAbsolutePath());
00214 }
00215 }
00216 }
00217
00218 return true;
00219 }
00220
00221 protected List<File> getFiles(final File directory, final String type) {
00222 List<File> result = new ArrayList<File>();
00223 final List<File> directories = new ArrayList<File>();
00224
00225 final File[] filesRive = directory.listFiles(new FileFilter() {
00226 @Override
00227 public boolean accept (final File f) {
00228 boolean result = !f.isDirectory();
00229
00230 if (!result) {
00231 directories.add(f);
00232 }
00233
00234 return result && f.getName().endsWith(type);
00235 }
00236 });
00237
00238 if (filesRive.length > 0) {
00239 result.addAll(Arrays.asList(filesRive));
00240 }
00241
00242 for (File file : directories) {
00243 result.addAll(this.getFiles(file, type));
00244 }
00245
00246 return result;
00247 }
00248
00254 public boolean loadDirectory (final String path) {
00255 final String[] exts = { ".rive", ".rs", "." + TYPE_JAVA };
00256 return this.loadDirectory(path, exts);
00257 }
00258
00264 public boolean loadFileRive (final String file) {
00265 this.say("Load file: " + file);
00266
00267
00268 final File fh = new File(file);
00269
00270
00271 if (!fh.exists()) {
00272 return error(file + ": file not found.");
00273 }
00274 if (!fh.isFile()) {
00275 return error(file + ": not a regular file.");
00276 }
00277 if (!fh.canRead()) {
00278 return error(file + ": can't read from file.");
00279 }
00280
00281 String[] code;
00282 try {
00283 code = loadFile(file, fh);
00284 } catch (FileNotFoundException e) {
00285
00286 return error(file + ": file not found exception.");
00287 } catch (IOException e) {
00288 trace(e);
00289 return error(file + ": IOException while reading.");
00290 }
00291
00292
00293 return parse (file, code);
00294 }
00295
00301 public boolean loadFileJava (String file) {
00302 say("Load file: " + file);
00303
00304
00305 File fh = new File(file);
00306
00307
00308 if (fh.exists() == false) {
00309 return error(file + ": file not found.");
00310 }
00311 if (fh.isFile() == false) {
00312 return error(file + ": not a regular file.");
00313 }
00314 if (fh.canRead() == false) {
00315 return error(file + ": can't read from file.");
00316 }
00317
00318 String[] code;
00319 try {
00320 code = loadFile(file, fh);
00321 } catch (FileNotFoundException e) {
00322
00323 return error(file + ": file not found exception.");
00324 } catch (IOException e) {
00325 trace(e);
00326 return error(file + ": IOException while reading.");
00327 }
00328
00329 String filePath = fh.getAbsolutePath().replaceAll("."+TYPE_JAVA, "");
00330
00331
00332 Java java = (Java) this.handlers.get(TYPE_JAVA);
00333 java.onLoad(filePath, code);
00334
00335
00336 if (!this.objects.containsKey(java.getLastCompiledClassName())) {
00337 this.objects.put(java.getLastCompiledClassName(), TYPE_JAVA);
00338 }
00339
00340 return true;
00341 }
00342
00349 private String[] loadFile(String file, File fh) throws IOException {
00350
00351 Vector<String> lines = new Vector<String>();
00352
00353 FileInputStream fis = new FileInputStream(fh);
00354
00355
00356 DataInputStream dis = new DataInputStream(fis);
00357 BufferedReader br = new BufferedReader(new InputStreamReader(dis));
00358
00359
00360 String line;
00361 while ((line = br.readLine()) != null) {
00362 lines.add(line);
00363 }
00364
00365
00366 dis.close();
00367
00368
00369 String[] code = Util.Sv2s (lines);
00370 return code;
00371 }
00372
00379 public boolean stream (String code) {
00380
00381 String[] lines = code.split("\n");
00382
00383
00384 return parse("(streamed)", lines);
00385 }
00386
00393 public boolean stream (String[] code) {
00394
00395 return parse("(streamed)", code);
00396 }
00397
00398
00399
00400
00401
00408 public void setHandler (String name, ObjectHandler<BotReply> handler) {
00409 this.handlers.put(name, handler);
00410 }
00411
00430 public boolean setGlobal (String name, String value) {
00431 boolean delete = false;
00432 if (value == null || value == "<undef>") {
00433 delete = true;
00434 }
00435
00436
00437 if (name.equals("debug")) {
00438
00439 if (value.equals("true") ||
00440 value.equals("1") ||
00441 value.equals("yes")) {
00442 this.debug = true;
00443 }
00444 else if (value.equals("false") ||
00445 value.equals("0") ||
00446 value.equals("no") ||
00447 delete) {
00448 this.debug = false;
00449 }
00450 else {
00451 return error("Global variable \"debug\" needs a boolean value");
00452 }
00453 }
00454 else if (name.equals("depth")) {
00455
00456 try {
00457 this.depth = Integer.parseInt(value);
00458 } catch (NumberFormatException e) {
00459 return error("Global variable \"depth\" needs an integer value");
00460 }
00461 }
00462
00463
00464 if (delete) {
00465 globals.remove(name);
00466 }
00467 else {
00468 globals.put(name, value);
00469 }
00470
00471 return true;
00472 }
00473
00483 public boolean setVariable (String name, String value) {
00484 if (value == null || value == "<undef>") {
00485 vars.remove(name);
00486 }
00487 else {
00488 vars.put(name, value);
00489 }
00490
00491 return true;
00492 }
00493
00503 public boolean setSubstitution (String pattern, String output) {
00504 if (output == null || output == "<undef>") {
00505 subs.remove(pattern);
00506 }
00507 else {
00508 subs.put(pattern, output);
00509 }
00510
00511 return true;
00512 }
00513
00524 public boolean setPersonSubstitution (String pattern, String output) {
00525 if (output == null || output == "<undef>") {
00526 person.remove(pattern);
00527 }
00528 else {
00529 person.put(pattern, output);
00530 }
00531
00532 return true;
00533 }
00534
00543 public boolean setUservar (String user, String name, String value) {
00544 if (value == null || value == "<undef>") {
00545 clients.client(user).delete(name);
00546 }
00547 else {
00548 clients.client(user).set(name, value);
00549 }
00550
00551 return true;
00552 }
00553
00563 public boolean setUservars (String user, HashMap<String, String> data) {
00564
00565 clients.client(user).setData(data);
00566 return true;
00567 }
00568
00569 public ClientManager getUservars() {
00570 return this.clients;
00571 }
00572
00573 public void setUservars(ClientManager clients) {
00574 this.clients = clients;
00575 }
00576
00580 public String[] getUsers () {
00581
00582 return clients.listClients();
00583 }
00584
00585 public String getCurrentUser() {
00586 return this.currentUser;
00587 }
00588
00595 public HashMap<String, String> getUservars (String user) {
00596 if (clients.clientExists(user)) {
00597 return clients.client(user).getData();
00598 }
00599 else {
00600 return null;
00601 }
00602 }
00603
00613 public String getUservar (String user, String name) {
00614 if (clients.clientExists(user)) {
00615 return clients.client(user).get(name);
00616 }
00617 else {
00618 return null;
00619 }
00620 }
00621
00622
00623
00624
00625
00632 private boolean parse (String filename, String[] code) {
00633
00634 String topic = "random";
00635 int lineno = 0;
00636 boolean comment = false;
00637 boolean inobj = false;
00638 String objName = "";
00639 String objLang = "";
00640 Vector<String> objBuff = null;
00641 String onTrig = "";
00642
00643 String isThat = "";
00644
00645
00646 for (int i = 0; i < code.length; i++) {
00647 lineno++;
00648 String line = code[i];
00649 say("Line: " + line);
00650
00651
00652 line = line.trim();
00653
00654
00655 if (inobj) {
00656 if (line.startsWith("<object") || line.startsWith("< object")) {
00657
00658 if (handlers.containsKey(objLang)) {
00659
00660 handlers.get(objLang).onLoad(objName, Util.Sv2s(objBuff));
00661
00662
00663 objects.put(objName, objLang);
00664 }
00665
00666 objName = "";
00667 objLang = "";
00668 objBuff = null;
00669 inobj = false;
00670 continue;
00671 }
00672
00673
00674 objBuff.add(line);
00675 continue;
00676 }
00677
00678
00679 if (line.startsWith("/*")) {
00680
00681 if (line.indexOf("*/") > -1) {
00682
00683 continue;
00684 }
00685 comment = true;
00686 }
00687 else if (line.startsWith("/")) {
00688
00689 continue;
00690 }
00691 else if (line.indexOf("*/") > -1) {
00692
00693 comment = false;
00694 continue;
00695 }
00696 if (comment) {
00697 continue;
00698 }
00699
00700
00701 if (line.length() < 2) {
00702 continue;
00703 }
00704
00705
00706 String cmd = line.substring(0,1);
00707 line = line.substring(1).trim();
00708 say("\tCmd: " + cmd);
00709
00710
00711 if (line.indexOf(" // ") > -1) {
00712 String[] split = line.split(" // ");
00713 line = split[0];
00714 }
00715
00716
00717 if (cmd.equals(CMD_TRIGGER)) {
00718 isThat = "";
00719 }
00720
00721
00722 for (int j = (i + 1); j < code.length; j++) {
00723
00724 String peek = code[j].trim();
00725
00726
00727 if (peek.length() == 0) {
00728 continue;
00729 }
00730
00731
00732 String peekCmd = peek.substring(0,1);
00733 peek = peek.substring(1).trim();
00734
00735
00736 if (peek.length() > 0) {
00737
00738 if (peekCmd.equals(CMD_CONTINUE) == false &&
00739 peekCmd.equals(CMD_PREVIOUS) == false) {
00740 break;
00741 }
00742
00743
00744 if (cmd.equals(CMD_TRIGGER)) {
00745 if (peekCmd.equals(CMD_PREVIOUS)) {
00746
00747 isThat = peek;
00748 break;
00749 }
00750 else {
00751 isThat = "";
00752 }
00753 }
00754
00755
00756
00757 if (cmd.equals(CMD_DEFINE)) {
00758 if (peekCmd.equals(CMD_CONTINUE)) {
00759 line += "<crlf>" + peek;
00760 }
00761 }
00762
00763
00764
00765
00766 if (cmd.equals(CMD_CONTINUE) == false &&
00767 cmd.equals(CMD_PREVIOUS) == false &&
00768 cmd.equals(CMD_DEFINE) == false) {
00769 if (peekCmd.equals(CMD_CONTINUE)) {
00770 line += peek;
00771 }
00772 else {
00773 break;
00774 }
00775 }
00776 }
00777 }
00778
00779
00780 if (cmd.equals(CMD_DEFINE)) {
00781 say("\t! DEFINE");
00782 String[] whatis = line.split("\\s*=\\s*", 2);
00783 String[] left = whatis[0].split("\\s+", 2);
00784 String type = left[0];
00785 String var = "";
00786 String value = "";
00787 boolean delete = false;
00788 if (left.length == 2) {
00789 var = left[1].trim().toLowerCase();
00790 }
00791 if (whatis.length == 2) {
00792 value = whatis[1].trim();
00793 }
00794
00795
00796 if (!type.equals("array")) {
00797 value = value.replaceAll("<crlf>", "");
00798 }
00799
00800
00801 if (type.equals("version")) {
00802 say("\tUsing RiveScript version " + value);
00803
00804
00805 double version = 0;
00806 try {
00807 version = Double.valueOf(value).doubleValue();
00808 } catch (NumberFormatException e) {
00809 cry("RiveScript version \"" + value +
00810 "\" not a valid floating number",
00811 filename, lineno);
00812 continue;
00813 }
00814
00815 if (version > RS_VERSION) {
00816 cry("We can't parse RiveScript v" + value +
00817 " documents",
00818 filename, lineno);
00819 return false;
00820 }
00821
00822 continue;
00823 }
00824 else {
00825
00826 if (var.equals("")) {
00827 cry("Missing a " + type +
00828 " variable name",
00829 filename, lineno);
00830 continue;
00831 }
00832 if (value.equals("")) {
00833 cry("Missing a " + type + " value", filename, lineno);
00834 continue;
00835 }
00836 if (value.equals("<undef>")) {
00837
00838 delete = true;
00839 }
00840 }
00841
00842
00843 if (type.equals("global")) {
00844
00845 say("\tSet global " + var + " = " + value);
00846 this.setGlobal(var, value);
00847 }
00848 else if (type.equals("var")) {
00849
00850 say("\tSet bot variable " + var + " = " + value);
00851 this.setVariable(var, value);
00852 }
00853 else if (type.equals("array")) {
00854
00855 say("\tSet array " + var);
00856
00857
00858 if (delete) {
00859 arrays.remove(var);
00860 continue;
00861 }
00862
00863
00864 String[] parts = value.split("<crlf>");
00865 Vector<String> items = new Vector<String>();
00866 for (int a = 0; a < parts.length; a++) {
00867
00868 String[] pieces;
00869 if (parts[a].indexOf("|") > -1) {
00870 pieces = parts[a].split("\\|");
00871 }
00872 else {
00873 pieces = parts[a].split("\\s+");
00874 }
00875
00876
00877 for (int b = 0; b < pieces.length; b++) {
00878 items.add(pieces[b]);
00879 }
00880 }
00881
00882
00883 arrays.put(var, items);
00884 }
00885 else if (type.equals("sub")) {
00886
00887 say("\tSubstitution " + var + " => " + value);
00888 this.setSubstitution(var, value);
00889 }
00890 else if (type.equals("person")) {
00891
00892 say("\tPerson substitution " + var + " => " + value);
00893 this.setPersonSubstitution(var, value);
00894 }
00895 else {
00896 cry("Unknown definition type \"" + type +
00897 "\"",
00898 filename, lineno);
00899 continue;
00900 }
00901 }
00902 else if (cmd.equals(CMD_LABEL)) {
00903
00904 say("\t> LABEL");
00905 String label[] = line.split("\\s+");
00906 String type = "";
00907 String name = "";
00908 if (label.length >= 1) {
00909 type = label[0].trim().toLowerCase();
00910 }
00911 if (label.length >= 2) {
00912 name = label[1].trim();
00913 }
00914
00915
00916 if (type.equals("begin")) {
00917
00918 say("\tFound the BEGIN Statement.");
00919
00920
00921 type = "topic";
00922 name = "__begin__";
00923 }
00924 if (type.equals("topic")) {
00925
00926 say("\tSet topic to " + name);
00927 onTrig = "";
00928 topic = name;
00929
00930
00931 if (label.length >= 3) {
00932 final int mode_includes = 1;
00933 final int mode_inherits = 2;
00934 int mode = 0;
00935 for (int a = 2; a < label.length; a++) {
00936 if (label[a].toLowerCase().equals("includes")) {
00937 mode = mode_includes;
00938 }
00939 else if (label[a].toLowerCase().equals("inherits")) {
00940 mode = mode_inherits;
00941 }
00942 else if (mode > 0) {
00943
00944 if (mode == mode_includes) {
00945 topics.topic(topic).includes(label[a]);
00946 }
00947 else if (mode == mode_inherits) {
00948 topics.topic(topic).inherits(label[a]);
00949 }
00950 }
00951 }
00952 }
00953 }
00954 if (type.equals("object")) {
00955
00956 String lang = "";
00957 if (label.length >= 3) {
00958 lang = label[2].toLowerCase();
00959 }
00960
00961
00962 onTrig = "";
00963 if (lang.length() == 0) {
00964 cry("Trying to parse unknown programming language (assuming it's JavaScript)",
00965 filename, lineno);
00966 lang = "javascript";
00967 }
00968 if (!handlers.containsKey(lang)) {
00969
00970 say("We can't handle " + lang + " object code!");
00971 continue;
00972 }
00973
00974
00975 objName = name;
00976 objLang = lang;
00977 objBuff = new Vector<String>();
00978 inobj = true;
00979 }
00980 }
00981 else if (cmd.equals(CMD_ENDLABEL)) {
00982
00983 say("\t< ENDLABEL");
00984 String type = line.trim().toLowerCase();
00985
00986 if (type.equals("begin") || type.equals("topic")) {
00987 say("\t\tEnd topic label.");
00988 topic = "random";
00989 }
00990 else if (type.equals("object")) {
00991 say("\t\tEnd object label.");
00992 inobj = false;
00993 }
00994 else {
00995 cry("Unknown end topic type \"" + type + "\"",
00996 filename, lineno);
00997 }
00998 }
00999 else if (cmd.equals(CMD_TRIGGER)) {
01000
01001 say("\t+ TRIGGER: " + line);
01002
01003 if (isThat.length() > 0) {
01004
01005
01006 onTrig = line + "{previous}" + isThat;
01007 topics.topic(topic).trigger(line).hasPrevious(true);
01008 topics.topic(topic).addPrevious(line, isThat);
01009 }
01010 else {
01011
01012 onTrig = line;
01013 }
01014 }
01015 else if (cmd.equals(CMD_REPLY)) {
01016
01017 say("\t- REPLY: " + line);
01018
01019
01020 if (onTrig.length() == 0) {
01021 cry("Reply found before trigger", filename, lineno);
01022 continue;
01023 }
01024
01025
01026 topics.topic(topic).trigger(onTrig).addReply(line);
01027 }
01028 else if (cmd.equals(CMD_PREVIOUS)) {
01029
01030
01031 }
01032 else if (cmd.equals(CMD_CONTINUE)) {
01033
01034
01035 }
01036 else if (cmd.equals(CMD_REDIRECT)) {
01037
01038 say("\t@ REDIRECT: " + line);
01039
01040
01041 if (onTrig.length() == 0) {
01042 cry("Redirect found before trigger", filename, lineno);
01043 continue;
01044 }
01045
01046
01047
01048 topics.topic(topic).trigger(onTrig).addRedirect(line);
01049 }
01050 else if (cmd.equals(CMD_CONDITION)) {
01051
01052 say("\t* CONDITION: " + line);
01053
01054
01055 if (onTrig.length() == 0) {
01056 cry("Redirect found before trigger", filename, lineno);
01057 continue;
01058 }
01059
01060
01061 topics.topic(topic).trigger(onTrig).addCondition(line);
01062 }
01063 else {
01064 cry("Unrecognized command \"" + cmd + "\"", filename, lineno);
01065 }
01066 }
01067
01068 return true;
01069 }
01070
01071
01072
01073
01074
01079 public void sortReplies () {
01080
01081 String[] topics = this.topics.listTopics();
01082 say("There are " + topics.length + " topics to sort replies for.");
01083
01084
01085 this.topics.sortReplies();
01086
01087
01088 subs_s = Util.sortByLength (Util.SSh2s(subs));
01089 person_s = Util.sortByLength (Util.SSh2s(person));
01090 }
01091
01092
01093
01094
01095
01102 public BotReply reply (String username, String message) {
01103 say("Get reply to [" + username + "] " + message);
01104
01105 this.currentUser = username;
01106
01107
01108 message = formatMessage (message);
01109
01110
01111 BotReply reply = new BotReply("");
01112
01113
01114 if (topics.exists("__begin__")) {
01115 BotReply begin = this.reply (username, "request", true, 0);
01116
01117
01118 if (begin.getReply().indexOf("{ok}") > -1) {
01119
01120 reply = this.reply (username, message, false, 0);
01121 begin.setReply(begin.getReply().replaceAll(
01122 "\\{ok\\}", reply.getReply()));
01123 reply.setReply(begin.getReply());
01124 }
01125
01126
01127 reply = processTags (username,
01128 clients.client(username),
01129 message,
01130 reply,
01131 new ArrayList<String>(),
01132 new ArrayList<String>(),
01133 0);
01134 }
01135 else {
01136
01137 reply = this.reply (username, message, false, 0);
01138 }
01139
01140
01141 clients.client(username).addInput(message);
01142 clients.client(username).addReply(reply.getReply());
01143
01144
01145 return reply;
01146 }
01147
01156 private BotReply reply (String user, String message, boolean begin, int step) {
01157 BotReply result = new BotReply();
01158
01159
01160
01161
01162
01163 String topic = "random";
01164 ArrayList<String> stars = new ArrayList<String>();
01165 ArrayList<String> botstars = new ArrayList<String>();
01166 String reply = "";
01167 Client profile;
01168
01169
01170 profile = clients.client(user);
01171
01172
01173 topic = profile.get("topic");
01174
01175
01176 if (topics.exists(topic) == false) {
01177 cry("User " + user + " was in a missing topic named \"" + topic + "\"!");
01178 topic = "random";
01179 profile.set("topic", "random");
01180 }
01181
01182
01183 if (step > depth) {
01184 reply = "ERR: Deep Recursion Detected!";
01185 cry(reply);
01186 result.setReply(reply);
01187 return result;
01188 }
01189
01190
01191 if (begin) {
01192
01193 topic = "__begin__";
01194 }
01195
01196
01197
01198
01199
01200
01201 Trigger matched = null;
01202 boolean foundMatch = false;
01203 String matchedTrigger = "";
01204
01205
01206
01207 if (step == 0) {
01208 say("Looking for a %Previous");
01209 String[] allTopics = { topic };
01210
01211
01212 allTopics = this.topics.getTopicTree(topic, 0);
01213
01214 for (int i = 0; i < allTopics.length; i++) {
01215
01216 say("Seeing if " + allTopics[i] + " has a %Previous");
01217 if (this.topics.topic(allTopics[i]).hasPrevious()) {
01218 say("Topic " + allTopics[i] + " has at least one %Previous");
01219
01220
01221 String[] previous = this.topics.topic(allTopics[i]).listPrevious();
01222 for (int j = 0; j < previous.length; j++) {
01223 say("Candidate: " + previous[j]);
01224
01225
01226 String lastReply = formatMessage(profile.getReply(1));
01227 String regexp = triggerRegexp(user, profile, previous[j]);
01228 say("Compare " + lastReply + " <=> " + previous[j] + " (" + regexp + ")");
01229
01230
01231 Pattern re = Pattern.compile("^" + regexp + "$");
01232 Matcher m = re.matcher(lastReply);
01233 while (m.find() == true) {
01234 say("OMFG the lastReply matches!");
01235
01236
01237 for (int s = 1; s <= m.groupCount(); s++) {
01238 say("Add botstar: " + m.group(s));
01239 botstars.add(m.group(s));
01240 }
01241
01242
01243 String[] candidates = this.topics.topic(allTopics[i]).listPreviousTriggers(previous[j]);
01244 for (int k = 0; k < candidates.length; k++) {
01245 say("Does the user's message match " + candidates[k] + "?");
01246 String humanside = triggerRegexp(user, profile, candidates[k]);
01247 say("Compare " + message + " <=> " + candidates[k] + " (" + humanside + ")");
01248
01249 Pattern reH = Pattern.compile("^" + humanside + "$");
01250 Matcher mH = reH.matcher(message);
01251 while (mH.find() == true) {
01252 say("It's a match!!!");
01253
01254
01255 String realTrigger = candidates[k] + "{previous}" + previous[j];
01256 if (this.topics.topic(allTopics[i]).triggerExists(realTrigger)) {
01257
01258 for (int s = 1; s <= mH.groupCount(); s++) {
01259 say("Add star: " + mH.group(s));
01260 stars.add(mH.group(s));
01261 }
01262
01263 foundMatch = true;
01264 matchedTrigger = candidates[k];
01265 matched = this.topics.topic(allTopics[i]).trigger(realTrigger);
01266 }
01267
01268 break;
01269 }
01270
01271 if (foundMatch) {
01272 break;
01273 }
01274 }
01275 if (foundMatch) {
01276 break;
01277 }
01278 }
01279 }
01280 }
01281 }
01282 }
01283
01284
01285 if (foundMatch == false) {
01286
01287 String[] triggers = topics.topic(topic).listTriggers();
01288 for (int a = 0; a < triggers.length; a++) {
01289 String trigger = triggers[a];
01290
01291
01292 String regexp = triggerRegexp(user, profile, trigger);
01293 say("Try to match \"" + message + "\" against \"" + trigger + "\" (" + regexp + ")");
01294
01295
01296 Pattern re = Pattern.compile("^" + regexp + "$");
01297 Matcher m = re.matcher(message);
01298 if (m.find() == true) {
01299 say("The trigger matches! Star count: " + m.groupCount());
01300
01301
01302 int starcount = m.groupCount();
01303 for (int s = 1; s <= starcount; s++) {
01304 say("Add star: " + m.group(s));
01305 stars.add(m.group(s));
01306 }
01307
01308
01309
01310 if (this.topics.topic(topic).triggerExists(trigger)) {
01311
01312 matched = this.topics.topic(topic).trigger(trigger);
01313 }
01314 else {
01315 say("Trigger doesn't exist under this topic, trying to find it!");
01316 matched = this.topics.findTriggerByInheritance(topic, trigger, 0);
01317 }
01318
01319 foundMatch = true;
01320 matchedTrigger = trigger;
01321 break;
01322 }
01323 }
01324 }
01325
01326
01327 profile.set("__lastmatch__", matchedTrigger);
01328
01329
01330 if (foundMatch) {
01331 say("They were successfully matched to a trigger!");
01332
01333
01334
01335
01336
01337
01338 for (int n = 0; n < 1; n++) {
01339
01340 if (matched == null) {
01341 cry("Unknown error: they matched trigger " + matchedTrigger + ", but it doesn't exist?");
01342 foundMatch = false;
01343 break;
01344 }
01345
01346
01347 Trigger trigger = matched;
01348 say("The trigger matched belongs to topic " + trigger.topic());
01349
01350
01351 if (matched.listRedirects() != null && matched.listRedirects().length > 0) {
01352 say("Redirecting us to " + matched.listRedirects()[0]);
01353 result = processTags(user, profile, message,
01354 new BotReply(matched.listRedirects()[0]),
01355 stars, botstars, step);
01356 say("Pretend user said: " + result.getReply());
01357 reply = reply(user, result.getReply(), false, step=(step+1)).getReply();
01358 break;
01359 }
01360
01361
01362 String[] conditions = trigger.listConditions();
01363 if (conditions.length > 0) {
01364 say("This trigger has some conditions!");
01365
01366
01367 boolean truth = false;
01368 for (int c = 0; c < conditions.length; c++) {
01369
01370 String[] halves = conditions[c].split("\\s*=>\\s*");
01371 String condition = halves[0].trim();
01372 String potreply = halves[1].trim();
01373
01374
01375 Pattern reCond = Pattern.compile("^(.+?)\\s+(==|eq|\\!=|ne|<>|<|<=|>|>=)\\s+(.+?)$");
01376 Matcher mCond = reCond.matcher(condition);
01377 while (mCond.find()) {
01378 String left = mCond.group(1).trim();
01379 String eq = mCond.group(2).trim();
01380 String right = mCond.group(3).trim();
01381
01382
01383 left = processTags(user, profile, message, left, stars, botstars, step+1);
01384 right = processTags(user, profile, message, right, stars, botstars, step+1);
01385 say("Compare: " + left + " " + eq + " " + right);
01386
01387
01388 if (left.length() == 0) {
01389 left = "undefined";
01390 }
01391 if (right.length() == 0) {
01392 right = "undefined";
01393 }
01394
01395
01396 if (eq.equals("eq") || eq.equals("ne") || eq.equals("==") || eq.equals("!=") || eq.equals("<>")) {
01397
01398 if ((eq.equals("eq") || eq.equals("==")) && left.equals(right)) {
01399 truth = true;
01400 break;
01401 }
01402 else if ((eq.equals("ne") || eq.equals("!=") || eq.equals("<>")) && !left.equals(right)) {
01403 truth = true;
01404 break;
01405 }
01406 }
01407
01408
01409 int lt = 0;
01410 int rt = 0;
01411
01412
01413 try {
01414 lt = Integer.parseInt(left);
01415 rt = Integer.parseInt(right);
01416 } catch (NumberFormatException e) {
01417
01418 break;
01419 }
01420
01421
01422 if (eq.equals("==") || eq.equals("!=") || eq.equals("<>")) {
01423
01424 if (eq.equals("==") && lt == rt) {
01425 truth = true;
01426 break;
01427 }
01428 else if ((eq.equals("!=") || eq.equals("<>")) && lt != rt) {
01429 truth = true;
01430 break;
01431 }
01432 }
01433 else if (eq.equals("<") && lt < rt) {
01434 truth = true;
01435 break;
01436 }
01437 else if (eq.equals("<=") && lt <= rt) {
01438 truth = true;
01439 break;
01440 }
01441 else if (eq.equals(">") && lt > rt) {
01442 truth = true;
01443 break;
01444 }
01445 else if (eq.equals(">=") && lt >= rt) {
01446 truth = true;
01447 break;
01448 }
01449 }
01450
01451
01452 if (truth) {
01453 reply = potreply;
01454 break;
01455 }
01456 }
01457 }
01458
01459
01460 if (reply.length() > 0) {
01461 break;
01462 }
01463
01464
01465 String[] redirects = trigger.listRedirects();
01466 String[] replies = trigger.listReplies();
01467
01468
01469 Vector<Integer> bucket = new Vector<Integer>();
01470 Pattern reWeight = Pattern.compile("\\{weight=(\\d+?)\\}");
01471
01472
01473 for (int i = 0; i < redirects.length; i++) {
01474 if (redirects[i].indexOf("{weight=") > -1) {
01475 Matcher mWeight = reWeight.matcher(redirects[i]);
01476 while (mWeight.find()) {
01477 int weight = Integer.parseInt(mWeight.group(1));
01478
01479
01480 if (weight > 1) {
01481 for (int j = 0; j < weight; j++) {
01482 say("Trigger has a redirect (weight " + weight + "): " + redirects[i]);
01483 bucket.add(i);
01484 }
01485 }
01486 else {
01487 say("Trigger has a redirect (weight " + weight + "): " + redirects[i]);
01488 bucket.add(i);
01489 }
01490
01491
01492 break;
01493 }
01494 }
01495 else {
01496 say("Trigger has a redirect: " + redirects[i]);
01497 bucket.add(i);
01498 }
01499 }
01500
01501
01502 for (int i = 0; i < replies.length; i++) {
01503 if (replies[i].indexOf("{weight=") > -1) {
01504 Matcher mWeight = reWeight.matcher(replies[i]);
01505 while (mWeight.find()) {
01506 int weight = Integer.parseInt(mWeight.group(1));
01507
01508
01509 if (weight > 1) {
01510 for (int j = 0; j < weight; j++) {
01511 say("Trigger has a reply (weight " + weight + "): " + replies[i]);
01512 bucket.add(redirects.length + i);
01513 }
01514 }
01515 else {
01516 say("Trigger has a reply (weight " + weight + "): " + replies[i]);
01517 bucket.add(redirects.length + i);
01518 }
01519
01520
01521 break;
01522 }
01523 }
01524 else {
01525 say("Trigger has a reply: " + replies[i]);
01526 bucket.add(redirects.length + i);
01527 }
01528 }
01529
01530
01531 int[] choices = Util.Iv2s(bucket);
01532 if (choices.length > 0) {
01533 int choice = choices [ rand.nextInt(choices.length) ];
01534 say("Possible choices: " + choices.length + "; chosen: " + choice);
01535 if (choice < redirects.length) {
01536
01537 String redirect = redirects[choice].replaceAll("\\{weight=\\d+\\}","");
01538 say("Chosen a redirect to " + redirect + "!");
01539 reply = reply(user, redirect, begin, step+1).getReply();
01540 }
01541 else {
01542
01543 choice -= redirects.length;
01544 if (choice < replies.length) {
01545 say("Chosen a reply: " + replies[choice]);
01546 reply = replies[choice];
01547 }
01548 }
01549 }
01550 }
01551 }
01552
01553
01554 if (!foundMatch) {
01555 reply = "ERR: No Reply Matched";
01556 }
01557 else if (reply.length() == 0) {
01558 reply = "ERR: No Reply Found";
01559 }
01560
01561 say("Final reply: " + reply);
01562
01563
01564 if (begin) {
01565
01566
01567 if (reply.indexOf("<set") > -1) {
01568 Pattern reSet = Pattern.compile("<set (.+?)=(.+?)>");
01569 Matcher mSet = reSet.matcher(reply);
01570 while (mSet.find()) {
01571 String tag = mSet.group(0);
01572 String var = mSet.group(1);
01573 String value = mSet.group(2);
01574
01575
01576 profile.set(var, value);
01577 reply = reply.replace(tag, "");
01578 }
01579 }
01580
01581
01582 if (reply.indexOf("{topic=") > -1) {
01583 Pattern reTopic = Pattern.compile("\\{topic=(.+?)\\}");
01584 Matcher mTopic = reTopic.matcher(reply);
01585 while (mTopic.find()) {
01586 String tag = mTopic.group(0);
01587 topic = mTopic.group(1);
01588 say("Set user's topic to: " + topic);
01589 profile.set("topic", topic);
01590 reply = reply.replace(tag, "");
01591 }
01592 }
01593
01594 result.setReply(reply);
01595 }
01596 else {
01597
01598 result.setReply(reply);
01599 result = processTags (user, profile, message, result, stars, botstars, step);
01600 }
01601
01602 return result;
01603 }
01604
01611 private String triggerRegexp (String user, Client profile, String trigger) {
01612
01613 String regexp = trigger.replaceAll("^\\*$", "<zerowidthstar>");
01614
01615
01616 regexp = regexp.replaceAll("\\*", "(.+?)");
01617 regexp = regexp.replaceAll("#", "(\\\\d+?)");
01618 regexp = regexp.replaceAll("_", "(\\\\w+?)");
01619 regexp = regexp.replaceAll("\\{weight=\\d+\\}", "");
01620 regexp = regexp.replaceAll("<zerowidthstar>", "(.*?)");
01621
01622
01623 if (regexp.indexOf("[") > -1) {
01624 Pattern reOpts = Pattern.compile("\\s*\\[(.+?)\\]\\s*");
01625 Matcher mOpts = reOpts.matcher(regexp);
01626 while (mOpts.find() == true) {
01627 String optional = mOpts.group(0);
01628 String contents = mOpts.group(1);
01629
01630
01631 String[] parts = contents.split("\\|");
01632
01633
01634 StringBuffer re = new StringBuffer();
01635 for (int i = 0; i < parts.length; i++) {
01636
01637 re.append("\\s*" + parts[i] + "\\s*");
01638 if (i < parts.length - 1) {
01639 re.append("|");
01640 }
01641 }
01642 String pipes = re.toString();
01643
01644
01645
01646 pipes = pipes.replaceAll("\\(.+?\\)", "(?:.+?)");
01647 pipes = pipes.replaceAll("\\(\\d+?\\)", "(?:\\\\d+?");
01648 pipes = pipes.replaceAll("\\(\\w+?\\)", "(?:\\\\w+?)");
01649
01650
01651 pipes = "(?:" + pipes + "|\\s*)";
01652 regexp = regexp.replace(optional, pipes);
01653 }
01654 }
01655
01656
01657 regexp = regexp.replaceAll("\\\\w", "[a-z ]");
01658
01659
01660 if (regexp.indexOf("@") > -1) {
01661
01662 Pattern reArray = Pattern.compile("\\@(.+?)\\b");
01663 Matcher mArray = reArray.matcher(regexp);
01664 while (mArray.find() == true) {
01665 String array = mArray.group(0);
01666 String name = mArray.group(1);
01667
01668
01669 if (arrays.containsKey(name)) {
01670 String[] values = Util.Sv2s(arrays.get(name));
01671 StringBuffer joined = new StringBuffer();
01672
01673
01674 for (int i = 0; i < values.length; i++) {
01675 joined.append(values[i]);
01676 if (i < values.length - 1) {
01677 joined.append("|");
01678 }
01679 }
01680
01681
01682 String rep = "(?:" + joined.toString() + ")";
01683 regexp = regexp.replace(array, rep);
01684 }
01685 else {
01686
01687 regexp = regexp.replace(array, "");
01688 }
01689 }
01690 }
01691
01692
01693 if (regexp.indexOf("<bot") > -1) {
01694 Pattern reBot = Pattern.compile("<bot (.+?)>");
01695 Matcher mBot = reBot.matcher(regexp);
01696 while (mBot.find()) {
01697 String tag = mBot.group(0);
01698 String var = mBot.group(1);
01699 String value = vars.get(var).toLowerCase().replace("[^a-z0-9 ]+","");
01700
01701
01702 if (vars.containsKey(var)) {
01703 regexp = regexp.replace(tag, value);
01704 }
01705 else {
01706 regexp = regexp.replace(tag, "undefined");
01707 }
01708 }
01709 }
01710
01711
01712 if (regexp.indexOf("<get") > -1) {
01713 Pattern reGet = Pattern.compile("<get (.+?)>");
01714 Matcher mGet = reGet.matcher(regexp);
01715 while (mGet.find()) {
01716 String tag = mGet.group(0);
01717 String var = mGet.group(1);
01718 String value = profile.get(var).toLowerCase().replaceAll("[^a-z0-9 ]+","");
01719
01720
01721 regexp = regexp.replace(tag, value);
01722 }
01723 }
01724
01725
01726 regexp = regexp.replaceAll("<input>", "<input1>");
01727 regexp = regexp.replaceAll("<reply>", "<reply1>");
01728 if (regexp.indexOf("<input") > -1) {
01729 Pattern reInput = Pattern.compile("<input([0-9])>");
01730 Matcher mInput = reInput.matcher(regexp);
01731 while (mInput.find()) {
01732 String tag = mInput.group(0);
01733 int index = Integer.parseInt(mInput.group(1));
01734 String text = profile.getInput(index).toLowerCase().replaceAll("[^a-z0-9 ]+","");
01735 regexp = regexp.replace(tag, text);
01736 }
01737 }
01738 if (regexp.indexOf("<reply") > -1) {
01739 Pattern reReply = Pattern.compile("<reply([0-9])>");
01740 Matcher mReply = reReply.matcher(regexp);
01741 while (mReply.find()) {
01742 String tag = mReply.group(0);
01743 int index = Integer.parseInt(mReply.group(1));
01744 String text = profile.getReply(index).toLowerCase().replaceAll("[^a-z0-9 ]+","");
01745 regexp = regexp.replace(tag, text);
01746 }
01747 }
01748
01749 return regexp;
01750 }
01751
01752 private String processTags(String user,
01753 Client profile,
01754 String message,
01755 String reply,
01756 ArrayList<String> vstars,
01757 ArrayList<String> vbotstars,
01758 int step) {
01759 return this.processTags(user, profile, message, new BotReply(reply),
01760 vstars, vbotstars, step).getReply();
01761 }
01762
01774 private BotReply processTags (String user,
01775 Client profile,
01776 String message,
01777 BotReply botReply,
01778 ArrayList<String> vstars,
01779 ArrayList<String> vbotstars,
01780 int step) {
01781
01782 String reply = botReply.getReply();
01783
01784 Vector<String> starsList = new Vector<String>(vstars);
01785 Vector<String> botstarsList = new Vector<String>(vbotstars);
01786
01787
01788 starsList.add(0, "");
01789 botstarsList.add(0, "");
01790
01791
01792 if (starsList.size() == 1) {
01793 starsList.add("undefined");
01794 }
01795 if (botstarsList.size() == 1) {
01796 botstarsList.add("undefined");
01797 }
01798
01799
01800 String[] stars = Util.Sv2s(starsList);
01801 String[] botstars = Util.Sv2s(botstarsList);
01802
01803
01804 reply = reply.replaceAll("<person>", "{person}<star>{/person}");
01805 reply = reply.replaceAll("<@>", "{@<star>}");
01806 reply = reply.replaceAll("<formal>", "{formal}<star>{/formal}");
01807 reply = reply.replaceAll("<sentence>", "{sentence}<star>{/sentence}");
01808 reply = reply.replaceAll("<uppercase>", "{uppercase}<star>{/uppercase}");
01809 reply = reply.replaceAll("<lowercase>", "{lowercase}<star>{/lowercase}");
01810
01811
01812 reply = reply.replaceAll("\\{weight=\\d+\\}", "");
01813 reply = reply.replaceAll("<input>", "<input1>");
01814 reply = reply.replaceAll("<reply>", "<reply1>");
01815 reply = reply.replaceAll("<id>", user);
01816 reply = reply.replaceAll("\\\\s", " ");
01817 reply = reply.replaceAll("\\\\n", "\n");
01818 reply = reply.replaceAll("\\\\", "\\");
01819 reply = reply.replaceAll("\\#", "#");
01820
01821
01822 reply = reply.replaceAll("<star>", stars[1]);
01823 reply = reply.replaceAll("<botstar>", botstars[1]);
01824 for (int i = 1; i < stars.length; i++) {
01825 reply = reply.replaceAll("<star" + i + ">", stars[i]);
01826 }
01827 for (int i = 1; i < botstars.length; i++) {
01828 reply = reply.replaceAll("<botstar" + i + ">", botstars[i]);
01829 }
01830 reply = reply.replaceAll("<(star|botstar)\\d+>", "");
01831
01832
01833 if (reply.indexOf("<input") > -1) {
01834 Pattern reInput = Pattern.compile("<input([0-9])>");
01835 Matcher mInput = reInput.matcher(reply);
01836 while (mInput.find()) {
01837 String tag = mInput.group(0);
01838 int index = Integer.parseInt(mInput.group(1));
01839 String text = profile.getInput(index).toLowerCase().replaceAll("[^a-z0-9 ]+","");
01840 reply = reply.replace(tag, text);
01841 }
01842 }
01843 if (reply.indexOf("<reply") > -1) {
01844 Pattern reReply = Pattern.compile("<reply([0-9])>");
01845 Matcher mReply = reReply.matcher(reply);
01846 while (mReply.find()) {
01847 String tag = mReply.group(0);
01848 int index = Integer.parseInt(mReply.group(1));
01849 String text = profile.getReply(index).toLowerCase().replaceAll("[^a-z0-9 ]+","");
01850 reply = reply.replace(tag, text);
01851 }
01852 }
01853
01854
01855 if (reply.indexOf("{random}") > -1) {
01856 Pattern reRandom = Pattern.compile("\\{random\\}(.+?)\\{\\/random\\}");
01857 Matcher mRandom = reRandom.matcher(reply);
01858 while (mRandom.find()) {
01859 String tag = mRandom.group(0);
01860 String[] candidates = mRandom.group(1).split("\\|");
01861 String chosen = candidates [ rand.nextInt(candidates.length) ];
01862 reply = reply.replace(tag, chosen);
01863 }
01864 }
01865
01866
01867 if (reply.indexOf("<bot") > -1) {
01868 Pattern reBot = Pattern.compile("<bot (.+?)>");
01869 Matcher mBot = reBot.matcher(reply);
01870 while (mBot.find()) {
01871 String tag = mBot.group(0);
01872 String var = mBot.group(1);
01873
01874
01875 if (vars.containsKey(var)) {
01876 reply = reply.replace(tag, vars.get(var));
01877 }
01878 else {
01879 reply = reply.replace(tag, "undefined");
01880 }
01881 }
01882 }
01883
01884
01885 if (reply.indexOf("<env") > -1) {
01886 Pattern reEnv = Pattern.compile("<env (.+?)>");
01887 Matcher mEnv = reEnv.matcher(reply);
01888 while (mEnv.find()) {
01889 String tag = mEnv.group(0);
01890 String var = mEnv.group(1);
01891
01892
01893 if (globals.containsKey(var)) {
01894 reply = reply.replace(tag, globals.get(var));
01895 }
01896 else {
01897 reply = reply.replace(tag, "undefined");
01898 }
01899 }
01900 }
01901
01902
01903 if (reply.indexOf("{!") > -1) {
01904 Pattern reStream = Pattern.compile("\\{\\!(.+?)\\}");
01905 Matcher mStream = reStream.matcher(reply);
01906 while (mStream.find()) {
01907 String tag = mStream.group(0);
01908 String code = mStream.group(1);
01909 say("Stream new code in: " + code);
01910
01911
01912 this.stream(code);
01913 reply = reply.replace(tag, "");
01914 }
01915 }
01916
01917
01918 if (reply.indexOf("{person}") > -1) {
01919 Pattern rePerson = Pattern.compile("\\{person\\}(.+?)\\{\\/person\\}");
01920 Matcher mPerson = rePerson.matcher(reply);
01921 while (mPerson.find()) {
01922 String tag = mPerson.group(0);
01923 String text = mPerson.group(1);
01924
01925
01926 say("Run person substitutions: before: " + text);
01927 text = Util.substitute(person_s, person, text);
01928 say("After: " + text);
01929 reply = reply.replace(tag, text);
01930 }
01931 }
01932
01933
01934 if (reply.indexOf("{formal}") > -1 || reply.indexOf("{sentence}") > -1 ||
01935 reply.indexOf("{uppercase}") > -1 || reply.indexOf("{lowercase}") > -1) {
01936 String[] tags = { "formal", "sentence", "uppercase", "lowercase" };
01937 for (int i = 0; i < tags.length; i++) {
01938 Pattern reTag = Pattern.compile("\\{" + tags[i] + "\\}(.+?)\\{\\/" + tags[i] + "\\}");
01939 Matcher mTag = reTag.matcher(reply);
01940 while (mTag.find()) {
01941 String tag = mTag.group(0);
01942 String text = mTag.group(1);
01943
01944
01945 text = stringTransform(tags[i], text);
01946 reply = reply.replace(tag, text);
01947 }
01948 }
01949 }
01950
01951
01952 if (reply.indexOf("<set") > -1) {
01953 Pattern reSet = Pattern.compile("<set (.+?)=(.+?)>");
01954 Matcher mSet = reSet.matcher(reply);
01955 while (mSet.find()) {
01956 String tag = mSet.group(0);
01957 String var = mSet.group(1);
01958 String value = mSet.group(2);
01959
01960
01961 profile.set(var, value);
01962 reply = reply.replace(tag, "");
01963 say("Set user var " + var + "=" + value);
01964 }
01965 }
01966
01967
01968 if (reply.indexOf("<add") > -1 || reply.indexOf("<sub") > -1 ||
01969 reply.indexOf("<mult") > -1 || reply.indexOf("<div") > -1) {
01970 String[] tags = { "add", "sub", "mult", "div" };
01971 for (int i = 0; i < tags.length; i++) {
01972 Pattern reTag = Pattern.compile("<" + tags[i] + " (.+?)=(.+?)>");
01973 Matcher mTag = reTag.matcher(reply);
01974 while (mTag.find()) {
01975 String tag = mTag.group(0);
01976 String var = mTag.group(1);
01977 String value = mTag.group(2);
01978
01979
01980 String curvalue = profile.get(var);
01981 int current = 0;
01982 if (!curvalue.equals("undefined")) {
01983
01984 try {
01985 current = Integer.parseInt(curvalue);
01986 } catch (NumberFormatException e) {
01987
01988 reply = reply.replace(tag, "[ERR: Can't \"" + tags[i] + "\" non-numeric variable " + var + "]");
01989 continue;
01990 }
01991 }
01992
01993
01994 int modifier = 0;
01995 try {
01996 modifier = Integer.parseInt(value);
01997 } catch (NumberFormatException e) {
01998 reply = reply.replace(tag, "[ERR: Can't \"" + tags[i] + "\" non-numeric value " + value + "]");
01999 continue;
02000 }
02001
02002
02003 if (tags[i].equals("add")) {
02004 current += modifier;
02005 }
02006 else if (tags[i].equals("sub")) {
02007 current -= modifier;
02008 }
02009 else if (tags[i].equals("mult")) {
02010 current *= modifier;
02011 }
02012 else {
02013
02014 if (modifier == 0) {
02015 reply = reply.replace(tag, "[ERR: Can't divide by zero!]");
02016 continue;
02017 }
02018 current /= modifier;
02019 }
02020
02021
02022 profile.set(var, Integer.toString(current));
02023 reply = reply.replace(tag, "");
02024 }
02025 }
02026 }
02027
02028
02029 if (reply.indexOf("<get") > -1) {
02030 Pattern reGet = Pattern.compile("<get (.+?)>");
02031 Matcher mGet = reGet.matcher(reply);
02032 while (mGet.find()) {
02033 String tag = mGet.group(0);
02034 String var = mGet.group(1);
02035
02036
02037 reply = reply.replace(tag, profile.get(var));
02038 }
02039 }
02040
02041
02042 if (reply.indexOf("{topic=") > -1) {
02043 Pattern reTopic = Pattern.compile("\\{topic=(.+?)\\}");
02044 Matcher mTopic = reTopic.matcher(reply);
02045 while (mTopic.find()) {
02046 String tag = mTopic.group(0);
02047 String topic = mTopic.group(1);
02048 say("Set user's topic to: " + topic);
02049 profile.set("topic", topic);
02050 reply = reply.replace(tag, "");
02051 }
02052 }
02053
02054
02055 if (reply.indexOf("{@") > -1) {
02056 Pattern reRed = Pattern.compile("\\{@(.+?)\\}");
02057 Matcher mRed = reRed.matcher(reply);
02058 while (mRed.find()) {
02059 String tag = mRed.group(0);
02060 String target = mRed.group(1).trim();
02061
02062
02063 botReply = this.reply(user, target, false, step+1);
02064 reply = reply.replace(tag, botReply.getReply());
02065 }
02066 }
02067
02068
02069
02070
02071 if (reply.indexOf("<call>") > -1) {
02072 Pattern reCall = Pattern.compile("<call>(.+?)<\\/call>");
02073 Matcher mCall = reCall.matcher(reply);
02074 while (mCall.find()) {
02075 String tag = mCall.group(0);
02076 String data = mCall.group(1);
02077 String[] parts = data.split(" ");
02078 String name = parts[0];
02079 Vector<String> args = new Vector<String>();
02080 for (int i = 1; i < parts.length; i++) {
02081 args.add(parts[i]);
02082 }
02083
02084
02085 if (objects.containsKey(name)) {
02086
02087 String lang = objects.get(name);
02088 botReply = handlers.get(lang).onCall(
02089 name, user, Util.Sv2s(args));
02090 if (botReply.getReply() != null) {
02091 reply = reply.replace(tag, botReply.getReply());
02092 } else {
02093 reply = reply.replace(tag, "[ERR: Handler found but reply is null]");
02094 }
02095 }
02096 else {
02097 reply = reply.replace(tag, "[ERR: Object Not Found]");
02098 }
02099 }
02100 }
02101
02102 botReply.setReply(reply);
02103
02104 return botReply;
02105 }
02106
02113 private String stringTransform (String format, String text) {
02114 if (format.equals("uppercase")) {
02115 return text.toUpperCase();
02116 }
02117 else if (format.equals("lowercase")) {
02118 return text.toLowerCase();
02119 }
02120 else if (format.equals("formal")) {
02121
02122 String[] words = text.split(" ");
02123 say("wc: " + words.length);
02124 for (int i = 0; i < words.length; i++) {
02125 say("word: " + words[i]);
02126 String[] letters = words[i].split("");
02127 say("cc: " + letters.length);
02128 if (letters.length > 1) {
02129 say("letter 1: " + letters[1]);
02130 letters[1] = letters[1].toUpperCase();
02131 say("new letter 1: " + letters[1]);
02132 words[i] = Util.join(letters, "");
02133 say("new word: " + words[i]);
02134 }
02135 }
02136 return Util.join(words, " ");
02137 }
02138 else if (format.equals("sentence")) {
02139
02140 String[] letters = text.split("");
02141 if (letters.length > 1) {
02142 letters[1] = letters[1].toUpperCase();
02143 }
02144 return Util.join(letters, "");
02145 }
02146 else {
02147 return "[ERR: Unknown String Transform " + format + "]";
02148 }
02149 }
02150
02157 private String formatMessage (String message) {
02158
02159 message = message.toLowerCase();
02160
02161
02162 message = Util.substitute(subs_s, subs, message);
02163
02164
02165 message = message.replaceAll("\\<>", "");
02166 message = Normalizer.normalize(message, Normalizer.Form.NFD);
02167 message = message.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
02168
02169 return message;
02170 }
02171
02172
02173
02174
02175
02179 public void dumpSorted() {
02180 String[] topics = this.topics.listTopics();
02181 for (int t = 0; t < topics.length; t++) {
02182 String topic = topics[t];
02183 String[] triggers = this.topics.topic(topic).listTriggers();
02184
02185
02186 System.out.println("Topic: " + topic);
02187 for (int i = 0; i < triggers.length; i++) {
02188 System.out.println(" " + triggers[i]);
02189 }
02190 }
02191 }
02192
02196 public void dumpTopics () {
02197
02198 System.out.println("{");
02199 String[] topicList = topics.listTopics();
02200 for (int t = 0; t < topicList.length; t++) {
02201 String topic = topicList[t];
02202 String extra = "";
02203
02204
02205 String[] includes = topics.topic(topic).includes();
02206 String[] inherits = topics.topic(topic).inherits();
02207 if (includes.length > 0) {
02208 extra = "includes ";
02209 for (int i = 0; i < includes.length; i++) {
02210 extra += includes[i] + " ";
02211 }
02212 }
02213 if (inherits.length > 0) {
02214 extra += "inherits ";
02215 for (int i = 0; i < inherits.length; i++) {
02216 extra += inherits[i] + " ";
02217 }
02218 }
02219 System.out.println(" '" + topic + "' " + extra + " => {");
02220
02221
02222 String[] trigList = topics.topic(topic).listTriggers();
02223 for (int i = 0; i < trigList.length; i++) {
02224 String trig = trigList[i];
02225 System.out.println(" '" + trig + "' => {");
02226
02227
02228 String[] reply = topics.topic(topic).trigger(trig).listReplies();
02229 if (reply.length > 0) {
02230 System.out.println(" 'reply' => [");
02231 for (int r = 0; r < reply.length; r++) {
02232 System.out.println(" '" + reply[r] + "',");
02233 }
02234 System.out.println(" ],");
02235 }
02236
02237
02238 String[] cond = topics.topic(topic).trigger(trig).listConditions();
02239 if (cond.length > 0) {
02240 System.out.println(" 'condition' => [");
02241 for (int r = 0; r < cond.length; r++) {
02242 System.out.println(" '" + cond[r] + "',");
02243 }
02244 System.out.println(" ],");
02245 }
02246
02247
02248 String[] red = topics.topic(topic).trigger(trig).listRedirects();
02249 if (red.length > 0) {
02250 System.out.println(" 'redirect' => [");
02251 for (int r = 0; r < red.length; r++) {
02252 System.out.println(" '" + red[r] + "',");
02253 }
02254 System.out.println(" ],");
02255 }
02256
02257 System.out.println(" },");
02258 }
02259
02260 System.out.println(" },");
02261 }
02262 }
02263
02264
02265
02266
02267
02273 protected void say (String line) {
02274 if (this.debug) {
02275 System.out.println("[RS] " + line);
02276 }
02277 }
02278
02284 private void cry (String line) {
02285 System.out.println("<RS> " + line);
02286 }
02287
02295 private void cry (String text, String file, int line) {
02296 System.out.println("<RS> " + text + " at " + file + " line " + line + ".");
02297 }
02298
02304 private void trace (IOException e) {
02305 if (this.debug) {
02306 e.printStackTrace();
02307 }
02308 }
02309 }