import java.util.*; import java.util.zip.*; import java.util.List; import java.util.regex.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.table.*; import java.io.*; import java.net.*; import java.lang.reflect.*; import java.lang.ref.*; import java.lang.management.*; import java.security.*; import java.security.spec.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.imageio.*; import java.math.*; public class main { static int slackSpeed = 500; static int historySuckSize = 10; static List attnPrefixes = litlist("!", "eleutheria", "eleu"); static List channels = new ArrayList(); static class Channel { String channelName; // includes the site String channelID, token; boolean generalRelease, postAsUser, dedicated; boolean firstTime = true; Map userInfos = new TreeMap(); String lastQuestion, lastAnswer; String lastAnswerTimestamp; // only for posted in this session int lastAnswerScore; void tendTo() { if (lastAnswerTimestamp != null) { // Poll reactions to our last answer - is there a more efficient way? int score = slackReactionScore(token, channelID, lastAnswerTimestamp); if (score != lastAnswerScore) { print("Reaction score changes to " + score); recordFeedback(lastQuestion, lastAnswer, lastAnswerTimestamp, "emoji", score-lastAnswerScore); lastAnswerScore = score; } } List qs = getNewQuestions(); //print(l(qs) + " question(s)."); //printList(qs); for (SlackMsg msg : qs) if (!isMyAnswer(msg)) { String q = msg.text; String userName = getUserName(msg.user); Map userInfo = getUserInfo(msg.user); if (userInfo != null && eq(true, userInfo.get("is_bot"))) { print("Skipping question from bot " + userName + ": " + quote(q)); continue; } q = q.trim(); print("Processing question: " + quote(q)); if (lastAnswer != null) { // TODO: maybe check the time delay int score = feedbackScore(q); if (score != 0) { recordFeedback(lastQuestion /*lastPostByUser.get(userName)*/, lastAnswer, lastAnswerTimestamp, q, score); if (score > 0) try { slackSmile(token, channelID, msg.ts); } catch (Throwable __e) { printStackTrace(__e); } else try { slackReact(token, channelID, msg.ts, "thinking_face"); } catch (Throwable __e) { printStackTrace(__e); } } lastAnswer = null; } //if (userName != null) lastPostByUser.put(userName, msg.text); try { originalLine.set(q); main.userName.set(userName); String dialogID = "slack-" + userName; main.dialogID.set(dialogID); main.slackTS.set(msg.ts); main.channelName.set(channelName); // attn yes/no? main.dedicated.set(dedicated); attn.set(dedicated); try { if (!dedicated && attnPrefixes.contains(get(javaTok(q), 1))) { attn.set(true); q = join(subList(javaTok(q), 3)).trim(); } } catch (Throwable __e) { printStackTrace(__e); } String a = answer(q, generalRelease); if (a != null) { // slack snippets have some size limit - let's forget that /*if (l(a) > maxAnswerLength) a = "```" + a + "```";*/ // post long stuff to tinybrain instead if (l(a) > maxAnswerLength) { String title = "Answer for " + userName + " (>> " + q + ")"; String id = ntUpload("eleutheria-for-user", title, unSlackSnippet(a)); boolean isSnippet = a.contains("```"); a = shorten(a, shortenTo) + (isSnippet ? "```" : "") + "\nFull answer here: tinybrain.de/" + parseSnippetID(id); } if (userName != null) a = "<@" + userName + "> " + a; print("Answering with: " + quote(a)); lastAnswer = a; lastQuestion = q; if (actuallyPost) { String answerTime = postAnswer(a); Map logEntry = litmap("question", q, "answertime", answerTime, "answer", a, "user", userName, "dedicated", dedicated); historyAdd(logEntry); } } } catch (Throwable __e) { printStackTrace(__e); } } } Map getUserInfo(String userID) { if (userID == null) return null; Map userInfo = userInfos.get(userID); if (userInfo == null) try { userInfo = slackGetUserInfo(token, userID); userInfos.put(userID, userInfo); } catch (Throwable __e) { printStackTrace(__e); } return userInfo; } String getUserName(String userID) { Map userInfo = getUserInfo(userID); return userInfo == null ? null : (String) userInfo.get("name"); } // returns the slack timestamp String postAnswer(String text) { String postAs = main.postAs.get(); String username = or(postAs, botUserName); String botIcon = main.botIcon.get(); String url = "https://slack.com/api/chat.postMessage"; Map postData = litmap( "token", token, "channel", channelID, "text", text, "username", username/*, "parse", "none", "link_names", "1"*/, "icon_url", botIcon); if (postAsUser && postAs == null) postData.put("as_user", "true"); printStructure(postData); String data = doPost(postData, url); Map map = jsonDecodeMap(data); lastAnswerTimestamp = (String) map.get("ts"); lastAnswerScore = 0; print("postAnswer: " + data); return lastAnswerTimestamp; } String getNewQuestions_lastSeen; List getNewQuestions() { // Get historySuckSize on first call (history before bot start) // and 1000 thereafter (just grab anything new) int limit = getNewQuestions_lastSeen == null ? historySuckSize : 1000; List msgs = slackReadChannel(channelID, token, limit, getNewQuestions_lastSeen); if (!msgs.isEmpty()) getNewQuestions_lastSeen = last(msgs).ts; int i = indexOfLastBotAnswer(msgs); if (i >= 0) { print("Last bot answer at " + (i+1) + "/" + l(msgs) + ": " + structure(msgs.get(i))); // update variables lastAnswer = msgs.get(i).text; for (int j = 0; j < i; j++) { SlackMsg msg = msgs.get(j); String userName = getUserName(msg.user); //if (userName != null) lastPostByUser.put(userName, msg.text); } if (firstTime) msgs = subList(msgs, i+1); } firstTime = false; return msgs; } } // end of class Channel static boolean slackBot_inited; static void initSlackBot() { if (slackBot_inited) return; slackBot_inited = true; String devChannelToken = devChannelToken(); String nlbotsToken = loadSecretTextFile("#1001925", "nlbots-slack-token").trim(); String eleuToken = loadSecretTextFile("#1001925", "eleu-slack-token").trim(); Channel c; c = new Channel(); c.channelName = "devchannel, #talkingbots"; c.channelID = "C0H0SR40J"; c.token = devChannelToken; c.postAsUser = true; channels.add(c); c = new Channel(); c.channelName = "devchannel, #eleudedi"; c.channelID = "C0MDDQEJY"; c.token = devChannelToken; c.postAsUser = true; c.dedicated = true; channels.add(c); c = new Channel(); c.channelName = "eleu, #general"; c.channelID = "C0MLVC346"; c.token = eleuToken; c.postAsUser = true; c.dedicated = true; channels.add(c); c = new Channel(); c.channelName = "devchannel, #general"; c.channelID = "C0H0Q0J3D"; c.token = devChannelToken; c.postAsUser = true; channels.add(c); c = new Channel(); c.channelName = "devchannel, #programming"; c.channelID = "C0H0MG0F6"; c.token = devChannelToken; c.postAsUser = true; channels.add(c); c = new Channel(); c.channelName = "devchannel, #c_sharp"; c.channelID = "C0H0T0EQ6"; c.token = devChannelToken; c.postAsUser = true; channels.add(c); c = new Channel(); c.channelName = "nlbots, #general"; c.channelID = "C0FHTG6SY"; // nlbots, #general c.token = nlbotsToken; channels.add(c); /*c = new Channel(); c.channelID = "C0G5GPEMR"; // nlbots, #talkingbots c.token = nlbotsToken; channels.add(c);*/ /*c = new Channel(); c.channelID = "G0HLW98RY"; // devchannel, #bot_test c.token = devChannelToken; c.postAsUser = true; channels.add(c);*/ } static int indexOfLastBotAnswer(List msgs) { for (int i = l(msgs)-1; i >= 0; i--) if (isMyAnswer(msgs.get(i))) return i; return -1; } static boolean isMyAnswer(SlackMsg msg) { return msg.botName != null || eq(msg.userName, actualBotUserName); } static void slackBotLoop() { try { long lastPrinted = now(); while (true) { for (Channel c : channels) try { c.tendTo(); } catch (Throwable __e) { printStackTrace(__e); } sleep(slackSpeed); if (now() > lastPrinted + 60*1000) { lastPrinted = now(); print("Slack loop still going. " + unixTime()); } } } finally { print("Slack bot loop exit!?"); } } static void sayInDedicated(String s) { for (Channel c : channels) try { if (c.dedicated) c.postAnswer(s); } catch (Throwable __e) { printStackTrace(__e); } } static void dediSay(String s) { sayInDedicated(s); } // Slack Bot static boolean hotwire_over_bot = true; static int maxAnswerLength = 1000, shortenTo = 200; static int askSelfMax = 10; static List botIDs = litlist("#1002317"); static List generalReleaseBots = litlist(); static List bots = new ArrayList(); static List activeBots = new ArrayList(); static Map botsByID = synchroTreeMap(); static List disabledBots = new ArrayList(); // information for "answer" functions static ThreadLocal userName = new ThreadLocal(); static ThreadLocal slackTS = new ThreadLocal(); static ThreadLocal channelName = new ThreadLocal(); static ThreadLocal dialogID = new ThreadLocal(); static ThreadLocal actualURI = new ThreadLocal(); static ThreadLocal attn = new ThreadLocal(); static ThreadLocal dedicated = new ThreadLocal(); static ThreadLocal originalLine = new ThreadLocal(); static ThreadLocal answeringBot = new ThreadLocal(); static ThreadLocal> involvedBots = new ThreadLocal(); // bots involved with making the current answer static ThreadLocal postAs = new ThreadLocal(); static ThreadLocal botIcon = new ThreadLocal(); static volatile String loadingBotID; static volatile Class loadingBot; static volatile boolean loaded; static List history = new ArrayList(); // stores all processed user questions (not whole chat log) static volatile String lastAnswerBy; static volatile List lastInvolvedBots; static void coreInit() { load("disabledBots"); reload(); readLog(); loaded = true; } static String answerImpl(String s, boolean generalRelease) { Matches m = new Matches(); String whoSaidThat = lastAnswerBy; List involvedBots = main.lastInvolvedBots; List tok = javaTok(s); if (isInteger(get(tok, 1)) && eq(get(tok, 3), ":")) { Class bot = getBot(get(tok, 1)); if (bot == null) return "Bot not found: " + get(tok, 1); s = join(subList(tok, 5)); String answer = callDehBots(litlist(bot), s); return empty(answer) ? "(no answer)" : answer; } if (match("get home page bot", s, m)) try { if (!hasField(mc(), "homePageBotID")) return "Eleu was compiled without web integration"; return str(get(mc(), "homePageBotID")); } catch (Throwable __e) { return exceptionToUser(__e); } if (master()) { if (match("set home page bot *", s, m)) try { String id = m.fsi(0); if (!hasField(mc(), "homePageBotID")) return "Eleu was compiled without web integration"; set(mc(), "homePageBotID", id); call(mc(), "save", "homePageBotID"); return format("OK, home page bot ID set to *", id); } catch (Throwable __e) { return exceptionToUser(__e); } if (match("set no home page bot *", s, m)) try { String id = m.fsi(0); if (!hasField(mc(), "homePageBotID")) return "Eleu was compiled without web integration"; set(mc(), "homePageBotID", null); call(mc(), "save", "homePageBotID"); return format("OK, home page bot ID set to null"); } catch (Throwable __e) { return exceptionToUser(__e); } if (match("disable bot *", s, m)) { setAdd(disabledBots, formatSnippetID(m.unq(0))); save("disabledBots"); return "OK"; } if (match("enable bot *", s, m)) { disabledBots.remove(formatSnippetID(m.unq(0))); save("disabledBots"); return "OK"; } } if (match("reboot", s) && master()) { int n = l(bots); cleanUp(bots); bots.clear(); Thread _t_0 = new Thread() { public void run() { try { sleepSeconds(1); // take latest args from source String vmArgs = (String) ( loadVariableDefinition("vmArgs")); nohupJavax(getProgramID(), vmArgs); System.exit(100); } catch (Exception _e) { throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } } }; _t_0.start(); return n(n, "bot") + " cleaned up. Rebooting..."; } if (match("who said that", s)) return (whoSaidThat == null ? "what?" : whoSaidThat) + " / " + structure(lastInvolvedBots); if (match("reload", s) && master()) { { long __time_startTime = now(); try { reload(); } finally { __time_startTime = now()-__time_startTime; saveTiming(__time_startTime); } } return l(bots) + "/" + l(botIDs) + " bots reloaded in "+ getLastTiming() + " ms."; } if (match("reload *", s, m) && isSnippetID(m.unq(0)) && master()) { reload(m.unq(0)); return "Bot " + formatSnippetID(m.unq(0)) + " reloaded."; } if (match("list bots", s)) { String answer = "Active bots: " + structure(activeBots); List inactiveBots = diff(botIDs, activeBots); if (!inactiveBots.isEmpty()) answer += ". Inactive/disabled bots: " + structure(inactiveBots); if (!disabledBots.isEmpty()) answer += ". Disabled bots: " + structure(disabledBots); return answer; } if (match("test", s)) return "test me yes test me please!"; return callDehBots(generalRelease ? getGeneralReleaseBots() : bots, s); } static String callDehBots(List bots, String s) { answeringBot.set(null); involvedBots.set(new TreeSet()); postAs.set(null); botIcon.set(null); for (Class c : bots) try { String botID = getBotID(c); if (disabledBots.contains(formatSnippetID(botID))) continue; //print("Calling bot " + botID); String answer = callStaticAnswerMethod(c, s); if (!empty(answer)) { String whoSaidIt = or((String) getOpt(c, "whoSaidThat"), botID); answeringBot.set(whoSaidIt); involvedBots.get().add(formatSnippetID(whoSaidIt)); lastAnswerBy = whoSaidIt; lastInvolvedBots = new ArrayList(involvedBots.get()); print("Answering bot: " + whoSaidIt); return answer; } } catch (Throwable e) { print("Error calling " + getProgramID(c) + ":\n" + getStackTrace(e)); } return null; } static String getBotID(Class bot) { return reverseLookup(botsByID, bot); } static int askSelfCounter; static synchronized String askSelf(String s) { if (askSelfCounter >= askSelfMax) fail("Too much recursion asking myself (depth " + askSelfMax + ")"); ++askSelfCounter; try { return answer(s); } finally { --askSelfCounter; } } static synchronized String answer(String s) { return answer(s, false); } static synchronized String answer(String s, boolean generalRelease) { long time = now(); String a = "ERREUR"; try { a = answerImpl(s, generalRelease); } finally { try { 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)))); } catch (Throwable __e) { printStackTrace(__e); } } return a; } // can give multiple answers static synchronized List answerPriv(String s) { List l = new ArrayList(); for (Class bot : bots) try { String a = (String) ( callOpt(bot, "answerPriv", s)); if (!empty(a)) l.add(a); } catch (Throwable __e) { printStackTrace(__e); } return l; } static List getGeneralReleaseBots() { List l = new ArrayList(); for (String botID : generalReleaseBots) { Class c = botsByID.get("" + parseSnippetID(botID)); if (c != null) l.add(c); } return l; } static void recordFeedback(String probableQuestion, String answer, String answerTime, String feedback, int score) { if (answer == null || probableQuestion == null) return; 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((List) loadVariableDefinition("botIDs"), botIDs); generalReleaseBots = or((List) loadVariableDefinition("generalReleaseBots"), generalReleaseBots); botIDs.addAll(generalReleaseBots); } static void reload() { reloadLists(); cleanUp(bots); activeBots = new ArrayList(); botsByID.clear(); for (String botID : botIDs) try { loadBot(botID); } catch (Throwable __e) { printStackTrace(__e); } } static void reload(String botID) { reloadLists(); String 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(String 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 (String prog : litlist("#1001915", getProgramID())) for (String s : scanLog(prog, "memory-log")) try { history.add((Map) safeUnstructure(s)); } catch (Throwable __e) { printStackTrace(__e); } print(l(history) + " history entries."); } // query threadlocals (called by sub-bots) static String getUserName() { return userName.get(); } static String getSlackTS() { return slackTS.get(); } static String getChannelName() { return channelName.get(); } static String getDialogID() { return dialogID.get(); } static String getActualURI() { return actualURI.get(); } static String getOriginalLine() { return originalLine.get(); } static boolean isAddressed() { return attn.get(); } static boolean dedicated() { return dedicated.get(); } static Set getInvolvedBots() { return involvedBots.get(); } // get the sub-bot dispatcher (#1002317) static Object getDispatcher() { return botsByID.get("" + parseSnippetID("#1002317")); } static Class getBot(String botID) { Class bot = botsByID.get("" + parseSnippetID(botID)); if (bot == null) { Object disp = getDispatcher(); if (disp != null) return (Class) call(disp, "getSubBot", botID); } return bot; } static synchronized void historyAdd(Map logEntry) { logEntry.put("realtime", now()); logEntry.put("dialogID", getDialogID()); logEntry.put("involvedBots", list(getInvolvedBots())); history.add(logEntry); logQuoted(new File(programDir(), "memory-log"), structure(logEntry)); } static void postAs(String name) { postAs.set(name); } static void botIcon(String url) { botIcon.set(url); } // Eleu Core just to satisfy dependencies of above static boolean actuallyPost = true; static String botUserName = "eleu_learns_to_think"; static String actualBotUserName = "eleu"; // token user name static Channel channel; public static void main(String[] args) throws Exception { initSlackBot(); // makes the channel list, but doesn't start any loop channel = channels.get(0); // #talkingbots //channel.postAnswer("I am a puppet!"); print("Please type \"say [bla]\" to make her say things."); Android3 android = new Android3("Eleu Sock Puppet Control"); android.responder = makeResponder(new Object() { String answer(String s) { Lisp tree = nlParse(s); HashMap m = new HashMap(); if (nlMatch("say $x", tree, m)) { String line = nlUnparse(m.get("x")); line += " [he made me say that!]"; channel.postAnswer(line); return "OK, posted!"; } return null; } }); makeBot(android); } static long getLastTiming() { ThreadLocal tl = (ThreadLocal) ( getOpt(getMainClass(), "saveTiming_last")); if (tl == null) return -1; Long l = tl.get(); return l == null ? -1 : l; } static boolean nlMatch(String pat, String nl, Map matches) { matches.clear(); return nlMatch_sub(nlParse/*_cached*/(pat), nlParse(nl), matches); } static boolean nlMatch(Lisp pat, String nl, Map matches) { matches.clear(); return nlMatch_sub(pat, nlParse(nl), matches); } static boolean nlMatch(String pat, Lisp nl, Map matches) { matches.clear(); return nlMatch_sub(nlParse/*_cached*/(pat), nl, matches); } static boolean nlMatch(Lisp pat, Lisp nl, Map matches) { matches.clear(); return nlMatch_sub(pat, nl, matches); } static boolean nlMatch_sub(Lisp pat, Lisp nl, Map m) { if (pat == null || nl == null) return false; if (pat.isA("*")) return true; if (nlIsVar(pat)) return nlMatch_putMatch(m, pat.head, nl); if (neq(pat.head, nl.head)) return false; // heads identical, proceed to children int n = pat.size(); if (n != nl.size()) return false; for (int i = 0; i < n; i++) if (!nlMatch_sub(pat.get(i), nl.get(i), m)) return false; return true; } static boolean nlMatch_putMatch(Map matches, String key, Lisp val) { key = dropPrefix("$", key); if (matches.containsKey(key) && neq(matches.get(key), val)) fail("multi-matching not implemented"); matches.put(key, val); return true; } static void callMain(Object c, String... args) { callOpt(c, "main", new Object[] {args}); } static void printList(List l) { for (Object x : unnull(l)) print(structure(x)); } static boolean empty(Collection c) { return isEmpty(c); } static boolean empty(String s) { return isEmpty(s); } static boolean empty(Map map) { return map == null || map.isEmpty(); } static boolean isInteger(String s) { return s != null && Pattern.matches("\\-?\\d+", s); } static String quote(String s) { if (s == null) return "null"; return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\""; } static String quote(long l) { return quote("" + l); } static String shorten(String s, int max) { if (s == null) return ""; return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "..."; } static int feedbackScore(String s) { s = dropWords(s, "bot"); s = dropPunctuation(s); List tok = nlTok(s); if (l(tok) != 3) return 0; String word = tok.get(1).toLowerCase(); word = collapseWord(word); if (litlist("nice", "god" /* matches both "god" and "good" :) */, "ok", "corect", "right", "true", "yes", "col" /* cool */).contains(word)) return 1; if (litlist("no", "wrong", "bad").contains(word)) return -1; return 0; } static String getStackTrace(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); return writer.toString(); } static Class mc() { return getMainClass(); } static String devChannelToken() { return loadSecretTextFileMandatory("#1001889", "devchannel-token").trim(); } static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { return new File(javaxDataDir(), formatSnippetID(snippetID)); } static String ntUpload(String gummipw, String title, String text) { return ntUpload(gummipw, title, text, ""); } static String ntUpload(String gummipw, String title, String text, String comment) { try { if (gummipw == null || gummipw.length() == 0) throw new RuntimeException("Need gummi password."); System.out.println("Uploading:"); System.out.println(" " + gummipw + " (" + text.length() + " chars)"); System.out.println(" " + title); int displayLength = 40; System.out.println(" " + (text.length() > displayLength ? quote(text.substring(0, displayLength)) + " [...]" : quote(text))); URL url = new URL("http://tinybrain.de:8080/nt-int/add_snippet.php"); String postData = "gummipw=" + URLEncoder.encode(unnull(gummipw), "UTF-8") + "&text=" + URLEncoder.encode(unnull(text), "UTF-8") + "&title=" + URLEncoder.encode(unnull(title), "UTF-8") + "&comment=" + URLEncoder.encode(unnull(comment), "UTF-8"); String contents = doPost(postData, url.openConnection(), url); if (isInteger(contents)) { long id = parseSnippetID(contents); System.out.println("=> #" + id); return "#" + id; } else throw new RuntimeException("Server error: " + contents); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} public static String join(String glue, Iterable strings) { StringBuilder buf = new StringBuilder(); Iterator i = strings.iterator(); if (i.hasNext()) { buf.append(i.next()); while (i.hasNext()) buf.append(glue).append(i.next()); } return buf.toString(); } public static String join(String glue, String[] strings) { return join(glue, Arrays.asList(strings)); } public static String join(Iterable strings) { return join("", strings); } public static String join(String[] strings) { return join("", strings); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } static void printStructure(Object o) { print(structure(o)); } static Class hotwire_overBot(String progID) { try { String compilerBot = "Compiler Bot with caching!"; startBot(compilerBot, "#1002203"); String s = sendToLocalBot_cached(compilerBot, "please compile this javax snippet: *", progID); Matches m = new Matches(); print(s); assertTrue(s, match("ok, *", s, m)); String jarPath = m.unq(0); File jar = new File(jarPath); assertTrue(jar.getAbsolutePath(), jar.isFile()); // collect urls (program + libraries) List urls = litlist(jar.toURI().toURL()); String dehlibs = unnull(loadTextFileFromZip(jar, "libraries")); Matcher matcher = Pattern.compile("\\d+").matcher(dehlibs); while (matcher.find()) { String libID = matcher.group(); urls.add(loadLibrary(libID).toURI().toURL()); } // make class loader URLClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[l(urls)])); // load & return main class Class theClass = classLoader.loadClass("main"); Class j = getJavaX(); String src = loadTextFileFromZip(jar, "main.java"); call(j, "registerSourceCode", theClass, src); synchronized(j) { // hopefully this goes well... call(j, "setVars", theClass, progID); callOpt(j, "addInstance", progID, theClass); } synchronized(StringBuffer.class) { Object print_log = get(getMainClass(), "print_log"); assertTrue(print_log != null); setOpt(theClass, "print_log", print_log); } return theClass; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static A last(List l) { return l.isEmpty() ? null : l.get(l.size()-1); } static String format(String pat, Object... args) { return format3(pat, args); } static List diff(List a, List b) { Set set = asSet(b); List l = new ArrayList(); for (String s : a) if (!set.contains(s)) l.add(s); return l; } static boolean eq(Object a, Object b) { if (a == null) return b == null; if (a.equals(b)) return true; if (a instanceof BigInteger) { if (b instanceof Integer) return a.equals(BigInteger.valueOf((Integer) b)); if (b instanceof Long) return a.equals(BigInteger.valueOf((Long) b)); } return false; } static Map litmap(Object... x) { TreeMap map = new TreeMap(); for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) map.put(x[i], x[i+1]); return map; } static ArrayList list(A[] a) { return asList(a); } static ArrayList list(int[] a) { return asList(a); } static ArrayList list(Set s) { return asList(s); } static String doPost(Map urlParameters, String url) { return doPost(makePostData(urlParameters), url); } static String doPost(String urlParameters, String url) { try { URL _url = new URL(url); return doPost(urlParameters, _url.openConnection(), _url); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static String doPost(String urlParameters, URLConnection conn, URL url) { try { // connect and do POST conn.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream()); writer.write(urlParameters); writer.flush(); String contents = loadPage(conn, url); writer.close(); return contents; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Object safeUnstructure(String s) { return unstructure(s, true); } static String str(Object o) { return String.valueOf(o); } static RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); } static RuntimeException fail(String msg) { throw new RuntimeException(unnull(msg)); } static RuntimeException fail(String msg, Object... args) { throw new RuntimeException(format(msg, args)); } static class SlackMsg { String user, botName; // one of these is going to be set String userName; // looked up if user != null String type, text, ts; Object reactions; String channelID; } static List slackReadChannel(String channelID, String token, int limit) { return slackReadChannel(channelID, token, limit, null); } static List slackReadChannel(String channelID, String token, int limit, String oldest) { String url = "https://slack.com/api/channels.history"; String postData = "token=" + urlencode(token) + "&channel=" + urlencode(channelID) + "&count=" + limit + (oldest != null ? "&oldest=" + urlencode(oldest) : ""); String data = doPost(postData, url); Map map = (Map) ( jsonDecode(data)); assertTrue(map.get("ok")); List _msgs = (List) ( map.get("messages")); List msgs = new ArrayList(); for (Map msg : reversedList(_msgs)) { SlackMsg m = new SlackMsg(); m.type = (String) msg.get("type"); m.user = (String) msg.get("user"); m.botName = (String) msg.get("username"); m.text = htmldecode((String) msg.get("text")); m.ts = (String) msg.get("ts"); m.reactions = msg.get("reactions"); m.channelID = channelID; m.userName = slackGetUserName(token, m.user); msgs.add(m); //print(structure(m)); } //print(l(msgs) + " messages sucked from slack channel."); return msgs; } static long unixTime() { return now()/1000; } static A reverseLookup(Map map, B value) { for (A key : map.keySet()) if (eq(map.get(key), value)) return key; return null; } static Map slackGetUserInfo(String token, String userID) { String url = "https://slack.com/api/users.info"; String postData = "token=" + urlencode(token) + "&user=" + urlencode(userID); String data = doPost(postData, url); //print(data); return (Map) jsonDecodeMap(data).get("user"); } static void sleepSeconds(long s) { if (s > 0) sleep(s*1000); } static String programID; static String getProgramID() { return programID; } // TODO: ask JavaX instead static String getProgramID(Class c) { return or((String) getOpt(c, "programID"), "?"); } static String getProgramID(Object o) { return getProgramID(getMainClass(o)); } static Object callOpt(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Method m = callOpt_findStaticMethod((Class) o, method, args, false); if (m == null) return null; m.setAccessible(true); return m.invoke(null, args); } else { Method m = callOpt_findMethod(o, method, args, false); if (m == null) return null; m.setAccessible(true); return m.invoke(o, args); } } catch (Exception e) { throw new RuntimeException(e); } } static Method callOpt_findStaticMethod(Class c, String method, Object[] args, boolean debug) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (!m.getName().equals(method)) { if (debug) System.out.println("Method name mismatch: " + method); continue; } if ((m.getModifiers() & Modifier.STATIC) == 0 || !callOpt_checkArgs(m, args, debug)) continue; return m; } c = c.getSuperclass(); } return null; } static Method callOpt_findMethod(Object o, String method, Object[] args, boolean debug) { Class c = o.getClass(); while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (m.getName().equals(method) && callOpt_checkArgs(m, args, debug)) return m; } c = c.getSuperclass(); } return null; } private static boolean callOpt_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } // replacement for class JavaTok // maybe incomplete, might want to add floating point numbers // todo also: extended multi-line strings static List javaTok(String s) { List tok = new ArrayList(); int l = s.length(); int i = 0; while (i < l) { int j = i; char c; String cc; // scan for whitespace while (j < l) { c = s.charAt(j); cc = s.substring(j, Math.min(j+2, l)); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (cc.equals("/*")) { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (cc.equals("//")) { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(s.substring(i, j)); i = j; if (i >= l) break; c = s.charAt(i); // cc is not needed in rest of loop body cc = s.substring(i, Math.min(i+2, l)); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener || s.charAt(j) == '\n') { // end at \n to not propagate unclosed string literal errors ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0)); // for stuff like "don't" else if (Character.isDigit(c)) { do ++j; while (j < l && Character.isDigit(s.charAt(j))); if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L } else if (cc.equals("[[")) { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]")); j = Math.min(j+3, l); } else ++j; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } static List javaTok(List tok) { return javaTok(join(tok)); } static void save(String varName) { saveLocally(varName); } static void save(String progID, String varName) { saveLocally(progID, varName); } static void slackSmile(String token, String channelID, String timestamp) { String name = "simple_smile"; String url = "https://slack.com/api/reactions.add"; Map postData = litmap("token", token, "channel", channelID, "timestamp", timestamp, "name", name); print(doPost(postData, url)); } static void logQuoted(String logFile, String line) { logQuoted(getProgramFile(logFile), line); } static void logQuoted(File logFile, String line) { appendToFile(logFile, quote(line) + "\n"); } static Map synchroTreeMap() { return Collections.synchronizedMap(new TreeMap()); } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } static void slackReact(String token, String channelID, String timestamp, String emoji) { String name = "simple_smile"; String url = "https://slack.com/api/reactions.add"; Map postData = litmap("token", token, "channel", channelID, "timestamp", timestamp, "name", emoji); print(doPost(postData, url)); } static String loadSecretTextFile(String name) { return loadTextFile(new File(getSecretProgramDir(), name)); } static String loadSecretTextFile(String progID, String name) { return loadTextFile(new File(getSecretProgramDir(progID), name)); } static void sleep(long ms) { try { Thread.sleep(ms); } catch (Exception e) { throw new RuntimeException(e); } } static void sleep() { try { print("Sleeping."); synchronized(main.class) { main.class.wait(); } } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} public static long parseSnippetID(String snippetID) { long id = Long.parseLong(shortenSnippetID(snippetID)); if (id == 0) fail("0 is not a snippet ID"); return id; } static void load(String varName) { readLocally(varName); } static void load(String progID, String varName) { readLocally(progID, varName); } static void cleanUp(Object c) { if (c instanceof List) { cleanUp((List) c); return; } if (!(c instanceof Class)) return; try { // revoke license callOpt(c, "licensed_off"); // call custom cleanUp() function try { callOpt(c, "cleanMeUp"); } catch (Throwable __e) { printStackTrace(__e); } // remove all virtual bots List responders = (List) getOpt(c, "record_list"); List ports = getMultiPorts(); if (responders != null) for (Object port : ports) for (Object responder : responders) call(port, "removeResponder", responder); } catch (Throwable __e) { printStackTrace(__e); } } static void cleanUp(List l) { for (Object c : l) cleanUp(c); l.clear(); } static ArrayList litlist(A... a) { return new ArrayList(Arrays.asList(a)); } // get purpose 1: access a list/array (safer version of x.get(y)) static A get(List l, int idx) { return idx >= 0 && idx < l(l) ? l.get(idx) : null; } static A get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } // get purpose 2: access a field by reflection or a map static Object get(Object o, String field) { if (o instanceof Class) return get((Class) o, field); if (o instanceof Map) return ((Map) o).get(field); if (o.getClass().getName().equals("main$DynamicObject")) return call(get_raw(o, "fieldValues"), "get", field); return get_raw(o, field); } static Object get_raw(Object o, String field) { try { Field f = get_findField(o.getClass(), field); f.setAccessible(true); return f.get(o); } catch (Exception e) { throw new RuntimeException(e); } } static Object get(Class c, String field) { try { Field f = get_findStaticField(c, field); f.setAccessible(true); return f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } static Field get_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field get_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } static void nohupJavax(String javaxargs) { nohupJavax(javaxargs, ""); } // vm args are ignored if pre-spun VM found... static void nohupJavax(String javaxargs, String vmArgs) { javaxargs = javaxargs.trim(); if (javaxargs.startsWith("#")) javaxargs = javaxargs.substring(1); String snippetID = javaTok(javaxargs).get(1); int idx = javaxargs.indexOf(' '); String args = idx < 0 ? "" : javaxargs.substring(idx+1).trim(); String line; if (args.length() != 0) line = format3("please start program * with arguments *", snippetID, args); else line = format3("please start program *", snippetID); String answer = sendToLocalBotOpt("A pre-spun VM.", line); if (match3("ok", answer)) { print("OK, used pre-spun VM."); } else { if (answer != null) print("> " + answer); print("Using standard nohup."); classicNohupJavax(javaxargs, vmArgs); } } static int makeBot(String greeting) { return makeAndroid3(greeting).port; } static void makeBot(Android3 a) { makeAndroid3(a); } static String unSlackSnippet(String s) { if (s.startsWith("```") && s.endsWith("```") && l(s) >= 6) s = s.substring(3, l(s)-3); return s; } static AtomicInteger nlParse_count = new AtomicInteger(); // how often did we parse something? static Lisp nlParse(String s) { return nlParse(s, true); } static Lisp nlParse(String s, boolean unbracket) { return nlParse(codeTokens(snlTok(s)), unbracket, null); } static Lisp nlParse(List tok) { return nlParse(tok, true, null); } static Lisp nlParse(List tok, boolean unbracket, List warnings) { nlParse_count.incrementAndGet(); class Entry { int i; Lisp tree; String wrapper; Entry(int i) { this.i = i; tree = lisp("[]"); } Entry(int i, String bracket) { this.i = i; tree = lisp("[]"); wrapper = bracket; } Lisp wrapped() { Lisp t = nlUnbracket(tree); return wrapper == null ? t : lisp(wrapper, t); } } List stack = new ArrayList(); stack.add(new Entry(0)); for (int i = 0; i < l(tok); i++) { String t = tok.get(i); if (eq(t, "[") || eq(t, "(")) { stack.add(new Entry(i, eq(t, "(") ? "()" : null)); } else if (eq(t, "]") || eq(t, ")")) { if (l(stack) == 1) warn("too many closing brackets", warnings); else { Entry e = popLast(stack); /*if (!bracketsMatch(tok.get(e.i), t)) warn("non-matching brackets");*/ last(stack).tree.add(e.wrapped()); } } else last(stack).tree.add(t); } while (l(stack) > 1) { warn("too many opening brackets", warnings); Entry e = popLast(stack); last(stack).tree.add(e.wrapped()); } Lisp result = last(stack).wrapped(); return unbracket ? nlUnbracket(result) : result; } static Object loadVariableDefinition(String varName) { return loadVariableDefinition(getProgramID(), varName); } // currently only works with string lists ("= litlist(...)") // and strings. static Object loadVariableDefinition(String progIDOrSrc, String varName) { if (isSnippetID(progIDOrSrc)) progIDOrSrc = loadSnippet(progIDOrSrc); List tok = javaTok(progIDOrSrc); int i = findCodeTokens(tok, varName, "="); if (i < 0) return null; i += 4; if (isQuoted(tok.get(i))) return unquote(tok.get(i)); if (eq(get(tok, i), "litlist") && eq(get(tok, i+2), "(")) { int opening = i+2; int closing = findEndOfBracketPart(tok, opening)-1; List l = new ArrayList(); for (i = opening+2; i < closing; i += 4) l.add(unquote(tok.get(i))); return l; } throw fail("Unknown variable type or no definition in source: " + shorten(progIDOrSrc, 100) + "/" + varName); } static void setAdd(Collection c, A a) { if (!c.contains(a)) c.add(a); } // compile JavaX source, load classes & return main class // src can be a snippet ID or actual source code // TODO: record injection? static Class hotwire(String src) { try { Class j = getJavaX(); synchronized(j) { // hopefully this goes well... List libraries = new ArrayList(); File srcDir = (File) call(j, "transpileMain", src, libraries); if (srcDir == null) fail("transpileMain returned null (src=" + quote(src) + ")"); Object androidContext = get(j, "androidContext"); if (androidContext != null) return (Class) call(j, "loadx2android", srcDir, src); File classesDir = (File) call(j, "TempDirMaker_make"); String javacOutput = (String) call(j, "compileJava", srcDir, libraries, classesDir); System.out.println(javacOutput); URL[] urls = new URL[libraries.size()+1]; urls[0] = classesDir.toURI().toURL(); for (int i = 0; i < libraries.size(); i++) urls[i+1] = libraries.get(i).toURI().toURL(); // make class loader URLClassLoader classLoader = new URLClassLoader(urls); // load & return main class Class theClass = classLoader.loadClass("main"); callOpt(j, "registerSourceCode", theClass, loadTextFile(new File(srcDir, "main.java"))); call(j, "setVars", theClass, isSnippetID(src) ? src: null); if (isSnippetID(src)) callOpt(j, "addInstance", src, theClass); return theClass; } } catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); } } static Class getMainClass() { try { return Class.forName("main"); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Class getMainClass(Object o) { try { return (o instanceof Class ? (Class) o : o.getClass()).getClassLoader().loadClass("main"); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static List scanLog(String progID, String fileName) { return scanLog(getProgramFile(progID, fileName)); } static List scanLog(String fileName) { return scanLog(getProgramFile(fileName)); } static List scanLog(File file) { List l = new ArrayList(); for (String s : toLines(file)) if (isQuoted(s)) l.add(unquote(s)); return l; } static List subList(List l, int startIndex) { return subList(l, startIndex, l(l)); } static List subList(List l, int startIndex, int endIndex) { startIndex = max(0, min(l(l), startIndex)); endIndex = max(0, min(l(l), endIndex)); if (startIndex > endIndex) return litlist(); return l.subList(startIndex, endIndex); } static A or(A a, A b) { return a != null ? a : b; } static void set(Object o, String field, Object value) { if (o instanceof Class) set((Class) o, field, value); else try { Field f = set_findField(o.getClass(), field); smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } } static void set(Class c, String field, Object value) { try { Field f = set_findStaticField(c, field); smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field set_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static Field set_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static boolean match(String pat, String s) { return match3(pat, s); } static boolean match(String pat, String s, Matches matches) { return match3(pat, s, matches); } static String callStaticAnswerMethod(List bots, String s) { for (Class c : bots) try { String answer = callStaticAnswerMethod(c, s); if (!empty(answer)) return answer; } catch (Throwable e) { print("Error calling " + getProgramID(c)); e.printStackTrace(); } return null; } static String callStaticAnswerMethod(Class c, String s) { String answer = (String) callOpt(c, "answer", s, litlist(s)); if (answer == null) answer = (String) callOpt(c, "answer", s); return emptyToNull(answer); } static String callStaticAnswerMethod(String s, List history) { String answer = (String) callOpt(getMainClass(), "answer", s, history); if (answer == null) answer = (String) callOpt(getMainClass(), "answer", s); return emptyToNull(answer); } static Map jsonDecodeMap(String s) { Object o = jsonDecode(s); if (o instanceof Map) return (Map) o; else throw fail("Not a JSON map: " + s); } static int l(Object[] array) { return array == null ? 0 : array.length; } static int l(byte[] array) { return array == null ? 0 : array.length; } static int l(int[] array) { return array == null ? 0 : array.length; } static int l(char[] array) { return array == null ? 0 : array.length; } static int l(Collection c) { return c == null ? 0 : c.size(); } static int l(Map m) { return m == null ? 0 : m.size(); } static int l(String s) { return s == null ? 0 : s.length(); } // "seen" is now default static String structure(Object o) { return structure(o, 0, new IdentityHashMap()); } static String structure_seen(Object o) { return structure(o, 0, new IdentityHashMap()); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static String structure(Object o, int stringSizeLimit, IdentityHashMap seen) { if (o == null) return "null"; // these are never back-referenced (for readability) if (o instanceof String) return quote(stringSizeLimit != 0 ? shorten((String) o, stringSizeLimit) : (String) o); if (o instanceof BigInteger) return "bigint(" + o + ")"; if (o instanceof Double) return "d(" + quote(str(o)) + ")"; if (o instanceof Long) return o + "L"; if (seen != null) { Integer ref = seen.get(o); if (ref != null) return "r" + ref; seen.put(o, seen.size()+1); } String name = o.getClass().getName(); StringBuilder buf = new StringBuilder(); if (o instanceof HashSet) return "hashset" + structure(new ArrayList((Set) o), stringSizeLimit, seen); if (o instanceof TreeSet) return "treeset" + structure(new ArrayList((Set) o), stringSizeLimit, seen); if (o instanceof Collection) { for (Object x : (Collection) o) { if (buf.length() != 0) buf.append(", "); buf.append(structure(x, stringSizeLimit, seen)); } return "[" + buf + "]"; } if (o instanceof Map) { for (Object e : ((Map) o).entrySet()) { if (buf.length() != 0) buf.append(", "); buf.append(structure(((Map.Entry) e).getKey(), stringSizeLimit, seen)); buf.append("="); buf.append(structure(((Map.Entry) e).getValue(), stringSizeLimit, seen)); } return (o instanceof HashMap ? "hashmap" : "") + "{" + buf + "}"; } if (o.getClass().isArray()) { int n = Array.getLength(o); for (int i = 0; i < n; i++) { if (buf.length() != 0) buf.append(", "); buf.append(structure(Array.get(o, i), stringSizeLimit, seen)); } return "array{" + buf + "}"; } if (o instanceof Class) return "class(" + quote(((Class) o).getName()) + ")"; if (o instanceof Throwable) return "exception(" + quote(((Throwable) o).getMessage()) + ")"; // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) return String.valueOf(o); String shortName = o.getClass().getName().replaceAll("^main\\$", ""); if (shortName.equals("Lisp")) { buf.append("l(" + structure(getOpt(o, "head"), stringSizeLimit, seen)); List args = (List) ( getOpt(o, "args")); if (nempty(args)) for (int i = 0; i < l(args); i++) { buf.append(", "); Object arg = args.get(i); // sweet shortening if (arg != null && eq(arg.getClass().getName(), "main$Lisp") && isTrue(call(arg, "isEmpty"))) arg = get(arg, "head"); buf.append(structure(arg, stringSizeLimit, seen)); } buf.append(")"); return str(buf); } int numFields = 0; String fieldName = ""; if (shortName.equals("DynamicObject")) { shortName = (String) get(o, "className"); Map fieldValues = (Map) get(o, "fieldValues"); for (String _fieldName : fieldValues.keySet()) { fieldName = _fieldName; Object value = fieldValues.get(fieldName); if (value != null) { if (buf.length() != 0) buf.append(", "); buf.append(fieldName + "=" + structure(value, stringSizeLimit, seen)); } ++numFields; } } else { // regular class // TODO: go to superclasses too Field[] fields = o.getClass().getDeclaredFields(); for (Field field : fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) continue; fieldName = field.getName(); // skip outer object reference if (fieldName.indexOf("$") >= 0) continue; Object value; try { field.setAccessible(true); value = field.get(o); } catch (Exception e) { value = "?"; } // put special cases here... if (value != null) { if (buf.length() != 0) buf.append(", "); buf.append(fieldName + "=" + structure(value, stringSizeLimit, seen)); } ++numFields; } } String b = buf.toString(); if (numFields == 1 && structure_allowShortening) b = b.replaceAll("^" + fieldName + "=", ""); // drop field name if only one String s = shortName; if (buf.length() != 0) s += "(" + b + ")"; return s; } static String n(long l, String name) { return l + " " + (l == 1 ? name : getPlural(name)); } static boolean master() { return webAuthed() || litlist("stefanreich", "bgrgndz", "okhan", "mrshutco").contains(getUserName()); } // varargs assignment fixer for a single string array argument static Object call(Object o, String method, String[] arg) { return call(o, method, new Object[] {arg}); } static Object call(Object o, String method, Object... args) { try { if (o instanceof Class) { Method m = call_findStaticMethod((Class) o, method, args, false); m.setAccessible(true); return m.invoke(null, args); } else { Method m = call_findMethod(o, method, args, false); m.setAccessible(true); return m.invoke(o, args); } } catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); } } static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (!m.getName().equals(method)) { if (debug) System.out.println("Method name mismatch: " + method); continue; } if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug)) continue; return m; } c = c.getSuperclass(); } throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + _c.getName()); } static Method call_findMethod(Object o, String method, Object[] args, boolean debug) { Class c = o.getClass(); while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (m.getName().equals(method) && call_checkArgs(m, args, debug)) return m; } c = c.getSuperclass(); } throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName()); } private static boolean call_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } static int slackReactionScore(String token, String channelID, String timestamp) { String url = "https://slack.com/api/reactions.get"; String postData = "token=" + urlencode(token) + "&channel=" + urlencode(channelID) + "×tamp=" + urlencode(timestamp); String data = doPost(postData, url); Map map = jsonDecodeMap(data); Map msg = (Map) ( map.get("message")); if (msg == null) return 0; //printStructure(msg); List reactions = (List) ( msg.get("reactions")); if (reactions == null) return 0; //printStructure(reactions); int score = 0; for (Map r : reactions) { String name = (String) ( r.get("name")); int count = (int) r.get("count"); if (litlist("confused").contains(name)) score -= count; else score += count; /*else print("Unknown reaction emoji: " + name);*/ } return score; } static volatile StringBuffer local_log = new StringBuffer(); // not redirected static volatile StringBuffer print_log = local_log; // might be redirected, e.g. to main bot // in bytes - will cut to half that static volatile int print_log_max = 1024*1024; static volatile int local_log_max = 100*1024; static void print() { print(""); } static void print(Object o) { String s = String.valueOf(o) + "\n"; StringBuffer loc = local_log; StringBuffer buf = print_log; int loc_max = print_log_max; if (buf != loc && buf != null) { print_append(buf, s, print_log_max); loc_max = local_log_max; } if (loc != null) print_append(loc, s, loc_max); System.out.print(s); } static void print(long l) { print(String.valueOf(l)); } static void print_append(StringBuffer buf, String s, int max) { synchronized(buf) { buf.append(s); max /= 2; if (buf.length() > max) try { int newLength = max/2; int ofs = buf.length()-newLength; String newString = buf.substring(ofs); buf.setLength(0); buf.append("[...] ").append(newString); } catch (Exception e) { buf.setLength(0); } } } static void setOpt(Object o, String field, Object value) { if (o instanceof Class) setOpt((Class) o, field, value); else try { Field f = setOpt_findField(o.getClass(), field); if (f != null) smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } } static void setOpt(Class c, String field, Object value) { try { Field f = setOpt_findStaticField(c, field); if (f != null) smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field setOpt_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; return null; } static Field setOpt_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; return null; } static String makeResponder_callAnswerMethod(Object bot, String s, List history) { String answer = (String) callOpt(bot, "answer", s, history); if (answer == null) answer = (String) callOpt(bot, "answer", s); return answer; } static Responder makeResponder(final Object bot) { return new Responder() { String answer(String s, List history) { return makeResponder_callAnswerMethod(bot, s, history); } }; } static Object getOpt(Object o, String field) { if (o instanceof String) o = getBot ((String) o); if (o == null) return null; if (o instanceof Class) return getOpt((Class) o, field); if (o.getClass().getName().equals("main$DynamicObject")) return call(getOpt_raw(o, "fieldValues"), "get", field); if (o instanceof Map) return ((Map) o).get(field); return getOpt_raw(o, field); } static Object getOpt_raw(Object o, String field) { try { Field f = getOpt_findField(o.getClass(), field); if (f == null) return null; f.setAccessible(true); return f.get(o); } catch (Exception e) { throw new RuntimeException(e); } } static Object getOpt(Class c, String field) { try { Field f = getOpt_findStaticField(c, field); if (f == null) return null; f.setAccessible(true); return f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } static Field getOpt_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Field getOpt_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static String nlUnparse(Lisp tree) { return nlUnparse(tree, true); } static String nlUnparse(Lisp tree, boolean dropOuterBrackets) { StringBuilder buf = new StringBuilder(); nlUnparse(tree, buf, dropOuterBrackets); return str(buf); } static void nlUnparse(Lisp tree, StringBuilder buf, boolean dropOuterBrackets) { if (tree == null) return; // special classes - Var etc. if (tree.getClass() != Lisp.class) { buf.append(tree); return; } if (tree.isA("[]")) { if (!dropOuterBrackets) buf.append("["); } else if (tree.isA("()")) buf.append("("); else { if (tree.isLeaf()) buf.append(tree.head); else buf.append("?? " + tree); return; } for (int i = 0; i < tree.size(); i++) { if (i != 0) buf.append(" "); nlUnparse(tree.get(i), buf, false); } if(tree.isA("[]")) { if (!dropOuterBrackets) buf.append("]"); } else buf.append(")"); } static boolean hasField(Object o, String field) { if (o instanceof Class) return hasField_findStaticField((Class) o, field) != null; else return hasField_findField(o.getClass(), field) != null; } static Field hasField_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; return null; } static Field hasField_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; return null; } static String urlencode(String x) { try { return URLEncoder.encode(unnull(x), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } static String dropWords(String s, String... words) { Set set = asSet(toLowerCase(words)); List tok = nlTok(s); for (int i = 1; i < l(tok); ) if (set.contains(tok.get(i).toLowerCase())) for (int __twice = 0; __twice < 2; __twice++) { tok.remove(i); } else i += 2; return join(tok); } static boolean nlIsVar(Lisp l) { if (l == null || !l.isLeaf()) return false; String h = l.head; return l(h) > 1 && h.startsWith("$"); } static ArrayList asList(A[] a) { return new ArrayList(Arrays.asList(a)); } static ArrayList asList(int[] a) { ArrayList l = new ArrayList(); for (int i : a) l.add(i); return l; } static ArrayList asList(Set s) { return s == null ? new ArrayList() : new ArrayList(s); } static void classicNohupJavax(String javaxargs) { classicNohupJavax(javaxargs, ""); } static void classicNohupJavax(String javaxargs, String vmArgs) { try { int x = latestInstalledJavaX(); File xfile = new File(userHome(), ".javax/x" + Math.max(x, 30) + ".jar"); if (!xfile.isFile()) { String url = "http://tinybrain.de/x30.jar"; byte[] data = loadBinaryPage(url); if (data.length < 1000000) fail("Could not load " + url); saveBinaryFile(xfile.getPath(), data); } String jarPath = xfile.getPath(); if (javaxargs.startsWith("#")) javaxargs = javaxargs.substring(1); nohup("java " + vmArgs + " -jar " + (isWindows() ? winQuote(jarPath) : bashQuote(jarPath)) + " " + javaxargs); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static String dropPrefix(String prefix, String s) { return s.startsWith(prefix) ? s.substring(l(prefix)) : s; } static String shortenSnippetID(String snippetID) { if (snippetID.startsWith("#")) snippetID = snippetID.substring(1); String httpBlaBla = "http://tinybrain.de/"; if (snippetID.startsWith(httpBlaBla)) snippetID = snippetID.substring(httpBlaBla.length()); return "" + parseLong(snippetID); } static String getPlural(String s) { if (s.endsWith("y")) return dropSuffix("y", s) + "ies"; return s + "s"; } static File getSecretProgramDir() { return getSecretProgramDir(getProgramID()); } static File getSecretProgramDir(String snippetID) { return new File(userHome(), "JavaX-Secret/" + formatSnippetID(snippetID)); } static Lisp nlUnbracket(Lisp tree) { while (tree.is("[]", 1)) tree = tree.get(0); return tree; } static String unnull(String s) { return s == null ? "" : s; } static List unnull(List l) { return l == null ? emptyList() : l; } static class DynamicObject { String className; Map fieldValues = new TreeMap(); } static Object unstructure(String text) { return unstructure(text, false); } // actually it's now almost the same as jsonDecode :) static Object unstructure(String text, final boolean allDynamic) { final List tok = javaTok(text); class X { int i = 1; Object parse() { String t = tok.get(i); if (t.startsWith("\"")) { String s = unquote(tok.get(i)); i += 2; return s; } if (t.equals("hashset")) return parseHashSet(); if (t.equals("treeset")) return parseTreeSet(); if (t.equals("hashmap")) return parseHashMap(); if (t.equals("{")) return parseMap(); if (t.equals("[")) return parseList(); if (t.equals("array")) return parseArray(); if (t.equals("class")) return parseClass(); if (t.equals("bigint")) return parseBigInt(); if (t.equals("d")) return parseDouble(); if (t.equals("l")) return parseLisp(); if (t.equals("null")) { i += 2; return null; } if (t.equals("false")) { i += 2; return false; } if (t.equals("true")) { i += 2; return true; } if (t.equals("-")) { t = tok.get(i+2); i += 4; t = dropSuffix("L", t); long l = -Long.parseLong(t); return l == (int) l ? (int) l : l; } if (isInteger(t) || isLongConstant(t)) { i += 2; t = dropSuffix("L", t); long l = Long.parseLong(t); return l == (int) l ? (int) l : l; } if (isJavaIdentifier(t)) { Class c = allDynamic ? null : findClass(t); DynamicObject dO = null; Object o = null; if (c != null) o = nuObject(c); else { dO = new DynamicObject(); dO.className = t; } i += 2; if (i < tok.size() && tok.get(i).equals("(")) { consume("("); while (!tok.get(i).equals(")")) { // It's like parsing a map. //Object key = parse(); //if (tok.get(i).equals(")")) // key = onlyField(); String key = unquote(tok.get(i)); i += 2; consume("="); Object value = parse(); if (o != null) setOpt(o, key, value); else dO.fieldValues.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume(")"); } return o != null ? o : dO; } throw new RuntimeException("Unknown token " + (i+1) + ": " + t); } Object parseSet(Set set) { set.addAll((List) parseList()); return set; } Object parseLisp() { consume("l"); consume("("); List list = new ArrayList(); while (!tok.get(i).equals(")")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume(")"); return newObject("main$Lisp", (String) list.get(0), subList(list, 1)); } Object parseList() { consume("["); List list = new ArrayList(); while (!tok.get(i).equals("]")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume("]"); return list; } Object parseArray() { consume("array"); consume("{"); List list = new ArrayList(); while (!tok.get(i).equals("}")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume("}"); return list.toArray(); } Object parseClass() { consume("class"); consume("("); String name = tok.get(i); i += 2; consume(")"); Class c = allDynamic ? null : findClass(name); if (c != null) return c; DynamicObject dO = new DynamicObject(); dO.className = "java.lang.Class"; dO.fieldValues.put("name", name); return dO; } Object parseBigInt() { consume("bigint"); consume("("); String val = tok.get(i); i += 2; if (eq(val, "-")) { val = "-" + tok.get(i); i += 2; } consume(")"); return new BigInteger(val); } Object parseDouble() { consume("d"); consume("("); String val = unquote(tok.get(i)); i += 2; consume(")"); return Double.parseDouble(val); } Object parseHashMap() { consume("hashmap"); return parseMap(new HashMap()); } Object parseHashSet() { consume("hashset"); return parseSet(new HashSet()); } Object parseTreeSet() { consume("treeset"); return parseSet(new TreeSet()); } Object parseMap() { return parseMap(new TreeMap()); } Object parseMap(Map map) { consume("{"); while (!tok.get(i).equals("}")) { Object key = unstructure(tok.get(i)); i += 2; consume("="); Object value = parse(); map.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume("}"); return map; } void consume(String s) { if (!tok.get(i).equals(s)) { String prevToken = i-2 >= 0 ? tok.get(i-2) : ""; String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i += 2; } } return new X().parse(); } static int min(int a, int b) { return Math.min(a, b); } static double min(double[] c) { double x = Double.MAX_VALUE; for (double d : c) x = Math.min(x, d); return x; } static void assertTrue(Object o) { assertEquals(true, o); } static boolean assertTrue(String msg, boolean b) { if (!b) fail(msg); return b; } static boolean assertTrue(boolean b) { if (!b) fail("oops"); return b; } static List toLines(File f) { return toLines(loadTextFile(f)); } public static List toLines(String s) { List lines = new ArrayList(); if (s == null) return lines; int start = 0; while (true) { int i = toLines_nextLineBreak(s, start); if (i < 0) { if (s.length() > start) lines.add(s.substring(start)); break; } lines.add(s.substring(start, i)); if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n') i += 2; else ++i; start = i; } return lines; } private static int toLines_nextLineBreak(String s, int start) { for (int i = start; i < s.length(); i++) { char c = s.charAt(i); if (c == '\r' || c == '\n') return i; } return -1; } static Set asSet(String[] array) { return new TreeSet(asList(array)); } static Set asSet(List l) { return new TreeSet(l); } static Class __javax; static Class getJavaX() { return __javax; } static List reversedList(List l) { List x = cloneList(l); Collections.reverse(x); return x; } static String htmldecode(final String input) { if (input == null) return null; final int MIN_ESCAPE = 2; final int MAX_ESCAPE = 6; StringWriter writer = null; int len = input.length(); int i = 1; int st = 0; while (true) { // look for '&' while (i < len && input.charAt(i-1) != '&') i++; if (i >= len) break; // found '&', look for ';' int j = i; while (j < len && j < i + MAX_ESCAPE + 1 && input.charAt(j) != ';') j++; if (j == len || j < i + MIN_ESCAPE || j == i + MAX_ESCAPE + 1) { i++; continue; } // found escape if (input.charAt(i) == '#') { // numeric escape int k = i + 1; int radix = 10; final char firstChar = input.charAt(k); if (firstChar == 'x' || firstChar == 'X') { k++; radix = 16; } try { int entityValue = Integer.parseInt(input.substring(k, j), radix); if (writer == null) writer = new StringWriter(input.length()); writer.append(input.substring(st, i - 1)); if (entityValue > 0xFFFF) { final char[] chrs = Character.toChars(entityValue); writer.write(chrs[0]); writer.write(chrs[1]); } else { writer.write(entityValue); } } catch (NumberFormatException ex) { i++; continue; } } else { // named escape CharSequence value = htmldecode_lookupMap.get(input.substring(i, j)); if (value == null) { i++; continue; } if (writer == null) writer = new StringWriter(input.length()); writer.append(input.substring(st, i - 1)); writer.append(value); } // skip escape st = j + 1; i = st; } if (writer != null) { writer.append(input.substring(st, len)); return writer.toString(); } return input; } private static final String[][] htmldecode_ESCAPES = { {"\"", "quot"}, // " - double-quote {"&", "amp"}, // & - ampersand {"<", "lt"}, // < - less-than {">", "gt"}, // > - greater-than // Mapping to escape ISO-8859-1 characters to their named HTML 3.x equivalents. {"\u00A0", "nbsp"}, // non-breaking space {"\u00A1", "iexcl"}, // inverted exclamation mark {"\u00A2", "cent"}, // cent sign {"\u00A3", "pound"}, // pound sign {"\u00A4", "curren"}, // currency sign {"\u00A5", "yen"}, // yen sign = yuan sign {"\u00A6", "brvbar"}, // broken bar = broken vertical bar {"\u00A7", "sect"}, // section sign {"\u00A8", "uml"}, // diaeresis = spacing diaeresis {"\u00A9", "copy"}, // copyright sign {"\u00AA", "ordf"}, // feminine ordinal indicator {"\u00AB", "laquo"}, // left-pointing double angle quotation mark = left pointing guillemet {"\u00AC", "not"}, // not sign {"\u00AD", "shy"}, // soft hyphen = discretionary hyphen {"\u00AE", "reg"}, // registered trademark sign {"\u00AF", "macr"}, // macron = spacing macron = overline = APL overbar {"\u00B0", "deg"}, // degree sign {"\u00B1", "plusmn"}, // plus-minus sign = plus-or-minus sign {"\u00B2", "sup2"}, // superscript two = superscript digit two = squared {"\u00B3", "sup3"}, // superscript three = superscript digit three = cubed {"\u00B4", "acute"}, // acute accent = spacing acute {"\u00B5", "micro"}, // micro sign {"\u00B6", "para"}, // pilcrow sign = paragraph sign {"\u00B7", "middot"}, // middle dot = Georgian comma = Greek middle dot {"\u00B8", "cedil"}, // cedilla = spacing cedilla {"\u00B9", "sup1"}, // superscript one = superscript digit one {"\u00BA", "ordm"}, // masculine ordinal indicator {"\u00BB", "raquo"}, // right-pointing double angle quotation mark = right pointing guillemet {"\u00BC", "frac14"}, // vulgar fraction one quarter = fraction one quarter {"\u00BD", "frac12"}, // vulgar fraction one half = fraction one half {"\u00BE", "frac34"}, // vulgar fraction three quarters = fraction three quarters {"\u00BF", "iquest"}, // inverted question mark = turned question mark {"\u00C0", "Agrave"}, // ? - uppercase A, grave accent {"\u00C1", "Aacute"}, // ? - uppercase A, acute accent {"\u00C2", "Acirc"}, // ? - uppercase A, circumflex accent {"\u00C3", "Atilde"}, // ? - uppercase A, tilde {"\u00C4", "Auml"}, // ? - uppercase A, umlaut {"\u00C5", "Aring"}, // ? - uppercase A, ring {"\u00C6", "AElig"}, // ? - uppercase AE {"\u00C7", "Ccedil"}, // ? - uppercase C, cedilla {"\u00C8", "Egrave"}, // ? - uppercase E, grave accent {"\u00C9", "Eacute"}, // ? - uppercase E, acute accent {"\u00CA", "Ecirc"}, // ? - uppercase E, circumflex accent {"\u00CB", "Euml"}, // ? - uppercase E, umlaut {"\u00CC", "Igrave"}, // ? - uppercase I, grave accent {"\u00CD", "Iacute"}, // ? - uppercase I, acute accent {"\u00CE", "Icirc"}, // ? - uppercase I, circumflex accent {"\u00CF", "Iuml"}, // ? - uppercase I, umlaut {"\u00D0", "ETH"}, // ? - uppercase Eth, Icelandic {"\u00D1", "Ntilde"}, // ? - uppercase N, tilde {"\u00D2", "Ograve"}, // ? - uppercase O, grave accent {"\u00D3", "Oacute"}, // ? - uppercase O, acute accent {"\u00D4", "Ocirc"}, // ? - uppercase O, circumflex accent {"\u00D5", "Otilde"}, // ? - uppercase O, tilde {"\u00D6", "Ouml"}, // ? - uppercase O, umlaut {"\u00D7", "times"}, // multiplication sign {"\u00D8", "Oslash"}, // ? - uppercase O, slash {"\u00D9", "Ugrave"}, // ? - uppercase U, grave accent {"\u00DA", "Uacute"}, // ? - uppercase U, acute accent {"\u00DB", "Ucirc"}, // ? - uppercase U, circumflex accent {"\u00DC", "Uuml"}, // ? - uppercase U, umlaut {"\u00DD", "Yacute"}, // ? - uppercase Y, acute accent {"\u00DE", "THORN"}, // ? - uppercase THORN, Icelandic {"\u00DF", "szlig"}, // ? - lowercase sharps, German {"\u00E0", "agrave"}, // ? - lowercase a, grave accent {"\u00E1", "aacute"}, // ? - lowercase a, acute accent {"\u00E2", "acirc"}, // ? - lowercase a, circumflex accent {"\u00E3", "atilde"}, // ? - lowercase a, tilde {"\u00E4", "auml"}, // ? - lowercase a, umlaut {"\u00E5", "aring"}, // ? - lowercase a, ring {"\u00E6", "aelig"}, // ? - lowercase ae {"\u00E7", "ccedil"}, // ? - lowercase c, cedilla {"\u00E8", "egrave"}, // ? - lowercase e, grave accent {"\u00E9", "eacute"}, // ? - lowercase e, acute accent {"\u00EA", "ecirc"}, // ? - lowercase e, circumflex accent {"\u00EB", "euml"}, // ? - lowercase e, umlaut {"\u00EC", "igrave"}, // ? - lowercase i, grave accent {"\u00ED", "iacute"}, // ? - lowercase i, acute accent {"\u00EE", "icirc"}, // ? - lowercase i, circumflex accent {"\u00EF", "iuml"}, // ? - lowercase i, umlaut {"\u00F0", "eth"}, // ? - lowercase eth, Icelandic {"\u00F1", "ntilde"}, // ? - lowercase n, tilde {"\u00F2", "ograve"}, // ? - lowercase o, grave accent {"\u00F3", "oacute"}, // ? - lowercase o, acute accent {"\u00F4", "ocirc"}, // ? - lowercase o, circumflex accent {"\u00F5", "otilde"}, // ? - lowercase o, tilde {"\u00F6", "ouml"}, // ? - lowercase o, umlaut {"\u00F7", "divide"}, // division sign {"\u00F8", "oslash"}, // ? - lowercase o, slash {"\u00F9", "ugrave"}, // ? - lowercase u, grave accent {"\u00FA", "uacute"}, // ? - lowercase u, acute accent {"\u00FB", "ucirc"}, // ? - lowercase u, circumflex accent {"\u00FC", "uuml"}, // ? - lowercase u, umlaut {"\u00FD", "yacute"}, // ? - lowercase y, acute accent {"\u00FE", "thorn"}, // ? - lowercase thorn, Icelandic {"\u00FF", "yuml"}, // ? - lowercase y, umlaut {"'", "apos"}, // the controversial (but who cares!) ' // stackoverflow.com/questions/2083754/why-shouldnt-apos-be-used-to-escape-single-quotes }; private static final HashMap htmldecode_lookupMap; static { htmldecode_lookupMap = new HashMap(); for (final CharSequence[] seq : htmldecode_ESCAPES) htmldecode_lookupMap.put(seq[1].toString(), seq[0]); } // This is made for SNL parsing. // It does NOT recognize multiline strings as these conflict // with syntax like [[a] [b]]. static List snlTok(String s) { List tok = new ArrayList(); int l = s.length(); int i = 0; while (i < l) { int j = i; char c; String cc; // scan for whitespace while (j < l) { c = s.charAt(j); cc = s.substring(j, Math.min(j+2, l)); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (cc.equals("/*")) { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (cc.equals("//")) { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(s.substring(i, j)); i = j; if (i >= l) break; c = s.charAt(i); cc = s.substring(i, Math.min(i+2, l)); // scan for non-whitespace if (c == '\u201C' || c == '\u201D') c = '"'; // normalize quotes if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { char _c = s.charAt(j); if (_c == '\u201C' || _c == '\u201D') _c = '"'; // normalize quotes if (_c == opener) { ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } if (j-1 >= i+1) { tok.add(opener + s.substring(i+1, j-1) + opener); i = j; continue; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for things like "this one's" else if (Character.isDigit(c)) do ++j; while (j < l && Character.isDigit(s.charAt(j))); /*else if (cc.equals("[[")) { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); }*/ else if (s.substring(j, Math.min(j+3, l)).equals("...")) j += 3; else if (c == '$' || c == '#') do ++j; while (j < l && Character.isLetterOrDigit(s.charAt(j))); else ++j; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } static List codeTokens(List tok) { return codeTokensOnly(tok); } static int max(int a, int b) { return Math.max(a, b); } static long max(int a, long b) { return Math.max((long) a, b); } static long max(long a, long b) { return Math.max(a, b); } static double max(int a, double b) { return Math.max((double) a, b); } static int max(Collection c) { int x = Integer.MIN_VALUE; for (int i : c) x = max(x, i); return x; } static double max(double[] c) { if (c.length == 0) return Double.MIN_VALUE; double x = c[0]; for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]); return x; } public static String loadPageSilently(String url) { try { return loadPageSilently(new URL(loadPage_preprocess(url))); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadPageSilently(URL url) { try { IOException e = null; for (int tries = 0; tries < 60; tries++) try { URLConnection con = url.openConnection(); return loadPage(con, url); } catch (IOException _e) { e = _e; print("Retrying because of: " + e); sleepSeconds(1); } throw e; } catch (IOException e) { throw new RuntimeException(e); } } static String loadPage_preprocess(String url) { if (url.startsWith("tb/")) url = "tinybrain.de:8080/" + url; if (url.indexOf("://") < 0) url = "http://" + url; return url; } public static String loadPage(String url) { try { return loadPage(new URL(loadPage_preprocess(url))); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadPage(URL url) { print("Loading: " + url.toExternalForm()); return loadPageSilently(url); } public static String loadPage(URLConnection con, URL url) throws IOException { String contentType = con.getContentType(); if (contentType == null) throw new IOException("Page could not be read: " + url); //Log.info("Content-Type: " + contentType); String charset = loadPage_guessCharset(contentType); Reader r = new InputStreamReader(con.getInputStream(), charset); StringBuilder buf = new StringBuilder(); while (true) { int ch = r.read(); if (ch < 0) break; //Log.info("Chars read: " + buf.length()); buf.append((char) ch); } return buf.toString(); } static String loadPage_guessCharset(String contentType) { Pattern p = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*"); Matcher m = p.matcher(contentType); /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */ return m.matches() ? m.group(1) : "ISO-8859-1"; } static boolean isTrue(Object o) { return booleanValue(o); } static String loadSecretTextFileMandatory(String name) { return loadTextFileMandatory(new File(getSecretProgramDir(), name)); } static String loadSecretTextFileMandatory(String progID, String name) { return loadTextFileMandatory(new File(getSecretProgramDir(progID), name)); } static Map startBot_assignments = new TreeMap(); static void startBot(String botName, String botID) { DialogIO bot = newFindBot(botName); if (bot != null) bot.close(); else { print("Bot " + quote(botName) + " not found. Starting " + botID); javaxBot(botID); waitForBotStartUp(botName); } } static void startBot(String botIDOrName) { String botID = startBot_assignments.get(botIDOrName); if (botID != null) startBot(botIDOrName, botID); else { String botName = getBotNameFromSnippet(botIDOrName); if (botName == null) fail("Bot name not found in source of " + botID); startBot(botName, botIDOrName); } } // i must point at the opening bracket (any of the 2 types, not type parameters) // index returned is index of closing bracket + 1 static int findEndOfBracketPart(List cnc, int i) { int j = i+2, level = 1; while (j < cnc.size()) { if (litlist("{", "(").contains(cnc.get(j))) ++level; else if (litlist("}", ")").contains(cnc.get(j))) --level; if (level == 0) return j+1; ++j; } return cnc.size(); } static String format3(String pat, Object... args) { if (args.length == 0) return pat; List tok = javaTokPlusPeriod(pat); int argidx = 0; for (int i = 1; i < tok.size(); i += 2) if (tok.get(i).equals("*")) tok.set(i, format3_formatArg(argidx < args.length ? args[argidx++] : "null")); return join(tok); } static String format3_formatArg(Object arg) { if (arg == null) return "null"; if (arg instanceof String) { String s = (String) arg; return isIdentifier(s) || isNonNegativeInteger(s) ? s : quote(s); } if (arg instanceof Integer || arg instanceof Long) return String.valueOf(arg); return quote(structure(arg)); } // class Matches is added by #752 static boolean match3(String pat, String s) { return match3(pat, s, null); } static boolean match3(String pat, String s, Matches matches) { if (s == null) return false; return match3(pat, parse3(s), matches); } static boolean match3(String pat, List toks, Matches matches) { List tokpat = parse3(pat); return match3(tokpat,toks,matches); } static boolean match3(List tokpat, List toks, Matches matches) { String[] m = match2(tokpat, toks); //print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m)); if (m == null) return false; else { if (matches != null) matches.m = m; return true; } } static class Android3 { String greeting; boolean publicOverride; // optionally set this in client int startPort = 5000; // optionally set this in client Responder responder; boolean console = true; boolean daemon = false; boolean incomingSilent = false; boolean useMultiPort = true; // set by system int port; long vport; DialogHandler handler; ServerSocket server; Android3(String greeting) { this.greeting = greeting;} Android3() {} synchronized void dispose() { if (server == null) return; try { server.close(); } catch (IOException e) { print("[internal] " + e); } server = null; } } static abstract class Responder { abstract String answer(String s, List history); } static Android3 makeAndroid3(final String greeting) { return makeAndroid3(new Android3(greeting)); } static Android3 makeAndroid3(final String greeting, Responder responder) { Android3 android = new Android3(greeting); android.responder = responder; return makeAndroid3(android); } static Android3 makeAndroid3(final Android3 a) { if (a.responder == null) a.responder = new Responder() { String answer(String s, List history) { return callStaticAnswerMethod(s, history); } }; print(a.greeting); if (a.useMultiPort) { a.vport = addToMultiPort(a.greeting, a.responder); if (a.vport == 1) makeAndroid3_handleConsole(a); return a; } a.handler = makeAndroid3_makeDialogHandler(a); a.port = a.daemon ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler) : startDialogServerOnPortAbove(a.startPort, a.handler); a.server = startDialogServer_serverSocket; if (a.console && makeAndroid3_consoleInUse()) a.console = false; if (a.console) makeAndroid3_handleConsole(a); record(a); return a; } static void makeAndroid3_handleConsole(final Android3 a) { // Console handling stuff print("You may also type on this console."); Thread _t_1 = new Thread() { public void run() { try { List history = new ArrayList(); String line; while ((line = readLine()) != null) { if (line == "bye") { print("> bye stranger"); history = new ArrayList(); } else { history.add(line); history.add(makeAndroid3_getAnswer(line, history, a)); // prints answer on console too } } } catch (Exception _e) { throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } } }; _t_1.start(); } static DialogHandler makeAndroid3_makeDialogHandler(final Android3 a) { return new DialogHandler() { public void run(final DialogIO io) { if (!a.publicOverride && !(publicCommOn() || io.isLocalConnection())) { io.sendLine("Sorry, not allowed"); return; } String dialogID = randomID(8); io.sendLine(a.greeting + " / Your ID: " + dialogID); List history = new ArrayList(); while (io.isStillConnected()) { if (io.waitForLine()) { final String line = io.readLineNoBlock(); String s = dialogID + " at " + now() + ": " + quote(line); if (!a.incomingSilent) print(s); if (line == "bye") { io.sendLine("bye stranger"); return; } Matches m = new Matches(); history.add(line); String answer; if (match3("this is a continuation of talk *", s, m) || match3("hello bot! this is a continuation of talk *", s, m)) { dialogID = unquote(m.m[0]); answer = "ok"; } else answer = makeAndroid3_getAnswer(line, history, a); history.add(answer); io.sendLine(answer); //appendToLog(logFile, s); } } }}; } static String makeAndroid3_getAnswer(String line, List history, Android3 a) { String answer; try { answer = makeAndroid3_fallback(line, history, a.responder.answer(line, history)); } catch (Throwable e) { e = getInnerException(e); e.printStackTrace(); answer = e.toString(); } if (!a.incomingSilent) print("> " + shorten(answer, 500)); return answer; } static String makeAndroid3_fallback(String s, List history, String answer) { // Now we only do the safe thing instead of VM inspection - give out our process ID if (answer == null && match3("what is your pid", s)) return getPID(); if (answer == null && match3("what is your program id", s)) // should be fairly safe, right? return getProgramID(); if (match3("get injection id", s)) return getInjectionID(); if (answer == null) answer = "?"; if (answer.indexOf('\n') >= 0 || answer.indexOf('\r') >= 0) answer = quote(answer); return answer; } static boolean makeAndroid3_consoleInUse() { for (Object o : record_list) if (o instanceof Android3 && ((Android3) o).console) return true; return false; } static Object jsonDecode(String text) { final List tok = jsonTok(text); class Y { int i = 1; Object parse() { String t = tok.get(i); if (t.startsWith("\"")) { String s = unquote(tok.get(i)); i += 2; return s; } if (t.equals("{")) return parseMap(); if (t.equals("[")) return parseList(); if (t.equals("null")) { i += 2; return null; } if (t.equals("false")) { i += 2; return false; } if (t.equals("true")) { i += 2; return true; } if (isInteger(t)) { i += 2; return parseInt(t); } if (t.equals("-")) { t = tok.get(i+2); i += 4; return -parseInt(t); } throw new RuntimeException("Unknown token " + (i+1) + ": " + t); } Object parseList() { consume("["); List list = new ArrayList(); while (!tok.get(i).equals("]")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume("]"); return list; } Object parseMap() { consume("{"); Map map = new TreeMap(); while (!tok.get(i).equals("}")) { String key = unquote(tok.get(i)); i += 2; consume(":"); Object value = parse(); map.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume("}"); return map; } void consume(String s) { if (!tok.get(i).equals(s)) { String prevToken = i-2 >= 0 ? tok.get(i-2) : ""; String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i += 2; } } return new Y().parse(); } // Let's just generally synchronize this to be safe. static synchronized void appendToFile(String path, String s) { try { new File(path).getParentFile().mkdirs(); //print("[Logging to " + path + "]"); Writer writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(path, true), "UTF-8")); writer.write(s); writer.close(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static void appendToFile(File path, String s) { appendToFile(path.getPath(), s); } static String makePostData(Map map) { List l = new ArrayList(); for (Map.Entry e : map.entrySet()) { String key = (String) ( e.getKey()); String value = structureOrText(e.getValue()); l.add(urlencode(key) + "=" + urlencode(value)); } return join("&", l); } // supports the usual quotings (', ", variable length double brackets) static boolean isQuoted(String s) { if (s.startsWith("'") || s.startsWith("\"")) return true; if (!s.startsWith("[")) return false; int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; return i < s.length() && s.charAt(i) == '['; //return Pattern.compile("^\\[=*\\[").matcher(s).find(); } static boolean neq(Object a, Object b) { return !eq(a, b); } static int findCodeTokens(List tok, String... tokens) { return findCodeTokens(tok, 1, false, tokens); } static int findCodeTokens(List tok, boolean ignoreCase, String... tokens) { return findCodeTokens(tok, 1, ignoreCase, tokens); } static int findCodeTokens(List tok, int startIdx, boolean ignoreCase, String... tokens) { outer: for (int i = startIdx | 1; i+tokens.length*2-2 < tok.size(); i += 2) { for (int j = 0; j < tokens.length; j++) { String p = tokens[j], t = tok.get(i+j*2); boolean match; if (eq(p, "*")) match = true; else if (eq(p, "")) match = isQuoted(t); else if (eq(p, "")) match = isIdentifier(t); else if (eq(p, "")) match = isInteger(t); else match = ignoreCase ? eqic(p, t) : eq(p, t); if (!match) continue outer; } return i; } return -1; } // extended over Class.isInstance() to handle primitive types static boolean isInstanceX(Class type, Object arg) { if (type == boolean.class) return arg instanceof Boolean; if (type == int.class) return arg instanceof Integer; if (type == long.class) return arg instanceof Long; if (type == float.class) return arg instanceof Float; if (type == short.class) return arg instanceof Short; if (type == char.class) return arg instanceof Character; if (type == byte.class) return arg instanceof Byte; if (type == double.class) return arg instanceof Double; return type.isInstance(arg); } static File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } // make a lisp form static Lisp lisp(String head, Object... args) { Lisp l = new Lisp(head); for (Object o : args) l.add(o); return l; } static Lisp lisp(String head, List args) { return new Lisp(head, args); } static List getMultiPorts() { return (List) call(getJavaX(), "getMultiPorts"); } static String emptyToNull(String s) { return eq(s, "") ? null : s; } static void saveLocally(String variableName) { saveLocally(programID(), variableName); } static synchronized void saveLocally(String progID, String variableName) { File textFile = new File(programDir(progID), variableName + ".text"); File structureFile = new File(programDir(progID), variableName + ".structure"); Object x = get(main.class, variableName); if (x == null) { textFile.delete(); structureFile.delete(); } else if (x instanceof String) { structureFile.delete(); saveTextFile(textFile, (String) x); } else { textFile.delete(); saveTextFile(structureFile, structure(x)); } } static boolean warn_on = false; static void warn(String s) { if (warn_on) print("Warn: " + s); } static void warn(String s, List warnings) { warn(s); if (warnings != null) warnings.add(s); } static File javaxDataDir_dir; // can be set to work on different base dir static File javaxDataDir() { return javaxDataDir_dir != null ? javaxDataDir_dir : new File(userHome(), "JavaX-Data"); } static String loadTextFileFromZip(File inZip, String fileName) { return loadTextFileFromZipFile(inZip, fileName); } static String sendToLocalBot_cached(String botName, String s, Object... args) { DialogIO io = newFindBot(botName); if (io == null) fail("Bot not found: " + botName); try { return io.ask(s, args); } finally { io.close(); } } static Map slackGetUserName_map = new TreeMap(); static synchronized String slackGetUserName(String token, String userID) { if (userID == null) return null; String userName = slackGetUserName_map.get(userID); if (userName == null) try { userName = (String) slackGetUserInfo(token, userID).get("name"); slackGetUserName_map.put(userID, userName); } catch (Throwable __e) { printStackTrace(__e); } return userName; } static boolean preferCached = false; public static String loadSnippet(String snippetID) { try { return loadSnippet(parseSnippetID(snippetID), preferCached); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadSnippet(String snippetID, boolean preferCached) throws IOException { return loadSnippet(parseSnippetID(snippetID), preferCached); } public static String loadSnippet(long snippetID, boolean preferCached) throws IOException { String text = getSnippetFromBossBot(snippetID); if (text != null) return text; initSnippetCache(); text = DiskSnippetCache_get(snippetID); if (preferCached && text != null) return text; try { if (text != null) System.err.println("md5: " + md5(text)); URL url = new URL("http://tinybrain.de:8080/getraw.php?id=" + snippetID + "&utf8=1"); text = loadPage(url); } catch (RuntimeException e) { e.printStackTrace(); throw new IOException("Snippet #" + snippetID + " not found or not public"); } try { initSnippetCache(); DiskSnippetCache_put(snippetID, text); } catch (IOException e) { System.err.println("Minor warning: Couldn't save snippet to cache (" + DiskSnippetCache_getDir() + ")"); } return text; } static File DiskSnippetCache_dir; public static void initDiskSnippetCache(File dir) { DiskSnippetCache_dir = dir; dir.mkdirs(); } public static synchronized String DiskSnippetCache_get(long snippetID) throws IOException { return loadTextFile(DiskSnippetCache_getFile(snippetID).getPath(), null); } private static File DiskSnippetCache_getFile(long snippetID) { return new File(DiskSnippetCache_dir, "" + snippetID); } public static synchronized void DiskSnippetCache_put(long snippetID, String snippet) throws IOException { saveTextFile(DiskSnippetCache_getFile(snippetID).getPath(), snippet); } public static File DiskSnippetCache_getDir() { return DiskSnippetCache_dir; } public static void initSnippetCache() { if (DiskSnippetCache_dir == null) initDiskSnippetCache(new File(System.getProperty("user.home"), ".tinybrain/snippet-cache")); } static void readLocally(String varNames) { readLocally(programID(), varNames); } // read a string variable from standard storage // does not overwrite variable contents if there is no file static synchronized void readLocally(String progID, String varNames) { for (String variableName : codeTokensOnly(javaTok(varNames))) { File textFile = new File(programDir(progID), variableName + ".text"); File structureFile = new File(programDir(progID), variableName + ".structure"); String value = loadTextFile(textFile); if (value != null) set(main.class, variableName, value); else { value = loadTextFile(structureFile); if (value != null) readLocally_set(main.class, variableName, unstructure(value)); } } } static void readLocally_set(Class c, String varName, Object value) { Object oldValue = get(c, varName); if (oldValue instanceof List && !(oldValue instanceof ArrayList) && value != null) { // Assume it's a synchroList. value = synchroList((List) value); } set(c, varName, value); } public static String unquote(String s) { if (s.startsWith("[")) { int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; if (i < s.length() && s.charAt(i) == '[') { String m = s.substring(1, i); if (s.endsWith("]" + m + "]")) return s.substring(i+1, s.length()-i-1); } } if (s.startsWith("\"") /*&& s.endsWith("\"")*/ && s.length() > 1) { String st = s.substring(1, s.endsWith("\"") ? s.length()-1 : s.length()); StringBuilder sb = new StringBuilder(st.length()); for (int i = 0; i < st.length(); i++) { char ch = st.charAt(i); if (ch == '\\') { char nextChar = (i == st.length() - 1) ? '\\' : st .charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) <= '7') { code += st.charAt(i + 1); i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) <= '7') { code += st.charAt(i + 1); i++; } } sb.append((char) Integer.parseInt(code, 8)); continue; } switch (nextChar) { case '\\': ch = '\\'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case '\"': ch = '\"'; break; case '\'': ch = '\''; break; // Hex Unicode: u???? case 'u': if (i >= st.length() - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + st.charAt(i + 2) + st.charAt(i + 3) + st.charAt(i + 4) + st.charAt(i + 5), 16); sb.append(Character.toChars(code)); i += 5; continue; default: ch = nextChar; // added by Stefan } i++; } sb.append(ch); } return sb.toString(); } else return s; // return original } static boolean webAuthed() { return eq(Boolean.TRUE, callOpt(getBot("#1002590"), "currentHttpRequestAuthorized")); } static File getProgramFile(String progID, String fileName) { return new File(getProgramDir(progID), fileName); } static File getProgramFile(String fileName) { return getProgramFile(getProgramID(), fileName); } public static String loadTextFile(String fileName) { try { return loadTextFile(fileName, null); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(String fileName, String defaultContents) throws IOException { if (!new File(fileName).exists()) return defaultContents; FileInputStream fileInputStream = new FileInputStream(fileName); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); return loadTextFile(inputStreamReader); } public static String loadTextFile(File fileName) { try { return loadTextFile(fileName, null); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(File fileName, String defaultContents) throws IOException { try { return loadTextFile(fileName.getPath(), defaultContents); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(Reader reader) throws IOException { StringBuilder builder = new StringBuilder(); try { char[] buffer = new char[1024]; int n; while (-1 != (n = reader.read(buffer))) builder.append(buffer, 0, n); } finally { reader.close(); } return builder.toString(); } static List dropPunctuation_keep = litlist("*", "<", ">"); static List dropPunctuation(List tok) { tok = new ArrayList(tok); for (int i = 1; i < tok.size(); i += 2) { String t = tok.get(i); if (t.length() == 1 && !Character.isLetter(t.charAt(0)) && !Character.isDigit(t.charAt(0)) && !dropPunctuation_keep.contains(t)) { tok.set(i-1, tok.get(i-1) + tok.get(i+1)); tok.remove(i); tok.remove(i); i -= 2; } } return tok; } static String dropPunctuation(String s) { return join(dropPunctuation(nlTok(s))); } static boolean isEmpty(Collection c) { return c == null || c.isEmpty(); } static boolean isEmpty(String s) { return s == null || s.length() == 0; } static boolean nempty(Collection c) { return !isEmpty(c); } static boolean nempty(String s) { return !isEmpty(s); } static void smartSet(Field f, Object o, Object value) throws Exception { f.setAccessible(true); // take care of common case (long to int) if (f.getType() == int.class && value instanceof Long) value = ((Long) value).intValue(); f.set(o, value); } static String collapseWord(String s) { if (s == null) return ""; StringBuilder buf = new StringBuilder(); for (int i = 0; i < l(s); i++) if (i == 0 || s.charAt(i) != s.charAt(i-1)) buf.append(s.charAt(i)); return buf.toString(); } static List nlTok(String s) { return javaTokPlusPeriod(s); } static A popLast(List l) { return liftLast(l); } static String sendToLocalBotOpt(String bot, String text) { if (bot == null) return null; DialogIO channel = findBot(bot); if (channel == null) { print(quote(bot) + " not found, skipping send: " + quote(text)); return null; } try { channel.readLine(); print(bot + "> " + text); channel.sendLine(text); String s = channel.readLine(); print(bot + "< " + s); return s; } catch (Throwable e) { e.printStackTrace(); return null; } finally { channel.close(); } } static List record_list = synchroList(); static void record(Object o) { record_list.add(o); } public static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } /** possibly improvable */ public static String bashQuote(String text) { if (text == null) return null; return "\"" + text .replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") + "\""; } static void javaxBot(String botID) { startBotVM(); if (injectTo("Bot VM", botID) == null) fail("Couldn't inject to Bot VM"); //nohupJavax(botID); } static long waitForBotStartUp_timeoutSeconds = 60; // returns address or fails static String waitForBotStartUp(String botName) { for (int i = 0; i < waitForBotStartUp_timeoutSeconds; i++) { sleepSeconds(i == 0 ? 0 : 1); String addr = getBotAddress(botName); if (addr != null) return addr; } throw fail("Bot not found: " + quote(botName)); } static File loadBinarySnippet(String snippetID) { try { long id = parseSnippetID(snippetID); File f = DiskSnippetCache_getLibrary(id); if (f == null) { byte[] data = loadDataSnippetImpl(snippetID); DiskSnippetCache_putLibrary(id, data); f = DiskSnippetCache_getLibrary(id); } return f; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Object addToMultiPort_responder; static long addToMultiPort(final String botName) { return addToMultiPort(botName, new Object() { public String answer(String s, List history) { String answer = (String) ( callOpt(getMainClass(), "answer", s, history)); if (answer != null) return answer; answer = (String) callOpt(getMainClass(), "answer", s); if (answer != null) return answer; if (match3("get injection id", s)) return getInjectionID(); return null; } }); } static long addToMultiPort(final String botName, final Object responder) { print(botName); addToMultiPort_responder = responder; startMultiPort(); List ports = getMultiPorts(); if (ports.isEmpty()) fail("No multiports!"); if (ports.size() > 1) print("Multiple multi-ports. Using last one."); Object port = last(ports); Object responder2 = new Object() { public String answer(String s, List history) { if (match3("get injection id", s)) return getInjectionID(); if (match3("your name", s)) return botName; return (String) call(responder, "answer", s, history); } }; record(responder2); return (Long) call(port, "addResponder", botName, responder2); } static A liftLast(List l) { if (l.isEmpty()) return null; int i = l(l)-1; A a = l.get(i); l.remove(i); return a; } static String loadTextFileMandatory(File file) { String contents = loadTextFile(file); if (contents == null) fail("File not found: " + file.getPath()); return contents; } static Object newObject(Class c, Object... args) { return nuObject(c, args); } static Object newObject(String className, Object... args) { return nuObject(className, args); } static Map findBot_cache = new TreeMap(); static int findBot_timeout = 5000; static DialogIO findBot(String searchPattern) { // first split off sub-bot suffix String subBot = null; int i = searchPattern.indexOf('/'); if (i >= 0 && (isJavaIdentifier(searchPattern.substring(0, i)) || isInteger(searchPattern.substring(0, i)))) { subBot = searchPattern.substring(i+1); searchPattern = searchPattern.substring(0, i); if (!isInteger(searchPattern)) searchPattern = "Multi-Port at " + searchPattern + "."; } // assume it's a port if it's an integer if (isInteger(searchPattern)) return talkToSubBot(subBot, talkTo(parseInt(searchPattern))); if (eq(searchPattern, "remote")) return talkToSubBot(subBot, talkTo("second.tinybrain.de", 4999)); Integer port = findBot_cache.get(searchPattern); if (port != null) try { DialogIO io = talkTo("localhost", port); io.waitForLine(/*findBot_timeout*/); // TODO: implement String line = io.readLineNoBlock(); if (indexOfIgnoreCase(line, searchPattern) == 0) { call(io, "pushback", line); // put hello string back in return talkToSubBot(subBot, io); } } catch (Exception e) { e.printStackTrace(); } List bots = quickBotScan(); // find top-level bots for (ProgramScan.Program p : bots) { if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) { // strict matching - start of hello string only, but case-insensitive findBot_cache.put(searchPattern, p.port); return talkToSubBot(subBot, talkTo("localhost", p.port)); } } // find sub-bots for (ProgramScan.Program p : bots) { String botName = firstPartOfHelloString(p.helloString); boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM."); boolean shouldRecurse = startsWithIgnoreCase(botName, "Multi-Port") || isVM; if (shouldRecurse) try { Map subBots = (Map) unstructure(sendToLocalBot(p.port, "list bots")); for (Number vport : subBots.keySet()) { String name = subBots.get(vport); if (startsWithIgnoreCase(name, searchPattern)) return talkToSubBot(vport.longValue(), talkTo("localhost", p.port)); } } catch (Exception e) { e.printStackTrace(); } } return null; } static Throwable getInnerException(Throwable e) { while (e.getCause() != null) e = e.getCause(); return e; } // covers the common cases static String getBotNameFromSnippet(String programID) { try { String src = loadSnippet(programID); List tok = javaTok(src); String[] m; for (String pat : litlist( "new Android3(*", "makeAndroid(*", "makeAndroid3(*", "makeSilentAndroid(*", "addToMultiPort(*")) { m = find2(javaTok(pat), tok); if (m != null && isQuoted(m[0])) return unquote(m[0]); } return null; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} /** possibly improvable */ public static String winQuote(String text) { if (text == null) return null; return "\"" + text .replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") + "\""; } // match2 matches multiple "*" (matches a single token) wildcards and zero or one "..." wildcards (matches multiple tokens) static String[] match2(List pat, List tok) { // standard case (no ...) int i = pat.indexOf("..."); if (i < 0) return match2_match(pat, tok); pat = new ArrayList(pat); // We're modifying it, so copy first pat.set(i, "*"); while (pat.size() < tok.size()) { pat.add(i, "*"); pat.add(i+1, ""); // doesn't matter } return match2_match(pat, tok); } static String[] match2_match(List pat, List tok) { List result = new ArrayList(); if (pat.size() != tok.size()) { /*if (debug) print("Size mismatch: " + structure(pat) + " vs " + structure(tok));*/ return null; } for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(i); /*if (debug) print("Checking " + p + " against " + t);*/ if (eq(p, "*")) result.add(t); else if (!equalsIgnoreCase(unquote(p), unquote(t))) // bold change - match quoted and unquoted now return null; } return result.toArray(new String[result.size()]); } static boolean isNonNegativeInteger(String s) { return s != null && Pattern.matches("\\d+", s); } static String getInjectionID() { return (String) call(getJavaX(), "getInjectionID", getMainClass()); } static List jsonTok(String s) { List tok = new ArrayList(); int l = s.length(); int i = 0; while (i < l) { int j = i; char c; String cc; // scan for whitespace while (j < l) { c = s.charAt(j); cc = s.substring(j, Math.min(j+2, l)); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (cc.equals("/*")) { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (cc.equals("//")) { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(s.substring(i, j)); i = j; if (i >= l) break; c = s.charAt(i); // cc is not needed in rest of loop body // scan for non-whitespace (json strings, "null" identifier, numbers. everything else automatically becomes a one character token.) if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener) { ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isLetter(c)) do ++j; while (j < l && Character.isLetter(s.charAt(j))); else if (Character.isDigit(c)) do ++j; while (j < l && Character.isDigit(s.charAt(j))); else ++j; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } /** writes safely (to temp file, then rename) */ public static void saveTextFile(String fileName, String contents) throws IOException { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; if (contents != null) { FileOutputStream fileOutputStream = new FileOutputStream(tempFileName); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); printWriter.print(contents); printWriter.close(); } if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (contents != null) if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); } public static void saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); } catch (IOException e) { throw new RuntimeException(e); } } // This is made for NL parsing. // It's javaTok extended with "..." token, "$n" and "#n" and // special quotes (which are converted to normal ones). static List javaTokPlusPeriod(String s) { List tok = new ArrayList(); int l = s.length(); int i = 0; while (i < l) { int j = i; char c; String cc; // scan for whitespace while (j < l) { c = s.charAt(j); cc = s.substring(j, Math.min(j+2, l)); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (cc.equals("/*")) { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (cc.equals("//")) { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } tok.add(s.substring(i, j)); i = j; if (i >= l) break; c = s.charAt(i); cc = s.substring(i, Math.min(i+2, l)); // scan for non-whitespace if (c == '\u201C' || c == '\u201D') c = '"'; // normalize quotes if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { char _c = s.charAt(j); if (_c == '\u201C' || _c == '\u201D') _c = '"'; // normalize quotes if (_c == opener) { ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } if (j-1 >= i+1) { tok.add(opener + s.substring(i+1, j-1) + opener); i = j; continue; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for things like "this one's" else if (Character.isDigit(c)) do ++j; while (j < l && Character.isDigit(s.charAt(j))); else if (cc.equals("[[")) { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]")); j = Math.min(j+3, l); } else if (s.substring(j, Math.min(j+3, l)).equals("...")) j += 3; else if (c == '$' || c == '#') do ++j; while (j < l && Character.isDigit(s.charAt(j))); else ++j; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } /** writes safely (to temp file, then rename) */ public static void saveBinaryFile(String fileName, byte[] contents) throws IOException { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; FileOutputStream fileOutputStream = new FileOutputStream(tempFileName); fileOutputStream.write(contents); fileOutputStream.close(); if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); } static void saveBinaryFile(File fileName, byte[] contents) { try { saveBinaryFile(fileName.getPath(), contents); } catch (IOException e) { throw new RuntimeException(e); } } static List emptyList() { return Collections.emptyList(); } static AtomicInteger dialogServer_clients = new AtomicInteger(); static boolean dialogServer_printConnects; static Set dialogServer_knownClients = synchroTreeSet(); static int startDialogServerOnPortAbove(int port, DialogHandler handler) { while (!startDialogServerIfPortAvailable(port, handler)) ++port; return port; } static int startDialogServerOnPortAboveDaemon(int port, DialogHandler handler) { while (!startDialogServerIfPortAvailable(port, handler, true)) ++port; return port; } static void startDialogServer(int port, DialogHandler handler) { if (!startDialogServerIfPortAvailable(port, handler)) fail("Can't start dialog server on port " + port); } static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler) { return startDialogServerIfPortAvailable(port, handler, false); } static ServerSocket startDialogServer_serverSocket; static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler, boolean daemon) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(port); } catch (IOException e) { // probably the port number is used - let's assume there already is a chat server. return false; } final ServerSocket _serverSocket = serverSocket; startDialogServer_serverSocket = serverSocket; Thread thread = new Thread("Socket accept port " + port) { public void run() { try { while (true) { try { final Socket s = _serverSocket.accept(); String client = s.getInetAddress().toString(); if (!dialogServer_knownClients.contains(client)) { print("connect from " + client + " - clients: " + dialogServer_clients.incrementAndGet()); dialogServer_knownClients.add(client); } String threadName = "Handling client " + s.getInetAddress(); Thread t2 = new Thread(threadName) { public void run() { try { final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); final BufferedReader in = new BufferedReader( new InputStreamReader(s.getInputStream(), "UTF-8")); DialogIO io = new DialogIO() { // This should be the same as #1001076 (talkTo) boolean isLocalConnection() { return s.getInetAddress().isLoopbackAddress(); } boolean isStillConnected() { return !(eos || s.isClosed()); } void sendLine(String line) { try { w.write(line + "\n"); w.flush(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} String readLineImpl() { try { return in.readLine(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} void close() { try { s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } }; try { handler.run(io); } finally { s.close(); } } catch (IOException e) { print("[internal] " + e); } finally { //print("client disconnect - " + dialogServer_clients.decrementAndGet() + " remaining"); } } }; // Thread t2 t2.setDaemon(true); // ? t2.start(); } catch (SocketTimeoutException e) { } } } catch (IOException e) { print("[internal] " + e); } }}; if (daemon) thread.setDaemon(true); thread.start(); print("Dialog server on port " + port + " started."); return true; } static List synchroList() { return Collections.synchronizedList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } static String programID() { return getProgramID(); } static boolean eqic(String a, String b) { if ((a == null) != (b == null)) return false; if (a == null) return true; return a.equalsIgnoreCase(b); } static BufferedReader readLine_reader; static String readLine() { return (String) call(getJavaX(), "readLine"); } static int parseInt(String s) { return Integer.parseInt(s); } static String structureOrText(Object o) { return o instanceof String ? (String) o : structure(o); } static int newFindBot_botVM; static synchronized DialogIO newFindBot(String name) { if (newFindBot_botVM == 0) { print("Looking for Bot VM."); List botVMs = quickBotScan("This is a JavaX VM. Bot VM."); if (!botVMs.isEmpty()) { newFindBot_botVM = botVMs.get(0).port; print("Bot VM is at port " + newFindBot_botVM + "."); } } if (newFindBot_botVM != 0) { DialogIO io = talkTo(newFindBot_botVM); String q = format("has bot *", name); String s = io.ask(q); //print(format("Answer of Bot VM to *: *", q, s)); if (match("yes", s)) { io = talkToSubBot(name, io); call(io, "pushback", "?"); // put some hello string in (yes, this should be improved.) return io; } } return findBot(name); } static List codeTokensOnly(List tok) { List l = new ArrayList(); for (int i = 1; i < tok.size(); i += 2) l.add(tok.get(i)); return l; } static List cloneList(List l) { //O mutex = getOpt(l, "mutex"); /*if (mutex != null) synchronized(mutex) { ret new ArrayList(l); } else ret new ArrayList(l);*/ // assume mutex is equal to collection, which will be true unless you explicitly pass a mutex to synchronizedList() which no one ever does. synchronized(l) { return new ArrayList(l); } } // currently finds only inner classes of class "main" // returns null on not found // this is the simple version that is not case-tolerant static Class findClass(String name) { try { return Class.forName("main$" + name); } catch (ClassNotFoundException e) { return null; } } static boolean isJavaIdentifier(String s) { if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i < s.length(); i++) if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; return true; } static List parse3(String s) { return dropPunctuation(javaTokPlusPeriod(s)); } static String _userHome; static String userHome() { if (_userHome == null) { if (isAndroid()) _userHome = "/storage/sdcard0/"; else _userHome = System.getProperty("user.home"); //System.out.println("userHome: " + _userHome); } return _userHome; } static boolean isLongConstant(String s) { if (!s.endsWith("L")) return false; s = s.substring(0, l(s)-1); return isInteger(s); } static void assertEquals(Object x, Object y) { assertEquals(null, x, y); } static void assertEquals(String msg, Object x, Object y) { if (!(x == null ? y == null : x.equals(y))) fail((msg != null ? msg + ": " : "") + structure(x) + " != " + structure(y)); } static File getProgramDir() { return programDir(); } static File getProgramDir(String snippetID) { return programDir(snippetID); } static long parseLong(String s) { return Long.parseLong(s); } static String md5(String text) { try { if (text == null) return "-"; return bytesToHex(md5_impl(text.getBytes("UTF-8"))); // maybe different than the way PHP does it... } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static String md5(byte[] data) { return bytesToHex(md5_impl(data)); } static byte[] md5_impl(byte[] data) { try { return MessageDigest.getInstance("MD5").digest(data); } catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); } } static String md5(File file) { try { return md5(loadBinaryFile(file)); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static boolean isIdentifier(String s) { return isJavaIdentifier(s); } static boolean publicCommOn() { return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication"))); } static String[] toLowerCase(String[] strings) { String[] x = new String[l(strings)]; for (int i = 0; i < l(strings); i++) x[i] = strings[i].toLowerCase(); return x; } static String toLowerCase(String s) { return s == null ? "" : s.toLowerCase(); } static String loadTextFileFromZipFile(File inZip, String fileName) { try { ZipFile zip = new ZipFile(inZip); try { ZipEntry entry = zip.getEntry(fileName); if (entry == null) //fail("Entry " + fileName + " not found in zip file: " + inZip.getAbsolutePath()); return null; InputStream fin = zip.getInputStream(entry); ByteArrayOutputStream baos = new ByteArrayOutputStream(); copyStream(fin, baos); fin.close(); return fromUTF8(baos.toByteArray()); } finally { zip.close(); } } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static String getSnippetFromBossBot(long snippetID) { return boss(format3("get text for *", snippetID)); } static Object nuObject(String className, Object... args) { try { return nuObject(Class.forName(className), args); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Object nuObject(Class c, Object... args) { try { Constructor m = nuObject_findConstructor(c, args); m.setAccessible(true); return m.newInstance(args); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Constructor nuObject_findConstructor(Class c, Object... args) { for (Constructor m : c.getDeclaredConstructors()) { if (!nuObject_checkArgs(m.getParameterTypes(), args, false)) continue; return m; } throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName()); } static boolean nuObject_checkArgs(Class[] types, Object[] args, boolean debug) { if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } static boolean booleanValue(Object o) { return eq(true, o); } static int latestInstalledJavaX() { File[] files = new File(userHome(), ".javax").listFiles(); int v = 0; if (files != null) for (File f : files) { Matcher m = Pattern.compile("x(\\d\\d\\d?)\\.jar").matcher(f.getName()); if (m.matches()) v = Math.max(v, Integer.parseInt(m.group(1))); } return v; } static double parseDouble(String s) { return Double.parseDouble(s); } // try to get our current process ID static String getPID() { String name = ManagementFactory.getRuntimeMXBean().getName(); return name.replaceAll("@.*", ""); } public static File nohup(String cmd) throws IOException { File outFile = File.createTempFile("nohup_" + nohup_sanitize(cmd), ".out"); nohup(cmd, outFile, false); return outFile; } static String nohup_sanitize(String s) { return s.replaceAll("[^a-zA-Z0-9\\-_]", ""); } /** outFile takes stdout and stderr. */ public static void nohup(String cmd, File outFile, boolean append) throws IOException { String command = nohup_makeNohupCommand(cmd, outFile, append); File scriptFile = File.createTempFile("_realnohup", isWindows() ? ".bat" : ""); System.out.println("[Nohup] " + command); try { //System.out.println("[RealNohup] Script file: " + scriptFile.getPath()); saveTextFile(scriptFile.getPath(), command); String[] command2; if (isWindows()) command2 = new String[] {"cmd", "/c", "start", "/b", scriptFile.getPath() }; else command2 = new String[] {"/bin/bash", scriptFile.getPath() }; Process process = Runtime.getRuntime().exec(command2); try { process.waitFor(); } catch (InterruptedException e) { throw new RuntimeException(e); } int value = process.exitValue(); //System.out.println("exit value: " + value); } finally { if (!isWindows()) scriptFile.delete(); } } public static String nohup_makeNohupCommand(String cmd, File outFile, boolean append) { mkdirsForFile(outFile); String command; if (isWindows()) command = cmd + (append ? " >>" : " >") + winQuote(outFile.getPath()) + " 2>&1"; else command = "nohup " + cmd + (append ? " >>" : " >") + bashQuote(outFile.getPath()) + " 2>&1 &"; return command; } static String dropSuffix(String suffix, String s) { return s.endsWith(suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static byte[] loadBinaryPage(String url) throws IOException { return loadBinaryPage(new URL(url).openConnection()); } public static byte[] loadBinaryPage(URLConnection con) throws IOException { //setHeaders(con); ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) System.err.println(" " + n + " bytes loaded."); } inputStream.close(); return buf.toByteArray(); } static String randomID(int length) { return makeRandomID(length); } static boolean equalsIgnoreCase(String a, String b) { return a == null ? b == null : a.equalsIgnoreCase(b); } static DialogIO talkToSubBot(final long vport, final DialogIO io) { return talkToSubBot(String.valueOf(vport), io); } static DialogIO talkToSubBot(final String subBot, final DialogIO io) { if (subBot == null) return io; return new DialogIO() { // delegate all but sendLine boolean isStillConnected() { return io.isStillConnected(); } String readLineImpl() { return io.readLineImpl(); } boolean isLocalConnection() { return io.isLocalConnection(); } Socket getSocket() { return io.getSocket(); } void close() { io.close(); } void sendLine(String line) { io.sendLine(format3("please forward to bot *: *", subBot, line)); } }; } static Set synchroTreeSet() { return Collections.synchronizedSet(new TreeSet()); } // Data files are immutable, use centralized cache public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException { File file = new File(getGlobalCache(), "data_" + snippetID + ".jar"); return file.exists() ? file : null; } public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException { saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data); } static byte[] loadDataSnippetImpl(String snippetID) throws IOException { byte[] data; try { URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + parseSnippetID(snippetID) + "&contentType=application/binary"); System.err.println("Loading library: " + url); try { data = loadBinaryPage(url.openConnection()); } catch (IOException e) { data = null; } if (data == null || data.length == 0) { url = new URL("http://data.tinybrain.de/blobs/" + parseSnippetID(snippetID)); System.err.println("Loading library: " + url); data = loadBinaryPage(url.openConnection()); } System.err.println("Bytes loaded: " + data.length); } catch (FileNotFoundException e) { throw new IOException("Binary snippet #" + snippetID + " not found or not public"); } return data; } // We dropped the "***" support here (use match3 for that) static String[] find2(List pat, List tok) { for (int idx = 0; idx < tok.size(); idx += 2) { String[] result = find2(pat, tok, idx); if (result != null) return result; } return null; } static String[] find2(List pat, List tok, int idx) { if (idx+pat.size() > tok.size()) return null; List result = new ArrayList(); for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(idx+i); if (eq(p, "*")) result.add(t); else if (!p.equalsIgnoreCase(t)) return null; } return toStringArray(result); } static boolean startsWithIgnoreCase(String a, String b) { return a != null && a.regionMatches(true, 0, b, 0, b.length()); } static String firstPartOfHelloString(String s) { int i = s.lastIndexOf('/'); return i < 0 ? s : rtrim(s.substring(0, i)); } public static String bytesToHex(byte[] bytes) { return bytesToHex(bytes, 0, bytes.length); } public static String bytesToHex(byte[] bytes, int ofs, int len) { StringBuilder stringBuilder = new StringBuilder(len*2); for (int i = 0; i < len; i++) { String s = "0" + Integer.toHexString(bytes[ofs+i]); stringBuilder.append(s.substring(s.length()-2, s.length())); } return stringBuilder.toString(); } static int getBotAddress_timeout = 5000; static String getBotAddress(String searchPattern) { String subBot = ""; int i = searchPattern.indexOf('/'); if (i >= 0 && (isJavaIdentifier(searchPattern.substring(0, i)) || isInteger(searchPattern.substring(0, i)))) { subBot = searchPattern.substring(i); searchPattern = searchPattern.substring(0, i); if (!isInteger(searchPattern)) searchPattern = "Multi-Port at " + searchPattern + "."; } // assume it's a port if it's an integer if (isInteger(searchPattern)) return searchPattern + subBot; Integer port = findBot_cache.get(searchPattern); if (port != null) try { DialogIO io = talkTo("localhost", port); try { io.waitForLine(/*getBotAddress_timeout*/); // TODO: implement String line = io.readLineNoBlock(); if (startsWithIgnoreCase(line, searchPattern)) return port + subBot; } finally { io.close(); } } catch (Exception e) { e.printStackTrace(); } List bots = quickBotScan(); // find top-level bots for (ProgramScan.Program p : bots) { if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) { // strict matching - start of hello string only, but case-insensitive return p.port + subBot; } } // find sub-bots for (ProgramScan.Program p : bots) { String botName = firstPartOfHelloString(p.helloString); boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM."); boolean shouldRecurse = startsWithIgnoreCase(botName, "Multi-Port") || isVM; if (shouldRecurse) try { Map subBots = (Map) unstructure(sendToLocalBot(p.port, "list bots")); for (Number vport : subBots.keySet()) { String name = subBots.get(vport); if (startsWithIgnoreCase(name, searchPattern)) return p.port + "/" + vport; } } catch (Exception e) { e.printStackTrace(); } } return null; } public static void mkdirsForFile(File file) { File dir = file.getParentFile(); if (dir != null) // is null if file is in current dir dir.mkdirs(); } static List quickBotScan() { return ProgramScan.quickBotScan(); } static List quickBotScan(int[] preferredPorts) { return ProgramScan.quickBotScan(preferredPorts); } static List quickBotScan(String searchPattern) { List l = new ArrayList(); for (ProgramScan.Program p : ProgramScan.quickBotScan()) if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) l.add(p); return l; } static String makeRandomID(int length) { Random random = new Random(); char[] id = new char[length]; for (int i = 0; i < id.length; i++) id[i] = (char) ((int) 'a' + random.nextInt(26)); return new String(id); } public static byte[] loadBinaryFile(String fileName) throws IOException { if (!new File(fileName).exists()) return null; FileInputStream in = new FileInputStream(fileName); byte buf[] = new byte[1024]; ByteArrayOutputStream out = new ByteArrayOutputStream(); int l; while (true) { l = in.read(buf); if (l <= 0) break; out.write(buf, 0, l); } in.close(); return out.toByteArray(); } public static byte[] loadBinaryFile(File file) throws IOException { return loadBinaryFile(file.getPath()); } public static void copyStream(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[65536]; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); } } static class Injection { int vmPort; String injectionID; } static Injection injectTo(int vmPort, String progID) { return injectTo(String.valueOf(vmPort), progID); } static Injection injectTo(String vmName, String progID) { String line = format3("Please inject program *.", progID); DialogIO injectionPoint = findBot(isInteger(vmName) ? vmName : "This is a JavaX VM. " + vmName); if (injectionPoint == null) return null; try { injectionPoint.readLine(); String answer = injectionPoint.askLoudly(line); Matches m = new Matches(); if (match3("OK. Injection ID: *", answer, m)) { Injection i = new Injection(); i.vmPort = injectionPoint.getSocket().getPort(); i.injectionID = m.unq(0); return i; } throw fail(answer); } finally { injectionPoint.close(); } } static DialogIO talkTo(int port) { return talkTo("localhost", port); } static DialogIO talkTo(String ip, int port) { try { final Socket s = new Socket(ip, port); //print("Talking to " + ip + ":" + port); final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8")); return new DialogIO() { boolean isLocalConnection() { return s.getInetAddress().isLoopbackAddress(); } boolean isStillConnected() { return !(eos || s.isClosed()); } void sendLine(String line) { try { w.write(line + "\n"); w.flush(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} String readLineImpl() { try { return in.readLine(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} void close() { try { s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } }; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static String boss(String line) { try { //S s = sendToLocalBotOpt("Boss Bot", line); DialogIO io = talkTo(4990); // Boss Bot port io.readLine(); io.sendLine(line); String s = io.readLine(); Matches m = new Matches(); if (match3("text: *", s, m)) return unquote(m.m[0]); return null; } catch (Exception e) { //e.printStackTrace(); return null; } } static String sendToLocalBot(String bot, String text, Object... args) { text = format3(text, args); DialogIO channel = findBot(bot); if (channel == null) fail(quote(bot) + " not found"); try { channel.readLine(); print(bot + "> " + shorten(text, 80)); channel.sendLine(text); String s = channel.readLine(); print(bot + "< " + shorten(s, 80)); return s; } catch (Throwable e) { e.printStackTrace(); return null; } finally { channel.close(); } } static String sendToLocalBot(int port, String text, Object... args) { text = format3(text, args); DialogIO channel = talkTo(port); try { channel.readLine(); print(port + "> " + shorten(text, 80)); channel.sendLine(text); String s = channel.readLine(); print(port + "< " + shorten(s, 80)); return s; } catch (Throwable e) { e.printStackTrace(); return null; } finally { if (channel != null) channel.close(); } } // works on lists and strings and null static int indexOfIgnoreCase(Object a, Object b) { if (a == null) return -1; if (a instanceof String) { Matcher m = Pattern.compile((String) b, Pattern.CASE_INSENSITIVE + Pattern.LITERAL).matcher((String) a); if (m.find()) return m.start(); else return -1; } if (a instanceof List) { for (int i = 0; i < ((List) a).size(); i++) { Object o = ((List) a).get(i); if (o != null && ((String) o).equalsIgnoreCase((String) b)) return i; } return -1; } throw fail("Unknown type: " + a); } static void startBotVM() { startBotSeparateVM("This is a JavaX VM. Bot VM.", "#1001710"); } static String fromUTF8(byte[] bytes) { return fromUtf8(bytes); } // start multi-port if none exists in current VM. static void startMultiPort() { if (getMultiPorts().isEmpty()) callMain(hotwire("#1001672")); } static boolean isAndroid() { return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0; } static String[] toStringArray(List list) { return list.toArray(new String[list.size()]); } static String[] toStringArray(Object o) { if (o instanceof String[]) return (String[]) o; else if (o instanceof List) return toStringArray((List) o); else throw fail("Not a list or array: " + structure(o)); } static File getGlobalCache() { File file = new File(userHome(), ".tinybrain/snippet-cache"); file.mkdirs(); return file; } public static String rtrim(String s) { int i = s.length(); while (i > 0 && " \t\r\n".indexOf(s.charAt(i-1)) >= 0) --i; return i < s.length() ? s.substring(0, i) : s; } static String fromUtf8(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static void startBotSeparateVM(String botName, String scriptID) { DialogIO bot = findBot(botName); if (bot != null) bot.close(); else { print("Bot " + quote(botName) + " not found. Starting separately: " + scriptID); nohupJavax(scriptID); waitForBotStartUp(botName); } } static class ProgramScan { static int threads = isWindows() ? 500 : 10; static int timeout = 5000; // hmm... static String ip = "127.0.0.1"; static int quickScanFrom = 10000, quickScanTo = 10999; static int maxNumberOfBotPorts = 100; static boolean verbose; static class Program { int port; String helloString; Program(int port, String helloString) { this.helloString = helloString; this.port = port;} } static List scan() { try { return scan(1, 65535); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static List scan(int fromPort, int toPort) { return scan(fromPort, toPort, new int[0]); } static List scan(int fromPort, int toPort, int[] preferredPorts) { try { Set preferredPortsSet = new HashSet(asList(preferredPorts)); String name = toPort < 10000 ? "bot" : "program"; final ExecutorService es = Executors.newFixedThreadPool(threads); if (verbose) print(firstToUpper(name) + "-scanning " + ip + " with timeout " + timeout + " ms in " + threads + " threads."); startTiming(); List> futures = new ArrayList>(); for (int port : preferredPorts) futures.add(checkPort(es, ip, port, timeout)); for (int port = fromPort; port <= toPort; port++) if (!preferredPortsSet.contains(port)) futures.add(checkPort(es, ip, port, timeout)); es.shutdown(); List programs = new ArrayList(); for (final Future f : futures) { Program p = f.get(); if (p != null) programs.add(p); } stopTiming(); if (verbose) print("Found " + programs.size() + " " + name + "(s) on " + ip); return programs; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Future checkPort(final ExecutorService es, final String ip, final int port, final int timeout) { return es.submit(new Callable() { @Override public Program call() { try { Socket socket = new Socket(); socket.setSoTimeout(timeout); socket.connect(new InetSocketAddress(ip, port), timeout); if (verbose) print("Connected to " + ip + ":" + port); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); String hello = or(in.readLine(), "?"); socket.close(); return new Program(port, hello); } catch (Exception ex) { return null; } } }); } static List quickScan() { return scan(quickScanFrom, quickScanTo); } static List quickBotScan() { return quickBotScan(new int[0]); } static List quickBotScan(int[] preferredPorts) { return scan(4990, 5000+maxNumberOfBotPorts-1, preferredPorts); } } // a Lisp-like form static class Lisp implements Iterable { String head; List args = new ArrayList(); Object more; // additional info, user-defined Lisp() {} Lisp(String head) { this.head = head;} Lisp(String head, Lisp... args) { this.head = head; this.args.addAll(asList(args)); } Lisp(String head, List args) { this.head = head; for (Object arg : args) add(arg); } // INEFFICIENT public String toString() { if (args.isEmpty()) return quoteIfNotIdentifier(head); List bla = new ArrayList(); for (Lisp a : args) bla.add(a.toString()); String inner = join(", ", bla); if (head.equals("")) return "{" + inner + "}"; // list else return quoteIfNotIdentifier(head) + "(" + inner + ")"; } String raw() { if (!isEmpty ()) fail("not raw: " + this); return head; } Lisp add(Lisp l) { args.add(l); return this; } Lisp add(String s) { args.add(new Lisp(s)); return this; } Lisp add(Object o) { if (o instanceof Lisp) add((Lisp) o); else if (o instanceof String) add((String) o); else fail("Bad argument type: " + structure(o)); return this; } int size() { return args.size(); } boolean isEmpty() { return args.isEmpty(); } boolean isLeaf() { return args.isEmpty(); } Lisp get(int i) { return args.get(i); } String getString(int i) { return get(i).head; } String s(int i) { return getString(i); } boolean isA(String head) { return eq(head, this.head); } boolean is(String head, int size) { return isA(head) && size() == size; } boolean is(String head) { return isA(head); } boolean is(String... heads) { return asList(heads).contains(head); } // check head for one of these (ignore case) boolean isic(String... heads) { return containsIgnoreCase(heads, head); } public Iterator iterator() { return args.iterator(); } Lisp subList(int fromIndex, int toIndex) { Lisp l = new Lisp(head); l.args.addAll(args.subList(fromIndex, toIndex)); // better to copy here I guess - safe return l; } public boolean equals(Object o) { if (o == null || o.getClass() != Lisp.class) return false; Lisp l = (Lisp) ( o); return eq (head, l.head) && eq(args, l.args); } Lisp addAll(List args) { for (Object arg : args) add(arg); return this; } String unquoted() { return unquote(raw()); } String unq() { return unquoted(); } } static abstract class DialogIO { String line; boolean eos; abstract String readLineImpl(); abstract boolean isStillConnected(); abstract void sendLine(String line); abstract boolean isLocalConnection(); abstract Socket getSocket(); abstract void close(); int getPort() { return getSocket().getPort(); } boolean helloRead; String readLineNoBlock() { String l = line; line = null; return l; } boolean waitForLine() { try { if (line != null) return true; //print("Readline"); line = readLineImpl(); //print("Readline done: " + line); if (line == null) eos = true; return line != null; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} String readLine() { waitForLine(); helloRead = true; return readLineNoBlock(); } String ask(String s, Object... args) { if (!helloRead) readLine(); if (args.length != 0) s = format3(s, args); sendLine(s); return readLine(); } String askLoudly(String s, Object... args) { if (!helloRead) readLine(); if (args.length != 0) s = format3(s, args); print("> " + s); sendLine(s); String answer = readLine(); print("< " + answer); return answer; } void pushback(String l) { if (line != null) fail(); line = l; helloRead = false; } } static abstract class DialogHandler { abstract void run(DialogIO io); } static class Matches { String[] m; String get(int i) { return m[i]; } String unq(int i) { return unquote(m[i]); } String fsi(int i) { return formatSnippetID(unq(i)); } boolean bool(int i) { return "true".equals(unq(i)); } String rest() { return m[m.length-1]; } // for matchStart int psi(int i) { return Integer.parseInt(unq(i)); } } static void printStackTrace(Throwable e) { // we go to system.out now - system.err is nonsense print(getStackTrace(e)); } static void printStackTrace() { printStackTrace(new Throwable()); } static long startTiming_startTime; static void startTiming() { startTiming_startTime = now(); } static void stopTiming() { long end = now(); print("Time: " + (end-startTiming_startTime) + " ms"); } static String quoteIfNotIdentifier(String s) { if (s == null) return null; return isJavaIdentifier(s) ? s : quote(s); } static String getString(Map map, Object key) { return map == null ? null : (String) map.get(key); } static String getString(List l, int idx) { return (String) get(l, idx); } static String getString(Object o, Object key) { if (o instanceof Map) return getString((Map) o, key); if (key instanceof String) return (String) get(o, (String) key); throw fail("Not a string key: " + getClassName(key)); } static boolean containsIgnoreCase(List l, String s) { for (String x : l) if (eqic(x, s)) return true; return false; } static boolean containsIgnoreCase(String[] l, String s) { for (String x : l) if (eqic(x, s)) return true; return false; } static ThreadLocal saveTiming_last = new ThreadLocal(); static void saveTiming(long ms) { print(ms + " ms"); saveTiming_last.set(ms); } static boolean equals(Object a, Object b) { return a == null ? b == null : a.equals(b); } // also logs full exception to console static String exceptionToUser(Throwable e) { return throwableToUser(e); } static String firstToUpper(String s) { if (s.length() == 0) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } static String getClassName(Object o) { return o == null ? "null" : o.getClass().getName(); } // also logs full exception to console static String throwableToUser(Throwable e) { return formatExceptionForUser(e); } // also logs full exception to console static String formatExceptionForUser(Throwable e) { printStackTrace(e); String msg = unnull(e.getMessage()); if (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0) return dropPrefix("java.lang.", str(e)); else return msg; } }