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

242
LINES

< > BotCompany Repo | #1007827 // Slack Bot Include (for Celestia)

JavaX fragment (include)

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

Author comment

Began life as a copy of #1002268

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1007827
Snippet name: Slack Bot Include (for Celestia)
Eternal ID of this version: #1007827/33
Text MD5: 7a5a4cba0e3038ddebc9e7e1a8a062bd
Author: stefan
Category: nl bots
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2017-05-06 16:34:20
Source code size: 7484 bytes / 242 lines
Pitched / IR pitched: No / No
Views / Downloads: 458 / 835
Version history: 32 change(s)
Referenced in: [show references]