sS botUserName = "electron";
sS logFile = "slack.log";
static new ThreadLocal<Bool> dedicated;
static new ThreadLocal<Channel> channel;
static new ThreadLocal<S> slackUserName;
static int maxAnswerLength = 1000, shortenTo = 200;
sbool actuallyPost = true;
static int slackSpeed = 5000, slackTimeout = 20000;
static int historySuckSize = 1;
sbool checkReactions = false;
static new L actionsAfterPost;
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 && checkReactions) {
// 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;
}
slackUserName.set(userName);
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);
print("dedicated: " + dedicated);
main.dedicated.set(dedicated);
main.channel.set(this);
S a;
try {
a = callStaticAnswerMethod(q);
} catch e {
printStackTrace(e);
a = "?" + getInnerMessage(e);
}
logStructure(logFile, ll(now(), channelID, channelName, msg.ts, msg.userName, a));
if (nempty(a)) {
// 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 = null; // main.postAs.get();
S username = or(postAs, botUserName);
S botIcon = null; // 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;
Channel c;
c = new Channel;
c.channelName = "xwired, #allgemein";
c.channelID = "C9FAC6EAE";
c.token = electronToken();
c.postAsUser = true;
c.dedicated = false;
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 licensed {
runAndClear(actionsAfterPost);
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);
}Began life as a copy of #1007827
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: | #1013776 |
| Snippet name: | Slack Bot Include (for Electron) |
| Eternal ID of this version: | #1013776/3 |
| Text MD5: | f3e1bce134ca10888eafa9f97923b6e7 |
| Author: | stefan |
| Category: | nl bots |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2018-02-27 11:34:17 |
| Source code size: | 7278 bytes / 234 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 777 / 962 |
| Version history: | 2 change(s) |
| Referenced in: | [show references] |