/
akolonin
/
aigents-java
Обзор
Документация
Войти
/
akolonin
/
aigents-java
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/main/java/net/webstructor/comm/Slacker.java
353 строки
13 KB
Anton Kolonin
3.1.8 Telegram Group Monitoring and Reporting Bug Fixing Release
15 дек 2020, 15:06
15 дек 2020, 15:06
9092aeb
Код
Авторство
О чём код?
/* * MIT License * * Copyright (c) 2019-2020 by Anton Kolonin, Aigents® * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package net.webstructor.comm; import java.io.IOException; import java.io.StringReader; import java.net.URLEncoder; import java.util.HashMap; import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonReader; import net.webstructor.agent.Body; import net.webstructor.al.AL; import net.webstructor.al.Parser; import net.webstructor.core.Anything; import net.webstructor.core.Thing; import net.webstructor.peer.Peer; import net.webstructor.peer.Session; import net.webstructor.util.Str; //https://api.slack.com/docs/oauth //https://api.slack.com/methods/oauth.access //https://api.slack.com/tutorials/app-creation-and-oauth //https://api.slack.com/bot-users //https://api.slack.com/docs/slack-button#add_the_slack_button //https://api.slack.com/tutorials/your-first-slash-command public class Slacker extends Mediator implements HTTPHandler { private HashMap groups = new HashMap(); public Slacker(Body body) { super(body,"slack"); } //TODO: move to Mediator but make sure about 'facebook_id' clash? public void login(Session session, Anything peer) { peer.setString(Body.slack_id, ids(session.getKey())[2]); } //https://api.slack.com/methods/channels.info private String groupName(String id){ String n = null; synchronized(groups){ n = (String)groups.get(id); } if (AL.empty(n)){ try { String url = "https://slack.com/api/channels.info"; String token = body.self().getString(Body.slack_token); String par = "token="+token+"&channel="+id; body.debug("Slack channel request "+par); String res = HTTP.simple(url, par, "POST", timeout); body.debug("Slack channel response "+res); JsonReader jsonReader = Json.createReader(new StringReader(res)); JsonObject json = jsonReader.readObject(); if (json.containsKey("channel")){ JsonObject channel = json.getJsonObject("channel"); n = HTTP.getJsonString(channel, "name"); synchronized(groups){ groups.put(id,n); } } } catch (IOException e) { // TODO Auto-generated catch block body.error("Slack channel error",e); } } return n; } /** * @param args - array with url, header, request * @return true if handled * @throws IOException */ public boolean handleHTTP(HTTPeer parent, String url, String header, String request, String cookie) throws IOException { if (url.startsWith("/slack")){ if (!AL.empty(request)){ body.debug("Slack input "+request); String code = null; String state = null; String challenge = null; String type = null; String subtype = null; String user = null; String channel = null; String text = null; String channel_type = null; String[] params = Parser.split(request, "&"); for (int i = 0; i < params.length; i++){ String[] keyvalue = Parser.split(params[i], "="); if (keyvalue.length == 2){ if (keyvalue[0].equals("code")) code = keyvalue[1]; if (keyvalue[0].equals("state")) state = keyvalue[1]; } } if (header.startsWith("POST")){ JsonReader jsonReader = Json.createReader(new StringReader(request)); JsonObject json = jsonReader.readObject(); if (json.containsKey("event")){ JsonObject event = json.getJsonObject("event"); type = HTTP.getJsonString(event, "type"); subtype = HTTP.getJsonString(event, "subtype"); text = HTTP.getJsonString(event, "text"); user = HTTP.getJsonString(event, "user"); channel = HTTP.getJsonString(event, "channel"); channel_type = HTTP.getJsonString(event, "channel_type"); } //else {//verification //https://api.slack.com/events-api#events_api_request_urls //token = HTTP.getJsonString(json, "token"); challenge = HTTP.getJsonString(json, "challenge"); type = HTTP.getJsonString(json, "type"); //} } String debug = "type="+type+" challenge="+challenge+" code="+code+" state="+state+" user="+user+" channel="+channel+" channel_type="+channel_type+" text="+text; body.debug("Slack debug "+debug); if (!AL.empty(challenge) && "url_verification".equals(type)){//url verification parent.respond(challenge); return true; }else //if (!AL.empty(code) && !AL.empty(state)){//user authentication if (!AL.empty(code)){//user authentication //https://api.slack.com/docs/slack-button //https://api.slack.com/tutorials/app-creation-and-oauth //https://api.slack.com/methods/oauth.access //https://api.slack.com/docs/oauth //TODO:check "state", according to //https://api.slack.com/slack-apps#direct_install String api = "https://slack.com/api/oauth.access"; String client_id = body.self().getString(Body.slack_id); String client_secret = body.self().getString(Body.slack_key); String par = "code="+code+"&client_id="+client_id+"&client_secret="+client_secret; //String res = HTTP.simple(api, par, "POST", 0, "application/x-www-form-urlencoded"); body.debug("Slack input "+request); body.debug("Slack auth request "+par); String res = HTTP.simple(api, par, "POST", timeout); body.debug("Slack auth response "+res); JsonReader jsonReader = Json.createReader(new StringReader(res)); JsonObject json = jsonReader.readObject(); if (json.getBoolean("ok")){ //TODO: identify user on basis of the code!? ; //String token = HTTP.getJsonString(json, "access_token"); //body.self().setString(Body.slack_token, token); } parent.respond("<html><body>Return to<a href=\""+body.site()+"\">"+body.site()+"</a></body></html>","301 Moved Permanently\nLocation: "+body.site(),"text/plain"); return true; }else if (AL.empty(type) || AL.empty(user) || AL.empty(text)){//ignore what we dont' understand parent.respond(""); return true; } else if ("im".equals(channel_type) && AL.empty(subtype)) //discard subtype==file_share, because it may have not a bot user for bot uploads!? {//private channel messages //TODO: translate "commands" at conversational level if (AL.news.equalsIgnoreCase(text)) text = "What new true sources, text, times, trust?"; else if (AL.topics.equalsIgnoreCase(text)) text = "What my topics name, trust?"; else if (AL.sites.equalsIgnoreCase(text)) text = "What my sites name, trust?"; else text = decodeEmail(text); parent.respond(""); net.webstructor.peer.Session session = body.sessioner.getSession(this,key(channel,user)); body.conversationer.handle(this, session, text); return true; }else //TODO: how to treat channel with multiple users - as "channel_type":"channel" or "channel_type":"im" if ("channel".equals(channel_type)){//TODO: if message to public channel parent.respond("");//respond first, to avoid resend /* {"token":"XXXXXXXXXXXX","team_id":"YYYY","api_app_id":"ZZZZ", "event":{"client_msg_id":"AAAAAAAAAAAA","type":"message", "text":"hifromsnet1","user":"BBBBBB","ts":"1556455265.001000","channel":"CGGNK2VGE", "event_ts":"1556455265.001000","channel_type":"channel"},"type":"event_callback", "event_id":"DDDDDDD","event_time":1556455265,"authed_users":["ZZZZZZ"]} */ //TODO:1) get channel name String channel_name = groupName(channel); //TODO:2) check if user is bot boolean is_bot = false;//TODO check if is bot //TODO: message_id updateGroup(null, channel, channel_name, channel_name, user, true, is_bot, text); //TODO: be able to do unauthorized group conversations and enable chat message handling! return true; }else{//ignore everything else parent.respond(""); return true; } } } return false; } String decodeEmail(String text){ for (;;){ int email_pos = text.indexOf("<mailto:"); if (email_pos == -1) break; int email_mid = text.indexOf("|",email_pos); int email_end = text.indexOf(">",email_pos); if (email_mid > email_pos && email_end > email_mid){ int pos = email_pos+8; String email = text.substring(pos,email_mid); text = text.substring(0,email_pos) + email + text.substring(email_end+1); body.debug("Slack email:"+email+":"+text); } } return text; } public void output(Session session, String message) throws IOException { String[] ids = ids(session.getKey()); if (!AL.empty(ids)){ //TODO: support group conversations output(ids[1], body.self().getString(Body.slack_token), message); } } public void output(String channel, String token, String message) throws IOException { try { if (!message.startsWith("<html>")) { String url = "https://slack.com/api/chat.postMessage"; //TODO limit slack messages or send them as files!!! //if (message.length() > 4096) // message = message.substring(0,4093) + "..."; //send text message String content = URLEncoder.encode(message,"UTF-8"); String par = "token="+token+"&channel="+channel+"&text="+content; body.debug("Slack chat request "+par); String res = HTTP.simple(url, par, "POST", timeout); body.debug("Slack chat response "+res); //TODO: slack reports } else { //https://api.slack.com/methods/files.upload String url = "https://slack.com/api/files.upload"; String title = Str.parseBetween(message, "<title>", "</title>"); if (AL.empty(title)) title = "Report"; String boundary = "--bndry--"; String par = "\r\n" +"--"+boundary+"\r\n" +"Content-Disposition: form-data; name=\"token\"\r\n\r\n"+token+"\r\n" +"--"+boundary+"\r\n" +"Content-Disposition: form-data; name=\"channels\"\r\n\r\n"+channel+"\r\n" +"--"+boundary+"\r\n" //+"Content-Disposition: form-data; name=\"title\"\r\n\r\n"+title+"\r\n" +"Content-Disposition: form-data; name=\"initial_comment\"\r\n\r\n"+title+"\r\n" +"--"+boundary+"\r\n" +"Content-Disposition: form-data; name=\"filetype\"\r\n\r\nhtml\r\n" +"--"+boundary+"\r\n" +"Content-Disposition: form-data; name=\"filename\"\r\n\r\nreport.html\r\n" +"--"+boundary+"\r\n" +"Content-Disposition: form-data; name=\"content\"\r\n" +"Content-Type: text/html; charset=utf-8\r\n" //+"Content-Length: "+message.length()+"\r\n" +"\r\n" +message+"\r\n" +"--"+boundary+"--\r\n"; body.debug("Slack report request channel "+par); String response = HTTP.simple(url,par,"POST",timeout,"multipart/form-data; boundary="+boundary); /* title= URLEncoder.encode(title,"UTF-8"); String content = URLEncoder.encode(message,"UTF-8"); //String par = "token="+token+"&channels="+channel+"&title="+title+"&filetype=HTML&filename=report.html&content="+content; String par = "token="+token+"&channels="+channel+"&filetype=html&filename=report.html&content="+content; body.debug("Slack chat request "+par); String response = HTTP.simple(url, par, "POST", timeout); */ body.debug("Slack report response "+Str.first(response,200)); JsonReader jr = Json.createReader(new StringReader(response)); JsonObject result = jr.readObject(); if (!HTTP.getJsonBoolean(result, "ok", false)) throw new Exception("Slack error files.upload: "+result.toString()); } } catch (Exception e){ body.error("Slack chat error",e); } } @Override public boolean update(Thing peer, String sessionKey, String subject, String content, String signature) throws IOException { String self_lack_token = body.self().getString(Body.slack_token); String psid = null; if (!AL.empty(self_lack_token)){ /*String login_token = peer.getString(Peer.login_token); if (!AL.empty(login_token)){ String[] ids = Parser.split(login_token,":"); if (ids != null && name.equals(ids[0]) && ids.length == 3)//facebook:psid:psuid psid = ids[1]; }*/ psid = getTokenSegment(peer.getString(Peer.login_token),1); } if (psid == null) psid = getPeerSession(peer); body.debug("Slack updating psid "+psid+" text "+content); if (!AL.empty(psid)){ StringBuilder sb = new StringBuilder(); if (!AL.empty(subject)) sb.append(subject).append('\n'); sb.append(content); //if (!AL.empty(signature)) // sb.append('\n').append(signature); body.debug("Slack update psid "+psid+" text "+content); output(psid, self_lack_token, sb.toString()); return true; } return false; } }