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

296
LINES

< > BotCompany Repo | #1002268 // Slack Bot Include

JavaX fragment (include)

1  
static int slackSpeed = 500, slackTimeout = 20000;
2  
3  
static int historySuckSize = 10;
4  
5  
static L<S> attnPrefixes = litlist("!" /*, "eleutheria", "eleu"*/);
6  
7  
static new L<Channel> channels;
8  
9  
static class Channel {
10  
  S channelName; // includes the site
11  
  S channelID, token;
12  
  boolean generalRelease, postAsUser, dedicated;
13  
  
14  
  boolean firstTime = true;
15  
16  
  new Map<S, Map> userInfos;
17  
  
18  
  S lastQuestion, lastAnswer;
19  
  S lastAnswerTimestamp; // only for posted in this session
20  
  int lastAnswerScore;
21  
  
22  
  void tendTo() {
23  
    if (lastAnswerTimestamp != null) {
24  
      // Poll reactions to our last answer - is there a more efficient way?
25  
      int score = slackReactionScore(token, channelID, lastAnswerTimestamp);
26  
      if (score != lastAnswerScore) {
27  
        print("Reaction score changes to " + score);
28  
        recordFeedback(lastQuestion, lastAnswer, lastAnswerTimestamp, "emoji", score-lastAnswerScore);
29  
        lastAnswerScore = score;
30  
      }
31  
    }
32  
    
33  
    L<SlackMsg> qs = getNewQuestions();
34  
    //print(l(qs) + " question(s).");
35  
    //printList(qs);
36  
    for (SlackMsg msg : qs) if (!isMyAnswer(msg)) {
37  
      S q = msg.text;
38  
      S userName = getUserName(msg.user);
39  
      Map userInfo = getUserInfo(msg.user);
40  
      if (userInfo != null && eq(true, userInfo.get("is_bot"))) {
41  
        print("Skipping question from bot " + userName + ": " + quote(q));
42  
        continue;
43  
      }
44  
      
45  
      q = q.trim();
46  
      print("Processing question: " + quote(q));
47  
      if (lastAnswer != null) { // TODO: maybe check the time delay
48  
        int score = feedbackScore(q);
49  
        if (score != 0) {
50  
          recordFeedback(lastQuestion /*lastPostByUser.get(userName)*/, lastAnswer, lastAnswerTimestamp, q, score);
51  
          if (score > 0)
52  
            pcall { slackSmile(token, channelID, msg.ts); }
53  
          else
54  
            pcall { slackReact(token, channelID, msg.ts, "thinking_face"); }
55  
        }
56  
        lastAnswer = null;
57  
      }
58  
      //if (userName != null) lastPostByUser.put(userName, msg.text);
59  
      pcall {
60  
        originalLine.set(q);
61  
        main.userName.set(userName);
62  
        S dialogID = "slack-" + userName;
63  
        main.dialogID.set(dialogID);
64  
        main.slackTS.set(msg.ts);
65  
        main.channelName.set(channelName);
66  
        
67  
        // attn yes/no?
68  
        
69  
        main.dedicated.set(dedicated);
70  
        attn.set(dedicated);
71  
        pcall {
72  
          if (!dedicated && attnPrefixes.contains(get(javaTok(q), 1))) {
73  
            attn.set(true);
74  
            q = join(subList(javaTok(q), 3)).trim();
75  
          }
76  
        }
77  
78  
        S a = answer(q, generalRelease);
79  
        if (a != null) {
80  
          // slack snippets have some size limit - let's forget that
81  
          /*if (l(a) > maxAnswerLength)
82  
            a = "```" + a + "```";*/
83  
            
84  
          // post long stuff to tinybrain instead
85  
          if (l(a) > maxAnswerLength) {
86  
            S title = "Answer for " + userName + " (>> " + q + ")";
87  
            S id = ntUpload("eleutheria-for-user", title, unSlackSnippet(a));
88  
            boolean isSnippet = a.contains("```");
89  
            a = shorten(a, shortenTo) + (isSnippet ? "```" : "") + "\nFull answer here: tinybrain.de/" + parseSnippetID(id);
90  
          }
91  
          if (userName != null)
92  
            a = "<@" + userName + "> " + a;
93  
          print("Answering with: " + quote(a));
94  
          lastAnswer = a;
95  
          lastQuestion = q;
96  
          if (actuallyPost) {
97  
            S answerTime = postAnswer(a);
98  
            Map logEntry = litmap("question", q, "answertime", answerTime, "answer", a, "user", userName, "dedicated", dedicated);
99  
            historyAdd(logEntry);
100  
          }
101  
        }
102  
      }
103  
    }
104  
  }
105  
  
106  
  Map getUserInfo(S userID) {
107  
    if (userID == null) ret null;
108  
    Map userInfo = userInfos.get(userID);
109  
    if (userInfo == null) pcall {
110  
      userInfo = slackGetUserInfo(token, userID);
111  
      userInfos.put(userID, userInfo);
112  
    }
113  
    ret userInfo;
114  
  }
115  
  
116  
  S getUserName(S userID) {
117  
    Map userInfo = getUserInfo(userID);
118  
    ret userInfo == null ? null : (S) userInfo.get("name");
119  
  }
120  
  
121  
  // returns the slack timestamp
122  
  S postAnswer(S text) {
123  
    S postAs = main.postAs.get();
124  
    S username = or(postAs, botUserName);
125  
    S botIcon = main.botIcon.get();
126  
    S url = "https://slack.com/api/chat.postMessage";
127  
    Map postData = litmap(
128  
      "token", token,
129  
      "channel", channelID,
130  
      "text", text,
131  
      "username", username/*,
132  
      "parse", "none",
133  
      "link_names", "1"*/,
134  
      "icon_url", botIcon);
135  
    if (postAsUser && postAs == null)
136  
      postData.put("as_user", "true");
137  
    printStructure(postData);
138  
    S data = doPostWithTimeout(postData, url, slackTimeout);
139  
    Map map = jsonDecodeMap(data);
140  
    lastAnswerTimestamp = (S) map.get("ts");
141  
    lastAnswerScore = 0;
142  
    print("postAnswer: " + data);
143  
    ret lastAnswerTimestamp;
144  
  }
145  
  
146  
  S getNewQuestions_lastSeen;
147  
  
148  
  L<SlackMsg> getNewQuestions() {
149  
    // Get historySuckSize on first call (history before bot start)
150  
    // and 1000 thereafter (just grab anything new)
151  
    int limit = getNewQuestions_lastSeen == null ? historySuckSize : 1000;
152  
    L<SlackMsg> msgs = slackReadChannel(channelID, token, limit, getNewQuestions_lastSeen);
153  
    if (!msgs.isEmpty()) getNewQuestions_lastSeen = last(msgs).ts;
154  
    int i = indexOfLastBotAnswer(msgs);
155  
    if (i >= 0) {
156  
      print("Last bot answer at " + (i+1) + "/" + l(msgs) + ": " + structure(msgs.get(i)));
157  
      
158  
      // update variables
159  
      lastAnswer = msgs.get(i).text;
160  
      for (int j = 0; j < i; j++) {
161  
        SlackMsg msg = msgs.get(j);
162  
        S userName = getUserName(msg.user);
163  
        //if (userName != null) lastPostByUser.put(userName, msg.text);
164  
      }
165  
  
166  
      if (firstTime)
167  
        msgs = subList(msgs, i+1);
168  
    }
169  
    firstTime = false;
170  
    ret msgs;
171  
  }
172  
173  
} // end of class Channel
174  
175  
static boolean slackBot_inited;
176  
177  
static void initSlackBot() {
178  
  if (slackBot_inited) ret;
179  
  slackBot_inited = true;
180  
  
181  
  //S devChannelToken = devChannelToken();
182  
  S nlbotsToken = loadSecretTextFile("#1001925", "nlbots-slack-token").trim();
183  
  S eleuToken = loadSecretTextFile("#1001925", "eleu-slack-token").trim();
184  
  S devchattesttoken = loadSecretTextFile("#1001925", "devchat-test-token").trim();
185  
  
186  
  Channel c;
187  
      
188  
  /*c = new Channel;
189  
  c.channelName = "devchannel, #talkingbots";
190  
  c.channelID = "C0H0SR40J";
191  
  c.token = devChannelToken;
192  
  c.postAsUser = true;
193  
  channels.add(c);
194  
  
195  
  c = new Channel;
196  
  c.channelName = "devchannel, #eleudedi";
197  
  c.channelID = "C0MDDQEJY";
198  
  c.token = devChannelToken;
199  
  c.postAsUser = true;
200  
  c.dedicated = true;
201  
  channels.add(c);*/
202  
  
203  
  /*c = new Channel;
204  
  c.channelName = "eleu, #general";
205  
  c.channelID = "C0MLVC346";
206  
  c.token = eleuToken;
207  
  c.postAsUser = true;
208  
  c.dedicated = true;
209  
  channels.add(c);*/
210  
211  
  /*c = new Channel;
212  
  c.channelName = "devchannel, #general";
213  
  c.channelID = "C0H0Q0J3D";
214  
  c.token = devChannelToken;
215  
  c.postAsUser = true;
216  
  channels.add(c);*/
217  
218  
  /*c = new Channel;
219  
  c.channelName = "devchannel, #programming";
220  
  c.channelID = "C0H0MG0F6";
221  
  c.token = devChannelToken;
222  
  c.postAsUser = true;
223  
  channels.add(c);*/
224  
225  
  /*c = new Channel;
226  
  c.channelName = "devchannel, #c_sharp";
227  
  c.channelID = "C0H0T0EQ6";
228  
  c.token = devChannelToken;
229  
  c.postAsUser = true;
230  
  channels.add(c);*/
231  
232  
  c = new Channel;
233  
  c.channelName = "nlbots, #general";
234  
  c.channelID = "C0FHTG6SY"; // nlbots, #general
235  
  c.token = nlbotsToken;
236  
  channels.add(c);
237  
  
238  
  /*c = new Channel;
239  
  c.channelID = "C0G5GPEMR"; // nlbots, #talkingbots
240  
  c.token = nlbotsToken;
241  
  
242  
  channels.add(c);*/
243  
  
244  
  /*c = new Channel;
245  
  c.channelID = "G0HLW98RY"; // devchannel, #bot_test
246  
  c.token = devChannelToken;
247  
  c.postAsUser = true;
248  
  channels.add(c);*/
249  
  
250  
  c = new Channel;
251  
  c.channelName = "devchat-test, #general";
252  
  c.channelID = "C0QK003PV";
253  
  c.token = devchattesttoken;
254  
  c.postAsUser = true;
255  
  channels.add(c);
256  
}
257  
258  
static int indexOfLastBotAnswer(L<SlackMsg> msgs) {
259  
  for (int i = l(msgs)-1; i >= 0; i--)
260  
    if (isMyAnswer(msgs.get(i))) ret i;
261  
  ret -1;
262  
}
263  
264  
static boolean isMyAnswer(SlackMsg msg) {
265  
  ret msg.botName != null || actualBotUserNames.contains(msg.userName);
266  
}
267  
268  
static void slackBotLoop() {
269  
  slackSetTimeout(slackTimeout);
270  
  try {
271  
    long lastPrinted = now();
272  
    while true {
273  
      for (Channel c : channels) pcall {
274  
        c.tendTo();
275  
      }
276  
      sleep(slackSpeed);
277  
      if (now() > lastPrinted + 60*1000) {
278  
        lastPrinted = now();
279  
        print("Slack loop still going. " + unixTime());
280  
      }
281  
    }
282  
  } finally {
283  
    print("Slack bot loop exit!?");
284  
  }
285  
}
286  
287  
static void sayInDedicated(S s) {
288  
  for (Channel c : channels) pcall {
289  
    if (c.dedicated)
290  
      c.postAnswer(s);
291  
  }
292  
}
293  
294  
static void dediSay(S s) {
295  
  sayInDedicated(s);
296  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1002268
Snippet name: Slack Bot Include
Eternal ID of this version: #1002268/1
Text MD5: fcc692700718522ace77c8a0e9c596b6
Author: stefan
Category: nl bots
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2016-05-05 13:56:13
Source code size: 8976 bytes / 296 lines
Pitched / IR pitched: No / No
Views / Downloads: 886 / 1503
Referenced in: [show references]