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

254
LINES

< > BotCompany Repo | #1002647 // Slack Bot Include (dynamic channels, dev.)

JavaX fragment (include)

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

Author comment

Began life as a copy of #1002268

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1002647
Snippet name: Slack Bot Include (dynamic channels, dev.)
Eternal ID of this version: #1002647/1
Text MD5: e9f858dde1b9910b4f7ca1c30443bccc
Author: stefan
Category: nl bots
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2016-02-09 20:42:57
Source code size: 7765 bytes / 254 lines
Pitched / IR pitched: No / No
Views / Downloads: 582 / 490
Referenced in: [show references]