Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

1689
LINES

< > BotCompany Repo | #1010745 // Smart Bot (LIVE)

JavaX source code (desktop) [tags: transpile-externally use-pretranspiled] - run with: x30.jar

Download Jar. Uses 17395K of libraries. Click here for Pure Java version (44978L/280K).

!7

set flag hotwire_here. // to honor classesToShare

sbool greetCookies, theoryOn;

sbool cloned;
static long clonedSince, cloningSince;
sS postStartUpMessageToChat;
static new L thoughtBots;
static long started, indexMakingTime, bootTime;
static Class theoryModule;
static Lock theoryLock = lock();
static S selectedWord;
static int inputNr = 1;
static int myPort = 4678;
static int ultimateAnswerLimit = 500000;
static double speculationTimeoutForHttp = 5.0; // 5 seconds
static double speculationTimeoutForNewChatLine = 30.0;
static int autoComplete_timeOut = 1000; // 1 second
static Set<S> speculationQueue = synchroHashSet();
static double spammerSleep = 10.0;
static Set<S> blockedIPs = synchroHashSet();

sS classesToShare = smartBot_sharedClasses();

static Lock aiLock = lock();
static new ThreadLocal<Bool> httpAuthed;
static Map<S, L<S>> lastUserLines = syncCIMap();
static new ThreadLocal<L> afterAnswering;
static new ThreadLocal<Bool> answering;
static L<GlobalID> latestWebs = synchroList(LatestList(1000));

static TimedCache<Int> cachedNumberOfThreads = TimedCache(f numberOfAllThreads, 10.0);

static L<S> tripleAnswerFunctions = ll(
  f ai_tripleAnswer_regexpMatches
);

set flag allpublic.

// save space
sclass E {}
set flag noCLParse.

p {
  set profilePoint_print;
  print("yo");
  started = sysNow();
  try { setGCFrequency(10*60.0); // 10 minutes
  } catch { updateJavaXAndRestart(); }
  
  doEvery(10*60*1000, r { blockedIPs.clear() });
  
  ai_useThoughtSpaces(true);
  cachedNodeIndex_autoMake = false;
  releaseDBLockOnTimeoutEval();
  ai_prepLoadFunctions();

  //set postToStefansChat_printStackTrace;
  db();
  load('selectedWord);
  load('inputNr);
  //autoRestart(5);
  //typicalDownload();
  
  O webSocketFactory = f makeWebSocket;
  if (canOccupyPort80())
    serveHttpWithWebsockets_multiplePorts(webSocketFactory, myPort, 80);
  else
    serveHttpWithWebsockets(myPort, webSocketFactory);
  pcall { serveHttps_botCompany(); }
  
  bot("Smart Bot.", func(S s) -> S {
    S x = "Input line " + (inputNr++);
    print(x);
    save("inputNr");
    ai_postTriple(x, "is", quote(s));
    //ai_speculate(x);

    stefansChat_onLine_fullParams.set(litmap(auth := true));
    try {
      ret actualAnswer(s);
    } finally {
      stefansChat_onLine_fullParams.set(null);
    }
  });
  
  loadPage_forcedTimeout = 70000;
  thoughtBots.add(mc());

  indexMakingTime = sysNow();

  ai_fillTripleIndex();
  //tripleIndex().addWeb(web_setGlobalID("abcdefghijklmnop", webFromTriple("It's", "a", "test")));

  indexMakingTime = sysNow()-indexMakingTime;
  
  // TODO ai_onNewIndexedText_do(func(S s) { speculationQueue.add(s) });
  
  //thoughtBots.add(memoryBot = runSubBot(#1001951));
  
  handleStefansChat();
  handleSmartBotsChat();
  
  //stefansChat_post("Smart Bot Upgraded! Boot took: " + formatDouble(fromMS(sysNow()-started), 1) + " s");
  
  if (greetCookies) runInNewThread(#1010793); // Post on UAIP
  
  ai_onNewWeb(f ai_spec_moveToMiddle);
  ai_onNewWeb(f addToLatestWebs);
  ai_extendKnownVerbs();
  
  // Do the slow stuff
  
  addVirtualNodeIndex(hugeEnglishDictionary_virtualNodeIndex());
  loadTheory();
  
  thread "Speculator" { speculatorLoop(); }
  
  doneLoading();
  bootTime = sysNow()-started;
  if (cloned)
    postToChatNamed(postStartUpMessageToChat, infoBoxAndReturn(
      "Smart Bot cloned in " + formatSeconds(bootTime, 1) + "s ("
      + formatSeconds(sysNow()-clonedSince, 1) + "s full)"));
  else
    infoBox("Smart Bot booted in " + formatSeconds(bootTime, 1) + "s");
}

sS actualAnswer(S s) {
  ret scanMultipleBots(thoughtBots, s);
}

svoid loadTheory {
  lock theoryLock;
  cleanUp(theoryModule);
  theoryModule = null;
  if (!theoryOn) ret;
  theoryModule = run(#1010963);
  setOpt(theoryModule, 'onUpdate, r { call(theoryModule, 'react) });
  call(theoryModule, 'react);
  print("Theory module loaded.");
}

static int lineCount;

sbool authed() {
  ret isTrue(mapGet(stefansChat_onLine_fullParams!, 'auth))
    || webAuthed();
}

sbool byBot() {
  ret isTrue(mapGet(stefansChat_onLine_fullParams!, 'botMark));
}

answer ctex {
  if (byBot()) null;
  /*temp tempSetThreadLocal(answering, true);
  try {*/
    selectWord(quote(s));
    S a = answer_2(s);
    //callAll(getAndClearThreadLocal(afterAnswering));
    try answer a;
  /*} on fail {
    clearThreadLocal(afterAnswering);
  }*/
}

sS answer_2(S s) {
  // repeat while ConcurrentModificationException
  // (should not occur anymore anyway)
  while licensed {
    try {
      ret trim(answer_go(s));
    } catch print e {
      S a = exceptionToStringShort(e);
      if (eq(a, "ConcurrentModificationException")) {
        sleep(50);
        continue;
      }
      ret a;
    }
  }
  null;
}


sS answer_go(S s) {
  ++lineCount;
  bool authed = authed();
  lock dbLock();
  
  S std = ai_standardAnswer(s);
  if (std == null) std = ai_standardAnswer_dollarVars(s); // TODO: might get slow eventually
  
  if (!startsWith(s, "!"))
    s = ai_dropDroppableStuff(s);
  
  int safety = 0;
  replaceloop: while (safety++ < 10) {
    s = trim(s);
    
    try answer answer_inner(s, lineCount);
    
    for (WebNode node : indexedNodes(s)) {
      S x = web_operandText(node, "replace with");
      if (x != null && neq(s, x)) {
        print("Replacing with " + x);
        s = x;
        continue replaceloop;
      }
    }
    
    break;
  }
  
  //if (ai_isQuestion_1(s)) ret "No idea";
  ret std;
}

sS chatName() {
  ret or2((S) mapGet(stefansChat_onLine_fullParams!, 'chatName), "Unknown Chat");
}

sS chatLineSymbol() {
  ret (S) mapGet(stefansChat_onLine_fullParams!, "Chat line symbol");
}

sS answer_inner(S s, int lineCount) {
  S chatName = chatName();
  
  final new Matches m;
  
  if (eqic(s, "!eval ps"))
    ret backtick("ps");
  if (swic_trim(s, "!eval ps ", m))
    ret backtick("ps " + noUnixTricks(m.rest()));

  if (authed()) {
    /*if (lineCount >= stefansChat_onLine_lookback)*/ {
      if (eqic(s, "!restart")) { restart(); ret "Yo"; }
      if (eqic(s, "!kill")) { cleanKillIn1Second(); ret "Yo"; }
      if (eqic(s, "!soft-restart 1")) {
        softRestart_level1();
        ret "Hold on...";
      }
      
      if (eqicOneOf(s, "!soft-restart", "!auto-restart")) {
        softRestart_level2();
        ret "Hold on...";
      }
      
      if (eqic(s, "!theory")) ret "OK" with loadTheory();
      if (swic_trim(s, "!theory ", m))
        ret callAnswerFunction(theoryModule, m.rest());
      if (eqic(s, "!peep")) {
        runAndCleanUp(#1010980);
        ret "OK, got " + fileLength(ai_chatAnalysis_peepHoleFile()) + " bytes";
      }
      
      if (swic_trim(s, "!subst-web ", m)) {
        L<S> tok = javaTokC(m.rest());
        if (l(tok) == 2)
          ret ai_subst_web_charBasedReplace(first(tok), selectWord(unquote(last(tok))));
        else
          ret ai_subst_web(m.rest());
      }
      
      if (eqic(s, "!idle-patterns")) {
        time2 {
          Either<Int, Thread> e = (Either) evalWithTimeout(60.0, f ai_idle_matchPatterns);
        }
        ret e.isA() ? "Made " + nWebs(e.a()) + " in " + toSeconds(lastTiming(), 1) + "s" : "Timeout";
      }
      
      if (swic_trim(s, "!experiment ", m)) {
        S code = m.rest();
        if (!isSnippetID(code))
          code = psI(#1013002) + " " + bashQuote(code);
        File outFile = directNohupJavax(code);
        ret "OK, " + f2s(outFile);
      }
      
      if (swic_trim(s, "!experiment ", m)) {
        File outFile = directNohupJavax(m.rest());
        ret "OK, " + f2s(outFile);
      }
      
      if (eqic(s, "!rotate")) {
        rotateSoftwareMadeWebs();
        ret "OK";
      }
      
      if (swic_trim(s, "!speculate-all ", m)) {
        time2 {
          Either<Int, Thread> e = (Either) evalWithTimeout(10.0, r { ai_speculate_all(m.rest()) });
        }
        int n = 0;
        
        pcall { n = e.isA() ? e.a() : websMadeInThread(e.b()); }
        
        ret "Made " + nWebs(n) + " in " + toSeconds(lastTiming(), 1) + "s";
      }
      
      if (eqic(s, "!quick-transpile yourself")) ret transpileMyself('quick);

      if (eqic(s, "!medium-transpile yourself")) ret transpileMyself('medium);
    }
    
    if "unlearn * *"
      ret "OK, was: " + uniq(MatchLearner, pattern := $1).examples.put($2, 'rejected);
      
    if (swic_trim(s, "!sf ", m))
      ret sfu(callAndMake_orDirect(m.rest()));

    if (swic_trim(s, "!specf ", m)) {
      S sf = m.rest();
      print("specf " + sf);
      Class c = loadFunctionsWithInclude(ll(sf), #1011841);
      try {
        callOpt(c, 'ai_spec_init);
        ret sfu(call(c, sf));
      } finally {
        cleanUp(c);
      }
    }
    
    if (swicOneOf_trim(s, m, "!eval ", "!j "))
      ret smartBotEval(m.rest());

    if (swic_trim(s, "!fresh ", m)) {
      dynClear();
      ret smartBotEval(m.rest());
    }

    if (eqicOneOf(s, "!fresh", "!dyn-clear")) {
      dynClear();
      ret "OK, refreshed";
    }
    
    if (swic_trim(s, "!spec ", m)) {
      Pair<S> p = splitAtFirstSpace(m.rest());
      S f = startsWith(p.a, "ai_") ? p.a : "ai_spec_" + p.a;
      ret smartBotEval("ai_callSpeculationFunction(f " + f + ", " + p.b + ");");
    }
      
    if (swic_trim(s, "!spec-all ", m)) {
      S f = m.rest();
      f = startsWith(f, "ai_") ? f : "ai_spec_" + f;
      fS _f = f;
      ret smartBotEval("ai_callSpeculationFunctionOnAllTerms(f " + f + ");");
    }
    
    if (swic_trim(s, "!run ", m)) {
      runAndCleanUp(m.rest());
      ret "OK";
    }
    
    if (swic_trim(s, "!match ", m)) {
      L<S> tok = javaTokC(m.rest());
      if (l(tok) != 2) ret "need 2 parameters";
      S pat = get(tok, 0), input = unquote(get(tok, 1));
      Matches mm = ai_matchPattern(pat, input);
      ret mm == null ? "No match" : sfu(toList(mm.m));
    }
    
    if (eqic(s, "!compact")) {
      thread "Compacting" {
        startedLoading();
        try {
          time {
            ai_compactLiveDB(true);
          }
          postToSmartBotsChat("Tripelized in " + toSeconds(lastTiming()) + " s");
        } finally {
          doneLoading();
        }
      }
      ret "OK, compacting";
    }
    
    if (eqic(s, "!sort-lists")) {
      time {
        tripleIndex().sortAllLists();
      } 
      ret "OK [" + lastTiming() + " ms]";
    }
    
    if (swic_trim(s, "!var ", m))
      ret sfu(get(mc(), m.rest());
      
    if (eqic(s, "!mtm")) {
      ai_spec_moveToMiddle_all();
      ret "OK";
    }
    
    if (eqic(s, "!gc"))
      ret ai_gc_scan("The most important word");
      
    if (eqic(s, "!num-invalid"))
      ret lstr(ai_allInvalidWebs());

    if (swic_trim(s, "!load-subspace ", m)) {
      AI_SubSpace ss = ai_swapInSubSpace($1);
      ret "OK, size=" + l(ss);
    }
    
    if (eqic(s, "!assume")) {
      assumeSmartBotURL();
      ret "OK";
    }
    
    if "done making that"
      ret ai_doneMakingThat(chatName);
    
    S c = "are making in " + chatName;
    
    if (flexMatchIC("let's make a *", s, m) || flexMatchIC("let's make an *", s, m)) {
      S id = ai_text_verified("We", c, "$X");
      if (id != null)
        ret "We're still making " + id;
      S aThing = a(m.rest());
      id = selectWord(aGlobalID());
      post("We", c, id);
      post(id, "is", aThing);
      
      S answer = "OK, ID for new " + m.rest() + ": " + id + ".";
      
      pcall {
        answer = appendWithSpaceIfNempty(answer, ai_makePartQuestion(chatName, id));
      }
      
      ret answer;
    }
  
    WebNode openQuestion = first(ai_search_dollarX_verified("Open question in " + chatName, "is", "$X"));
    if (openQuestion != null) {
      post(web_text(openQuestion), "is", s);
      ai_invalidateWeb(openQuestion);
      S answer = "OK, stored: " + web_text(openQuestion) + " is " + quoteUnlessWordsAndSpaces(s) + ".";
      S id = ai_text("We", c, "$X"); // What we're making
      S q = ai_makePartQuestion(chatName, id);
      if (q == null) {
        ai_doneMakingThat(chatName);
        q = "I think we're done here. Made " + id + ", "
          + ai_renderThingDescriptionWithParts(id);
      }
      answer = appendWithSpaceIfNempty(answer, q);
      ret answer;
    }
    
    for (S sf : ai_texts_verified("$X", "is a good function to call when user says", quote(s))) {
      sf = unquote(sf);
      if (endsWith(sf, "()", m))
        if (isIdentifier(m.rest()))
          ret sfu(makeAndCall(m.rest()));
    }

    // end of priviledged commands
  }
  
  if (ai_isQuestion_1(s) && !byBot()) {
    selectWord(s);
    ai_postTriple(s, "should be", "answered in the chat");
  }
  
  if "Are you online?" ret "Yes";
  if (eqicOneOf(s, "!question", "!gac"))
    ret random_gac36k();
    
  if (swic_trim(s, "!word ", m)) {
    selectWord(m.rest());
    ret "OK " + unicode_rightPointingTriangle() + unicode_rightPointingTriangle();
  }
  
  if (swic_trim(s, "!image ", m)) {
    S x = m.rest();
    print("Googling " + x); 
    BufferedImage img = quickVisualize(x);
    ret "[IMAGE] " + uploadJPEGToImageServer(img, x);
  }
  
  if "memory stats|full memory stats"
    ret nWeb(ai_allWebsCount()) + ". " + diskSpaceEtcBot_answer(s) + ". " + diskSpaceInfo();
    
  try answer diskSpaceEtcBot_answer(s);
  
  if (swic_trim(s, "!search ", m)) {
    selectWord("[search] " + m.rest());
    ret "OK " + unicode_rightPointingTriangle() + unicode_rightPointingTriangle();
  }
  
  if (swic_trim(s, "!solve ", m)) {
    S a;
    time "Solve in subspace" {
      a = ai_solveQuestionInSubSpace(m.rest());
    }
    ret "[" + lastTiming() + " ms] " + (a != null ? a : "I don't know");
  }
  
  if (eqic(s, "!loaded-subspaces"))
    ret sfu(ai_subSpaceNames());
    
  if (eqic(s, "!subspaces"))
    ret sfu(ai_subSpaceNamesOnDisk());
    
  if (eqic(s, "!index-time")) ret toS(indexMakingTime, 1) + " s";
  if (eqic(s, "!boot-time"))
    ret toS(bootTime, 1) + " s (" + toS(bootTime+started-vmStarted(), 1) + "s full)";
  
  if (swic_trim(s, "!bench-search", m)) {
    T3<S> t = ai_parseArrowTriple(m.rest());
    time2 {
      int n = l(ai_search_dollarX(t));
    }
    ret n(n, "result") + ", " + lastTiming() + " ms";
  }
  
  if (swic_trim(s, "!vary ", m))
    ret ifEq(ai_nlVary(m.rest()), m.rest(), "Sorry, no variation found");
  
  if (swic_trim(s, "!google ", m))
    ret join(" - ", swapPair(first(quickGoogle($1))));
    
  if (swic_trim(s, "!google-texts", m))
    ret lines(uniquify(map tripleC(quickGoogle2($1))));
    
  if (swic_trim(s, "!google-define", m))
    ret lines(uniquify(map(func(T3<S> t) { t.b + " ~ " + t.c },
      quickGoogle2($1))));
    
  if (swic_trim(s, "!google-complete ", m))
    ret lines(googleAutoComplete($1));
    
  if (swic_trim(s, "!antonyms ", m))
    ret join(", ", antonymsFromSynonymDotCom($1));
    
  if (swic_trim(s, "!has-triple ", m)) {
    Triple<S> t = ai_parseArrowTriple(m.rest());
    ret yn(ai_cache_hasTriple(t));
  }
    
  if (swic_trim(s, "!triple ", m) || swic_trim(s, "!triples ", m)) {
    L<S> elements = trimAll(splitAt(m.rest(), "->"));
    if (l(elements) != 3) ret "3 parts please";
    ret "[IMAGE] " + "http://ai1.space/1007546?triples=" + urlencode(lines(elements));
  }
  
  if (swic_trim(s, "!answer ", m)) {
    time2 {
      S a = ai_specReturn_answerToExternalQuestion(10.0, m.rest());
    }
    ret "[" + lastTiming() + " ms] " + (a != null ? a : "I don't know");
  }
  

  if (swic_trim(s, "!web ", m)) {
    S id = $1;
    Web web = indexedWebWithGlobalID(id);
    if (web == null) ret "Not found";
    ret "[IMAGE] " + smartBotURL() + "/diagram?id=" + id;
  }
  
  if (swic_trim(s, "!parse ", m))
    ret ai_renderTriple(ai_tripelize(m.rest()));
    
  if (eqic(s, "!worst-case"))
    ret sfu(getOptMC('ai_hasTriple_vary_worstCase));
    
  if (eqic(s, "!uptime"))
    ret n(secondsSinceSysTime(started), "second");
    
  if (eqic(s, "!typical-set"))
    ret sfu(diagramServer_typicalSet());
    
  if (eqic(s, "!num-unverified"))
    ret lstr(ai_unverifiedWebs());
    
  if (eqic(s, "!num-true"))
    ret lstr(ai_trueWebs());
    
  if (eqic(s, "!version"))
    ret autoRestart_localMD5();
    
  if (swic_trim(s, "!blob ", m)) {
    S a = postNodeFromInput(m.rest(), s);
    selectWord(m.rest());
    ret a;
  }

  if (swic_trim(s, "!store ", m))
    ret postTripleFromInput(ai_tripelize(m.rest()), s);

  if (swic_trim(s, "!store-triple ", m))
    ret postTripleFromInput(ai_parseArrowTriple(m.rest()), s);

  if (swic(s, "!nodes ", m)) {
    if (dropSuffixTrim("...", m.rest(), m))
      ret lstr(indexedNodesStartingWith(m.rest()));
    ret lstr(indexedNodes(m.rest()));
  }
  
  if (eqic(s, "!webs"))
    ret lstr(allIndexedWebs());
    
  if (swic(s, "!webs ", m))
    ret joinWithSpace(collect(indexedWebs($1), 'globalID));
    
  if (swic(s, "!splitAtBaseFormVerb ", m))
    ret sfu(splitAtBaseFormVerb($1));
    
  if (swic(s, "!maxRunlengthOfRepeatingChars ", m))
    ret str(maxRunlengthOfRepeatingChars($1));
    
  if (swic(s, "!collapseWord ", m))
    ret collapseWord($1);
    
  if (swic(s, "!gac ", m))
    ret or2(first(scoredSearch(m.get(0), keys(gac36k()))), "-");
    
  if (eqic(s, "!vms"))
    ret computerVMs_text();
    
  if (eqic(s, "!id")) ret aGlobalID();
    
  if (swic_trim(s, "!count-triple ", m)) {
    T3<S> t = ai_parseArrowTriple(m.rest());
    print("Searching: " + sfu(t));
    Pair<Int> p = ai_countBoth_dollarX(t);
    ret eq(p.a, p.b) ? str(p.a)
      : p.a + " (+" + (p.b-p.a) + " unverified)";
  }
    
  pcall { try answer ai_sfCommands(s); }
  
  if (swic_trim(s, "!macmillan ", m)) try {
    Pair<S, L<S>> p = macmillanDefinitions3(m.rest());
    ret toUpper(p.a) + "\n" + lines(prependAll("-", p.b));
  } catch e {
    printShortException(e);
    ret "Macmillan is quiet today.";
  }
    
  try answer ai_answerFromCache(s);
  
  S _s = s;
  s = ai_dropLeadingAdditions(s);
  
  if (neq(_s, s))
    try answer ai_answerFromCache(s);

  if "cache size"
    ret n2(indexedTerms(), "different term")
      //+ ", " + n(web_countNodes(allWebs_cached()), "node")
      //+ ", " + n(allWebs_cached(), "web");
      + ", " + nWeb(tripleIndex().numWebs());
  
  if "give me subtypes of ..."
    ret ai_renderList(ai_subtypesOf(m.rest()));
    
  if "give me a third person verb"
    ret random(thirdPersonVerbs());
    
  if (learnedFlexMatch("give me some *", s, m)) {
    S query = singular($1);
    ret ai_renderNodesList(concatLists(
      ai_index_search_dollarX("$X", "is a", query),
      ai_index_search_dollarX("$X", "is", a(query))));
  }
    
  if "authed?"
    ret authed() ? "You are authed." : "You are not authed.";
    
  if "what is the singular of *"
    ret or2(getSingularFromWordHippo($1), singular($1));
    
  if (learnedFlexMatch("what is the relation between * and *", s, m)) {
    L<S> l = ai_getRelation($1, $2);
    //ret ai_renderList(l);
    if (nempty(l)) ret $1 + " " + first(l) + " " + $2;
  }
    
  if (containsAll(asCISet(javaTokC(s)), "list", "long"))
    pcall { try answer callAnswerMethod(hotwireOnce(#1013113), s); }
  
  if "what unix day is it"
    ret str(unixDay());
    
  if "show me the ..."
    ret "What " + $1 + "?";
    
  if (learnedFlexMatch("What do * have?", s, m))
    ret ai_renderNodesList(ai_index_search_dollarX($1, "have", "$X"));
    
  if (learnedFlexMatch("What does * do?", s, m))
    ret ai_renderList(map(f web_nodePairToText, webs_search_dollarXY(webFromTriples($1, "$X", "$Y"), indexedWebs($1))));
    
  if (learnedFlexMatch_multi(s, m, "What is *", "Who is *", "Was ist *", "Wer ist *"))
    try answer ai_renderList(ai_whatIs($1), "");
    
  S sf = web_text(first(ai_search_dollarX_verified("$X", "implements", quote(s))));
  if (startsWith(sf, "sf ", m))
    ret str(makeAndCall($1));
    
  if (learnedFlexMatch("* how many visitors *", s) || match("how many visitors", s))
    ret str(ai1_cookiesToday_int());
    
  // Once more with generated webs (replacing $ vars)
  
  if (learnedFlexMatch("What is *", s, m)) {
    L<Web> extendedWebs = indexedWebsAfterVariableSubstitution($1);
    print("Have " + n(extendedWebs, "extended web") + " for " + quote($1));
    try answer ai_renderNodesList(webs_search_dollarX(webFromTriples($1, "is", "$X"), extendedWebs), null);
  }
  
  try answer ai_tryIfAndInput(s);
  try answer ai_autoApplyRules(s);
  
  if (!ai_isQuestion_1(s)
    && learnedFlexMatch_multi(s, m, "* is *", "* ist *"))
    try answer postTripleFromInput(triple(m.get(0), "is", m.get(1)), s);
    
  try answer ai_standardAnswer(s);
  try answer ai_standardAnswer_dollarVars(s); // TODO: might get slow eventually
  try answer ai_standardAnswerEval(s);
  
  pcall {
    final T3<S> t = ai_tripelize(s);
    if (t != null) {
      try answer evalWithTimeoutOrNull(20.0, func -> S {
        for (O f : tripleAnswerFunctions)
          try answer (S) pcallF(f, t);
        null;
      });
      try answer ai_standardAnswerSF_triple_dollarVars(t);
    }
  }

  // 10 second timeout
  S a = ai_specReturn_answerToExternalQuestion(10.0, s);
  if (nempty(a) && !swicOneOf(a, "[", "What is ")) ret a;
  
  null;
}

svoid loadBots(S... botIDs) {
  for (S id : botIDs) pcall {
    thoughtBots.add(runSubBot(id));
  }
}

static O html(S uri, SS params) {
  profilePoint('html_1);
  S ip = clientIP();
  bool bad = eq(uri, "/simulate-bad-client");
  Pair<Int, Bool> pair = simpleSpamClientDetect2(ip, uri);
  if (pair.b) {
    if (eq(ip, pcall_myFirewallIP()))
      print("Not blocking myself.");
    else {
      print("Blocking IP " + ip);
      blockedIPs.add(ip);
    }
  }
  
  if (blockedIPs.contains(ip)) {
    bad = true;
    post_verified(print(t3("an HTTP request from a blocked IP", "came in at", dateWithTimeGMT())));
  }
  
  if (bad) {
    NanoHTTPD.IHTTPSession httpSession = NanoHTTPD.currentSession!;
    if (httpSession == null) {
      print("No HTML session?");
      sleepSeconds(spammerSleep);
      ret "go away";
    }
    httpSession.badClient(true);
    null;
  }
  
  print(gmtWithSeconds() + ": Serving HTTP " + quote(uri) + " to " + ip + " (anti-spam count: " + pair.a + "/" + simpleSpamClientDetect2_spamMax + ")");
  profilePoint('html_2);
  
  S requestID = aGlobalID();
  S symbol = "HTTP request " + requestID;
  if (http_isBot()) symbol = "Bot " + symbol;
  //S domain = serveHttp_domainName();
  {
    temp temp_ai_startBuffering();
    post_verified(symbol, "came in at", dateWithTimeGMT());
    post_verified(symbol, "was for path", uri);
    post_verified(symbol, "had user agent", serveHttp_userAgent());
    post_verified(symbol, "came from IP", ip);
  } 
  profilePoint('html_3);
  
  //set serveHttp_cookieHandling_verbose;
  S cookie = serveHttp_cookieHandling();
  S loginUrl = "/";
  S pw = params.get("pw");
  Pair<S, Bool> p = standaloneWebAuth(loginUrl, cookie, pw);
  print("ip=" + ip + ", cookie=" + l(cookie) + ", authed: " + p.b);
  if (nempty(p.a)) ret p.a;
  if (eq(uri, "/login")) ret standaloneWebAuth_showForm(loginUrl, cookie);
  
  httpAuthed.set(p.b);
  try {
    long time = sysNow();
    O html = html_2(uri, params);
    done2(time, "Making HTML " + uri);
    ret html;
  } finally {
    httpAuthed.set(false);
  }
}

static O html_2(S s, SS params) {
  new Matches m;
  
  S domain = serveHttp_domainName();
  
  if (ewic(domain, ".supersrv.de")) ret "Super server!";

  if (eqic(s, "/favicon.ico"))
    ret serveFile(loadLibrary(#1013028), "image/x-icon");
  
  if (nempty(params.get("_pretendAuthed")) || authed())
    html_pretendAuthed.set(true);
    
  if (eqic(s, "/number-of-threads")) ret str(cachedNumberOfThreads!);
    
  if (eqic(s, "/realurl"))
    ret hrefresh(smartBotDirectURL());
    
  if (swic(domain, "pixelboys.") || eqic(domain, "www.botcompany.de"))
    ret hrefresh("https://www.botcompany.de:8443/1016323/raw");
    
  S input = params.get("input");
  if (nempty(input)) {
    if (webAuthed())
      ret htmlencode(callAnswerMethodVerbose(input));
    else
      ret "Can't send input (not authed)";
  }
  
  if (swic(s, "/matrix/", m)) {
    s = "/dyn/" + psI(#1013031);
    if (nempty(m.rest()))
      params.put("topic", urldecode(m.rest()));
  }

  if (eqicOptSlash(s, "/rules"))
    s = "/dyn/1013529/ai_rulesTable";
    
  if (eqicOptSlash(s, "/livecount"))
    s = "/dyn/1013377";
    
  if (eqic(s, "/dyn/fresh")) {
    if (!webAuthed()) ret "No";
    dynClear();
    ret "OK";
  }
  
  if (swic(s, "/dyn/", m)) {
    if (loading()) ret "LOADING, please try again";
    S u = m.rest();
    int i = smartIndexOf(u, '/');
    S id = fsI(takeFirst(u, i));
    if (!has_verified(id, "is", "a safe web module")) ret "Unsafe";
    O mod = hotwireCached(id);
    ret callHtmlMethod2(mod, or2(substring(u, i), "/"), params);
  }
  
  if (swic(s, "/sf/", m))
    ret htmlencode2(struct(callSafeSF(m.rest())));
    
  if "random-web"
    s = str(randomIndexedTriple().globalID());

  try {
    if "threads" {
      time2 {
        S text = renderRunnableThreadsWithStackTraces();
      }
      ret hpre(text + "\n\n" + lastTiming() + " ms");
    }
    
    if (eqic(s, "/livecheck")) ret "Smart Bot";
    
    if (eqic(s, "/load")) ret formatDouble(systemLoad(), 2);
    
    if (eqic(s, "/1-sec-profile")) ret html_profile(1);
    if (eqic(s, "/10-sec-profile")) ret html_profile(10);
    
    if (eqic(s, "/deadlocks")) ret htmlencode(solveDeadlocks());
    
    if (eqic(s, "/source-stats"))
      ret h1_title("Source Stats") + multiSetToUL(ai_sourceStats());
      
    if "thoughts"
      ret serveHTML(html_thoughts());
      
    if "diagram" {
      S id = params.get("id");
      Web web = indexedWebWithGlobalID(id);
      ret serveJPEG(webToCAL(web).makeImage(600, 400));
    }
  
    if "log"
      ret serveText_direct(printLog());
      
    if "learners"
      ret serveText_direct(renderConcepts(list(MatchLearner)));
      
    if "unreached"
      ret h3_title("Unreached")
        + ul(map html_encyclopediaTopic(ai_gc_unreachedWords()));
      
    // Serve Web With Global ID
      
    s = dropPrefix("/", s);
    if (possibleGlobalID(s)) {
      L<Web> webs = ai_getWebs(s);
      if (nempty(webs)) {
        for (Web web : webs) ai_speculateOnWeb(web);
        
        Web web = first(webs);
        ret h2_title("Web " + s + (ai_isInvalidWeb(web) ? " [INVALID]" : "")) + loadjquery()
          + pre(htmlencode(renderWeb_multiLine(web)))
          + p(himg(ai_webImage_link(s), title := "Web " + s))
          + (l(webs) > 1 ? p("Warning: Multiple webs") : "")
          + ai_html_wordThoughts("Web " + web.globalID);
      }
      
      /*if (theoryOn && theoryModule == null) ret serveHTML("Loading theory module");
      Map<S, O> map = (Map) getOpt(theoryModule, 'theoryForLineMap);
      if (map.containsKey(toLower(s)))
        ret serveHTML("A line in the chat.");*/
      ret serveHTML("Unknown: " + s);
    }
    
    if (swic(s, "json/", m)) {
      s = m.rest();
      if (swic(s, "relations/", m)) {
        new L out;
        Map map = litorderedmap();
        if (loading()) map.put("loading", true);
        int max = 1000;
        for (WebRelation r : indexedRelations(m.rest()))
          if (l(out) >= max) {
            map.put("more", true);
            break;
          } else {
            L<S> l = tripleToList(web_relationToTriple(r));
            l.add(r.web.globalID());
            l.add(r.web.source);
            if (r.web.verified()) l.add("v");
            out.add(l);
          }
        map.put("data", out);
        ret serveText(jsonEncode(map));
      }
      ret "unknown";
    }
    
    if (swic(s, "e/", m)) { // serve encyclopedia topic
      S topic = urldecode(m.rest());
      ret html_topic(topic, eq("1", params.get("random")));
    }
    
    if (swic(s, "autocomplete/", m)) {
      int max = min(100, parseInt(or2(params.get("n"), "10")));
      L<S> completed = ai_autoComplete(urldecode(m.rest()), max, autoComplete_timeOut);
      new L<Map> out;
      for (S c : completed) {
        new HashMap map;
        if (startsWith(s, c))
          map.put(follow := substring(c, l(s)));
        else
          map.put(whole := c);
        out.add(map);
      }
      ret serveText(jsonEncode_breakAtLevel1(out));
    }

    if (eqic(s, "alphabetical"))
      ret html_alphabetical(params);
      
    if (eqic(s, "latest-webs")) {
      int n = toInt(params.get('n));
      if (n == 0) n = 100;
      ret html_latestWebs(min(1000, max(n, 10)));
    }
    
    if (eqic(s, "the-big-number"))
      ret formatWithThousandsSeparator(ai_allWebsCount());
    
    if (eqic(s, "random"))
      ret hrefresh(smartBot_encyclopediaLink(ai_randomString()) + "?random=1");

    if (eqic(s, "classes")) ret html_classes();
    if (eqic(s, "standard-relations")) ret html_standardRelations();
    if (eqic(s, "active")) ret html_active();

    if (eqic(s, "all-web-ids")) {
      L<S> l = allToString(allWebIDs_cloned());
      ret serveText(
          l(l) + " ids follow.\n" + lines(l) 
        + l(l) + " ids written.");
    }
    
    if (eqic(s, "internal-files"))
      ret serveText(lines(map(func(File f) -> S {
        quote(f.getName()) + " " + fileSize(f) + " " + fileModificationTime(f)
      }, listFiles(programDir()))));
      
    if (swic(s, "internal-files/", m)) {
      S name = urldecode(m.rest());
      if (!isProperFileName(name)) ret serve404("No");
      File f = getProgramFile(name);
      ret f.isFile() ? serveFile(f) : serve404();
    }
    
    if (eqic(s, "submit-triple")) {
      S a = params.get("a"), b = params.get("b"), c = params.get("c");
      if (empty(a) || empty(b) || empty(c)) ret "Empty field(s)";
      ret htmlencode(postTripleFromInput(t3(a, b, c), null));
    }
    
    if (swic_trim(s, "texts/", m)) {
      ret serveText((loading() ? "[LOADING]\n" : "\n") + lines(map quoteBorderless2(ai_texts(ai_parseSlashTriple(urldecode(m.rest()))))));
    }
  
    T3<S> texts_big_t = null;
    
    if (swic_trim(s, "texts-big/", m))
      texts_big_t = ai_parseSlashTriple(urldecode(m.rest()));

    if (eqic(s, "texts-big")) {
      texts_big_t = t3(
        params.get("a"), params.get("b"), params.get("c"));
    }
    
    if (swic_trim(s, "rev/", m))
      texts_big_t = triple("$X", "is", urldecode(m.rest()));

    if (texts_big_t != null) {
      final bool strip = eq("1", params.get('strip));
      fS style = or2(params.get("style"), "font-size: 20px");
      T3<S> t = texts_big_t;
      //L<S> texts = ai_texts(t = ai_parseSlashTriple(urldecode(m.rest())));
      L<TripleRef<S>> refs = ai_tripleRefs(t);
      ret htitle_htmlencode(ai_renderTriple(t))
        + (loading() ? "[LOADING]\n" : "\n") +
        //joinMap(texts, func(S s) -> S { pre(htmlencode_noQuote(s), +style, title := ) });
        joinMap(refs, func(TripleRef<S> r) -> S {
          TripleWeb w = cast r.triple;
          S title = "Web " + w.globalID();
          S source = w.source();
          if (nempty(source)) title += ", source: " + source;
          S text = r!;
          if (strip) text = unquoteMultiline(text);
          ret pre(htmlencode_noQuote(text), +style, +title);
        });
    }
  
    // Search Results
    
    S q = params.get("q");
    if (nempty(q))
      ret html_searchResults(q, params.get("mode"));
  
    if (eqic(s, "popular"))
      ret html_popular(params);
    
    // Default for unknown URL  
    
    S topic = nempty(s) ? s : params.get("topic");
    if (nempty(topic))
      ret html_topic(urldecode(topic), false);
    
    // Home page
    
    ret html_home();
  } finally {
    html_pretendAuthed.set(null);
  }
}

static NanoHTTPD.Response html_topic(S topic, bool random) {
  long time;
  bool timeout = false;
  print("User agent: " + serveHttp_userAgent());
  if (http_isBot()) {
    time = -1;
    print("Bot request");
  } else {
    time2 {
      timeout = ai_speculateDeepWithActiveAndTimeout(topic, speculationTimeoutForHttp);
    }
    time = lastTiming();
  }
  L<S> words = ll(topic);
  //if (ai_getWeb(topic) != null) words.add("Web " + topic);
  ret serveHTMLNoCache(
    htitle(topic + " | Smart Bot")
    + h1(html_smallSmartBotLogo() + " " + htmlencode_noQuote("Topic: " + topic))
    + loadjquery()
    + (random ? ahref("/random", "Another random term") + " | " : "")
    + "Search: " + html_searchField(topic) + "<br><br>"
    + ai_html_wordThoughts(words)
    + ai_html_enterTripleForm(first(words))
    + p(
      (time < 0 ? "" : "Speculated for " + time + " ms" + (timeout ? " (TIMED OUT)" : "") + " ") +
      ahref(smartBot_directTopicLink(topic), "[Direct link]"))
      );
}

sS html_home() {
  temp tempProfileBlock('html_home);
  ret tag('link, "", rel := "search",
        type := "application/opensearchdescription+xml",
        title := "Smart Bot",
        href := "http://tinybrain.de:8080/getraw.php?id=1013357")
    + hcenter3(
        hsnippetimg(#1101146, width := 64, height := 64) //#1011942)
      + h1_title("Smart Bot")
      + p("by " + ahref("http://BotCompany.de/", "BotCompany.de")
      + (webAuthed() ? " Authed." : ""))
      + ahref("/popular", "Most occurring") + " | "
        + ahref("/alphabetical", "Alphabetical") + " | "
        + ahref("/latest-webs", "Latest") + " | "
        + ahref("/random", "Random term") + " | "
        + html_searchField() + "<br><br>"
      + h3("Triple Search")
      + form(
        htextfield("a") + " -> " + htextfield("b") + " -> " + htextfield("c", "$X")
        + " " + hsubmit("Search triples"),
        action := "texts-big"));
}

sS html_popular(SS params) {
  time "Popularity search" {
    //L<S> keys = multiMapKeysByPopularity(ai_mainIndex());
    L<S> keys = multiSetByPopularity(tripleIndex().wordIndex);
  }
  
  int step = 100, n = toInt(params.get("n"));
  int count = l(keys);
  L<S> l = subList(keys, n, n+step);
  
  ret hcenter3(hsnippetimg(#1011942))
    + h1_title("Smart Bot's Encyclopedia (" + n_fancy(keys, "entry") + ")")
    + "Most occurring | "
      + ahref("/alphabetical", "Alphabetical") + " | "
      + ahref("/latest-webs", "Latest") + " | "
      + html_searchField() + "<br><br>"
    + pageNav2("/", count, n, step, 'n)
    + ul(map(func(S s) -> S {
      ahref(smartBot_encyclopediaLink(s), htmlencode(or2(s, "-")))
        + " [" + ai_approximateIndexedNodesCount(s) + "]"
    }, l));
}

sS html_searchField() { ret html_searchField(""); }
sS html_searchField(S q) { ret html_searchField(q, ""); }

sS html_searchField(S q, S mode) {
  ret hform(
    htextinput("q", value := q, autofocus := 'autofocus) +
    " Mode: " + 
    hselect_list(ll("starts with", "multiple words", "ends with", "contains"), mode, name := "mode") + 
    " " + 
    hsubmit("Search"),
    style := "display: inline", action := smartBotOfficialURL());
}

static O html_profile(int seconds) {
  if (poorMansProfiling_isOn()) ret "Already on";
  poorMansProfiling(100);
  sleepSeconds(seconds);
  ret serveText(poorMansProfiling_stopAndRenderResults());
}

static S html_searchResults(S q, S mode) {
  int max = 1000; 
  time2 {
    L<S> l;
    if (eqic(mode, "multiple words"))
      l = indexedTerms_scoredSearch(q, max+1);
    else if (eqic(mode, "ends with"))
      l = asList(termsEndingWith(q));
    else if (eqic(mode, "contains"))
      l = asList(indexedTermsContaining(q));
    else
      l = asList(fullIndexedTermsStartingWith(q));
    if (possibleGlobalID(q) && !eq(first(l), q))
      l.add(0, q);
  }
  long time = lastTiming();
  bool more = l(l) > max;
  l = takeFirst(max, l);
  sortInPlaceAlternativeCI(l);
  
  S directHit = "";
  S trimmed = trim(q);
  S direct = ai_findIndexedTerm(trimmed);
  if (direct != null)
    directHit = p(unicode_blackRightArrow() + " " + b(html_encyclopediaTopic(direct)));
  else
    directHit = p("No direct hit. Create entry: " + b(html_encyclopediaTopic(trimmed)));
  
  ret h1_title("Smart Bot Search: " + htmlencode(q) + " (" + n_fancy(l, "result") + (loading() ? ", LOADING" : "") + ")")
    + "Search again: " + html_searchField(q, mode) + "<br><br>"
    + directHit
    + p(l(l) > max ? max + "+ search results" : n(l, "search result") + " [" + time + " ms]")
    + ul(map html_encyclopediaTopic(l));
}

static S html_alphabetical(SS params) {
  int step = 100, n = toInt(params.get("n"));
  Collection<S> all = indexedTerms();
  int count = l(all);
  L<S> l = subListOfCollection(all, n, n+step);

  ret h1_title("Smart Bot's Encyclopedia (Alphabetical) :)")
    + ahref("/popular", "Most occurring") + " | "
      + "Alphabetical" + " | "
      + ahref("/latest-webs", "Latest") + " | "
      + html_searchField() + "<br><br>"
    + pageNav2("/alphabetical", count, n, step, 'n)
    + ul(map(func(S s) -> S {
      ahref(smartBot_encyclopediaLink(s), htmlencode(or2(s, "-")))
    }, l));
}

sS html_classes() {
  L<S> l = sortedIC(ai_texts_verified("$X", "is", "a standard class"));
  
  ret h1_title("Smart Bot Classes")
    + ul(map(func(S s) -> S {
      ahref(smartBot_encyclopediaLink(s), htmlencode(or2(s, "-")))
        + " [" + ai_approximateIndexedNodesCount(s) + "]"
    }, l));
}

sS html_standardRelations() {
  L<S> l = sortedIC(unquoteAll(ai_texts_verified("$X", "is", "a standard relation")));
  
  ret h1_title("Smart Bot Standard Relations")
    + ul(map(func(S s) -> S {
      ahref(smartBot_encyclopediaLink(s), htmlencode(or2(s, "-")))
        + " [" + ai_approximateIndexedNodesCount(s) + "]"
    }, l));
}

sS html_active() {
  L<S> l = sortedIC(unquoteAll(ai_texts_verified("$X", "is", "an active topic")));
  L<S> lRules = sortedIC(unquoteAll(ai_texts_verified("$X", "is", "an active rule")));
  
  ret h1_title("Smart Bot's Active Topics")
    + ul(map(func(S s) -> S {
      ahref(smartBot_encyclopediaLink(s), htmlencode(or2(s, "-")))
        + " [" + ai_approximateIndexedNodesCount(s) + "]"
    }, l))
    + h2("Active Rules")
    + ul(map(func(S s) -> S {
      ahref(smartBot_encyclopediaLink(s), htmlencode(or2(s, "-")))
        + " [" + ai_approximateIndexedNodesCount(s) + "]"
    }, lRules))
    ;
}

static S html_latestWebs(int n) {
  L<GlobalID> l = takeLast(n, cloneList(latestWebs));
  S bn = formatWithThousands(ai_theBigNumber());
  ret htitle("Latest Webs (" + bn + ") | Smart Bot")
    + h1(html_smallSmartBotLogo() + " - Latest Webs from " + bn)
    + ul(mapReversed(func(GlobalID id) -> S {
      TripleWeb w = ai_getTriple(id);
      if (w == null) ret "[not found] Web " + id;
      ret ai_html_linkedWeb(id)
        + htmlencode(
            " [" + renderGMTDate(w.created()) + "] "
          + w.source() + ": ")
      + ai_html_renderTriple(w);
    }, l));
}

sS html_thoughts() {
  S html = /*hrefresh(5) +*/ hGoogleFontOswald() + loadjquery();
  S status = "";
  
  /*Pair<Int> p = evalWithTimeout_numberOfCalculations();
  if (p.a > 0) status = p(n(p.a, "calculation")
    + (p.b == 0 ? "" : ", " + p.b + " timed out"));*/
  
  Pair<Set<Thread>> p = evalWithTimeout_calculations();
  if (nempty(p.a) || nempty(p.b))
    status = ul(
      nempty(p.a) ? "In time calculations" + ul_htmlEncode(map threadName(p.a)) : null,
      nempty(p.b) ? "Timed out calculations" + ul_htmlEncode(map threadName(p.b)) : null
    );
    
  ai_html_wordThoughts_nodesMax.set(100);
  try {
    ret html + wordThoughts() + status;
  } finally {
    ai_html_wordThoughts_nodesMax.set(null);
  }
  
  /*
  if (theoryModule == null)
    ret html + "Loading theory module...";
  S thoughts = (S) call(theoryModule, 'html_thoughts);
  if (nempty(selectedWord))
    ret html + tag('table, tr(td_top(thoughts, style := "background: #CCC") + td_top(wordThoughts(), style := "padding-left: 20px")));
  ret html + thoughts;
  */
}

sS wordThoughts() {
  try {
    if (empty(selectedWord)) ret "";
    S html = ai_html_wordThoughts(selectedWord);
    html = html_addLinkToSpan(html, 'title, encyclopediaLink(selectedWord));
    ret html_addTargetBlank(html);
  } catch e {
    printStackTrace(e);
    ret "Erreur";
  }
}

sS postTripleFromInput(T3<S> triple, S input) {
  if (triple == null) null;
  if (swic(input, "OK, stored")) null;
  if (!ai_tripleAllowedToPost(triple)) null;
  
  new Matches m;
  if (!(swic(triple.a, "Web ", m) && possibleGlobalID(m.rest())))       selectWord(triple.a);
  
  if (ai_cache_hasTriple(triple))
    ret "I know";
  else {
    Web web = webFromTriple(triple);
    web.unverified = !authed();
    postSoftwareMadeWeb(web, +input);
    
    if (eqic(triple.b, "is") && eqic(triple.c, "invalid"))
      ai_invalidateWeb(triple.a);
    if (eqic(triple.b, "is") && eqic(triple.c, "wrong"))
      ai_wrongWeb(triple.a);
      
    ai_postTriple("Web " + web.globalID, "stored because of", chatLineSymbol());
    
    ret "OK, stored" + (web.unverified ? " (unverified)" : "") + ": " + ai_renderTriple(triple);
  }
}

sS postNodeFromInput(S node, S input) {
  if (!ai_nodeNameAllowedToPost(node)) null;
  if (hasIndexedNode(node))
    ret "I know";
  else {
    Web web = oneNodeWeb(node);
    web.unverified = !authed();
    postSoftwareMadeWeb(web, +input);
    ret "OK, stored" + (web.unverified ? " (unverified)" : "") + ": " + node;
  }
}

// might want to get rid of this; do it in speculation
svoid processSelectedWord {
  fS word = selectedWord;
  if (empty(word)) ret;
  ai_withMaker('processSelectedWord, r {
    //ai_speculate(word);
    pcall {
      ai_greetingRule1(word);
      //ai_greetingRule2(word);
      if (ai_hasTriple(word, "should be", "answered by me")
        && ai_postTriple(word, "was", "answered by me") != null) {
        S text = firstQuoted(web_texts(ai_search_dollarX(word, "is", "$X")));
        postToStefansChat((nempty(text) ? text + " << " : "") + "Greetings back to you!");
      }
    }
  });
}

svoid makeChatLinesUnrecent {
  new Matches m;
  for (WebNode node : ai_search_dollarX("$X", "is", "recent"))
    if (web_match("Chat line *", node, m)) pcall {
      int n = parseInt($1);
      if (n <= stefansChat_n_value-100) {
        //print("Unrecenting " + n);
        ai_invalidateWeb(node.web);
      }
    } else if (web_match("Smart Bot's Chat line *", node, m)) pcall {
      int n = parseInt($1);
      if (n <= smartBotsChat_n_value-100) {
        //print("Unrecenting " + n);
        ai_invalidateWeb(node.web);
      }
    }
}

svoid onHistoryRead {
  lock dbLock();
  print("History read.");
  processSelectedWord();
  pcall { makeChatLinesUnrecent(); }
}

sS selectWord(S word) {
  if (nempty(word)) {
    selectedWord = word;
    save('selectedWord);
    processSelectedWord();
  }
  ret word;
}

sS transpileMyself(S mode) {
  postToStefansChat("Transpiling...");
  Pair<Bool, S> p = transpileOnServer(programID(), 'medium);
  ret p.a ? "OK" : "Not OK";
}

svoid speculatorLoop {
  repeat with sleep 5 {
    ai_speculateDeepAndActive(selectedWord);
    for (L<S> words : cloneValues(lastUserLines))
      for (S word : words)
        ai_speculateDeepAndActive(word);
      
    S s;
    while ((s = first_sync(speculationQueue)) != null) {
      speculationQueue.remove(s);
      long time = sysNow();
      ai_speculateWithActive(s);
      done2_always(time, "Speculation Queue > " + s);
    }
  }
}

public static ISpec ispec = ai_defaultISpec();

// share ISpec interface with sub-modules
static JavaXClassLoader hotwire_makeClassLoader(L<File> files) {
  ClassLoader cl = myClassLoader();
  
  // Avoid class loader chaining, always reference base class loader
  if (cloned && cloningSince != 0) {
    ClassLoader parent = cast getOpt(cl, 'virtualParent);
    print("Cloned class loader. " + parent);
    if (parent != null) cl = parent;
  }
  
  ret new JavaXClassLoaderWithParent2(null, files, cl, parseClassesToShareList(classesToShare));
}

static Lock aiLock() {
  ret aiLock;
}

static bool webAuthed() {
  ret isTrue(httpAuthed!);
}

svoid handleStefansChat {
  stefansChat_n_onChange(r {
    clearTransientWebs();
    addTransientWeb(webFromTriple("Latest chat line", "is", "Chat line " + stefansChat_n_value));
  });
  
  stefansChat_onLine_lookback = 1; // 2 does double restarts etc.
  stefansChat_onLine_onHistoryRead.set(f onHistoryRead);
  stefansChat_onLine_safe(voidfunc(fS text) ctex {
    pcall {
      int nr = toInt(mapGet(stefansChat_onLine_fullParams!, "nr"));
      print("> [" + nr + "] " + text);
      if (nr != 0) {
        S x = "Chat line " + nr;
        stefansChat_onLine_fullParams->put('chatName, "Stefan's Chat");
        stefansChat_onLine_fullParams->put("Chat line symbol", x);
        post(x, "is", "a chat line");
        post(x, "is", "recent");
        postChatLineInfo(x, text);
        stefansChat_n_notify(nr);
        ai_speculateWithActiveAndTimeout(x, speculationTimeoutForNewChatLine);
        if (!byBot())
          lastUserLines.put("Stefan's Chat", ll(text, quote(text), x));
      }
    }
    
    temp tempSetThreadLocal(answering, true);
    try {
      S a = actualAnswer(text);
      a = shortenAnswerForChatAndUploadFull(text, a);
      postToStefansChat(a);
      callAll(getAndClearThreadLocal(afterAnswering));
    } on fail {
      clearThreadLocal(afterAnswering);
    }
  });
}

svoid handleSmartBotsChat {
  smartBotsChat_onLine_lookback = 1;
  smartBotsChat_onLine_onHistoryRead.set(f onHistoryRead);
  smartBotsChat_onLine_safe(voidfunc(fS text) ctex {
    pcall {
      int nr = toInt(mapGet(stefansChat_onLine_fullParams!, "nr"));
      print("> [" + nr + "] " + text);
      if (nr != 0) {
        S x = "Smart Bot's Chat line " + nr;
        stefansChat_onLine_fullParams->put('chatName, "Smart Bot's Chat");
        stefansChat_onLine_fullParams->put("Chat line symbol", x);
        post(x, "is", "a Smart Bot's chat line");
        post(x, "is", "recent");
        postChatLineInfo(x, text);
        smartBotsChat_n_notify(nr);
        ai_speculateWithActiveAndTimeout(x, speculationTimeoutForNewChatLine);
        if (!byBot())
          lastUserLines.put("Smart Bot's Chat", ll(text, quote(text), x));
      }
    }
    
    temp tempSetThreadLocal(answering, true);
    try {
      S a = actualAnswer(text);
      a = shortenAnswerForChatAndUploadFull(text, a);
      postToSmartBotsChat(a);
      callAll(getAndClearThreadLocal(afterAnswering));
    } on fail {
      clearThreadLocal(afterAnswering);
    }
  });
}

sS handleArchiveMsg(S msgSymbol) {
  S text = firstQuoted(ai_texts(msgSymbol, "is", "$X"));
  if (text == null) ret "No text found for " + msgSymbol;
  text = unquote(text);
  SS oldParams = setThreadLocal(stefansChat_onLine_fullParams, lithashmap("Chat line symbol", msgSymbol));
  try {
    ret actualAnswer(text);
  } finally {
    stefansChat_onLine_fullParams.set(oldParams);
  }
}

sS shortenAnswerForChatAndUploadFull(S text, S a) {
  a = shorten(a, ultimateAnswerLimit);
  if (l(a) > stefansChat_messageLimit()) {
    S title = "Smart Bot's answer to: " + text;
    S id = ntUpload("smart-bot-for-user", title, unSlackSnippet(a));
    a = snippetURL(id) + " | " + a;
  }
  ret a;
}

static bool ai_enabled() {
  true;
}

sbool http_isBot() {
  ret userAgentIsBot(serveHttp_userAgent());
}

// UNUSED
svoid softRestart_level1 {
  startedLoading();
  cloningSince = sysNow();
  classesToShare = "T3, TripleWeb, Symbol, GlobalID, MultiSet";
  Class c = hotwire(programID());
  set(c, ai_fillTripleIndex_useThese := asList(ai_allTriples()));
  
  softRestart_phase2(c);
}

svoid softRestart_phase2(final Class c) {
  print("Cloning phase 2");
  set(c, cloned := true);
  set(c, clonedSince := cloningSince);
  set(c, postStartUpMessageToChat := chatName());
  addAll((L) getOpt(c, 'latestWebs), latestWebs);
  
  removeFirstInjection();
    
  afterAnswering(r {
    // Create raw thread without any registering/pinging etc.
    //new Thread("Soft-Restarting") {
    //  public void run() ctex {
        //Thread.sleep(2000);
        
        print("Cloning, new thread");
        // copy JavaX translator over
        copyFields(mc(), c, 'transpileRaw_trans);
        transpileRaw_trans = null;
  
        cleanUp(mc()); // This should end all the threads & the HTTP server
        System.out.println("Cloning: Cleaned up");
        clearAllVariables();
        System.out.println("Cloning: Running main");
        runMain(c);
        System.out.println("Cloning: Done!");
    //  }
    //}.start();
  });
}

// also reuse the index
svoid softRestart_level2 {
  startedLoading();
  cloningSince = sysNow();
  classesToShare = smartBot_sharedClassesForRestart();
  Class c = hotwire(programID());
  //set(c, ai_fillTripleIndex_useThese := asList(ai_allTriples()));
  set(c, ai_fillTripleIndex_useTripleIndex := tripleIndex());
  
  softRestart_phase2(c);
}

svoid afterAnswering(O runnable) {
  if (isTrue(answering!))
    afterAnswering.set(addToOrCreateList(afterAnswering!, runnable));
  else
    callF(runnable);
}

svoid addToLatestWebs(Web web) {
  latestWebs.add(web.globalIDObj());
}

svoid dynClear {
  veryQuickJava_refresh();
  hotwireCached_clear();
  clearAllCaches();
}

svoid postChatLineInfo(S x, S text) {
  post(x, "has text", quote(text));
  post(x, "was", byBot() ? "sent by bot" : "sent by human");
  post(x, "was sent by IP", (S) lookupPossiblyIgnoreCase(stefansChat_onLine_fullParams!, "ip"));
}

static WebSocket makeWebSocket(NanoHTTPD.IHTTPSession handshake) {
  print("New WebSocket.");
  ret new WebSocket(handshake) {
    AutoCloseable streamer;
    
    @Override
    protected void onMessage(WebSocketFrame messageFrame) {
      S s = messageFrame.getTextPayload();
      print("WebSocket msg: " + s);
      if (streamer == null && eq(s, "stream-big-number")) {
        streamer = timerAsAutocloseable(doEveryStartingNow(100, new Runnable {
          S lastS = null;
          public void run() ctex {
            S s = formatWithThousands(ai_theBigNumber());
            if (neq(s, lastS)) {
              lastS = s;
              //print("Sending: " + s);
              send(s);
            }
          }
        }));
        println("Streaming!");
      }
    }
    
    protected void onClose(WebSocketFrame.CloseCode code, String reason, boolean initiatedByRemote) {
      print("WebSocket close");
      _close(streamer);
      streamer = null;
    }
  };
};

sS diskSpaceInfo() {
  ret toM(fileSize(programFile("triples.gz"))) + " MB (triples.gz) + " + toM(fileSize(programFile("webs-made.txt"))) + " MB (webs-made.txt). "
  + toM(userDir().getUsableSpace()) + " MB free disk space";
}

please include function ai_possibleRuleNames.

Author comment

Began life as a copy of #1009195

download  show line numbers  debug dex  old transpilations   

Travelled to 20 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, jmyaexzvplwz, jtubtzbbkimh, lpdgvwnxivlt, mjouhigkdmyk, mqqgnosmbjvj, onxytkatvevr, ppjhyzlbdabe, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, ydilcnlqtmvn

No comments. add comment

Snippet ID: #1010745
Snippet name: Smart Bot (LIVE)
Eternal ID of this version: #1010745/939
Text MD5: cd0a6a4aaf387d0b2c5dd9090c9e2157
Transpilation MD5: 73477117d0a453acc4c02eb07f33d8a4
Author: stefan
Category: javax / chat
Type: JavaX source code (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2018-06-15 02:59:13
Source code size: 51838 bytes / 1689 lines
Pitched / IR pitched: No / No
Views / Downloads: 3261 / 1150814
Version history: 938 change(s)
Referenced in: [show references]