!7 set flag DynModule. // for transpilation cmodule2 GazelleMultiBot > DynGazelleBot { switchable int maxEvalResultLength = oneMegabyte_int(); switchable double evalTimeout = 60.0; switchable long botProcessed; // timestamp of last post processed by bots transient new L bots; Map allPosts = syncTreeMap(); class Bot { S name; *() {} *(S *name) {} *(S *name, IVF1 *handlePost) {} swappable void handlePost(GazellePost post) {} GazelleBotCred cred() { ret GazelleBotCred(_user, _botToken, name); } void postReply(GazellePost post, S text, S type, S title default null) { gazelle_createPost(cred(), text, type, refs := post.id, +title); } } start { dm_useLocalMechListCopies(); grabLoop.handlePosts = posts -> { dm_mediumRefreshTranspiler(); grabLoop.handlePosts_base(posts); for (GazellePost post : posts) setField(botProcessed := max(botProcessed, post.modified)); }; // legacy conversion to sort allPosts setField_noCheck(allPosts := asSyncTreeMap(allPosts)); bots.add(new Bot("Math Bot", post -> { if (post.creating) ret; gazelle_mathBot1_handlePost_2(_user, _botToken, post); })); bots.add(new Bot("Code Safety Checker") { void handlePost(GazellePost post) { if (post.creating) ret; if (eqic(post.type, "JavaX Code")) gazelle_createPost(cred(), codeSafetyCheckResult(post.text), "Code Safety", refs := post.id); } }); bots.add(new Bot("Safe Code Runner") { void handlePost(GazellePost post) { if (post.creating) ret; if (eqic(post.type, "JavaX Code")) { S code = post.text; if (isCodeSafe(code)) { code = tok_addReturn(code); // define implicit vars and functions if (containsJavaToken(code, "post")) { long ref = gazelle_firstPostRef(post.id); if (ref != 0) code = "S post = " + quote(gazelle_text(ref)) + ";\n" + code; } if (containsJavaToken(code, "post2")) { long ref = gazelle_firstPostRef(gazelle_firstPostRef(post.id)); if (ref != 0) code = "S post2 = " + quote(gazelle_text(ref)) + ";\n" + code; } if (containsJavaToken(code, "getAllPosts")) code = [[ embedded Cl getAllPosts() { ret (Cl) quickImport(dm_call(dm_current_generic(), "getAllPosts")); } ]] + code; printWithIndent("CODE> ", code); S out = shorten(maxEvalResultLength, runCode(code)); gazelle_createPost(cred(), out, "Code Result", refs := post.id); } } } }); bots.add(new Bot("Run Code On All Posts") { void handlePost(GazellePost post) { if (post.creating) ret; if (eqic(post.type, "Instruction") && match("Please run this code on all posts", post.text)) { long ref = gazelle_firstPostRef(post.id); if (ref == 0) ret; S code = gazelle_text(ref); if (!isCodeSafe(code)) ret; S code2 = "ret func(S post) { " + code + " };"; O function = dm_javaEval(code2); new LS lines; S out = shorten(maxEvalResultLength, runCode(code)); for (GazellePost post2 : cloneValues(allPosts)) { lines.add("Post " + post2.id + " (" + quote(shorten(20, post2.text)) + "): " + shorten(80, runFunc(() -> callF(function, post2.text)))); } gazelle_createPost(cred(), lines(lines), "Code Result", refs := post.id); } } }); bots.add(new Bot("Mark identifiers safe") { void handlePost(GazellePost post) { if (post.creating) ret; if (/*eqic(post.type, "Instruction") &&*/ post.creator.isMaster && match("Mark safe", post.text)) { S text = getPost(first(post.postRefs)).text; LS ids = tok_identifiersInOrder(regexpFirstGroupIC("Unknown identifiers: (.+)", text)); print("Marking safe: " + ids); postReply(post, markSafe(ids), "Marked safe"); } } }); bots.add(new Bot("Post Deleter") { void handlePost(GazellePost post) { if (post.creating) ret; if (eqic(post.type, "Instruction") && post.creator.isMaster && match("Delete mentioned posts", post.text)) { long ref = gazelle_firstPostRef(post.id); if (ref == 0) ret; S text = gazelle_text(ref); L postIDs = allToLong(regexpAllFirstGroups(gazelle_postMentionRegexp(), text)); print("Deleting posts: " + postIDs); if (nempty(postIDs)) { Map result = gazelle_deletePosts(cred(), postIDs); postReply(post, str(result), "Deletion result"); } else postReply(post, "No mentioned posts found", "Deletion result"); } } }); bots.add(new Bot("Post Deleter") { void handlePost(GazellePost post) { if (post.creating) ret; ret unless eqic(post.type, "Instruction") && match("Please run detector", post.text); try { long detectorID = post.refWithTagOrFail("detector"); long posExamplesID = post.refWithTagOrFail("positive examples"); long negExamplesID = post.refWithTagOrFail("negative examples"); S code = getPost(detectorID).text; LS posExamples = tlft(getPost(posExamplesID).text); LS negExamples = tlft(getPost(negExamplesID).text); LPair examples = trueFalseBPairs(posExamples, negExamples); if (!isCodeSafe(code)) fail("Detector code not safe"); IF1 detector = proxy IF1(dm_javaEval(code)); new LS good; new LS bad; new Scorer scorer; for (Pair example : examples) { S result = runFunc(() -> detector.get(example.a)); bool ok = eq(result, str(example.b)); scorer.add(ok); S line = (ok ? "OK" : example.b ? "False negative" : "False positive") + " (" + shorten(10, result) + "): " + example.a; (ok ? good : bad).add(line); } S text = "Detector code:\n\n" + indentx(code) + "\n\n" + or2(trim(lines(concatLists(bad, ll(""), good))), "No errors"); S title = "Score for detector " + detectorID + ": " + scorer; gazelle_createPost(cred(), text, "Detector Score", +title, refs := joinWithSpace(ll(post.id, detectorID, posExamplesID, negExamplesID)), refTags := linesLL_rtrim("", "detector", "positive examples", "negative examples")); } catch e { postReply(post, getStackTrace(e), "Error"); } } }); } void handlePost(GazellePost post) { allPosts.put(post.id, post); change(); if (post.modified > botProcessed) { print("modified: " + post.modified + "/" + botProcessed); for (Bot bot : bots) pcall { bot.handlePost(post); } } } // assumes code is safety-checked // TODO: timeout? S runCode(S code) { ret runFunc(() -> str_shortenSynthetic(dm_javaEval(code))); } S runFunc(IF0 f) { ret str_shortenSynthetic(evalWithTimeoutOrException(evalTimeout, func { try { ret str_shortenSynthetic(f!); } catch e { ret getStackTrace(e); } })); } L repliesTo(GazellePost post) { ret filter(values(allPosts), p -> contains(p.postRefs, post.id)); } L repliesWithTag(GazellePost post, S tag) { Pair pair = pair(post.id, upper(tag)); ret filter(repliesTo(post), p -> contains(mapPairsB toUpper(p.taggedRefs()), pair)); } GazellePost getPost(long id) { ret allPosts.get(id); } Cl getAllPosts() { ret values(allPosts); } CodeSafetyChecker codeSafetyChecker() { new CodeSafetyChecker checker; checker.init(); checker.markSafe("getAllPosts"); ret checker; } S codeSafetyCheckResult(S code) { CodeSafetyChecker checker = codeSafetyChecker(); checker.checkCode(code); ret checker.verbalCheckResult(); } bool isCodeSafe(S code) { CodeSafetyChecker checker = codeSafetyChecker(); checker.checkCode(code); ret checker.isSafe(); } }