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)

static int slackSpeed = 500, slackTimeout = 20000;

static int historySuckSize = 10;

static L<S> attnPrefixes = litlist("!" /*, "eleutheria", "eleu"*/);

static new L<Channel> channels;

static class Channel {
  S channelName; // includes the site
  S channelID, token;
  boolean generalRelease, postAsUser, dedicated;
  
  boolean firstTime = true;

  new Map<S, Map> userInfos;
  
  S lastQuestion, lastAnswer;
  S lastAnswerTimestamp; // only for posted in this session
  int lastAnswerScore;
  
  void tendTo() {
    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) if (!isMyAnswer(msg)) {
      S q = msg.text;
      S userName = getUserName(msg.user);
      Map userInfo = getUserInfo(msg.user);
      if (userInfo != null && eq(true, userInfo.get("is_bot"))) {
        print("Skipping question from bot " + userName + ": " + quote(q));
        continue;
      }
      
      q = q.trim();
      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 {
        originalLine.set(q);
        main.userName.set(userName);
        S dialogID = "slack-" + userName;
        main.dialogID.set(dialogID);
        main.slackTS.set(msg.ts);
        main.channelName.set(channelName);
        
        // attn yes/no?
        
        main.dedicated.set(dedicated);
        attn.set(dedicated);
        pcall {
          if (!dedicated && attnPrefixes.contains(get(javaTok(q), 1))) {
            attn.set(true);
            q = join(subList(javaTok(q), 3)).trim();
          }
        }

        S a = answer(q, generalRelease);
        if (a != null) {
          // slack snippets have some size limit - let's forget that
          /*if (l(a) > maxAnswerLength)
            a = "```" + a + "```";*/
            
          // post long stuff to tinybrain instead
          if (l(a) > maxAnswerLength) {
            S title = "Answer for " + userName + " (>> " + q + ")";
            S id = ntUpload("eleutheria-for-user", title, unSlackSnippet(a));
            boolean isSnippet = a.contains("```");
            a = shorten(a, shortenTo) + (isSnippet ? "```" : "") + "\nFull answer here: tinybrain.de/" + parseSnippetID(id);
          }
          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, "answertime", answerTime, "answer", a, "user", userName, "dedicated", dedicated);
            historyAdd(logEntry);
          }
        }
      }
    }
  }
  
  Map getUserInfo(S userID) {
    if (userID == null) ret null;
    Map userInfo = userInfos.get(userID);
    if (userInfo == null) pcall {
      userInfo = slackGetUserInfo(token, userID);
      userInfos.put(userID, userInfo);
    }
    ret userInfo;
  }
  
  S getUserName(S userID) {
    Map userInfo = getUserInfo(userID);
    ret userInfo == null ? null : (S) userInfo.get("name");
  }
  
  // returns the slack timestamp
  S postAnswer(S text) {
    S postAs = main.postAs.get();
    S username = or(postAs, botUserName);
    S botIcon = main.botIcon.get();
    S url = "https://slack.com/api/chat.postMessage";
    Map postData = litmap(
      "token", token,
      "channel", channelID,
      "text", text,
      "username", username/*,
      "parse", "none",
      "link_names", "1"*/,
      "icon_url", botIcon);
    if (postAsUser && postAs == null)
      postData.put("as_user", "true");
    printStructure(postData);
    S data = doPostWithTimeout(postData, url, slackTimeout);
    Map map = jsonDecodeMap(data);
    lastAnswerTimestamp = (S) map.get("ts");
    lastAnswerScore = 0;
    print("postAnswer: " + data);
    ret lastAnswerTimestamp;
  }
  
  S getNewQuestions_lastSeen;
  
  L<SlackMsg> getNewQuestions() {
    // Get historySuckSize on first call (history before bot start)
    // and 1000 thereafter (just grab anything new)
    int limit = getNewQuestions_lastSeen == null ? historySuckSize : 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);
      }
  
      if (firstTime)
        msgs = subList(msgs, i+1);
    }
    firstTime = false;
    ret msgs;
  }

} // end of class Channel

static boolean slackBot_inited;

static void initSlackBot() {
  if (slackBot_inited) ret;
  slackBot_inited = true;
  
  //S devChannelToken = devChannelToken();
  S nlbotsToken = loadSecretTextFile("#1001925", "nlbots-slack-token").trim();
  S eleuToken = loadSecretTextFile("#1001925", "eleu-slack-token").trim();
  S devchattesttoken = loadSecretTextFile("#1001925", "devchat-test-token").trim();
  
  Channel c;
      
  /*c = new Channel;
  c.channelName = "devchannel, #talkingbots";
  c.channelID = "C0H0SR40J";
  c.token = devChannelToken;
  c.postAsUser = true;
  channels.add(c);
  
  c = new Channel;
  c.channelName = "devchannel, #eleudedi";
  c.channelID = "C0MDDQEJY";
  c.token = devChannelToken;
  c.postAsUser = true;
  c.dedicated = true;
  channels.add(c);*/
  
  /*c = new Channel;
  c.channelName = "eleu, #general";
  c.channelID = "C0MLVC346";
  c.token = eleuToken;
  c.postAsUser = true;
  c.dedicated = true;
  channels.add(c);*/

  /*c = new Channel;
  c.channelName = "devchannel, #general";
  c.channelID = "C0H0Q0J3D";
  c.token = devChannelToken;
  c.postAsUser = true;
  channels.add(c);*/

  /*c = new Channel;
  c.channelName = "devchannel, #programming";
  c.channelID = "C0H0MG0F6";
  c.token = devChannelToken;
  c.postAsUser = true;
  channels.add(c);*/

  /*c = new Channel;
  c.channelName = "devchannel, #c_sharp";
  c.channelID = "C0H0T0EQ6";
  c.token = devChannelToken;
  c.postAsUser = true;
  channels.add(c);*/

  c = new Channel;
  c.channelName = "nlbots, #general";
  c.channelID = "C0FHTG6SY"; // nlbots, #general
  c.token = nlbotsToken;
  channels.add(c);
  
  /*c = new Channel;
  c.channelID = "C0G5GPEMR"; // nlbots, #talkingbots
  c.token = nlbotsToken;
  
  channels.add(c);*/
  
  /*c = new Channel;
  c.channelID = "G0HLW98RY"; // devchannel, #bot_test
  c.token = devChannelToken;
  c.postAsUser = true;
  channels.add(c);*/
  
  c = new Channel;
  c.channelName = "devchat-test, #general";
  c.channelID = "C0QK003PV";
  c.token = devchattesttoken;
  c.postAsUser = true;
  channels.add(c);
}

static int indexOfLastBotAnswer(L<SlackMsg> msgs) {
  for (int i = l(msgs)-1; i >= 0; i--)
    if (isMyAnswer(msgs.get(i))) ret i;
  ret -1;
}

static boolean isMyAnswer(SlackMsg msg) {
  ret msg.botName != null || actualBotUserNames.contains(msg.userName);
}

static void slackBotLoop() {
  slackSetTimeout(slackTimeout);
  try {
    long lastPrinted = now();
    while true {
      for (Channel c : channels) pcall {
        c.tendTo();
      }
      sleep(slackSpeed);
      if (now() > lastPrinted + 60*1000) {
        lastPrinted = now();
        print("Slack loop still going. " + unixTime());
      }
    }
  } finally {
    print("Slack bot loop exit!?");
  }
}

static void sayInDedicated(S s) {
  for (Channel c : channels) pcall {
    if (c.dedicated)
      c.postAnswer(s);
  }
}

static void dediSay(S s) {
  sayInDedicated(s);
}

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: 885 / 1502
Referenced in: #1002017 - Eleutheria Main, including Slack Bot (LIVE)
#1002278 - Eleutheria Main 2 (developing)
#1002647 - Slack Bot Include (dynamic channels, dev.)
#1002891 - Eleu Sock Puppet Test
#1007827 - Slack Bot Include (for Celestia)
#3000190 - Answer for stefanreich(>> t 20 questions)
#3000202 - Answer for stefanreich (>> T conversion bot)
#3000238 - Answer for stefanreich (>> t power bot)
#3000382 - Answer for ferdie (>> t = 1, f = 0)
#3000383 - Answer for funkoverflow (>> t=1, f=0 okay)