Transpiled version (5456L) is out of date.
!752 static boolean actuallyPost = true; static L<S> botIDs = litlist("#1001890", "#1001923", "#1001937", "#1001942", "#1001945", "#1001951" /*, "#1001747"*/, "#1001989", "#1001861", "#1001991", "#1001994", "#1002007"); static new L<Class> bots; static new L<S> activeBots; static Map<S, Class> botsByID = synchroTreeMap(); static S channelID = "C0FH9PY8J"; // relp, #talkingbots static S token; //static new Map<S, S> lastPostByUser; static new Map<S, S> userNames; static S lastQuestion, lastAnswer; static S lastAnswerTimestamp; // only for posted in this session static int lastAnswerScore; static int webServerPort = 80; // set to 0 for no web serving static long hitCount; /* static class Interaction { S question, user, answertime, answer; long realtime; int score; // not used yet } static new L<Interaction> interactions; */ static new L<Map> history; // stores all processed user questions (not whole chat log) p { if (token == null) token = loadSecretTextFileMandatory("#1001889", "relp-slack-botstuff-token").trim(); reload(); readLog(); if (webServerPort != 0) { readLocally("hitCount"); serveHttp(webServerPort); } while true { pcall { 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; } } L<SlackMsg> qs = getNewQuestions(); //print(l(qs) + " question(s)."); //printList(qs); for (SlackMsg msg : qs) { S q = msg.text; S userName = getUserName(msg.user); 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) pcall { slackSmile(token, channelID, msg.ts); } else pcall { slackReact(token, channelID, msg.ts, "thinking_face"); } } lastAnswer = null; } //if (userName != null) lastPostByUser.put(userName, msg.text); pcall { S a = answer(q); if (a != null) { if (userName != null) a = "<@" + userName + "> " + a; print("Answering with: " + quote(a)); lastAnswer = a; lastQuestion = q; if (actuallyPost) { S answerTime = postAnswer(a); Map logEntry = litmap("question", q, "user", userName, "answertime", answerTime, "answer", a, "realtime", now()); history.add(logEntry); logQuoted(new File(programDir(), "memory-log"), structure(logEntry)); } } } } } sleepSeconds(5); } } // returns the slack timestamp static S postAnswer(S text) { S username = "botstuff"; S url = "https://slack.com/api/chat.postMessage"; S postData = "token=" + urlencode(token) + "&channel=" + urlencode(channelID) + "&text=" + urlencode(text) + "&username=" + urlencode(username); print(postData); S data = doPost(postData, url); Map map = jsonDecodeMap(data); lastAnswerTimestamp = (S) map.get("ts"); lastAnswerScore = 0; print("postAnswer: " + data); ret lastAnswerTimestamp; } static S getNewQuestions_lastSeen; static L<SlackMsg> getNewQuestions() { // Get 150 on first call (history before bot start) // and 1000 thereafter (just grab anything new) int limit = getNewQuestions_lastSeen == null ? 150 : 1000; L<SlackMsg> 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); S userName = getUserName(msg.user); //if (userName != null) lastPostByUser.put(userName, msg.text); } msgs = subList(msgs, i+1); } ret msgs; } static int indexOfLastBotAnswer(L<SlackMsg> msgs) { for (int i = l(msgs)-1; i >= 0; i--) if (msgs.get(i).botName != null) ret i; ret -1; } static synchronized S answer(S s) { new Matches m; if (match("reload", s)) { reload(); ret l(bots) + "/" + l(botIDs) + " bots reloaded."; } if (match("reload *", s, m)) { reload(m.unq(0)); ret "Bot " + formatSnippetID(m.unq(0)) + " reloaded."; } if (match("list bots", s)) { S answer = "Active bots: " + structure(activeBots); L<S> inactiveBots = diff(botIDs, activeBots); if (!inactiveBots.isEmpty()) answer += ". Inactive bots: " + structure(inactiveBots); ret answer; } if (match("test", s)) ret "blah!"; ret callStaticAnswerMethod(bots, s); } static void recordFeedback(S probableQuestion, S answer, S answerTime, S feedback, int score) { 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 reload() { botIDs = or((L<S>) loadVariableDefinition("botIDs"), botIDs); cleanUp(bots); activeBots = new ArrayList<S>(); botsByID.clear(); for (S botID : botIDs) pcall { loadBot(botID); } } static void reload(S botID) { S parsedID = "" + parseSnippetID(botID); Class bot = botsByID.get(parsedID); if (bot == null) ret; cleanUp(bot); botsByID.remove(parsedID); activeBots.remove(bot); loadBot(botID); } static void loadBot(S botID) { print("Loading bot: " + botID); Class c = run(botID); bots.add(c); activeBots.add(botID); // only if loading doesn't fail botsByID.put("" + parseSnippetID(botID), c); print("Loaded bot: " + botID + ", bots now: " + l(activeBots)); } static S getUserName(S userID) { if (userID == null) ret null; S userName = userNames.get(userID); if (userName == null) pcall { userName = (S) slackGetUserInfo(token, userID).get("name"); userNames.put(userID, userName); } ret userName; } static NanoHTTPD.Response serve(S uri, NanoHTTPD.Method method, Map<S,S> header, Map<S,S> parms, Map<S,S> files) { print("Serving HTTP " + quote(uri)); ++hitCount; saveLocally("hitCount"); uri = dropPrefixMandatory("/", uri); if (eq(uri, "")) ret serveHomePage(); if (eq(uri, "favicon.ico")) ret serve404(); int i = uri.indexOf('/'); S firstPart = i >= 0 ? uri.substring(0, i) : uri; S rest = i >= 0 ? substr(uri, i) : "/"; // rest always starts with "/" ret serveBot(firstPart, rest); } static NanoHTTPD.Response serveBot(S botID, S subUri) { Class bot = botsByID.get("" + parseSnippetID(botID)); boolean raw = subUri.equals("/raw") || subUri.startsWith("/raw/"); if (raw) { subUri = substr(subUri, "/raw".length()); if (subUri.length() == 0) subUri = "/"; } if (bot != null) { S botHtml = callHtmlMethod(bot, subUri); if (raw) ret serveHTML(unnull(botHtml)); if (botHtml == null) botHtml = "Bot has no HTML output."; S title = "TinyBrain Bot " + formatSnippetID(botID); S html = "<html><head><title>" + title + "</title></head>" + "<body><h3>Bot <a href=\"http://tinybrain.de/" + parseSnippetID(botID) + "\">" + formatSnippetID(botID) + "</a></h3>\n" + botHtml + "\n</body></html>"; ret serveHTML(html); } ret serve404(); } static NanoHTTPD.Response serveHomePage() { new StringBuilder buf; buf.append("<html>"); buf.append("<head><title>TinyBrain Chat Bots</title></head>"); buf.append("<body>"); buf.append("<h3><a href=" + htmlQuote("http://tinybrain.de") + ">TinyBrain</a> Bots</h3>"); buf.append("<ul>"); for (S botID : activeBots) { botID = formatSnippetID(botID); S parsedID = "" + parseSnippetID(botID); S title = "?"; pcall { title = getSnippetTitle_overBot(botID); } S name = botID + " - " + htmlencode(title); buf.append("<li><a href=" + htmlQuote(parsedID) + ">" + name + "</a> ("); buf.append("<a href=" + htmlQuote("http://tinybrain.de/" + parsedID) + ">source</a>)</li>"); } buf.append("</ul>"); buf.append("<p>Hit count: " + hitCount + "</p>"); buf.append("<p>Last interactions:</p>"); int maxInteractionsToPrint = 10; for (int i = l(history)-1; i >= 0 && i >= l(history)-maxInteractionsToPrint; i--) { Map m = history.get(i); buf.append("<p>" + htmlencode(m.get("question")) + "<br> " + htmlencode(m.get("answer")) + "</p>"); } buf.append("</body>"); buf.append("</html>"); ret serveHTML(buf); } static S getSnippetTitle_overBot(S snippetID) { startBot("Snippet Title Bot", "#1001747"); // Use local version //S s = sendToLocalBot(getVMPort() + "/Snippet Title Bot", "what is the title of snippet *", snippetID); // Use bot VM version S s = sendToLocalBot_cached("Snippet Title Bot", "what is the title of snippet *", snippetID); new Matches m; if (match("The title of snippet * is *.", s, m)) ret m.unq(1); throw fail("Bot said: " + s); } static void readLog() { for (S s : scanLog("#1001915", "memory-log")) pcall { history.add((Map) safeUnstructure(s)); } print(l(history) + " history entries."); }
Began life as a copy of #1001912
download show line numbers debug dex old transpilations
Travelled to 16 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, jtubtzbbkimh, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, teubizvjbppd, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1001915 |
Snippet name: | Slack Bot! (not used anymore. relp.slack.com, #talkingbots, can be redirected to other channel) |
Eternal ID of this version: | #1001915/1 |
Text MD5: | 18ec645b1afc97e73478c26771190c76 |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-12-14 19:21:12 |
Source code size: | 10137 bytes / 309 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 958 / 2165 |
Referenced in: | [show references] |