/
akolonin
/
aigents-java
Обзор
Документация
Войти
/
akolonin
/
aigents-java
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/main/java/net/webstructor/comm/twitter/Twitterer.java
485 строк
22 KB
Anton Kolonin
2.5.9 More bug fixes and diagnistics for NPE and OOM
10 май 2020, 13:57
10 май 2020, 13:57
0fe2d07
Код
Авторство
О чём код?
/* * 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.twitter; import java.io.IOException; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; import net.webstructor.agent.Body; import net.webstructor.al.AL; import net.webstructor.comm.HTTP; import net.webstructor.comm.HTTPHandler; import net.webstructor.comm.HTTPeer; import net.webstructor.comm.SocialBinder; import net.webstructor.core.Thing; import net.webstructor.peer.Session; import net.webstructor.util.Array; import net.webstructor.util.Code; import net.webstructor.util.JSON; import net.webstructor.util.Str; //https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens //https://developer.twitter.com/en/docs/basics/authentication/guides/log-in-with-twitter public class Twitterer extends SocialBinder implements HTTPHandler { public Twitterer(Body body) { super(body,"twitter"); } HashMap<String,String> token_secrets = new HashMap<String,String>(); /** * @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("/twitter") || AL.empty(request)) return false; try { Thing self = body.self(); String oauth_consumer_key = self.getString(Body.twitter_key); String oauth_сonsumer_secret = self.getString(Body.twitter_key_secret); String oauth_token = self.getString(Body.twitter_token); String oauth_token_secret = self.getString(Body.twitter_token_secret); if (AL.empty(oauth_consumer_key) || AL.empty(oauth_сonsumer_secret) || AL.empty(oauth_token) || AL.empty(oauth_token_secret)) return false; //https://developer.twitter.com/en/docs/basics/authentication/guides/log-in-with-twitter //https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens body.debug("Twitter input "+url+" "+request); if (request.contains("login")){//step 1 //https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token String[][] params = new String[][] {new String [] {"oauth_callback","https%3A%2F%2Faigents.com%2Fal%2Ftwitter"}}; String result = request("https://api.twitter.com/oauth/request_token", "POST",params,oauth_consumer_key,oauth_сonsumer_secret,oauth_token,oauth_token_secret); body.debug("Twitter login token "+result); String token = Str.parseBetween(result, "oauth_token=", "&"); String secret = Str.parseBetween(result, "oauth_token_secret=", "&"); synchronized (token_secrets) { token_secrets.put(token,secret); } String redirect = "https://api.twitter.com/oauth/authenticate?oauth_token="+token; parent.respond("<html><body>Proceed to<a href=\""+redirect+"\">"+body.site()+"</a></body></html>","302 Found\nLocation: "+redirect,"text/plain"); return true; } else if (request.contains("oauth_token")){//step 3 //https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token String token = Str.parseBetween(request, "oauth_token=", "&", false); String verifier = Str.parseBetween(request, "oauth_verifier=", "&", false); String secret; synchronized (token_secrets) { secret = token_secrets.get(token); } body.debug("Twitter login approved "+token+" "+verifier); String[][] params = new String[][] { new String [] {"oauth_token",token}, new String [] {"oauth_verifier",verifier} }; String result = request("https://api.twitter.com/oauth/access_token?oauth_token="+token+"&oauth_verifier="+verifier, "POST",params,oauth_consumer_key,oauth_сonsumer_secret,token,secret); body.debug("Twitter login verification "+result); String new_token = Str.parseBetween(result, "oauth_token=", "&"); String new_secret = Str.parseBetween(result, "oauth_token_secret=", "&"); String sname = Str.parseBetween(result, "screen_name=", "&", false); body.debug("Twitter login succeeded "+sname+" "+new_token+" "+new_secret); params = new String[][] { new String [] {"include_entities","true"}, new String [] {"skip_status","true"}, new String [] {"include_email","true"} }; result = request("https://api.twitter.com/1.1/account/verify_credentials.json","GET",params,oauth_consumer_key,oauth_сonsumer_secret,new_token,new_secret); body.debug("Twitter login completed "+result); JsonReader jsonReader = Json.createReader(new StringReader(result)); JsonObject json = jsonReader.readObject(); String id = JSON.getJsonString(json, "id_str"); String uname = JSON.getJsonString(json, "name"); sname = JSON.getJsonString(json, "screen_name"); String email = JSON.getJsonString(json, "email"); String image = JSON.getJsonString(json, "profile_image_url_https"); body.debug("Twitter login processed "+String.format("%s %s %s %s %s",id,uname,sname,email,image)); Thing peer = bindPeer(parent, cookie, id, uname, sname, email, new_token); if (peer != null) { peer.setString(Body.twitter_token_secret, new_secret); if (!AL.empty(image) && AL.isIMG(image)) peer.setString(Body.twitter_image, image); } //TODO: figure out how to delver welcome message to chat view on the Web parent.respond("<html><body>Return to<a href=\""+body.site()+"\">"+body.site()+"</a></body></html>","302 Found\nLocation: "+body.site(),"text/plain"); return true; } } catch (Exception e) { body.error("Twitter error", e); } return false; } protected String getEmail(String id) { return id + "@twitter.com"; } @Override public void output(Session session, String message) throws IOException { //TODO should we disregard Twitter as a Communicator so no need to have the fake output(...)? //TODO post direct messages by Twitter } //https://stackoverflow.com/questions/1609899/java-equivalent-to-phps-hmac-sha1 public static String hash_hmac(String baseString, String keyString) throws GeneralSecurityException, UnsupportedEncodingException { SecretKey secretKey = null; byte[] keyBytes = keyString.getBytes(); secretKey = new SecretKeySpec(keyBytes, "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(secretKey); byte[] text = baseString.getBytes(); //return new String(Base64.encodeBase64(mac.doFinal(text))).trim(); String str2b64 = Code.str2b64(mac.doFinal(text),false); return str2b64; } //https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/percent-encoding-parameters public static String percent_encoding(String input) { StringBuilder output = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); if (('0' <=c && c <= '9') || ('A' <=c && c <= 'Z') || ('a' <=c && c <= 'z') || c == '-' || c == '.' || c == '_' || c == '~') output.append(c); else //https://stackoverflow.com/questions/4477714/how-to-convert-a-char-from-alphabetical-character-to-hexadecimal-number-in-java output.append('%').append(Integer.toHexString((int) c).toUpperCase()); } return output.toString(); } public static String create_parameter_string(String[][] header) { String[][] encoded = new String[header.length][]; for (int i = 0; i < header.length; i++) encoded[i] = new String[] {percent_encoding(header[i][0]), percent_encoding(header[i][1])}; Arrays.sort(encoded,new Comparator() { @Override public int compare(Object o1, Object o2) { String[] s1 = (String[]) o1; String[] s2 = (String[]) o2; int c = s1[0].compareTo(s2[0]); return c != 0 ? c : s1[1].compareTo(s2[1]); }}); StringBuilder output = new StringBuilder(); for (int i = 0; i < encoded.length; i++) { if (i > 0) output.append("&"); output.append(encoded[i][0]).append("=").append(encoded[i][1]); } return output.toString(); } public static String create_params(String[][] params) throws UnsupportedEncodingException { StringBuilder output = new StringBuilder(); for (int i = 0; i < params.length; i++) { if (i > 0) output.append("&"); output.append(params[i][0]).append("=").append(URLEncoder.encode(params[i][1],"UTF-8")); } return output.toString(); } public static String create_oauth_header_string(String[][] header) { StringBuilder output = new StringBuilder("OAuth "); for (int i = 0; i < header.length; i++) { if (i > 0) output.append(", "); output.append(percent_encoding(header[i][0])).append("=\"").append(percent_encoding(header[i][1])).append("\""); } return output.toString(); } //https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/creating-a-signature public static String sign_request(String method, String url, String[][] header, String сonsumer_secret, String oauth_token_secret) throws UnsupportedEncodingException, GeneralSecurityException { String parameter_string = create_parameter_string(header); String signature_base_string = method + "&" + percent_encoding(url) + "&" + percent_encoding(parameter_string); //String сonsumer_secret = "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw"; //String oauth_token_secret = "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"; String signing_key = сonsumer_secret+"&"+oauth_token_secret; String signature = hash_hmac(signature_base_string,signing_key); return signature; } public static String request(String url,String method,String[][] params, String oauth_consumer_key,String сonsumer_secret,String oauth_token,String oauth_token_secret) throws GeneralSecurityException, IOException { String[][] oauth = new String[][] { new String [] {"oauth_consumer_key",oauth_consumer_key}, new String [] {"oauth_nonce",HTTP.nonce()}, new String [] {"oauth_signature_method","HMAC-SHA1"}, new String [] {"oauth_timestamp",String.valueOf(System.currentTimeMillis()/1000)}, new String [] {"oauth_token",oauth_token}, new String [] {"oauth_version","1.0"} }; String[][] forsign = Array.add(params, oauth); String signature = sign_request(method,url,forsign,сonsumer_secret,oauth_token_secret); String[][] oauth_data = Array.add(oauth, new String[][] {{"oauth_signature",signature}}); String oauth_header = create_oauth_header_string(oauth_data); String params_string = create_params(params); //hack around the stupid HTTP.simple if ("GET".equals(method) && !url.contains("?")) { url = url + "?" + params_string; params_string = null; } //TODO: fix HTTP.simple so it does not write params of GET but adds them to the url String result = HTTP.simple(url, AL.empty(params_string) ? null : params_string, method, 0, null, new String[][] { new String[] {"Authorization",oauth_header} }); return result; } public static void main(String args[]) { try { /* {//Test percent encoding String pe = percent_encoding("tnnArxj06cWHq44gCs1OSKk/jLY="); if ("tnnArxj06cWHq44gCs1OSKk%2FjLY%3D".equals(pe)) System.out.println(pe); } {//Test request signing String method = "POST"; String url = "https://api.twitter.com/1.1/statuses/update.json"; String сonsumer_secret = "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw"; String oauth_token_secret = "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"; String[][] header = new String[][] { new String [] {"status","Hello Ladies + Gentlemen, a signed OAuth request!"}, new String [] {"include_entities","true"}, new String [] {"oauth_consumer_key","xvz1evFS4wEEPTGEFPHBog"}, new String [] {"oauth_nonce","kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg"}, new String [] {"oauth_signature_method","HMAC-SHA1"}, new String [] {"oauth_timestamp","1318622958"}, new String [] {"oauth_token","370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb"}, new String [] {"oauth_version","1.0"} }; String signature = sign_request(method,url,header,сonsumer_secret,oauth_token_secret); if (signature.equals("hCtSmYh+iHYCEqBWrE7C7hYmtUk=")) System.out.println(signature); } {//Test request authorizing with data from tutorial //https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/authorizing-a-request String method = "POST"; String url = "https://api.twitter.com/1.1/statuses/update.json"; String сonsumer_secret = "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw"; String oauth_token_secret = "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"; String[][] params = new String[][] { new String [] {"status","Hello Ladies + Gentlemen, a signed OAuth request!"}, new String [] {"include_entities","true"} }; String[][] oauth = new String[][] { new String [] {"oauth_consumer_key","xvz1evFS4wEEPTGEFPHBog"}, new String [] {"oauth_nonce","kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg"}, new String [] {"oauth_signature_method","HMAC-SHA1"}, new String [] {"oauth_timestamp","1318622958"}, new String [] {"oauth_token","370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb"}, new String [] {"oauth_version","1.0"} }; String[][] forsign = Array.add(params, oauth); String signature = sign_request(method,url,forsign,сonsumer_secret,oauth_token_secret); if (signature.equals("hCtSmYh+iHYCEqBWrE7C7hYmtUk=")) System.out.println(signature); //signature from the document String[][] oauth_data = Array.add(oauth, new String[][] {{"oauth_signature","tnnArxj06cWHq44gCs1OSKk/jLY="}}); String oauth_header = create_oauth_header_string(oauth_data); System.out.println(oauth_header); //actual signature from the above oauth_data = Array.add(oauth, new String[][] {{"oauth_signature",signature}}); oauth_header = create_oauth_header_string(oauth_data); System.out.println(oauth_header); String params_string = create_params(params); String result = HTTP.simple(url, params_string, "POST", 0, null, new String[][] { new String[] {"Authorization",oauth_header} }); System.out.println(result);//{"errors":[{"code":89,"message":"Invalid or expired token."}]} } if (args.length >= 4) {//Test request authorizing with real app //https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/authorizing-a-request String method = "POST"; String url = "https://api.twitter.com/1.1/statuses/update.json"; String oauth_consumer_key = args[0]; String сonsumer_secret = args[1]; String oauth_token = args[2]; String oauth_token_secret = args[3]; String[][] params = new String[][] { new String [] {"status","Aigents are testing Twitter OAuth at "+(new Date())+"!"}, new String [] {"include_entities","true"} }; String result = request(url,method,params,oauth_consumer_key,сonsumer_secret,oauth_token,oauth_token_secret); System.out.println(result);//it works - creates status for a user!!! } if (args.length >= 4){//Test getting actual token //https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token String method = "POST"; String url = "https://api.twitter.com/oauth/request_token"; String oauth_consumer_key = args[0]; String сonsumer_secret = args[1]; String oauth_token = args[2]; String oauth_token_secret = args[3]; String[][] params = new String[][] { new String [] {"oauth_callback","https%3A%2F%2Faigents.com%2Fal%2Ftwitter"} }; String result = request(url,method,params,oauth_consumer_key,сonsumer_secret,oauth_token,oauth_token_secret); System.out.println(result);//it works - returns the token!!! String token = Str.parseBetween(result, "oauth_token=", "&"); String secret = Str.parseBetween(result, "oauth_token_secret=", "&"); System.out.println(token+" "+secret); } if (args.length >= 4){//Test verifying credentials String method = "GET"; String url = "https://api.twitter.com/1.1/account/verify_credentials.json"; String oauth_consumer_key = args[0]; String сonsumer_secret = args[1]; String oauth_token = args[2]; String oauth_token_secret = args[3]; String[][] params = new String[][] { new String [] {"include_entities","true"}, new String [] {"skip_status","true"}, new String [] {"include_email","true"} }; String result = request(url,method,params,oauth_consumer_key,сonsumer_secret,oauth_token,oauth_token_secret); System.out.println(result);//it works - creates status for a user!!! JsonReader jsonReader = Json.createReader(new StringReader(result)); JsonObject json = jsonReader.readObject(); String id = JSON.getJsonString(json, "id_str"); String name = JSON.getJsonString(json, "name"); String sname = JSON.getJsonString(json, "screen_name"); String email = JSON.getJsonString(json, "email"); String image = JSON.getJsonString(json, "profile_image_url_https"); System.out.format("%s %s %s %s %s",id,name,sname,email,image);//it works - creates status for a user!!! } if (args.length >= 4){//Test getting status - returns current day only String method = "GET"; String url = "https://api.twitter.com/1.1/statuses/home_timeline.json"; String oauth_consumer_key = args[0]; String сonsumer_secret = args[1]; String oauth_token = args[2]; String oauth_token_secret = args[3]; String[][] params = new String[][] { new String [] {"count","100"},//200 //new String [] {"since_id","false"}, //new String [] {"max_id","1257053173916659712"}, new String [] {"trim_user","false"}, new String [] {"exclude_replies","false"}, new String [] {"include_entities","false"}//true? }; String result = request(url,method,params,oauth_consumer_key,сonsumer_secret,oauth_token,oauth_token_secret); System.out.println(result);//it works - creates status for a user!!! JsonReader jsonReader = Json.createReader(new StringReader(result)); JsonArray tweets = jsonReader.readArray(); System.out.println(tweets.size()); for (int i = 0; i < tweets.size(); i++) { Twit t = new Twit(tweets.getJsonObject(i)); System.out.format("%tc %s %s %s %s %s %s %s %s\n", t.created_at, t.id_str, t.user_screen_name, t.in_reply_to_screen_name, t.retweet_count, t.favorite_count, t.retweeted, t.favorited, t.text); } } */ if (args.length >= 4){//Test getting status - returns current day only String method = "GET"; String url = "https://api.twitter.com/1.1/statuses/user_timeline.json"; String oauth_consumer_key = args[0]; String сonsumer_secret = args[1]; String oauth_token = args[2]; String oauth_token_secret = args[3]; String[][] params = new String[][] { //new String [] {"user_id","563516882"}, //new String [] {"screen_name","bengoertzel"}, new String [] {"count","200"},//200 //new String [] {"since_id","false"}, //new String [] {"max_id","1257053173916659712"}, new String [] {"trim_user","false"}, new String [] {"exclude_replies","false"}, new String [] {"include_rts","true"} }; String result = request(url,method,params,oauth_consumer_key,сonsumer_secret,oauth_token,oauth_token_secret); System.out.println(result);//it works - creates status for a user!!! JsonReader jsonReader = Json.createReader(new StringReader(result)); JsonArray tweets = jsonReader.readArray(); System.out.println(tweets.size()); for (int i = 0; i < tweets.size(); i++) { Twit t = new Twit(tweets.getJsonObject(i)); System.out.format("%tc %s %s %s %s %s %s %s %s\n", t.created_at, t.id_str, t.user_screen_name, t.in_reply_to_screen_name, t.retweet_count, t.favorite_count, t.retweeted, t.favorited, t.text); } } /**/ //- get followers //https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-followers-list //- get friends (followed) //https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-list //- get top 20 likes by users //https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/get-favorites-list } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (GeneralSecurityException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }