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

309
LINES

< > BotCompany Repo | #1001915 // Slack Bot! (not used anymore. relp.slack.com, #talkingbots, can be redirected to other channel)

JavaX source code [tags: use-pretranspiled] - run with: x30.jar

Transpiled version (5456L) is out of date.

1  
!752
2  
3  
static boolean actuallyPost = true;
4  
5  
static L<S> botIDs = litlist("#1001890", "#1001923", "#1001937", "#1001942", "#1001945", "#1001951" /*, "#1001747"*/,
6  
  "#1001989", "#1001861", "#1001991", "#1001994", "#1002007");
7  
static new L<Class> bots;
8  
static new L<S> activeBots;
9  
static Map<S, Class> botsByID = synchroTreeMap();
10  
11  
static S channelID = "C0FH9PY8J"; // relp, #talkingbots
12  
static S token;
13  
14  
//static new Map<S, S> lastPostByUser;
15  
static new Map<S, S> userNames;
16  
17  
static S lastQuestion, lastAnswer;
18  
static S lastAnswerTimestamp; // only for posted in this session
19  
static int lastAnswerScore;
20  
21  
static int webServerPort = 80; // set to 0 for no web serving
22  
static long hitCount;
23  
24  
/*
25  
static class Interaction {
26  
  S question, user, answertime, answer;
27  
  long realtime;
28  
  int score; // not used yet
29  
}
30  
31  
static new L<Interaction> interactions;
32  
*/
33  
static new L<Map> history; // stores all processed user questions (not whole chat log)
34  
35  
p {
36  
  if (token == null)
37  
    token = loadSecretTextFileMandatory("#1001889", "relp-slack-botstuff-token").trim();
38  
39  
  reload();
40  
  readLog();
41  
  
42  
  if (webServerPort != 0) {
43  
    readLocally("hitCount");
44  
    serveHttp(webServerPort);
45  
  }
46  
  
47  
  while true {
48  
    pcall {
49  
      if (lastAnswerTimestamp != null) {
50  
        // Poll reactions to our last answer - is there a more efficient way?
51  
        int score = slackReactionScore(token, channelID, lastAnswerTimestamp);
52  
        if (score != lastAnswerScore) {
53  
          print("Reaction score changes to " + score);
54  
          recordFeedback(lastQuestion, lastAnswer, lastAnswerTimestamp, "emoji", score-lastAnswerScore);
55  
          lastAnswerScore = score;
56  
        }
57  
      }
58  
      
59  
      L<SlackMsg> qs = getNewQuestions();
60  
      //print(l(qs) + " question(s).");
61  
      //printList(qs);
62  
      for (SlackMsg msg : qs) {
63  
        S q = msg.text;
64  
        S userName = getUserName(msg.user);
65  
        print("Processing question: " + quote(q));
66  
        if (lastAnswer != null) { // TODO: maybe check the time delay
67  
          int score = feedbackScore(q);
68  
          if (score != 0) {
69  
            recordFeedback(lastQuestion /*lastPostByUser.get(userName)*/, lastAnswer, lastAnswerTimestamp, q, score);
70  
            if (score > 0)
71  
              pcall { slackSmile(token, channelID, msg.ts); }
72  
            else
73  
              pcall { slackReact(token, channelID, msg.ts, "thinking_face"); }
74  
          }
75  
          lastAnswer = null;
76  
        }
77  
        //if (userName != null) lastPostByUser.put(userName, msg.text);
78  
        pcall {
79  
          S a = answer(q);
80  
          if (a != null) {
81  
            if (userName != null)
82  
              a = "<@" + userName + "> " + a;
83  
            print("Answering with: " + quote(a));
84  
            lastAnswer = a;
85  
            lastQuestion = q;
86  
            if (actuallyPost) {
87  
              S answerTime = postAnswer(a);
88  
              Map logEntry = litmap("question", q, "user", userName, "answertime", answerTime, "answer", a, "realtime", now());
89  
              history.add(logEntry);
90  
              logQuoted(new File(programDir(), "memory-log"), structure(logEntry));
91  
            }
92  
          }
93  
        }
94  
      }
95  
    }
96  
    sleepSeconds(5);
97  
  }
98  
}
99  
100  
// returns the slack timestamp
101  
static S postAnswer(S text) {
102  
  S username = "botstuff";
103  
  S url = "https://slack.com/api/chat.postMessage";
104  
  S postData = "token=" + urlencode(token) + "&channel=" + urlencode(channelID) + "&text=" + urlencode(text)
105  
    + "&username=" + urlencode(username);
106  
  print(postData);
107  
  S data = doPost(postData, url);
108  
  Map map = jsonDecodeMap(data);
109  
  lastAnswerTimestamp = (S) map.get("ts");
110  
  lastAnswerScore = 0;
111  
  print("postAnswer: " + data);
112  
  ret lastAnswerTimestamp;
113  
}
114  
115  
static S getNewQuestions_lastSeen;
116  
117  
static L<SlackMsg> getNewQuestions() {
118  
  // Get 150 on first call (history before bot start)
119  
  // and 1000 thereafter (just grab anything new)
120  
  int limit = getNewQuestions_lastSeen == null ? 150 : 1000;
121  
  L<SlackMsg> msgs = slackReadChannel(channelID, token, limit, getNewQuestions_lastSeen);
122  
  if (!msgs.isEmpty()) getNewQuestions_lastSeen = last(msgs).ts;
123  
  int i = indexOfLastBotAnswer(msgs);
124  
  if (i >= 0) {
125  
    print("Last bot answer at " + (i+1) + "/" + l(msgs) + ": " + structure(msgs.get(i)));
126  
    
127  
    // update variables
128  
    lastAnswer = msgs.get(i).text;
129  
    for (int j = 0; j < i; j++) {
130  
      SlackMsg msg = msgs.get(j);
131  
      S userName = getUserName(msg.user);
132  
      //if (userName != null) lastPostByUser.put(userName, msg.text);
133  
    }
134  
135  
    msgs = subList(msgs, i+1);
136  
  }
137  
  ret msgs;
138  
}
139  
140  
static int indexOfLastBotAnswer(L<SlackMsg> msgs) {
141  
  for (int i = l(msgs)-1; i >= 0; i--)
142  
    if (msgs.get(i).botName != null) ret i;
143  
  ret -1;
144  
}
145  
146  
static synchronized S answer(S s) {
147  
  new Matches m;
148  
  
149  
  if (match("reload", s)) {
150  
    reload();
151  
    ret l(bots) + "/" + l(botIDs) + " bots reloaded.";
152  
  }
153  
  
154  
  if (match("reload *", s, m)) {
155  
    reload(m.unq(0));
156  
    ret "Bot " + formatSnippetID(m.unq(0)) + " reloaded.";
157  
  }
158  
  
159  
  if (match("list bots", s)) {
160  
    S answer = "Active bots: " + structure(activeBots);
161  
    L<S> inactiveBots = diff(botIDs, activeBots);
162  
    if (!inactiveBots.isEmpty())
163  
      answer += ". Inactive bots: " + structure(inactiveBots);
164  
    ret answer;
165  
  }
166  
    
167  
  if (match("test", s))
168  
    ret "blah!";
169  
    
170  
  ret callStaticAnswerMethod(bots, s);
171  
}
172  
173  
static void recordFeedback(S probableQuestion, S answer, S answerTime, S feedback, int score) {
174  
  Map data = litmap("realtime", now(), "question", probableQuestion, "answer", answer, "answertime", answerTime, "feedback", feedback, "score", score);
175  
  print("Recording feedback: " + data);
176  
  logQuoted(new File(programDir(), "feedback-log"), structure(data));
177  
  logQuoted(new File(programDir(), "memory-log"), structure(data));
178  
}
179  
180  
static void reload() {
181  
  botIDs = or((L<S>) loadVariableDefinition("botIDs"), botIDs);
182  
  cleanUp(bots);
183  
  activeBots = new ArrayList<S>();
184  
  botsByID.clear();
185  
  for (S botID : botIDs)
186  
    pcall {
187  
      loadBot(botID);
188  
    }
189  
}
190  
191  
static void reload(S botID) {
192  
  S parsedID = "" + parseSnippetID(botID);
193  
  Class bot = botsByID.get(parsedID);
194  
  if (bot == null) ret;
195  
  cleanUp(bot);
196  
  botsByID.remove(parsedID);
197  
  activeBots.remove(bot);
198  
  loadBot(botID);
199  
}
200  
201  
static void loadBot(S botID) {
202  
  print("Loading bot: " + botID);
203  
  Class c = run(botID);
204  
  bots.add(c);
205  
  activeBots.add(botID); // only if loading doesn't fail
206  
  botsByID.put("" + parseSnippetID(botID), c);
207  
  print("Loaded bot: " + botID + ", bots now: " + l(activeBots));
208  
}
209  
210  
static S getUserName(S userID) {
211  
  if (userID == null) ret null;
212  
  S userName = userNames.get(userID);
213  
  if (userName == null) pcall {
214  
    userName = (S) slackGetUserInfo(token, userID).get("name");
215  
    userNames.put(userID, userName);
216  
  }
217  
  ret userName;
218  
}
219  
220  
static NanoHTTPD.Response serve(S uri, NanoHTTPD.Method method,
221  
  Map<S,S> header, Map<S,S> parms, Map<S,S> files) {
222  
  print("Serving HTTP " + quote(uri));
223  
  ++hitCount;
224  
  saveLocally("hitCount");
225  
  uri = dropPrefixMandatory("/", uri);
226  
  if (eq(uri, ""))
227  
    ret serveHomePage();
228  
  if (eq(uri, "favicon.ico"))
229  
    ret serve404();
230  
  
231  
  int i = uri.indexOf('/');
232  
  S firstPart = i >= 0 ? uri.substring(0, i) : uri;
233  
  S rest = i >= 0 ? substr(uri, i) : "/"; // rest always starts with "/"
234  
  ret serveBot(firstPart, rest);
235  
}
236  
237  
static NanoHTTPD.Response serveBot(S botID, S subUri) {
238  
  Class bot = botsByID.get("" + parseSnippetID(botID));
239  
  boolean raw = subUri.equals("/raw") || subUri.startsWith("/raw/");
240  
  if (raw) {
241  
    subUri = substr(subUri, "/raw".length());
242  
    if (subUri.length() == 0) subUri = "/";
243  
  }
244  
  if (bot != null) {
245  
    S botHtml = callHtmlMethod(bot, subUri);
246  
    if (raw) ret serveHTML(unnull(botHtml));
247  
    if (botHtml == null)
248  
      botHtml = "Bot has no HTML output.";
249  
    S title = "TinyBrain Bot " + formatSnippetID(botID);
250  
    S html = "<html><head><title>" + title + "</title></head>" +
251  
      "<body><h3>Bot <a href=\"http://tinybrain.de/" + parseSnippetID(botID) + "\">" + formatSnippetID(botID) + "</a></h3>\n" + botHtml +
252  
      "\n</body></html>";
253  
    ret serveHTML(html);
254  
  }
255  
  
256  
  ret serve404();
257  
}
258  
259  
static NanoHTTPD.Response serveHomePage() {
260  
  new StringBuilder buf;
261  
  buf.append("<html>");
262  
  buf.append("<head><title>TinyBrain Chat Bots</title></head>");
263  
  buf.append("<body>");
264  
  buf.append("<h3><a href=" + htmlQuote("http://tinybrain.de") + ">TinyBrain</a> Bots</h3>");
265  
  buf.append("<ul>");
266  
  for (S botID : activeBots) {
267  
    botID = formatSnippetID(botID);
268  
    S parsedID = "" + parseSnippetID(botID);
269  
    S title = "?";
270  
    pcall { title = getSnippetTitle_overBot(botID); }
271  
    S name = botID + " - " + htmlencode(title);
272  
    buf.append("<li><a href=" + htmlQuote(parsedID) + ">" + name + "</a> (");
273  
    buf.append("<a href=" + htmlQuote("http://tinybrain.de/" + parsedID) + ">source</a>)</li>");
274  
  }
275  
  buf.append("</ul>");
276  
  buf.append("<p>Hit count: " + hitCount + "</p>");
277  
  
278  
  buf.append("<p>Last interactions:</p>");
279  
  
280  
  int maxInteractionsToPrint = 10;
281  
  
282  
  for (int i = l(history)-1; i >= 0 && i >= l(history)-maxInteractionsToPrint; i--) {
283  
    Map m = history.get(i);
284  
    buf.append("<p>" + htmlencode(m.get("question")) + "<br>&nbsp; &nbsp; " + htmlencode(m.get("answer")) + "</p>");
285  
  }
286  
  
287  
  buf.append("</body>");
288  
  buf.append("</html>");
289  
  ret serveHTML(buf);
290  
}
291  
292  
static S getSnippetTitle_overBot(S snippetID) {
293  
  startBot("Snippet Title Bot", "#1001747");
294  
  // Use local version
295  
  //S s = sendToLocalBot(getVMPort() + "/Snippet Title Bot", "what is the title of snippet *", snippetID);
296  
  // Use bot VM version
297  
  S s = sendToLocalBot_cached("Snippet Title Bot", "what is the title of snippet *", snippetID);
298  
  new Matches m;
299  
  if (match("The title of snippet * is *.", s, m))
300  
    ret m.unq(1);
301  
  throw fail("Bot said: " + s);
302  
}
303  
304  
static void readLog() {
305  
  for (S s : scanLog("#1001915", "memory-log")) pcall {
306  
    history.add((Map) safeUnstructure(s));
307  
  }
308  
  print(l(history) + " history entries.");
309  
}

Author comment

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: 960 / 2169
Referenced in: [show references]