From cf4d8a7973b78b85ad1463145bfcc4ab791e17c4 Mon Sep 17 00:00:00 2001 From: hotlcc Date: Mon, 23 Jul 2018 12:45:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/hotlcc/wechat4j/Wechat.java | 89 ++-- .../com/hotlcc/wechat4j/api/WebWeixinApi.java | 492 +++++++++--------- .../hotlcc/wechat4j/enums/LoginTipEnum.java | 28 + .../wechat4j/enums/OperatingSystemEnum.java | 38 ++ .../com/hotlcc/wechat4j/util/QRCodeUtil.java | 68 +++ src/main/resources/config/app.properties | 4 +- .../resources/config/webwx-url.properties | 2 +- 7 files changed, 425 insertions(+), 296 deletions(-) create mode 100644 src/main/java/com/hotlcc/wechat4j/enums/LoginTipEnum.java create mode 100644 src/main/java/com/hotlcc/wechat4j/enums/OperatingSystemEnum.java diff --git a/src/main/java/com/hotlcc/wechat4j/Wechat.java b/src/main/java/com/hotlcc/wechat4j/Wechat.java index c5a777f..b78339e 100644 --- a/src/main/java/com/hotlcc/wechat4j/Wechat.java +++ b/src/main/java/com/hotlcc/wechat4j/Wechat.java @@ -1,8 +1,8 @@ package com.hotlcc.wechat4j; -import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.hotlcc.wechat4j.api.WebWeixinApi; +import com.hotlcc.wechat4j.enums.LoginTipEnum; import com.hotlcc.wechat4j.util.QRCodeUtil; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; @@ -74,13 +74,14 @@ public class Wechat { //2、获取二维码 logger.info("开始获取二维码..."); - result = webWeixinApi.getQR(httpClient, uuid); - logger.info("获取二维码成功,请扫描二维码:\n{}", QRCodeUtil.toCharMatrix(result.getBytes("data"))); + byte[] data = webWeixinApi.getQR(httpClient, uuid); + logger.info("获取二维码成功,请扫描二维码:\n{}", QRCodeUtil.toCharMatrix(data)); + QRCodeUtil.openQRCodeImage(data); //3、轮询 String code = null; while (!"200".equals(code)) { - result = webWeixinApi.getRedirectUri(httpClient, uuid); + result = webWeixinApi.getRedirectUri(httpClient, LoginTipEnum.TIP_0, uuid); code = result.getString("code"); if ("408".equals(code)) { continue; @@ -118,37 +119,55 @@ public class Wechat { logger.info("开始开启消息状态通知"); result = webWeixinApi.statusNotify(httpClient, passTicket, wxsid, skey, wxuin, loginUser.getString("UserName")); logger.info("开启消息状态通知完成"); + //7、获取联系人信息 +// logger.info("开始获取全部联系人"); +// result = webWeixinApi.getContact(httpClient, passTicket, wxsid); +// JSONArray MemberList = result.getJSONArray("MemberList"); +// logger.info("获取全部联系人完成,共{}条:", MemberList.size()); +// System.out.println("昵称\t备注名\tUserName"); +// for (int i = 0, len = MemberList.size(); i < len; i++) { +// JSONObject Member = MemberList.getJSONObject(i); +// System.out.println(Member.getString("NickName") + "\t" + Member.getString("RemarkName") + "\t" + Member.getString("UserName")); +// } +// logger.info("获取全部联系人完成,共{}条", MemberList.size()); //7、服务端状态同步 - logger.info("开始轮询服务端状态"); - while (true) { - result = webWeixinApi.syncCheck(httpClient, wxsid, skey, wxuin, SyncKey.getJSONArray("List")); - System.out.println(result); - int retcode = result.getIntValue("retcode"); - if (retcode != 0) { - logger.info("微信已退出登录"); - break; - } else { - int selector = result.getIntValue("selector"); - if (selector == 2) { - logger.info("收到新消息"); - //8、获取新消息内容 - result = webWeixinApi.pullNewMsg(httpClient, passTicket, wxsid, skey, wxuin, SyncKey); - SyncKey = result.getJSONObject("SyncKey"); - JSONArray AddMsgList = result.getJSONArray("AddMsgList"); - if (AddMsgList != null) { - for (int i = 0, len = AddMsgList.size(); i < len; i++) { - JSONObject Msg = AddMsgList.getJSONObject(i); - String Content = Msg.getString("Content"); - int MsgType = Msg.getIntValue("MsgType"); - String FromUserName = Msg.getString("FromUserName"); - String ToUserName = Msg.getString("ToUserName"); - logger.info("消息类型:{},消息内容:{},发送方:{},接收方:{}", MsgType, Content, FromUserName, ToUserName); - result = webWeixinApi.sendMsg(httpClient, passTicket, wxsid, skey, wxuin, Content, MsgType, ToUserName, FromUserName); - logger.info("自动回复消息完成,返回:{}", result); - } - } - } - } - } +// logger.info("开始轮询服务端状态"); +// while (true) { +// result = webWeixinApi.syncCheck(httpClient, wxsid, skey, wxuin, SyncKey.getJSONArray("List")); +// System.out.println(result); +// int retcode = result.getIntValue("retcode"); +// if (retcode != 0) { +// logger.info("微信已退出登录:retcode={}", retcode); +// break; +// } else { +// int selector = result.getIntValue("selector"); +// if (selector == 2) { +// logger.info("收到新消息"); +// //8、获取新消息内容 +// result = webWeixinApi.pullNewMsg(httpClient, passTicket, wxsid, skey, wxuin, SyncKey); +// SyncKey = result.getJSONObject("SyncKey"); +// JSONArray AddMsgList = result.getJSONArray("AddMsgList"); +// if (AddMsgList != null) { +// for (int i = 0, len = AddMsgList.size(); i < len; i++) { +// JSONObject Msg = AddMsgList.getJSONObject(i); +// String Content = Msg.getString("Content"); +// int MsgType = Msg.getIntValue("MsgType"); +// String FromUserName = Msg.getString("FromUserName"); +// String ToUserName = Msg.getString("ToUserName"); +// logger.info("消息类型:{},消息内容:{},发送方:{},接收方:{}", MsgType, Content, FromUserName, ToUserName); +//// result = webWeixinApi.sendMsg(httpClient, passTicket, wxsid, skey, wxuin, Content, MsgType, ToUserName, FromUserName); +//// logger.info("自动回复消息完成,返回:{}", result); +// } +// } +// } +// } +// } + //测试给特定联系人发送文本信息 + result = webWeixinApi.sendMsg(httpClient, passTicket, wxsid, skey, wxuin, + "测试消息", + 1, + loginUser.getString("UserName"), + "@493f6dbadb4ed2f471b8098fd7f6db1bbcc7294e829e8ecbdc6d2b32647bc2d2"); + System.out.println(result); } } diff --git a/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java b/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java index bafe99f..b1753a1 100644 --- a/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java +++ b/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java @@ -2,6 +2,7 @@ package com.hotlcc.wechat4j.api; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.hotlcc.wechat4j.enums.LoginTipEnum; import com.hotlcc.wechat4j.util.PropertiesUtil; import com.hotlcc.wechat4j.util.StringUtil; import com.hotlcc.wechat4j.util.WechatUtil; @@ -20,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.stringtemplate.v4.ST; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -35,6 +35,7 @@ import java.util.regex.Pattern; public class WebWeixinApi { private static Logger logger = LoggerFactory.getLogger(WebWeixinApi.class); + //预编译正则匹配 private static Pattern PATTERN_UUID_1 = Pattern.compile("window.QRLogin.code = (\\d+);"); private static Pattern PATTERN_UUID_2 = Pattern.compile("window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";"); private static Pattern PATTERN_REDIRECT_URI_1 = Pattern.compile("window.code=(\\d+);"); @@ -44,56 +45,53 @@ public class WebWeixinApi { * 获取微信uuid */ public JSONObject getWxUuid(HttpClient httpClient) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.uuid_url")) - .add("appid", PropertiesUtil.getProperty("webwx.appid")) - .add("_", System.currentTimeMillis()) - .render(); - - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); - - JSONObject result = new JSONObject(); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.uuid_url")) + .add("appid", PropertiesUtil.getProperty("webwx.appid")) + .add("_", System.currentTimeMillis()) + .render(); + + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); + HttpResponse response = httpClient.execute(httpGet); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); - String res = EntityUtils.toString(entity); + String res = EntityUtils.toString(entity, Consts.UTF_8); Matcher matcher = PATTERN_UUID_1.matcher(res); if (!matcher.find()) { - throw new RuntimeException("获取登录uuid失败"); + throw new RuntimeException("返回数据错误"); } String code = matcher.group(1); + JSONObject result = new JSONObject(); result.put("code", code); if (!"200".equals(code)) { - result.put("code", code); - result.put("msg", "获取登录uuid失败,请确认appid是否有效"); + result.put("msg", "返回code错误,请确认appid是否有效"); return result; } matcher = PATTERN_UUID_2.matcher(res); if (!matcher.find()) { - throw new RuntimeException("获取登录uuid失败"); + throw new RuntimeException("没有匹配到uuid"); } String uuid = matcher.group(2); result.put("uuid", uuid); if (StringUtil.isEmpty(uuid)) { - throw new RuntimeException("获取登录uuid失败"); + throw new RuntimeException("获取的uuid为空"); } return result; - } catch (IOException e) { - logger.error("获取登录uuid异常", e); - result.put("code", "-1"); - result.put("msg", "获取登录uuid异常"); - return result; + } catch (Exception e) { + logger.error("获取uuid异常", e); + return null; } } @@ -102,38 +100,33 @@ public class WebWeixinApi { * * @param uuid */ - public JSONObject getQR(HttpClient httpClient, - String uuid) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.qrcode_url")) - .add("uuid", uuid) - .render(); - - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); - - JSONObject result = new JSONObject(); - + public byte[] getQR(HttpClient httpClient, + String uuid) { try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.qrcode_url")) + .add("uuid", uuid) + .render(); + + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); + HttpResponse response = httpClient.execute(httpGet); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); byte[] data = EntityUtils.toByteArray(entity); if (data == null || data.length <= 0) { - throw new RuntimeException("获取二维码失败"); + throw new RuntimeException("二维码数据为空"); } - result.put("code", "200"); - result.put("data", data); - return result; - } catch (IOException e) { + return data; + } catch (Exception e) { logger.error("获取二维码异常", e); - result.put("code", "-1"); - result.put("msg", "获取二维码异常"); - return result; + return null; } } @@ -143,35 +136,37 @@ public class WebWeixinApi { * @return */ public JSONObject getRedirectUri(HttpClient httpClient, + LoginTipEnum tip, String uuid) { - long millis = System.currentTimeMillis(); - String url = new ST(PropertiesUtil.getProperty("webwx-url.redirect_uri")) - .add("uuid", uuid) - .add("r", millis / 1252L) - .add("_", millis) - .render(); - - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); - - JSONObject result = new JSONObject(); - try { + long millis = System.currentTimeMillis(); + String url = new ST(PropertiesUtil.getProperty("webwx-url.redirect_uri")) + .add("tip", tip.getCode()) + .add("uuid", uuid) + .add("r", millis / 1252L) + .add("_", millis) + .render(); + + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); + HttpResponse response = httpClient.execute(httpGet); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); - String res = EntityUtils.toString(entity); + String res = EntityUtils.toString(entity, Consts.UTF_8); Matcher matcher = PATTERN_REDIRECT_URI_1.matcher(res); if (!matcher.find()) { - throw new RuntimeException("获取跳转uri失败"); + throw new RuntimeException("返回数据错误"); } String code = matcher.group(1); + JSONObject result = new JSONObject(); result.put("code", code); if ("408".equals(code)) { result.put("msg", "请扫描二维码"); @@ -182,20 +177,18 @@ public class WebWeixinApi { } else if ("200".equals(code)) { matcher = PATTERN_REDIRECT_URI_2.matcher(res); if (!matcher.find()) { - throw new RuntimeException("获取跳转uri失败"); + throw new RuntimeException("没有匹配到跳转uri"); } result.put("msg", "手机确认成功"); result.put("redirectUri", matcher.group(2)); } else { - result.put("msg", "扫码失败"); + throw new RuntimeException("返回code错误"); } return result; - } catch (IOException e) { + } catch (Exception e) { logger.error("获取跳转uri异常", e); - result.put("code", "-3"); - result.put("msg", "获取跳转uri异常"); - return result; + return null; } } @@ -205,38 +198,30 @@ public class WebWeixinApi { */ public JSONObject getLoginCode(HttpClient httpClient, String redirectUri) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.newlogin_url")) - .add("redirectUri", redirectUri) - .render(); - - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); - - JSONObject result = new JSONObject(); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.newlogin_url")) + .add("redirectUri", redirectUri) + .render(); + + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); + HttpResponse response = httpClient.execute(httpGet); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); - String res = EntityUtils.toString(entity); + String res = EntityUtils.toString(entity, Consts.UTF_8); - JSONObject json = JSONObject.parseObject(XML.toJSONObject(res).toString()).getJSONObject("error"); - result.putAll(json); - result.put("msg", result.getString("message")); - if (result.getIntValue("ret") == 0) { - result.put("code", "200"); - } + JSONObject result = JSONObject.parseObject(XML.toJSONObject(res).toString()).getJSONObject("error"); return result; - } catch (IOException e) { + } catch (Exception e) { logger.error("获取登录认证码异常", e); - result.put("code", "-1"); - result.put("msg", "获取登录认证码异常"); - return result; + return null; } } @@ -247,83 +232,67 @@ public class WebWeixinApi { String wxsid, String skey, String wxuin) { - //type=0 - String url = new ST(PropertiesUtil.getProperty("webwx-url.logout_url")) - .add("type", 0) - .add("skey", StringUtil.encodeURL(skey, "UTF-8")) - .render(); - - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_FORM_URLENCODED.toString()); - - List pairList = new ArrayList<>(); - pairList.add(new BasicNameValuePair("sid", wxsid)); - pairList.add(new BasicNameValuePair("uin", wxuin)); - try { - HttpEntity paramEntity = new UrlEncodedFormEntity(pairList); - httpPost.setEntity(paramEntity); + List pairList = new ArrayList<>(); + pairList.add(new BasicNameValuePair("sid", wxsid)); + pairList.add(new BasicNameValuePair("uin", wxuin)); - httpClient.execute(httpPost); - } catch (IOException e) { - logger.error("退出登录异常", e); - } + //分两步进行 + for (int i = 0; i <= 1; i++) { + String url = new ST(PropertiesUtil.getProperty("webwx-url.logout_url")) + .add("type", i) + .add("skey", StringUtil.encodeURL(skey, Consts.UTF_8.name())) + .render(); - //type=1 - String url1 = new ST(PropertiesUtil.getProperty("webwx-url.logout_url")) - .add("type", 1) - .add("skey", StringUtil.encodeURL(skey, "UTF-8")) - .render(); + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpPost.setHeader("Content-type", ContentType.APPLICATION_FORM_URLENCODED.toString()); - HttpPost httpPost1 = new HttpPost(url1); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_FORM_URLENCODED.toString()); + HttpEntity paramEntity = new UrlEncodedFormEntity(pairList); + httpPost.setEntity(paramEntity); - try { - HttpEntity paramEntity = new UrlEncodedFormEntity(pairList); - httpPost.setEntity(paramEntity); - - httpClient.execute(httpPost1); - } catch (IOException e) { + httpClient.execute(httpPost); + } + } catch (Exception e) { logger.error("退出登录异常", e); } } /** - * 数据初始化 + * 获取初始化数据 */ public JSONObject webWeixinInit(HttpClient httpClient, String passticket, String wxsid, String skey, String wxuin) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxinit_url")) - .add("pass_ticket", passticket) - .add("r", System.currentTimeMillis() / 1252L) - .render(); - - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); - - JSONObject paramJson = new JSONObject(); - paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); - HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); - httpPost.setEntity(paramEntity); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxinit_url")) + .add("pass_ticket", passticket) + .add("r", System.currentTimeMillis() / 1252L) + .render(); + + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + + JSONObject paramJson = new JSONObject(); + paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); + HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); + httpPost.setEntity(paramEntity); + HttpResponse response = httpClient.execute(httpPost); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); String res = EntityUtils.toString(entity, Consts.UTF_8); return JSONObject.parseObject(res); - } catch (IOException e) { - logger.error("数据初始化异常", e); + } catch (Exception e) { + logger.error("获取初始化数据异常", e); return null; } } @@ -339,34 +308,35 @@ public class WebWeixinApi { String skey, String wxuin, String loginUserName) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.statusnotify_url")) - .add("pass_ticket", passticket) - .render(); - - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); - - JSONObject paramJson = new JSONObject(); - paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); - paramJson.put("ClientMsgId", System.currentTimeMillis()); - paramJson.put("Code", 3); - paramJson.put("FromUserName", loginUserName); - paramJson.put("ToUserName", loginUserName); - HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); - httpPost.setEntity(paramEntity); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.statusnotify_url")) + .add("pass_ticket", passticket) + .render(); + + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + + JSONObject paramJson = new JSONObject(); + paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); + paramJson.put("ClientMsgId", System.currentTimeMillis()); + paramJson.put("Code", 3); + paramJson.put("FromUserName", loginUserName); + paramJson.put("ToUserName", loginUserName); + HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); + httpPost.setEntity(paramEntity); + HttpResponse response = httpClient.execute(httpPost); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); String res = EntityUtils.toString(entity, Consts.UTF_8); return JSONObject.parseObject(res); - } catch (IOException e) { + } catch (Exception e) { logger.error("开启消息状态通知异常", e); return null; } @@ -380,34 +350,35 @@ public class WebWeixinApi { String skey, String wxuin, JSONArray SyncKeyList) { - long millis = System.currentTimeMillis(); - String url = new ST(PropertiesUtil.getProperty("webwx-url.synccheck_url")) - .add("r", millis) - .add("skey", StringUtil.encodeURL(skey, "UTF-8")) - .add("sid", wxsid) - .add("uin", wxuin) - .add("deviceid", WechatUtil.createDeviceID()) - .add("synckey", StringUtil.encodeURL(WechatUtil.syncKeyListToString(SyncKeyList), "UTF-8")) - .add("_", millis) - .render(); - - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); - try { + long millis = System.currentTimeMillis(); + String url = new ST(PropertiesUtil.getProperty("webwx-url.synccheck_url")) + .add("r", millis) + .add("skey", StringUtil.encodeURL(skey, Consts.UTF_8.name())) + .add("sid", wxsid) + .add("uin", wxuin) + .add("deviceid", WechatUtil.createDeviceID()) + .add("synckey", StringUtil.encodeURL(WechatUtil.syncKeyListToString(SyncKeyList), Consts.UTF_8.name())) + .add("_", millis) + .render(); + + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); + HttpResponse response = httpClient.execute(httpGet); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); - String res = EntityUtils.toString(entity); + String res = EntityUtils.toString(entity, Consts.UTF_8); String regExp = "window.synccheck=\\{retcode:\"(\\d+)\",selector:\"(\\d+)\"}"; Matcher matcher = Pattern.compile(regExp).matcher(res); if (!matcher.find()) { - throw new RuntimeException("服务端状态同步失败"); + throw new RuntimeException("返回数据错误"); } JSONObject result = new JSONObject(); @@ -415,7 +386,7 @@ public class WebWeixinApi { result.put("selector", matcher.group(2)); return result; - } catch (IOException e) { + } catch (Exception e) { logger.error("服务端状态同步异常", e); return null; } @@ -427,27 +398,27 @@ public class WebWeixinApi { public JSONObject getContact(HttpClient httpClient, String passticket, String skey) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.getcontact_url")) - .add("pass_ticket", StringUtil.encodeURL(passticket, "UTF-8")) - .add("r", System.currentTimeMillis()) - .add("skey", StringUtil.encodeURL(skey, "UTF-8")) - .render(); - - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.getcontact_url")) + .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name())) + .add("r", System.currentTimeMillis()) + .add("skey", StringUtil.encodeURL(skey, Consts.UTF_8.name())) + .render(); + + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); HttpResponse response = httpClient.execute(httpGet); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); - String res = EntityUtils.toString(entity); + String res = EntityUtils.toString(entity, Consts.UTF_8); return JSONObject.parseObject(res); - } catch (IOException e) { + } catch (Exception e) { logger.error("获取全部联系人列表异常", e); return null; } @@ -462,33 +433,34 @@ public class WebWeixinApi { String skey, String wxuin, JSONArray batchContactList) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.batchgetcontact_url")) - .add("pass_ticket", StringUtil.encodeURL(passticket, "UTF-8")) - .add("r", System.currentTimeMillis()) - .render(); - - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); - - JSONObject paramJson = new JSONObject(); - paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); - paramJson.put("Count", batchContactList.size()); - paramJson.put("List", batchContactList); - HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); - httpPost.setEntity(paramEntity); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.batchgetcontact_url")) + .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name())) + .add("r", System.currentTimeMillis()) + .render(); + + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + + JSONObject paramJson = new JSONObject(); + paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); + paramJson.put("Count", batchContactList.size()); + paramJson.put("List", batchContactList); + HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); + httpPost.setEntity(paramEntity); + HttpResponse response = httpClient.execute(httpPost); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); - String res = EntityUtils.toString(entity); + String res = EntityUtils.toString(entity, Consts.UTF_8); return JSONObject.parseObject(res); - } catch (IOException e) { + } catch (Exception e) { logger.error("批量获取指定联系人信息异常", e); return null; } @@ -503,34 +475,35 @@ public class WebWeixinApi { String skey, String wxuin, JSONObject SyncKey) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsync_url")) - .add("skey", skey) - .add("sid", wxsid) - .add("pass_ticket", passticket) - .render(); - - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); - - JSONObject paramJson = new JSONObject(); - paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); - paramJson.put("SyncKey", SyncKey); - HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); - httpPost.setEntity(paramEntity); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsync_url")) + .add("skey", skey) + .add("sid", wxsid) + .add("pass_ticket", passticket) + .render(); + + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + + JSONObject paramJson = new JSONObject(); + paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); + paramJson.put("SyncKey", SyncKey); + HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); + httpPost.setEntity(paramEntity); + HttpResponse response = httpClient.execute(httpPost); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); String res = EntityUtils.toString(entity, Consts.UTF_8); return JSONObject.parseObject(res); - } catch (IOException e) { - logger.error("开启消息状态通知异常", e); + } catch (Exception e) { + logger.error("从服务端拉取新消息异常", e); return null; } } @@ -547,25 +520,26 @@ public class WebWeixinApi { int Type, String FromUserName, String ToUserName) { - String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsendmsg_url")) - .add("pass_ticket", passticket) - .render(); - - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); - httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); - - JSONObject paramJson = new JSONObject(); - paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); - paramJson.put("Msg", WechatUtil.createSendMsg(Content, Type, FromUserName, ToUserName)); - paramJson.put("Scene", 0); - HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); - httpPost.setEntity(paramEntity); - try { + String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsendmsg_url")) + .add("pass_ticket", passticket) + .render(); + + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("User-Agent", PropertiesUtil.getProperty("wechat4j.userAgent")); + httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + + JSONObject paramJson = new JSONObject(); + paramJson.put("BaseRequest", WechatUtil.createBaseRequest(wxsid, skey, wxuin)); + paramJson.put("Msg", WechatUtil.createSendMsg(Content, Type, FromUserName, ToUserName)); + paramJson.put("Scene", 0); + HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8); + httpPost.setEntity(paramEntity); + HttpResponse response = httpClient.execute(httpPost); - if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) { - throw new RuntimeException("请求错误"); + int statusCode = response.getStatusLine().getStatusCode(); + if (HttpStatus.SC_OK != statusCode) { + throw new RuntimeException("响应失败(" + statusCode + ")"); } HttpEntity entity = response.getEntity(); @@ -574,8 +548,8 @@ public class WebWeixinApi { JSONObject result = JSONObject.parseObject(res); return result; - } catch (IOException e) { - logger.error("开启消息状态通知异常", e); + } catch (Exception e) { + logger.error("发送消息异常", e); return null; } } diff --git a/src/main/java/com/hotlcc/wechat4j/enums/LoginTipEnum.java b/src/main/java/com/hotlcc/wechat4j/enums/LoginTipEnum.java new file mode 100644 index 0000000..76acd07 --- /dev/null +++ b/src/main/java/com/hotlcc/wechat4j/enums/LoginTipEnum.java @@ -0,0 +1,28 @@ +package com.hotlcc.wechat4j.enums; + +/** + * 等待确认登录的tip + * + * @author https://gitee.com/hotlcc + */ +public enum LoginTipEnum { + TIP_0(0, "扫码登录"), + TIP_1(1, "确认登录"); + + private int code; + private String desc; + + LoginTipEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + public int getCode() { + return code; + } + + @Override + public String toString() { + return code + ""; + } +} diff --git a/src/main/java/com/hotlcc/wechat4j/enums/OperatingSystemEnum.java b/src/main/java/com/hotlcc/wechat4j/enums/OperatingSystemEnum.java new file mode 100644 index 0000000..096dbad --- /dev/null +++ b/src/main/java/com/hotlcc/wechat4j/enums/OperatingSystemEnum.java @@ -0,0 +1,38 @@ +package com.hotlcc.wechat4j.enums; + +/** + * 操作系统enum + * + * @author https://gitee.com/hotlcc + */ +public enum OperatingSystemEnum { + DARWIN("darwin"), + WINDOWS("windows"), + LINUX("linux"), + MAC_OS("mac"), + OTHER("other"); + + private String value; + + public String getValue() { + return value; + } + + OperatingSystemEnum(String value) { + this.value = value; + } + + public static OperatingSystemEnum currentOperatingSystem() { + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.indexOf(OperatingSystemEnum.DARWIN.getValue()) >= 0) { + return OperatingSystemEnum.DARWIN; + } else if (osName.indexOf(OperatingSystemEnum.WINDOWS.getValue()) >= 0) { + return OperatingSystemEnum.WINDOWS; + } else if (osName.indexOf(OperatingSystemEnum.LINUX.getValue()) >= 0) { + return OperatingSystemEnum.LINUX; + } else if (osName.indexOf(OperatingSystemEnum.MAC_OS.getValue()) >= 0) { + return OperatingSystemEnum.MAC_OS; + } + return OperatingSystemEnum.OTHER; + } +} diff --git a/src/main/java/com/hotlcc/wechat4j/util/QRCodeUtil.java b/src/main/java/com/hotlcc/wechat4j/util/QRCodeUtil.java index c9ac534..445677b 100644 --- a/src/main/java/com/hotlcc/wechat4j/util/QRCodeUtil.java +++ b/src/main/java/com/hotlcc/wechat4j/util/QRCodeUtil.java @@ -6,10 +6,15 @@ import com.google.zxing.NotFoundException; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; +import com.hotlcc.wechat4j.enums.OperatingSystemEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -21,6 +26,8 @@ import java.util.Set; * @author Allen */ public final class QRCodeUtil { + private static Logger logger = LoggerFactory.getLogger(QRCodeUtil.class); + private QRCodeUtil() { } @@ -227,4 +234,65 @@ public final class QRCodeUtil { public static String toCharMatrix(byte[] data) { return toCharMatrix(data, " ", "██"); } + + /** + * 将二维码图片数据写入到临时文件 + * + * @param data + * @return + */ + public static File writeToTempFile(byte[] data) { + FileOutputStream fos = null; + try { + File tmp = File.createTempFile(PropertiesUtil.getProperty("wechat4j.qrcode.tmpfile.prefix"), ".jpg"); + fos = new FileOutputStream(tmp); + fos.write(data); + fos.flush(); + return tmp; + } catch (IOException e) { + logger.error("二维码写入临时文件异常", e); + throw new RuntimeException(e); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 打开二维码图片 + * + * @param data + */ + public static void openQRCodeImage(byte[] data) { + OperatingSystemEnum os = OperatingSystemEnum.currentOperatingSystem(); + Runtime runtime = null; + File tmp = null; + switch (os) { + case WINDOWS: + runtime = Runtime.getRuntime(); + tmp = writeToTempFile(data); + try { + runtime.exec("cmd /c start " + tmp.getPath()); + } catch (Exception e) { + e.printStackTrace(); + } + break; + case MAC_OS: + runtime = Runtime.getRuntime(); + tmp = writeToTempFile(data); + try { + runtime.exec("open " + tmp.getPath()); + } catch (Exception e) { + e.printStackTrace(); + } + break; + default: + break; + } + } } \ No newline at end of file diff --git a/src/main/resources/config/app.properties b/src/main/resources/config/app.properties index d8eca08..d4556d1 100644 --- a/src/main/resources/config/app.properties +++ b/src/main/resources/config/app.properties @@ -1,4 +1,6 @@ # web微信对应的appid webwx.appid=wx782c26e4c19acffb # httpclient的UserAgent -wechat4j.userAgent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 \ No newline at end of file +wechat4j.userAgent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 +# QRCode图片临时文件前缀 +wechat4j.qrcode.tmpfile.prefix=wechat4j_tmp_ \ No newline at end of file diff --git a/src/main/resources/config/webwx-url.properties b/src/main/resources/config/webwx-url.properties index f81c1de..c46816b 100644 --- a/src/main/resources/config/webwx-url.properties +++ b/src/main/resources/config/webwx-url.properties @@ -4,7 +4,7 @@ webwx-url.uuid_url=https://login.wx2.qq.com/jslogin?appid=&redirect_uri=h ## 1.2、获取二维码 webwx-url.qrcode_url=https://login.weixin.qq.com/qrcode/ ## 1.3、等待扫码登录并获取跳转url -webwx-url.redirect_uri=https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=&tip=0&r=&_=<_> +webwx-url.redirect_uri=https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=&tip=&r=&_=<_> ## 1.4、获取登录认证码 webwx-url.newlogin_url=&fun=new&version=v2 ## 1.5、退出登录