set flag Matches_fsi. static boolean hotwire_over_bot = false; static int maxAnswerLength = 1000, shortenTo = 200; static int askSelfMax = 10; static L botIDs = litlist("#1002317"); static L generalReleaseBots = litlist(); static new L bots; static new L activeBots; static Map botsByID = synchroTreeMap(); static new L disabledBots; // information for "answer" functions static new ThreadLocal userName; static new ThreadLocal slackTS; static new ThreadLocal channelName; static new ThreadLocal dialogID; static new ThreadLocal actualURI; static ThreadLocal> uploadFiles = new ThreadLocal; static new ThreadLocal attn; static new ThreadLocal dedicated; static new ThreadLocal originalLine; static new ThreadLocal answeringBot; static ThreadLocal> involvedBots = new ThreadLocal; // bots involved with making the current answer static new ThreadLocal postAs; static new ThreadLocal botIcon; static volatile S loadingBotID; static volatile Class loadingBot; static volatile boolean loaded; static new L history; // stores all processed user questions (not whole chat log) static volatile S lastAnswerBy; static volatile L lastInvolvedBots; static void coreInit() { load("disabledBots"); reload(); readLog(); loaded = true; } static S answerImpl(S s, boolean generalRelease) { new Matches m; S whoSaidThat = lastAnswerBy; L involvedBots = main.lastInvolvedBots; L tok = javaTok(s); if (isInteger(get(tok, 1)) && eq(get(tok, 3), ":")) { Class bot = getBot(get(tok, 1)); if (bot == null) ret "Bot not found: " + get(tok, 1); s = join(subList(tok, 5)); S answer = callDehBots(litlist(bot), s); ret empty(answer) ? "(no answer)" : answer; } if "get home page bot" exceptionToUser { if (!hasField(mc(), "homePageBotID")) ret "Eleu was compiled without web integration"; ret str(get(mc(), "homePageBotID")); } if (master()) { if "set home page bot *" exceptionToUser { S id = m.fsi(0); if (!hasField(mc(), "homePageBotID")) ret "Eleu was compiled without web integration"; set(mc(), "homePageBotID", id); call(mc(), "save", "homePageBotID"); ret format("OK, home page bot ID set to *", id); } if "set no home page bot *" exceptionToUser { S id = m.fsi(0); if (!hasField(mc(), "homePageBotID")) ret "Eleu was compiled without web integration"; set(mc(), "homePageBotID", null); call(mc(), "save", "homePageBotID"); ret format("OK, home page bot ID set to null"); } if "disable bot *" { setAdd(disabledBots, formatSnippetID(m.unq(0))); save("disabledBots"); ret "OK"; } if "enable bot *" { disabledBots.remove(formatSnippetID(m.unq(0))); save("disabledBots"); ret "OK"; } } if (match("reboot", s) && master()) { int n = l(bots); cleanUp(bots); bots.clear(); thread { sleepSeconds(1); // take latest args from source S vmArgs = cast loadVariableDefinition("vmArgs"); directNohupJavax(getProgramID(), vmArgs); System.exit(100); } ret n(n, "bot") + " cleaned up. Rebooting..."; } if (match("who said that", s)) ret (whoSaidThat == null ? "what?" : whoSaidThat) + " / " + structure(lastInvolvedBots); if (match("reload", s) && master()) { time { reload(); } ret l(bots) + "/" + l(botIDs) + " bots reloaded in "+ getLastTiming() + " ms."; } if (match("reload *", s, m) && isSnippetID(m.unq(0)) && master()) { reload(m.unq(0)); ret "Bot " + formatSnippetID(m.unq(0)) + " reloaded."; } if (match("list bots", s)) { S answer = "Active bots: " + structure(activeBots); L inactiveBots = diff(botIDs, activeBots); if (!inactiveBots.isEmpty()) answer += ". Inactive/disabled bots: " + structure(inactiveBots); if (!disabledBots.isEmpty()) answer += ". Disabled bots: " + structure(disabledBots); ret answer; } if (match("test", s)) ret "test me yes test me please!"; ret callDehBots(generalRelease ? getGeneralReleaseBots() : bots, s); } static S callDehBots(L bots, S s) { answeringBot.set(null); involvedBots.set(new TreeSet); postAs.set(null); botIcon.set(null); for (Class c : bots) try { S botID = getBotID(c); if (disabledBots.contains(formatSnippetID(botID))) continue; //print("Calling bot " + botID); S answer = callStaticAnswerMethod(c, s); if (!empty(answer)) { S whoSaidIt = or((S) getOpt(c, "whoSaidThat"), botID); answeringBot.set(whoSaidIt); involvedBots.get().add(formatSnippetID(whoSaidIt)); lastAnswerBy = whoSaidIt; lastInvolvedBots = new ArrayList(involvedBots.get()); print("Answering bot: " + whoSaidIt); ret answer; } } catch (Throwable e) { print("Error calling " + getProgramID(c) + ":\n" + getStackTrace(e)); } ret null; } static S getBotID(Class bot) { ret reverseLookup(botsByID, bot); } static int askSelfCounter; static synchronized S askSelf(S s) { if (askSelfCounter >= askSelfMax) fail("Too much recursion asking myself (depth " + askSelfMax + ")"); ++askSelfCounter; try { ret answer(s); } finally { --askSelfCounter; } } static synchronized S answer(S s) { ret answer(s, false); } static synchronized S answer(S s, boolean generalRelease) { long time = now(); S a = "ERREUR"; try { a = answerImpl(s, generalRelease); } finally { pcall { long x = now(); // We should record which bot answered! Map map = litmap("startTime", time, "duration", x-time, "question", s, "answer", a, "botID", "?"); printList(answerPriv(format("log timing *", structure(map)))); } } ret a; } // can give multiple answers static synchronized L answerPriv(S s) { new L l; for (Class bot : bots) pcall { S a = cast callOpt(bot, "answerPriv", s); if (!empty(a)) l.add(a); } ret l; } static L getGeneralReleaseBots() { new L l; for (S botID : generalReleaseBots) { Class c = botsByID.get("" + parseSnippetID(botID)); if (c != null) l.add(c); } ret l; } static void recordFeedback(S probableQuestion, S answer, S answerTime, S feedback, int score) { if (answer == null || probableQuestion == null) ret; Map data = litmap("realtime", now(), "question", probableQuestion, "answer", answer, "answertime", answerTime, "feedback", feedback, "score", score); print("Recording feedback: " + data); logQuoted(new File(programDir(), "feedback-log"), structure(data)); logQuoted(new File(programDir(), "memory-log"), structure(data)); } static void reloadLists() { botIDs = or((L) loadVariableDefinition("botIDs"), botIDs); generalReleaseBots = or((L) loadVariableDefinition("generalReleaseBots"), generalReleaseBots); botIDs.addAll(generalReleaseBots); } static void reload() { reloadLists(); cleanUp(bots); activeBots = new ArrayList(); botsByID.clear(); for (S botID : botIDs) pcall { loadBot(botID); } } static void reload(S botID) { reloadLists(); S parsedID = "" + parseSnippetID(botID); Class bot = botsByID.get(parsedID); if (bot != null) { cleanUp(bot); botsByID.remove(parsedID); activeBots.remove(formatSnippetID(botID)); bots.remove(bot); } loadBot(botID); } static void loadBot(S botID) { botID = formatSnippetID(botID); if (!botIDs.contains(botID)) fail("Bot not listed: " + botID); print("Loading bot: " + botID); Class c = hotwire_over_bot ? hotwire_overBot(botID) : hotwire(botID); setOpt(c, "mainBot", getMainClass()); loadingBotID = botID; loadingBot = c; try { callMain(c); bots.add(c); activeBots.add(formatSnippetID(botID)); // only if loading doesn't fail botsByID.put("" + parseSnippetID(botID), c); print("Loaded bot: " + botID + ", bots now: " + l(activeBots)); } finally { loadingBotID = null; loadingBot = null; } } static void readLog() { for (S prog : litlist("#1001915", getProgramID())) for (S s : scanLog(prog, "memory-log")) pcall { history.add((Map) safeUnstructure(s)); } print(l(history) + " history entries."); } // query threadlocals (called by sub-bots) static S getUserName() { ret userName.get(); } static S getSlackTS() { ret slackTS.get(); } static S getChannelName() { ret channelName.get(); } static S getDialogID() { ret dialogID.get(); } static S getActualURI() { ret actualURI.get(); } static Map getUploadFiles() { ret uploadFiles.get(); } static S getOriginalLine() { ret originalLine.get(); } static boolean isAddressed() { ret attn.get(); } static boolean dedicated() { ret dedicated.get(); } static Set getInvolvedBots() { ret involvedBots.get(); } // get the sub-bot dispatcher (#1002317) static O getDispatcher() { ret botsByID.get("" + parseSnippetID("#1002317")); } static Class getBot(S botID) { Class bot = botsByID.get("" + parseSnippetID(botID)); if (bot == null) { O disp = getDispatcher(); if (disp != null) ret (Class) call(disp, "getSubBot", botID); } ret bot; } static synchronized void historyAdd(Map logEntry) { logEntry.put("realtime", now()); logEntry.put("dialogID", getDialogID()); logEntry.put("involvedBots", toList(getInvolvedBots())); history.add(logEntry); logQuoted(new File(programDir(), "memory-log"), structure(logEntry)); } static void postAs(S name) { postAs.set(name); } static void botIcon(S url) { botIcon.set(url); }