// An "Android" is a program that accepts text questions (on console or TCP) and outputs one response text per question //please include function myJavaSource. // for getting my known commands static bool makeAndroid3_disable; // disable all android making static class Android3 implements AutoCloseable { S greeting; boolean publicOverride; // optionally set this in client int startPort = 5000; // optionally set this in client IResponder responder; boolean console = true; boolean quiet; // no messages on console boolean daemon = false; boolean incomingSilent = false; int incomingPrintLimit = 200; boolean useMultiPort = true; boolean recordHistory; boolean verbose; int answerPrintLimit = 500; bool newLineAboveAnswer, newLineBelowAnswer; // set by system int port; long vport; IDialogHandler handler; ServerSocket server; *(S *greeting) {} *() {} public void close { dispose(); } synchronized void dispose() { if (server != null) { try { server.close(); } catch (IOException e) { print("[internal] " + e); } server = null; } if (vport != 0) pcall { print("Disposing " + this); removeFromMultiPort(vport); vport = 0; } } toString { ret "Bot: " + greeting + " [port " + port + + (vport == 0 ? "" : ", vport " + vport) + "]"; } } static abstract class Responder is IResponder {} interface IResponder { S answer(S s, L history); } static Android3 makeAndroid3(final S greeting) { return makeAndroid3(new Android3(greeting)); } static Android3 makeAndroid3(final S greeting, Responder responder) { Android3 android = new Android3(greeting); android.responder = responder; return makeAndroid3(android); } static Android3 makeAndroid3(final Android3 a) { if (makeAndroid3_disable) ret a; if (a.responder == null) a.responder = new Responder() { public S answer(S s, L history) { return callStaticAnswerMethod(s, history); } }; if (!a.quiet) print("[bot] " + a.greeting); if (a.console && (readLine_noReadLine || makeAndroid3_consoleInUse())) a.console = false; record(a); if (a.useMultiPort) a.vport = addToMultiPort(a.greeting, makeAndroid3_verboseResponder(a)); if (a.console) makeAndroid3_handleConsole(a); if (a.useMultiPort) ret a; a.handler = makeAndroid3_makeDialogHandler(a); if (a.quiet) startDialogServer_quiet.set(true); try { a.port = a.daemon ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler) : startDialogServerOnPortAbove(a.startPort, a.handler); } finally { startDialogServer_quiet.set(null); } a.server = startDialogServer_serverSocket; return a; } static void makeAndroid3_handleConsole(final Android3 a) { // Console handling stuff if (!a.quiet) print("You may also type on this console."); thread { new L history; while licensed { S line; try { line = readLine(); } catch e { print(getInnerMessage(e)); break; } if (line == null) break; /*if (eq(line, "bye")) { print("> bye stranger"); history = new ArrayList(); } else*/ { history.add(line); history.add(makeAndroid3_getAnswer(line, history, a)); // prints answer on console too } } } } static DialogHandler makeAndroid3_makeDialogHandler(final Android3 a) { return dialogHandler { if (!a.publicOverride && !(publicCommOn() || io.isLocalConnection())) { io.sendLine("Sorry, not allowed"); return; } String dialogID = randomID(8); io.sendLine(a.greeting + " / Your ID: " + dialogID); new L history; while (io.isStillConnected()) { if (io.waitForLine()) { final String line = io.readLineNoBlock(); S s = dialogID + " at " + now() + ": " + quote(line); if (!a.incomingSilent) print(shorten(s, a.incomingPrintLimit)); if (eq(line, "bye")) { io.sendLine("bye stranger"); return; } new Matches m; if (a.recordHistory) history.add(line); S 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 try { makeAndroid3_io.set(io); answer = makeAndroid3_getAnswer(line, history, a); } finally { makeAndroid3_io.set(null); } if (a.recordHistory) history.add(answer); io.sendLine(answer); //appendToLog(logFile, s); } } }; } static S makeAndroid3_getAnswer(S line, L history, Android3 a) { S answer, originalAnswer; try { originalAnswer = a.responder.answer(line, history); answer = makeAndroid3_fallback(line, history, originalAnswer); } catch (Throwable e) { e = getInnerException(e); printStackTrace(e); originalAnswer = answer = e.toString(); } if (!a.incomingSilent) { if (originalAnswer == null) originalAnswer = "?"; if (a.newLineAboveAnswer) print(); print(">" + dropFirst(indentx(2, shorten(rtrim(originalAnswer), a.answerPrintLimit)))); if (a.newLineBelowAnswer) print(); } return answer; } static S makeAndroid3_fallback(S s, L history, S 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() { if (isTrue(vm_generalMap_get('consoleInUse))) true; for (O o : record_list) if (o instanceof Android3 && ((Android3) o).console) return true; return false; } static Responder makeAndroid3_verboseResponder(final Android3 a) { ret new Responder() { public S answer(S s, L history) { if (a.verbose) print("> " + shorten(s, a.incomingPrintLimit)); S answer = a.responder.answer(s, history); if (a.verbose) print("< " + shorten(answer, a.incomingPrintLimit)); ret answer; } }; } static new ThreadLocal makeAndroid3_io; static Android3 makeAndroid3() { ret makeAndroid3(getProgramTitle() + "."); }