diff --git a/pom.xml b/pom.xml index 6f90c99..8d86c03 100644 --- a/pom.xml +++ b/pom.xml @@ -1,145 +1,150 @@ - 4.0.0 - - cn.zhouyafeng - itchat4j - 1.1.0 - jar - - - itchat4j - http://maven.apache.org - - - UTF-8 - - - - - - - org.apache.httpcomponents - httpclient - 4.5.3 - - - - org.apache.httpcomponents - httpmime - 4.5 - - - - - com.alibaba - fastjson - 1.2.31 - - - - - org.apache.commons - commons-lang3 - 3.0 - - - - - com.vdurmont - emoji-java - 3.2.0 - - - - - javax.activation - activation - 1.1.1 - - - - - junit - junit - 4.12 - - - - - org.slf4j - slf4j-api - 1.6.6 - - - org.slf4j - slf4j-log4j12 - 1.6.6 - - - log4j - log4j - 1.2.16 - - - - - com.squareup.okhttp3 - okhttp - 3.8.0 - - - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.0 - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - attach-sources - package - - jar-no-fork - - - - - - - maven-assembly-plugin - - - package - - single - - - - - - jar-with-dependencies - src - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + cn.zhouyafeng + itchat4j + 1.1.0 + jar + + + itchat4j + http://maven.apache.org + + + UTF-8 + + + + + + + org.apache.httpcomponents + httpclient + 4.5.3 + + + + org.apache.httpcomponents + httpmime + 4.5 + + + + + com.alibaba + fastjson + 1.2.31 + + + + + org.apache.commons + commons-lang3 + 3.0 + + + + + com.vdurmont + emoji-java + 3.2.0 + + + + + javax.activation + activation + 1.1.1 + + + + + junit + junit + 4.12 + + + + + org.slf4j + slf4j-api + 1.6.6 + + + org.slf4j + slf4j-log4j12 + 1.6.6 + + + log4j + log4j + 1.2.16 + + + + + com.squareup.okhttp3 + okhttp + 3.8.0 + + + + + javax.mail + mail + 1.4.7 + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + package + + jar-no-fork + + + + + + + maven-assembly-plugin + + + package + + single + + + + + + jar-with-dependencies + src + + + + + + diff --git a/src/main/java/cn/zhouyafeng/itchat4j/Wechat.java b/src/main/java/cn/zhouyafeng/itchat4j/Wechat.java index baf4ee2..d5fd475 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/Wechat.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/Wechat.java @@ -1,5 +1,7 @@ package cn.zhouyafeng.itchat4j; +import cn.zhouyafeng.itchat4j.core.Core; +import cn.zhouyafeng.itchat4j.utils.EmailUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -7,27 +9,38 @@ import cn.zhouyafeng.itchat4j.core.MsgCenter; import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; +import java.util.List; + public class Wechat { - private static final Logger LOG = LoggerFactory.getLogger(Wechat.class); - private IMsgHandlerFace msgHandler; - - public Wechat(IMsgHandlerFace msgHandler, String qrPath) { - System.setProperty("jsse.enableSNIExtension", "false"); // 防止SSL错误 - this.msgHandler = msgHandler; - - // 登陆 - LoginController login = new LoginController(); - login.login(qrPath); - } - - public void start() { - LOG.info("+++++++++++++++++++开始消息处理+++++++++++++++++++++"); - new Thread(new Runnable() { - @Override - public void run() { - MsgCenter.handleMsg(msgHandler); - } - }).start(); - } + private static final Logger LOG = LoggerFactory.getLogger(Wechat.class); + private IMsgHandlerFace msgHandler; + + private Core core = Core.getInstance(); + + public Wechat(IMsgHandlerFace msgHandler, String qrPath) { + System.setProperty("jsse.enableSNIExtension", "false"); // 防止SSL错误 + this.msgHandler = msgHandler; + core.setQrPath(qrPath); + + // 登陆 + LoginController login = new LoginController(); + login.login(qrPath); + } + + public void start() { + LOG.info("+++++++++++++++++++开始消息处理+++++++++++++++++++++"); + new Thread(() -> MsgCenter.handleMsg(msgHandler)).start(); + } + /** + * 设置离线发送邮件提醒 + * + * @param config + * @param emails + */ + public void setLoginOutSendEmail(EmailUtils.EmailConfig config, EmailUtils.Email... emails) { + EmailUtils emailUtils = new EmailUtils(); + emailUtils.init(config); + core.setLoginOutSendEmail(emailUtils, emails); + } } diff --git a/src/main/java/cn/zhouyafeng/itchat4j/api/WechatTools.java b/src/main/java/cn/zhouyafeng/itchat4j/api/WechatTools.java index d5b91a2..9a342c3 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/api/WechatTools.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/api/WechatTools.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.Map; +import cn.zhouyafeng.itchat4j.controller.LoginController; +import cn.zhouyafeng.itchat4j.service.ILoginService; +import cn.zhouyafeng.itchat4j.service.impl.LoginServiceImpl; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.message.BasicNameValuePair; @@ -22,194 +25,212 @@ /** * 微信小工具,如获好友列表等 - * + * * @author https://github.com/yaphone - * @date 创建时间:2017年5月4日 下午10:49:16 * @version 1.0 - * + * @date 创建时间:2017年5月4日 下午10:49:16 */ public class WechatTools { - private static Logger LOG = LoggerFactory.getLogger(WechatTools.class); - - private static Core core = Core.getInstance(); - - /** - * 根据用户名发送文本消息 - * - * @author https://github.com/yaphone - * @date 2017年5月4日 下午10:43:14 - * @param msg - * @param toUserName - */ - public static void sendMsgByUserName(String msg, String toUserName) { - MessageTools.sendMsgById(msg, toUserName); - } - - /** - *

- * 通过RealName获取本次UserName - *

- *

- * 如NickName为"yaphone",则获取UserName= - * "@1212d3356aea8285e5bbe7b91229936bc183780a8ffa469f2d638bf0d2e4fc63", - * 可通过UserName发送消息 - *

- * - * @author https://github.com/yaphone - * @date 2017年5月4日 下午10:56:31 - * @param name - * @return - */ - public static String getUserNameByNickName(String nickName) { - for (JSONObject o : core.getContactList()) { - if (o.getString("NickName").equals(nickName)) { - return o.getString("UserName"); - } - } - return null; - } - - /** - * 返回好友昵称列表 - * - * @author https://github.com/yaphone - * @date 2017年5月4日 下午11:37:20 - * @return - */ - public static List getContactNickNameList() { - List contactNickNameList = new ArrayList(); - for (JSONObject o : core.getContactList()) { - contactNickNameList.add(o.getString("NickName")); - } - return contactNickNameList; - } - - /** - * 返回好友完整信息列表 - * - * @date 2017年6月26日 下午9:45:39 - * @return - */ - public static List getContactList() { - return core.getContactList(); - } - - /** - * 返回群列表 - * - * @author https://github.com/yaphone - * @date 2017年5月5日 下午9:55:21 - * @return - */ - public static List getGroupList() { - return core.getGroupList(); - } - - /** - * 获取群ID列表 - * - * @date 2017年6月21日 下午11:42:56 - * @return - */ - public static List getGroupIdList() { - return core.getGroupIdList(); - } - - /** - * 获取群NickName列表 - * - * @date 2017年6月21日 下午11:43:38 - * @return - */ - public static List getGroupNickNameList() { - return core.getGroupNickNameList(); - } - - /** - * 根据groupIdList返回群成员列表 - * - * @date 2017年6月13日 下午11:12:31 - * @param groupId - * @return - */ - public static JSONArray getMemberListByGroupId(String groupId) { - return core.getGroupMemeberMap().get(groupId); - } - - /** - * 退出微信 - * - * @author https://github.com/yaphone - * @date 2017年5月18日 下午11:56:54 - */ - public static void logout() { - webWxLogout(); - } - - private static boolean webWxLogout() { - String url = String.format(URLEnum.WEB_WX_LOGOUT.getUrl(), - core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey())); - List params = new ArrayList(); - params.add(new BasicNameValuePair("redirect", "1")); - params.add(new BasicNameValuePair("type", "1")); - params.add( - new BasicNameValuePair("skey", (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()))); - try { - HttpEntity entity = core.getMyHttpClient().doGet(url, params, false, null); - String text = EntityUtils.toString(entity, Consts.UTF_8); // 无消息 - return true; - } catch (Exception e) { - LOG.debug(e.getMessage()); - } - return false; - } - - public static void setUserInfo() { - for (JSONObject o : core.getContactList()) { - core.getUserInfoMap().put(o.getString("NickName"), o); - core.getUserInfoMap().put(o.getString("UserName"), o); - } - } - - /** - * - * 根据用户昵称设置备注名称 - * - * @date 2017年5月27日 上午12:21:40 - * @param userName - * @param remName - */ - public static void remarkNameByNickName(String nickName, String remName) { - String url = String.format(URLEnum.WEB_WX_REMARKNAME.getUrl(), core.getLoginInfo().get("url"), - core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); - Map msgMap = new HashMap(); - Map msgMap_BaseRequest = new HashMap(); - msgMap.put("CmdId", 2); - msgMap.put("RemarkName", remName); - msgMap.put("UserName", core.getUserInfoMap().get(nickName).get("UserName")); - msgMap_BaseRequest.put("Uin", core.getLoginInfo().get(StorageLoginInfoEnum.wxuin.getKey())); - msgMap_BaseRequest.put("Sid", core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey())); - msgMap_BaseRequest.put("Skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey())); - msgMap_BaseRequest.put("DeviceID", core.getLoginInfo().get(StorageLoginInfoEnum.deviceid.getKey())); - msgMap.put("BaseRequest", msgMap_BaseRequest); - try { - String paramStr = JSON.toJSONString(msgMap); - HttpEntity entity = core.getMyHttpClient().doPost(url, paramStr); - // String result = EntityUtils.toString(entity, Consts.UTF_8); - LOG.info("修改备注" + remName); - } catch (Exception e) { - LOG.error("remarkNameByUserName", e); - } - } - - /** - * 获取微信在线状态 - * - * @date 2017年6月16日 上午12:47:46 - * @return - */ - public static boolean getWechatStatus() { - return core.isAlive(); - } - + private static Logger LOG = LoggerFactory.getLogger(WechatTools.class); + + private static Core core = Core.getInstance(); + + private static ILoginService loginService = LoginServiceImpl.getInstance(); + + /** + * 根据用户名发送文本消息 + * + * @param msg + * @param toUserName + * @author https://github.com/yaphone + * @date 2017年5月4日 下午10:43:14 + */ + public static void sendMsgByUserName(String msg, String toUserName) { + MessageTools.sendMsgById(msg, toUserName); + } + + /** + *

+ * 通过RealName获取本次UserName + *

+ *

+ * 如NickName为"yaphone",则获取UserName= + * "@1212d3356aea8285e5bbe7b91229936bc183780a8ffa469f2d638bf0d2e4fc63", + * 可通过UserName发送消息 + *

+ * + * @param + * @return + * @author https://github.com/yaphone + * @date 2017年5月4日 下午10:56:31 + */ + public static String getUserNameByNickName(String nickName) { + for (JSONObject o : core.getContactList()) { + if (o.getString("NickName").equals(nickName)) { + return o.getString("UserName"); + } + } + return null; + } + + /** + * 返回好友昵称列表 + * + * @return + * @author https://github.com/yaphone + * @date 2017年5月4日 下午11:37:20 + */ + public static List getContactNickNameList() { + List contactNickNameList = new ArrayList(); + for (JSONObject o : core.getContactList()) { + contactNickNameList.add(o.getString("NickName")); + } + return contactNickNameList; + } + + /** + * 返回好友完整信息列表 + * + * @return + * @date 2017年6月26日 下午9:45:39 + */ + public static List getContactList() { + return core.getContactList(); + } + + /** + * 返回群列表 + * + * @return + * @author https://github.com/yaphone + * @date 2017年5月5日 下午9:55:21 + */ + public static List getGroupList() { + return core.getGroupList(); + } + + /** + * 获取群ID列表 + * + * @return + * @date 2017年6月21日 下午11:42:56 + */ + public static List getGroupIdList() { + return core.getGroupIdList(); + } + + /** + * 获取群NickName列表 + * + * @return + * @date 2017年6月21日 下午11:43:38 + */ + public static List getGroupNickNameList() { + return core.getGroupNickNameList(); + } + + /** + * 根据groupIdList返回群成员列表 + * + * @param groupId + * @return + * @date 2017年6月13日 下午11:12:31 + */ + public static JSONArray getMemberListByGroupId(String groupId) { + return core.getGroupMemeberMap().get(groupId); + } + + /** + * 退出微信 + * + * @author https://github.com/yaphone + * @date 2017年5月18日 下午11:56:54 + */ + public static void logout() { + webWxLogout(); + } + + private static boolean webWxLogout() { + String url = String.format(URLEnum.WEB_WX_LOGOUT.getUrl(), + core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey())); + List params = new ArrayList(); + params.add(new BasicNameValuePair("redirect", "1")); + params.add(new BasicNameValuePair("type", "1")); + params.add( + new BasicNameValuePair("skey", (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()))); + try { + HttpEntity entity = core.getMyHttpClient().doGet(url, params, false, null); + String text = EntityUtils.toString(entity, Consts.UTF_8); // 无消息 + return true; + } catch (Exception e) { + LOG.debug(e.getMessage()); + } + return false; + } + + public static void setUserInfo() { + for (JSONObject o : core.getContactList()) { + core.getUserInfoMap().put(o.getString("NickName"), o); + core.getUserInfoMap().put(o.getString("UserName"), o); + } + } + + /** + * 根据用户昵称设置备注名称 + * + * @param + * @param remName + * @date 2017年5月27日 上午12:21:40 + */ + public static void remarkNameByNickName(String nickName, String remName) { + String url = String.format(URLEnum.WEB_WX_REMARKNAME.getUrl(), core.getLoginInfo().get("url"), + core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); + Map msgMap = new HashMap(); + Map msgMap_BaseRequest = new HashMap(); + msgMap.put("CmdId", 2); + msgMap.put("RemarkName", remName); + msgMap.put("UserName", core.getUserInfoMap().get(nickName).get("UserName")); + msgMap_BaseRequest.put("Uin", core.getLoginInfo().get(StorageLoginInfoEnum.wxuin.getKey())); + msgMap_BaseRequest.put("Sid", core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey())); + msgMap_BaseRequest.put("Skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey())); + msgMap_BaseRequest.put("DeviceID", core.getLoginInfo().get(StorageLoginInfoEnum.deviceid.getKey())); + msgMap.put("BaseRequest", msgMap_BaseRequest); + try { + String paramStr = JSON.toJSONString(msgMap); + HttpEntity entity = core.getMyHttpClient().doPost(url, paramStr); + // String result = EntityUtils.toString(entity, Consts.UTF_8); + LOG.info("修改备注" + remName); + } catch (Exception e) { + LOG.error("remarkNameByUserName", e); + } + } + + /** + * 获取微信在线状态 + * + * @return + * @date 2017年6月16日 上午12:47:46 + */ + public static boolean getWechatStatus() { + return core.isAlive(); + } + + /** + * 重新获取二维码登录 + * + * @return + */ + public static boolean reLogin() { + if (core.isAlive() || core.isLogin()) { + return false; + } else { + LOG.info("+++++++++++++++++++开始重新启动+++++++++++++++++++++"); + new Thread(() -> { + loginService.reLogin(); + LoginController login = new LoginController(); + login.login(core.getQrPath()); + }).start(); + return true; + } + } } diff --git a/src/main/java/cn/zhouyafeng/itchat4j/controller/LoginController.java b/src/main/java/cn/zhouyafeng/itchat4j/controller/LoginController.java index 11545dd..4d7e8d7 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/controller/LoginController.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/controller/LoginController.java @@ -11,78 +11,88 @@ import cn.zhouyafeng.itchat4j.utils.SleepUtils; import cn.zhouyafeng.itchat4j.utils.tools.CommonTools; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + /** * 登陆控制器 - * + * * @author https://github.com/yaphone - * @date 创建时间:2017年5月13日 下午12:56:07 * @version 1.0 - * + * @date 创建时间:2017年5月13日 下午12:56:07 */ public class LoginController { - private static Logger LOG = LoggerFactory.getLogger(LoginController.class); - private ILoginService loginService = new LoginServiceImpl(); - private static Core core = Core.getInstance(); + private static Logger LOG = LoggerFactory.getLogger(LoginController.class); + private ILoginService loginService = LoginServiceImpl.getInstance(); + private static Core core = Core.getInstance(); + + private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 5, + TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy()); - public void login(String qrPath) { - if (core.isAlive()) { // 已登陆 - LOG.info("itchat4j已登陆"); - return; - } - while (true) { - for (int count = 0; count < 10; count++) { - LOG.info("获取UUID"); - while (loginService.getUuid() == null) { - LOG.info("1. 获取微信UUID"); - while (loginService.getUuid() == null) { - LOG.warn("1.1. 获取微信UUID失败,两秒后重新获取"); - SleepUtils.sleep(2000); - } - } - LOG.info("2. 获取登陆二维码图片"); - if (loginService.getQR(qrPath)) { - break; - } else if (count == 10) { - LOG.error("2.2. 获取登陆二维码图片失败,系统退出"); - System.exit(0); - } - } - LOG.info("3. 请扫描二维码图片,并在手机上确认"); - if (!core.isAlive()) { - loginService.login(); - core.setAlive(true); - LOG.info(("登陆成功")); - break; - } - LOG.info("4. 登陆超时,请重新扫描二维码图片"); - } + public void login(String qrPath) { + if (core.isAlive()) { // 已登陆 + LOG.info("itchat4j已登陆"); + return; + } + while (true) { + for (int count = 0; count < 10; count++) { + LOG.info("获取UUID"); + while (loginService.getUuid() == null) { + LOG.info("1. 获取微信UUID"); + while (loginService.getUuid() == null) { + LOG.warn("1.1. 获取微信UUID失败,两秒后重新获取"); + SleepUtils.sleep(2000); + } + } + LOG.info("2. 获取登陆二维码图片"); + if (loginService.getQR(qrPath)) { + break; + } else if (count == 10) { + LOG.error("2.2. 获取登陆二维码图片失败,系统退出"); + System.exit(0); + } + } + LOG.info("3. 请扫描二维码图片,并在手机上确认"); + if (!core.isLogin()) { + loginService.login(); + if (core.isLogin()) { + core.setAlive(true); + LOG.info(("登陆成功")); + break; + } + LOG.info(("登陆失败,请重新获取二维码")); + return; + } + } - LOG.info("5. 登陆成功,微信初始化"); - if (!loginService.webWxInit()) { - LOG.info("6. 微信初始化异常"); - System.exit(0); - } + LOG.info("5. 登陆成功,微信初始化"); + if (!loginService.webWxInit()) { + LOG.info("6. 微信初始化异常"); + System.exit(0); + } - LOG.info("6. 开启微信状态通知"); - loginService.wxStatusNotify(); + LOG.info("6. 开启微信状态通知"); + loginService.wxStatusNotify(); - LOG.info("7. 清除。。。。"); - CommonTools.clearScreen(); - LOG.info(String.format("欢迎回来, %s", core.getNickName())); + LOG.info("7. 清除。。。。"); + CommonTools.clearScreen(); + LOG.info(String.format("欢迎回来, %s", core.getNickName())); - LOG.info("8. 开始接收消息"); - loginService.startReceiving(); + LOG.info("8. 开始接收消息"); + loginService.startReceiving(); - LOG.info("9. 获取联系人信息"); - loginService.webWxGetContact(); + LOG.info("9. 获取联系人信息"); + loginService.webWxGetContact(); - LOG.info("10. 获取群好友及群好友列表"); - loginService.WebWxBatchGetContact(); + LOG.info("10. 获取群好友及群好友列表"); + loginService.WebWxBatchGetContact(); - LOG.info("11. 缓存本次登陆好友相关消息"); - WechatTools.setUserInfo(); // 登陆成功后缓存本次登陆好友相关消息(NickName, UserName) + LOG.info("11. 缓存本次登陆好友相关消息"); + WechatTools.setUserInfo(); // 登陆成功后缓存本次登陆好友相关消息(NickName, UserName) - LOG.info("12.开启微信状态检测线程"); - new Thread(new CheckLoginStatusThread()).start(); - } + LOG.info("12.开启微信状态检测线程"); +// new Thread(new CheckLoginStatusThread()).start(); + executor.execute(new CheckLoginStatusThread()); + } } \ No newline at end of file diff --git a/src/main/java/cn/zhouyafeng/itchat4j/core/Core.java b/src/main/java/cn/zhouyafeng/itchat4j/core/Core.java index 2e70785..1b72c14 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/core/Core.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/core/Core.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; +import cn.zhouyafeng.itchat4j.utils.EmailUtils; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -14,263 +15,306 @@ /** * 核心存储类,全局只保存一份,单例模式 - * + * * @author https://github.com/yaphone - * @date 创建时间:2017年4月23日 下午2:33:56 * @version 1.0 - * + * @date 创建时间:2017年4月23日 下午2:33:56 */ public class Core { - private static Core instance; - - private Core() { - - } - - public static Core getInstance() { - if (instance == null) { - synchronized (Core.class) { - instance = new Core(); - } - } - return instance; - } - - boolean alive = false; - private int memberCount = 0; - - private String indexUrl; - - private String userName; - private String nickName; - private List msgList = new ArrayList(); - - private JSONObject userSelf; // 登陆账号自身信息 - private List memberList = new ArrayList(); // 好友+群聊+公众号+特殊账号 - private List contactList = new ArrayList();// 好友 - private List groupList = new ArrayList();; // 群 - private Map groupMemeberMap = new HashMap(); // 群聊成员字典 - private List publicUsersList = new ArrayList();;// 公众号/服务号 - private List specialUsersList = new ArrayList();;// 特殊账号 - private List groupIdList = new ArrayList(); // 群ID列表 - private List groupNickNameList = new ArrayList(); // 群NickName列表 - - private Map userInfoMap = new HashMap(); - - Map loginInfo = new HashMap(); - // CloseableHttpClient httpClient = HttpClients.createDefault(); - MyHttpClient myHttpClient = MyHttpClient.getInstance(); - String uuid = null; - - boolean useHotReload = false; - String hotReloadDir = "itchat.pkl"; - int receivingRetryCount = 5; - - private long lastNormalRetcodeTime; // 最后一次收到正常retcode的时间,秒为单位 - - /** - * 请求参数 - */ - public Map getParamMap() { - return new HashMap(1) { - /** - * - */ - private static final long serialVersionUID = 1L; - - { - Map map = new HashMap(); - for (BaseParaEnum baseRequest : BaseParaEnum.values()) { - map.put(baseRequest.para(), getLoginInfo().get(baseRequest.value()).toString()); - } - put("BaseRequest", map); - } - }; - } - - public boolean isAlive() { - return alive; - } - - public void setAlive(boolean alive) { - this.alive = alive; - } - - public List getMemberList() { - return memberList; - } - - public void setMemberList(List memberList) { - this.memberList = memberList; - } - - public Map getLoginInfo() { - return loginInfo; - } - - public void setLoginInfo(Map loginInfo) { - this.loginInfo = loginInfo; - } - - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - - public int getMemberCount() { - return memberCount; - } - - public void setMemberCount(int memberCount) { - this.memberCount = memberCount; - } - - public boolean isUseHotReload() { - return useHotReload; - } - - public void setUseHotReload(boolean useHotReload) { - this.useHotReload = useHotReload; - } - - public String getHotReloadDir() { - return hotReloadDir; - } - - public void setHotReloadDir(String hotReloadDir) { - this.hotReloadDir = hotReloadDir; - } - - public int getReceivingRetryCount() { - return receivingRetryCount; - } - - public void setReceivingRetryCount(int receivingRetryCount) { - this.receivingRetryCount = receivingRetryCount; - } - - public MyHttpClient getMyHttpClient() { - return myHttpClient; - } - - public List getMsgList() { - return msgList; - } - - public void setMsgList(List msgList) { - this.msgList = msgList; - } - - public void setMyHttpClient(MyHttpClient myHttpClient) { - this.myHttpClient = myHttpClient; - } - - public List getGroupIdList() { - return groupIdList; - } - - public void setGroupIdList(List groupIdList) { - this.groupIdList = groupIdList; - } - - public List getContactList() { - return contactList; - } - - public void setContactList(List contactList) { - this.contactList = contactList; - } + private static volatile Core instance; + + private Core() { + + } + + public static Core getInstance() { + if (instance == null) { + synchronized (Core.class) { + instance = new Core(); + } + } + return instance; + } + + private volatile String qrPath; + + private volatile boolean login = false; + private volatile boolean alive = false; + private int memberCount = 0; + + private String indexUrl; + + private String userName; + private String nickName; + private List msgList = new ArrayList(); + + private JSONObject userSelf; // 登陆账号自身信息 + private List memberList = new ArrayList(); // 好友+群聊+公众号+特殊账号 + private List contactList = new ArrayList();// 好友 + private List groupList = new ArrayList(); + ; // 群 + private Map groupMemeberMap = new HashMap(); // 群聊成员字典 + private List publicUsersList = new ArrayList(); + ;// 公众号/服务号 + private List specialUsersList = new ArrayList(); + ;// 特殊账号 + private List groupIdList = new ArrayList(); // 群ID列表 + private List groupNickNameList = new ArrayList(); // 群NickName列表 + + private Map userInfoMap = new HashMap(); + + Map loginInfo = new HashMap(); + // CloseableHttpClient httpClient = HttpClients.createDefault(); + MyHttpClient myHttpClient = MyHttpClient.getInstance(); + String uuid = null; + + boolean useHotReload = false; + String hotReloadDir = "itchat.pkl"; + int receivingRetryCount = 5; + + private long lastNormalRetcodeTime; // 最后一次收到正常retcode的时间,秒为单位 + + /** + * 请求参数 + */ + public Map getParamMap() { + return new HashMap(1) { + /** + * + */ + private static final long serialVersionUID = 1L; + + { + Map map = new HashMap(); + for (BaseParaEnum baseRequest : BaseParaEnum.values()) { + map.put(baseRequest.para(), getLoginInfo().get(baseRequest.value()).toString()); + } + put("BaseRequest", map); + } + }; + } + + public String getQrPath() { + return qrPath; + } + + public synchronized void setQrPath(String qrPath) { + this.qrPath = qrPath; + } + + public boolean isAlive() { + return alive; + } + + public synchronized void setAlive(boolean alive) { + this.alive = alive; + } + + public boolean isLogin() { + return login; + } + + public synchronized void setLogin(boolean login) { + this.login = login; + } + + public List getMemberList() { + return memberList; + } + + public void setMemberList(List memberList) { + this.memberList = memberList; + } + + public Map getLoginInfo() { + return loginInfo; + } + + public void setLoginInfo(Map loginInfo) { + this.loginInfo = loginInfo; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public int getMemberCount() { + return memberCount; + } + + public void setMemberCount(int memberCount) { + this.memberCount = memberCount; + } + + public boolean isUseHotReload() { + return useHotReload; + } + + public void setUseHotReload(boolean useHotReload) { + this.useHotReload = useHotReload; + } + + public String getHotReloadDir() { + return hotReloadDir; + } + + public void setHotReloadDir(String hotReloadDir) { + this.hotReloadDir = hotReloadDir; + } + + public int getReceivingRetryCount() { + return receivingRetryCount; + } + + public void setReceivingRetryCount(int receivingRetryCount) { + this.receivingRetryCount = receivingRetryCount; + } + + public MyHttpClient getMyHttpClient() { + return myHttpClient; + } + + public List getMsgList() { + return msgList; + } + + public void setMsgList(List msgList) { + this.msgList = msgList; + } + + public void setMyHttpClient(MyHttpClient myHttpClient) { + this.myHttpClient = myHttpClient; + } + + public List getGroupIdList() { + return groupIdList; + } + + public void setGroupIdList(List groupIdList) { + this.groupIdList = groupIdList; + } + + public List getContactList() { + return contactList; + } + + public void setContactList(List contactList) { + this.contactList = contactList; + } + + public List getGroupList() { + return groupList; + } - public List getGroupList() { - return groupList; - } + public void setGroupList(List groupList) { + this.groupList = groupList; + } - public void setGroupList(List groupList) { - this.groupList = groupList; - } + public List getPublicUsersList() { + return publicUsersList; + } - public List getPublicUsersList() { - return publicUsersList; - } + public void setPublicUsersList(List publicUsersList) { + this.publicUsersList = publicUsersList; + } - public void setPublicUsersList(List publicUsersList) { - this.publicUsersList = publicUsersList; - } + public List getSpecialUsersList() { + return specialUsersList; + } - public List getSpecialUsersList() { - return specialUsersList; - } + public void setSpecialUsersList(List specialUsersList) { + this.specialUsersList = specialUsersList; + } - public void setSpecialUsersList(List specialUsersList) { - this.specialUsersList = specialUsersList; - } + public String getUserName() { + return userName; + } - public String getUserName() { - return userName; - } + public void setUserName(String userName) { + this.userName = userName; + } - public void setUserName(String userName) { - this.userName = userName; - } + public String getNickName() { + return nickName; + } - public String getNickName() { - return nickName; - } + public void setNickName(String nickName) { + this.nickName = nickName; + } - public void setNickName(String nickName) { - this.nickName = nickName; - } + public JSONObject getUserSelf() { + return userSelf; + } - public JSONObject getUserSelf() { - return userSelf; - } + public void setUserSelf(JSONObject userSelf) { + this.userSelf = userSelf; + } - public void setUserSelf(JSONObject userSelf) { - this.userSelf = userSelf; - } + public Map getUserInfoMap() { + return userInfoMap; + } - public Map getUserInfoMap() { - return userInfoMap; - } + public void setUserInfoMap(Map userInfoMap) { + this.userInfoMap = userInfoMap; + } - public void setUserInfoMap(Map userInfoMap) { - this.userInfoMap = userInfoMap; - } + public synchronized long getLastNormalRetcodeTime() { + return lastNormalRetcodeTime; + } - public synchronized long getLastNormalRetcodeTime() { - return lastNormalRetcodeTime; - } + public synchronized void setLastNormalRetcodeTime(long lastNormalRetcodeTime) { + this.lastNormalRetcodeTime = lastNormalRetcodeTime; + } - public synchronized void setLastNormalRetcodeTime(long lastNormalRetcodeTime) { - this.lastNormalRetcodeTime = lastNormalRetcodeTime; - } + public List getGroupNickNameList() { + return groupNickNameList; + } - public List getGroupNickNameList() { - return groupNickNameList; - } + public void setGroupNickNameList(List groupNickNameList) { + this.groupNickNameList = groupNickNameList; + } - public void setGroupNickNameList(List groupNickNameList) { - this.groupNickNameList = groupNickNameList; - } + public Map getGroupMemeberMap() { + return groupMemeberMap; + } - public Map getGroupMemeberMap() { - return groupMemeberMap; - } + public void setGroupMemeberMap(Map groupMemeberMap) { + this.groupMemeberMap = groupMemeberMap; + } - public void setGroupMemeberMap(Map groupMemeberMap) { - this.groupMemeberMap = groupMemeberMap; - } + public String getIndexUrl() { + return indexUrl; + } - public String getIndexUrl() { - return indexUrl; - } + public void setIndexUrl(String indexUrl) { + this.indexUrl = indexUrl; + } - public void setIndexUrl(String indexUrl) { - this.indexUrl = indexUrl; - } + private EmailUtils emailUtils; + + private EmailUtils.Email[] emails; + /** + * 设置登出发送提醒邮件 + * + * @param emailUtils + * @param emails + */ + public synchronized void setLoginOutSendEmail(EmailUtils emailUtils, EmailUtils.Email[] emails) { + this.emailUtils = emailUtils; + this.emails = emails; + } + + public EmailUtils getEmailUtils() { + return emailUtils; + } + + public EmailUtils.Email[] getEmails() { + return emails; + } } diff --git a/src/main/java/cn/zhouyafeng/itchat4j/core/MsgCenter.java b/src/main/java/cn/zhouyafeng/itchat4j/core/MsgCenter.java index 6482406..e86e668 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/core/MsgCenter.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/core/MsgCenter.java @@ -3,6 +3,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; +import cn.zhouyafeng.itchat4j.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +19,7 @@ /** * 消息处理中心 - * + * * @author https://github.com/yaphone * @date 创建时间:2017年5月14日 下午12:47:50 * @version 1.0 @@ -31,7 +32,7 @@ public class MsgCenter { /** * 接收消息,放入队列 - * + * * @author https://github.com/yaphone * @date 2017年4月23日 下午2:30:48 * @param msgList @@ -114,7 +115,7 @@ public static JSONArray produceMsg(JSONArray msgList) { /** * 消息处理 - * + * * @author https://github.com/yaphone * @date 2017年5月14日 上午10:52:34 * @param msgHandler @@ -128,29 +129,43 @@ public static void handleMsg(IMsgHandlerFace msgHandler) { try { if (msg.getType().equals(MsgTypeEnum.TEXT.getType())) { String result = msgHandler.textMsgHandle(msg); - MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + } } else if (msg.getType().equals(MsgTypeEnum.PIC.getType())) { String result = msgHandler.picMsgHandle(msg); - MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + } } else if (msg.getType().equals(MsgTypeEnum.VOICE.getType())) { String result = msgHandler.voiceMsgHandle(msg); - MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + } } else if (msg.getType().equals(MsgTypeEnum.VIEDO.getType())) { String result = msgHandler.viedoMsgHandle(msg); - MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + } } else if (msg.getType().equals(MsgTypeEnum.NAMECARD.getType())) { String result = msgHandler.nameCardMsgHandle(msg); - MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + } } else if (msg.getType().equals(MsgTypeEnum.SYS.getType())) { // 系统消息 msgHandler.sysMsgHandle(msg); } else if (msg.getType().equals(MsgTypeEnum.VERIFYMSG.getType())) { // 确认添加好友消息 String result = msgHandler.verifyAddFriendMsgHandle(msg); - MessageTools.sendMsgById(result, - core.getMsgList().get(0).getRecommendInfo().getUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, + core.getMsgList().get(0).getRecommendInfo().getUserName()); + } } else if (msg.getType().equals(MsgTypeEnum.MEDIA.getType())) { // 多媒体消息 String result = msgHandler.mediaMsgHandle(msg); - MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + if(StringUtils.isNotEmpty(result)){ + MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); + } } } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/cn/zhouyafeng/itchat4j/service/ILoginService.java b/src/main/java/cn/zhouyafeng/itchat4j/service/ILoginService.java index d902f77..b694fd0 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/service/ILoginService.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/service/ILoginService.java @@ -2,81 +2,85 @@ /** * 登陆服务接口 - * + * * @author https://github.com/yaphone - * @date 创建时间:2017年5月13日 上午12:07:21 * @version 1.0 - * + * @date 创建时间:2017年5月13日 上午12:07:21 */ public interface ILoginService { - /** - * 登陆 - * - * @author https://github.com/yaphone - * @date 2017年5月13日 上午12:14:07 - * @return - */ - boolean login(); + /** + * 重新登录 + */ + void reLogin(); + + /** + * 登陆 + * + * @return + * @author https://github.com/yaphone + * @date 2017年5月13日 上午12:14:07 + */ + boolean login(); - /** - * 获取UUID - * - * @author https://github.com/yaphone - * @date 2017年5月13日 上午12:21:40 - * @param qrPath - * @return - */ - String getUuid(); + /** + * 获取UUID + * + * @param + * @return + * @author https://github.com/yaphone + * @date 2017年5月13日 上午12:21:40 + */ + String getUuid(); - /** - * 获取二维码图片 - * - * @author https://github.com/yaphone - * @date 2017年5月13日 上午12:13:51 - * @param qrPath - * @return - */ - boolean getQR(String qrPath); + /** + * 获取二维码图片 + * + * @param qrPath + * @return + * @author https://github.com/yaphone + * @date 2017年5月13日 上午12:13:51 + */ + boolean getQR(String qrPath); - /** - * web初始化 - * - * @author https://github.com/yaphone - * @date 2017年5月13日 上午12:14:13 - * @return - */ - boolean webWxInit(); + /** + * web初始化 + * + * @return + * @author https://github.com/yaphone + * @date 2017年5月13日 上午12:14:13 + */ + boolean webWxInit(); - /** - * 微信状态通知 - * - * @author https://github.com/yaphone - * @date 2017年5月13日 上午12:14:24 - */ - void wxStatusNotify(); + /** + * 微信状态通知 + * + * @author https://github.com/yaphone + * @date 2017年5月13日 上午12:14:24 + */ + void wxStatusNotify(); - /** - * 接收消息 - * - * @author https://github.com/yaphone - * @date 2017年5月13日 上午12:14:37 - */ - void startReceiving(); + /** + * 接收消息 + * + * @author https://github.com/yaphone + * @date 2017年5月13日 上午12:14:37 + */ + void startReceiving(); - /** - * 获取微信联系人 - * - * @author https://github.com/yaphone - * @date 2017年5月13日 下午2:26:18 - */ - void webWxGetContact(); + /** + * 获取微信联系人 + * + * @author https://github.com/yaphone + * @date 2017年5月13日 下午2:26:18 + */ + void webWxGetContact(); - /** - * 批量获取联系人信息 - * - * @date 2017年6月22日 下午11:24:35 - */ - void WebWxBatchGetContact(); + /** + * 批量获取联系人信息 + * + * @date 2017年6月22日 下午11:24:35 + */ + void WebWxBatchGetContact(); } diff --git a/src/main/java/cn/zhouyafeng/itchat4j/service/impl/LoginServiceImpl.java b/src/main/java/cn/zhouyafeng/itchat4j/service/impl/LoginServiceImpl.java index c68e7fb..9227167 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/service/impl/LoginServiceImpl.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/service/impl/LoginServiceImpl.java @@ -11,6 +11,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Random; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import org.apache.http.Consts; @@ -44,641 +48,674 @@ /** * 登陆服务实现类 - * + * * @author https://github.com/yaphone - * @date 创建时间:2017年5月13日 上午12:09:35 * @version 1.0 - * + * @date 创建时间:2017年5月13日 上午12:09:35 */ public class LoginServiceImpl implements ILoginService { - private static Logger LOG = LoggerFactory.getLogger(LoginServiceImpl.class); - - private Core core = Core.getInstance(); - private MyHttpClient httpClient = core.getMyHttpClient(); - - private MyHttpClient myHttpClient = core.getMyHttpClient(); - - public LoginServiceImpl() { - - } - - @Override - public boolean login() { - - boolean isLogin = false; - // 组装参数和URL - List params = new ArrayList(); - params.add(new BasicNameValuePair(LoginParaEnum.LOGIN_ICON.para(), LoginParaEnum.LOGIN_ICON.value())); - params.add(new BasicNameValuePair(LoginParaEnum.UUID.para(), core.getUuid())); - params.add(new BasicNameValuePair(LoginParaEnum.TIP.para(), LoginParaEnum.TIP.value())); - - // long time = 4000; - while (!isLogin) { - // SleepUtils.sleep(time += 1000); - long millis = System.currentTimeMillis(); - params.add(new BasicNameValuePair(LoginParaEnum.R.para(), String.valueOf(millis / 1579L))); - params.add(new BasicNameValuePair(LoginParaEnum._.para(), String.valueOf(millis))); - HttpEntity entity = httpClient.doGet(URLEnum.LOGIN_URL.getUrl(), params, true, null); - - try { - String result = EntityUtils.toString(entity); - String status = checklogin(result); - - if (ResultEnum.SUCCESS.getCode().equals(status)) { - processLoginInfo(result); // 处理结果 - isLogin = true; - core.setAlive(isLogin); - break; - } - if (ResultEnum.WAIT_CONFIRM.getCode().equals(status)) { - LOG.info("请点击微信确认按钮,进行登陆"); - } - - } catch (Exception e) { - LOG.error("微信登陆异常!", e); - } - } - return isLogin; - } - - @Override - public String getUuid() { - // 组装参数和URL - List params = new ArrayList(); - params.add(new BasicNameValuePair(UUIDParaEnum.APP_ID.para(), UUIDParaEnum.APP_ID.value())); - params.add(new BasicNameValuePair(UUIDParaEnum.FUN.para(), UUIDParaEnum.FUN.value())); - params.add(new BasicNameValuePair(UUIDParaEnum.LANG.para(), UUIDParaEnum.LANG.value())); - params.add(new BasicNameValuePair(UUIDParaEnum._.para(), String.valueOf(System.currentTimeMillis()))); - - HttpEntity entity = httpClient.doGet(URLEnum.UUID_URL.getUrl(), params, true, null); - - try { - String result = EntityUtils.toString(entity); - String regEx = "window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";"; - Matcher matcher = CommonTools.getMatcher(regEx, result); - if (matcher.find()) { - if ((ResultEnum.SUCCESS.getCode().equals(matcher.group(1)))) { - core.setUuid(matcher.group(2)); - } - } - } catch (Exception e) { - LOG.error(e.getMessage(), e); - } - - return core.getUuid(); - } - - @Override - public boolean getQR(String qrPath) { - qrPath = qrPath + File.separator + "QR.jpg"; - String qrUrl = URLEnum.QRCODE_URL.getUrl() + core.getUuid(); - HttpEntity entity = myHttpClient.doGet(qrUrl, null, true, null); - try { - OutputStream out = new FileOutputStream(qrPath); - byte[] bytes = EntityUtils.toByteArray(entity); - out.write(bytes); - out.flush(); - out.close(); - try { - CommonTools.printQr(qrPath); // 打开登陆二维码图片 - } catch (Exception e) { - LOG.info(e.getMessage()); - } - - } catch (Exception e) { - LOG.info(e.getMessage()); - return false; - } - - return true; - } - - @Override - public boolean webWxInit() { - core.setAlive(true); - core.setLastNormalRetcodeTime(System.currentTimeMillis()); - // 组装请求URL和参数 - String url = String.format(URLEnum.INIT_URL.getUrl(), - core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), - String.valueOf(System.currentTimeMillis() / 3158L), - core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); - - Map paramMap = core.getParamMap(); - - // 请求初始化接口 - HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); - try { - String result = EntityUtils.toString(entity, Consts.UTF_8); - JSONObject obj = JSON.parseObject(result); - - JSONObject user = obj.getJSONObject(StorageLoginInfoEnum.User.getKey()); - JSONObject syncKey = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()); - - core.getLoginInfo().put(StorageLoginInfoEnum.InviteStartCount.getKey(), - obj.getInteger(StorageLoginInfoEnum.InviteStartCount.getKey())); - core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), syncKey); - - JSONArray syncArray = syncKey.getJSONArray("List"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < syncArray.size(); i++) { - sb.append(syncArray.getJSONObject(i).getString("Key") + "_" - + syncArray.getJSONObject(i).getString("Val") + "|"); - } - // 1_661706053|2_661706420|3_661706415|1000_1494151022| - String synckey = sb.toString(); - - // 1_661706053|2_661706420|3_661706415|1000_1494151022 - core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(), synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192 - core.setUserName(user.getString("UserName")); - core.setNickName(user.getString("NickName")); - core.setUserSelf(obj.getJSONObject("User")); - - String chatSet = obj.getString("ChatSet"); - String[] chatSetArray = chatSet.split(","); - for (int i = 0; i < chatSetArray.length; i++) { - if (chatSetArray[i].indexOf("@@") != -1) { - // 更新GroupIdList - core.getGroupIdList().add(chatSetArray[i]); // - } - } - // JSONArray contactListArray = obj.getJSONArray("ContactList"); - // for (int i = 0; i < contactListArray.size(); i++) { - // JSONObject o = contactListArray.getJSONObject(i); - // if (o.getString("UserName").indexOf("@@") != -1) { - // core.getGroupIdList().add(o.getString("UserName")); // - // // 更新GroupIdList - // core.getGroupList().add(o); // 更新GroupList - // core.getGroupNickNameList().add(o.getString("NickName")); - // } - // } - } catch (Exception e) { - e.printStackTrace(); - return false; - } - return true; - } - - @Override - public void wxStatusNotify() { - // 组装请求URL和参数 - String url = String.format(URLEnum.STATUS_NOTIFY_URL.getUrl(), - core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); - - Map paramMap = core.getParamMap(); - paramMap.put(StatusNotifyParaEnum.CODE.para(), StatusNotifyParaEnum.CODE.value()); - paramMap.put(StatusNotifyParaEnum.FROM_USERNAME.para(), core.getUserName()); - paramMap.put(StatusNotifyParaEnum.TO_USERNAME.para(), core.getUserName()); - paramMap.put(StatusNotifyParaEnum.CLIENT_MSG_ID.para(), System.currentTimeMillis()); - String paramStr = JSON.toJSONString(paramMap); - - try { - HttpEntity entity = httpClient.doPost(url, paramStr); - EntityUtils.toString(entity, Consts.UTF_8); - } catch (Exception e) { - LOG.error("微信状态通知接口失败!", e); - } - - } - - @Override - public void startReceiving() { - core.setAlive(true); - new Thread(new Runnable() { - int retryCount = 0; - - @Override - public void run() { - while (core.isAlive()) { - try { - Map resultMap = syncCheck(); - LOG.info(JSONObject.toJSONString(resultMap)); - String retcode = resultMap.get("retcode"); - String selector = resultMap.get("selector"); - if (retcode.equals(RetCodeEnum.UNKOWN.getCode())) { - LOG.info(RetCodeEnum.UNKOWN.getType()); - continue; - } else if (retcode.equals(RetCodeEnum.LOGIN_OUT.getCode())) { // 退出 - LOG.info(RetCodeEnum.LOGIN_OUT.getType()); - break; - } else if (retcode.equals(RetCodeEnum.LOGIN_OTHERWHERE.getCode())) { // 其它地方登陆 - LOG.info(RetCodeEnum.LOGIN_OTHERWHERE.getType()); - break; - } else if (retcode.equals(RetCodeEnum.MOBILE_LOGIN_OUT.getCode())) { // 移动端退出 - LOG.info(RetCodeEnum.MOBILE_LOGIN_OUT.getType()); - break; - } else if (retcode.equals(RetCodeEnum.NORMAL.getCode())) { - core.setLastNormalRetcodeTime(System.currentTimeMillis()); // 最后收到正常报文时间 - JSONObject msgObj = webWxSync(); - if (selector.equals("2")) { - if (msgObj != null) { - try { - JSONArray msgList = new JSONArray(); - msgList = msgObj.getJSONArray("AddMsgList"); - msgList = MsgCenter.produceMsg(msgList); - for (int j = 0; j < msgList.size(); j++) { - BaseMsg baseMsg = JSON.toJavaObject(msgList.getJSONObject(j), - BaseMsg.class); - core.getMsgList().add(baseMsg); - } - } catch (Exception e) { - LOG.info(e.getMessage()); - } - } - } else if (selector.equals("7")) { - webWxSync(); - } else if (selector.equals("4")) { - continue; - } else if (selector.equals("3")) { - continue; - } else if (selector.equals("6")) { - if (msgObj != null) { - try { - JSONArray msgList = new JSONArray(); - msgList = msgObj.getJSONArray("AddMsgList"); - JSONArray modContactList = msgObj.getJSONArray("ModContactList"); // 存在删除或者新增的好友信息 - msgList = MsgCenter.produceMsg(msgList); - for (int j = 0; j < msgList.size(); j++) { - JSONObject userInfo = modContactList.getJSONObject(j); - // 存在主动加好友之后的同步联系人到本地 - core.getContactList().add(userInfo); - } - } catch (Exception e) { - LOG.info(e.getMessage()); - } - } - - } - } else { - JSONObject obj = webWxSync(); - } - } catch (Exception e) { - LOG.info(e.getMessage()); - retryCount += 1; - if (core.getReceivingRetryCount() < retryCount) { - core.setAlive(false); - } else { - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - LOG.info(e.getMessage()); - } - } - } - - } - } - }).start(); - - } - - @Override - public void webWxGetContact() { - String url = String.format(URLEnum.WEB_WX_GET_CONTACT.getUrl(), - core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey())); - Map paramMap = core.getParamMap(); - HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); - - try { - String result = EntityUtils.toString(entity, Consts.UTF_8); - JSONObject fullFriendsJsonList = JSON.parseObject(result); - // 查看seq是否为0,0表示好友列表已全部获取完毕,若大于0,则表示好友列表未获取完毕,当前的字节数(断点续传) - long seq = 0; - long currentTime = 0L; - List params = new ArrayList(); - if (fullFriendsJsonList.get("Seq") != null) { - seq = fullFriendsJsonList.getLong("Seq"); - currentTime = new Date().getTime(); - } - core.setMemberCount(fullFriendsJsonList.getInteger(StorageLoginInfoEnum.MemberCount.getKey())); - JSONArray member = fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey()); - // 循环获取seq直到为0,即获取全部好友列表 ==0:好友获取完毕 >0:好友未获取完毕,此时seq为已获取的字节数 - while (seq > 0) { - // 设置seq传参 - params.add(new BasicNameValuePair("r", String.valueOf(currentTime))); - params.add(new BasicNameValuePair("seq", String.valueOf(seq))); - entity = httpClient.doGet(url, params, false, null); - - params.remove(new BasicNameValuePair("r", String.valueOf(currentTime))); - params.remove(new BasicNameValuePair("seq", String.valueOf(seq))); - - result = EntityUtils.toString(entity, Consts.UTF_8); - fullFriendsJsonList = JSON.parseObject(result); - - if (fullFriendsJsonList.get("Seq") != null) { - seq = fullFriendsJsonList.getLong("Seq"); - currentTime = new Date().getTime(); - } - - // 累加好友列表 - member.addAll(fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey())); - } - core.setMemberCount(member.size()); - for (Iterator iterator = member.iterator(); iterator.hasNext();) { - JSONObject o = (JSONObject) iterator.next(); - if ((o.getInteger("VerifyFlag") & 8) != 0) { // 公众号/服务号 - core.getPublicUsersList().add(o); - } else if (Config.API_SPECIAL_USER.contains(o.getString("UserName"))) { // 特殊账号 - core.getSpecialUsersList().add(o); - } else if (o.getString("UserName").indexOf("@@") != -1) { // 群聊 - if (!core.getGroupIdList().contains(o.getString("UserName"))) { - core.getGroupNickNameList().add(o.getString("NickName")); - core.getGroupIdList().add(o.getString("UserName")); - core.getGroupList().add(o); - } - } else if (o.getString("UserName").equals(core.getUserSelf().getString("UserName"))) { // 自己 - core.getContactList().remove(o); - } else { // 普通联系人 - core.getContactList().add(o); - } - } - return; - } catch (Exception e) { - LOG.error(e.getMessage(), e); - } - return; - } - - @Override - public void WebWxBatchGetContact() { - String url = String.format(URLEnum.WEB_WX_BATCH_GET_CONTACT.getUrl(), - core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), new Date().getTime(), - core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); - Map paramMap = core.getParamMap(); - paramMap.put("Count", core.getGroupIdList().size()); - List> list = new ArrayList>(); - for (int i = 0; i < core.getGroupIdList().size(); i++) { - HashMap map = new HashMap(); - map.put("UserName", core.getGroupIdList().get(i)); - map.put("EncryChatRoomId", ""); - list.add(map); - } - paramMap.put("List", list); - HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); - try { - String text = EntityUtils.toString(entity, Consts.UTF_8); - JSONObject obj = JSON.parseObject(text); - JSONArray contactList = obj.getJSONArray("ContactList"); - for (int i = 0; i < contactList.size(); i++) { // 群好友 - if (contactList.getJSONObject(i).getString("UserName").indexOf("@@") > -1) { // 群 - core.getGroupNickNameList().add(contactList.getJSONObject(i).getString("NickName")); // 更新群昵称列表 - core.getGroupList().add(contactList.getJSONObject(i)); // 更新群信息(所有)列表 - core.getGroupMemeberMap().put(contactList.getJSONObject(i).getString("UserName"), - contactList.getJSONObject(i).getJSONArray("MemberList")); // 更新群成员Map - } - } - } catch (Exception e) { - LOG.info(e.getMessage()); - } - } - - /** - * 检查登陆状态 - * - * @param result - * @return - */ - public String checklogin(String result) { - String regEx = "window.code=(\\d+)"; - Matcher matcher = CommonTools.getMatcher(regEx, result); - if (matcher.find()) { - return matcher.group(1); - } - return null; - } - - /** - * 处理登陆信息 - * - * @author https://github.com/yaphone - * @date 2017年4月9日 下午12:16:26 - * @param result - */ - private void processLoginInfo(String loginContent) { - String regEx = "window.redirect_uri=\"(\\S+)\";"; - Matcher matcher = CommonTools.getMatcher(regEx, loginContent); - if (matcher.find()) { - String originalUrl = matcher.group(1); - String url = originalUrl.substring(0, originalUrl.lastIndexOf('/')); // https://wx2.qq.com/cgi-bin/mmwebwx-bin - core.getLoginInfo().put("url", url); - Map> possibleUrlMap = this.getPossibleUrlMap(); - Iterator>> iterator = possibleUrlMap.entrySet().iterator(); - Map.Entry> entry; - String fileUrl; - String syncUrl; - while (iterator.hasNext()) { - entry = iterator.next(); - String indexUrl = entry.getKey(); - fileUrl = "https://" + entry.getValue().get(0) + "/cgi-bin/mmwebwx-bin"; - syncUrl = "https://" + entry.getValue().get(1) + "/cgi-bin/mmwebwx-bin"; - if (core.getLoginInfo().get("url").toString().contains(indexUrl)) { - core.setIndexUrl(indexUrl); - core.getLoginInfo().put("fileUrl", fileUrl); - core.getLoginInfo().put("syncUrl", syncUrl); - break; - } - } - if (core.getLoginInfo().get("fileUrl") == null && core.getLoginInfo().get("syncUrl") == null) { - core.getLoginInfo().put("fileUrl", url); - core.getLoginInfo().put("syncUrl", url); - } - core.getLoginInfo().put("deviceid", "e" + String.valueOf(new Random().nextLong()).substring(1, 16)); // 生成15位随机数 - core.getLoginInfo().put("BaseRequest", new ArrayList()); - String text = ""; - - try { - HttpEntity entity = myHttpClient.doGet(originalUrl, null, false, null); - text = EntityUtils.toString(entity); - } catch (Exception e) { - LOG.info(e.getMessage()); - return; - } - //add by 默非默 2017-08-01 22:28:09 - //如果登录被禁止时,则登录返回的message内容不为空,下面代码则判断登录内容是否为空,不为空则退出程序 - String msg = getLoginMessage(text); - if (!"".equals(msg)){ - LOG.info(msg); - System.exit(0); - } - Document doc = CommonTools.xmlParser(text); - if (doc != null) { - core.getLoginInfo().put(StorageLoginInfoEnum.skey.getKey(), - doc.getElementsByTagName(StorageLoginInfoEnum.skey.getKey()).item(0).getFirstChild() - .getNodeValue()); - core.getLoginInfo().put(StorageLoginInfoEnum.wxsid.getKey(), - doc.getElementsByTagName(StorageLoginInfoEnum.wxsid.getKey()).item(0).getFirstChild() - .getNodeValue()); - core.getLoginInfo().put(StorageLoginInfoEnum.wxuin.getKey(), - doc.getElementsByTagName(StorageLoginInfoEnum.wxuin.getKey()).item(0).getFirstChild() - .getNodeValue()); - core.getLoginInfo().put(StorageLoginInfoEnum.pass_ticket.getKey(), - doc.getElementsByTagName(StorageLoginInfoEnum.pass_ticket.getKey()).item(0).getFirstChild() - .getNodeValue()); - } - - } - } - - private Map> getPossibleUrlMap() { - Map> possibleUrlMap = new HashMap>(); - possibleUrlMap.put("wx.qq.com", new ArrayList() { - /** - * - */ - private static final long serialVersionUID = 1L; - - { - add("file.wx.qq.com"); - add("webpush.wx.qq.com"); - } - }); - - possibleUrlMap.put("wx2.qq.com", new ArrayList() { - /** - * - */ - private static final long serialVersionUID = 1L; - - { - add("file.wx2.qq.com"); - add("webpush.wx2.qq.com"); - } - }); - possibleUrlMap.put("wx8.qq.com", new ArrayList() { - /** - * - */ - private static final long serialVersionUID = 1L; - - { - add("file.wx8.qq.com"); - add("webpush.wx8.qq.com"); - } - }); - - possibleUrlMap.put("web2.wechat.com", new ArrayList() { - /** - * - */ - private static final long serialVersionUID = 1L; - - { - add("file.web2.wechat.com"); - add("webpush.web2.wechat.com"); - } - }); - possibleUrlMap.put("wechat.com", new ArrayList() { - /** - * - */ - private static final long serialVersionUID = 1L; - - { - add("file.web.wechat.com"); - add("webpush.web.wechat.com"); - } - }); - return possibleUrlMap; - } - - /** - * 同步消息 sync the messages - * - * @author https://github.com/yaphone - * @date 2017年5月12日 上午12:24:55 - * @return - */ - private JSONObject webWxSync() { - JSONObject result = null; - String url = String.format(URLEnum.WEB_WX_SYNC_URL.getUrl(), - core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), - core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey()), - core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()), - core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); - Map paramMap = core.getParamMap(); - paramMap.put(StorageLoginInfoEnum.SyncKey.getKey(), - core.getLoginInfo().get(StorageLoginInfoEnum.SyncKey.getKey())); - paramMap.put("rr", -new Date().getTime() / 1000); - String paramStr = JSON.toJSONString(paramMap); - try { - HttpEntity entity = myHttpClient.doPost(url, paramStr); - String text = EntityUtils.toString(entity, Consts.UTF_8); - JSONObject obj = JSON.parseObject(text); - if (obj.getJSONObject("BaseResponse").getInteger("Ret") != 0) { - result = null; - } else { - result = obj; - core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), obj.getJSONObject("SyncCheckKey")); - JSONArray syncArray = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()).getJSONArray("List"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < syncArray.size(); i++) { - sb.append(syncArray.getJSONObject(i).getString("Key") + "_" - + syncArray.getJSONObject(i).getString("Val") + "|"); - } - String synckey = sb.toString(); - core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(), - synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192 - } - } catch (Exception e) { - LOG.info(e.getMessage()); - } - return result; - - } - - /** - * 检查是否有新消息 check whether there's a message - * - * @author https://github.com/yaphone - * @date 2017年4月16日 上午11:11:34 - * @return - * - */ - private Map syncCheck() { - Map resultMap = new HashMap(); - // 组装请求URL和参数 - String url = core.getLoginInfo().get(StorageLoginInfoEnum.syncUrl.getKey()) + URLEnum.SYNC_CHECK_URL.getUrl(); - List params = new ArrayList(); - for (BaseParaEnum baseRequest : BaseParaEnum.values()) { - params.add(new BasicNameValuePair(baseRequest.para().toLowerCase(), - core.getLoginInfo().get(baseRequest.value()).toString())); - } - params.add(new BasicNameValuePair("r", String.valueOf(new Date().getTime()))); - params.add(new BasicNameValuePair("synckey", (String) core.getLoginInfo().get("synckey"))); - params.add(new BasicNameValuePair("_", String.valueOf(new Date().getTime()))); - SleepUtils.sleep(7); - try { - HttpEntity entity = myHttpClient.doGet(url, params, true, null); - if (entity == null) { - resultMap.put("retcode", "9999"); - resultMap.put("selector", "9999"); - return resultMap; - } - String text = EntityUtils.toString(entity); - String regEx = "window.synccheck=\\{retcode:\"(\\d+)\",selector:\"(\\d+)\"\\}"; - Matcher matcher = CommonTools.getMatcher(regEx, text); - if (!matcher.find() || matcher.group(1).equals("2")) { - LOG.info(String.format("Unexpected sync check result: %s", text)); - } else { - resultMap.put("retcode", matcher.group(1)); - resultMap.put("selector", matcher.group(2)); - } - } catch (Exception e) { - e.printStackTrace(); - } - return resultMap; - } - - /** - * 解析登录返回的消息,如果成功登录,则message为空 - * @param result - * @return - */ - public String getLoginMessage(String result){ - String[] strArr = result.split(""); - String[] rs = strArr[1].split(""); - if (rs!=null && rs.length>1) { - return rs[0]; - } - return ""; - } + private static volatile LoginServiceImpl instance; + + private final AtomicBoolean isLoginLoop = new AtomicBoolean(true); + private static final Object LOGIN_LOCK = new Object(); + private static AtomicBoolean LOCK = new AtomicBoolean(false); + + private static Logger LOG = LoggerFactory.getLogger(LoginServiceImpl.class); + + private Core core = Core.getInstance(); + private MyHttpClient httpClient = core.getMyHttpClient(); + + private MyHttpClient myHttpClient = core.getMyHttpClient(); + + private LoginServiceImpl() { + + } + + public static LoginServiceImpl getInstance() { + if (instance == null) { + synchronized (LoginServiceImpl.class) { + instance = new LoginServiceImpl(); + } + } + return instance; + } + + @Override + public synchronized void reLogin() { + if (isLoginLoop.get() && LOCK.get()) { + isLoginLoop.set(false); + } + } + + private static int login_count = 0; + + @Override + public boolean login() { + + + boolean isLogin = false; + // 组装参数和URL + List params = new ArrayList(); + params.add(new BasicNameValuePair(LoginParaEnum.LOGIN_ICON.para(), LoginParaEnum.LOGIN_ICON.value())); + params.add(new BasicNameValuePair(LoginParaEnum.UUID.para(), core.getUuid())); + params.add(new BasicNameValuePair(LoginParaEnum.TIP.para(), LoginParaEnum.TIP.value())); + + int count; + synchronized (LOGIN_LOCK) { + LOCK.set(true); + isLoginLoop.set(true); + count = ++login_count; + LOG.info("登录,第【" + count + "】次进入锁"); + + // long time = 4000; + while (isLoginLoop.get()) { + // SleepUtils.sleep(time += 1000); + long millis = System.currentTimeMillis(); + params.add(new BasicNameValuePair(LoginParaEnum.R.para(), String.valueOf(millis / 1579L))); + params.add(new BasicNameValuePair(LoginParaEnum._.para(), String.valueOf(millis))); + HttpEntity entity = httpClient.doGet(URLEnum.LOGIN_URL.getUrl(), params, true, null); + + try { + String result = EntityUtils.toString(entity); + String status = checklogin(result); + + if (ResultEnum.SUCCESS.getCode().equals(status)) { + processLoginInfo(result); // 处理结果 + isLogin = true; + core.setLogin(true); + break; + } + if (ResultEnum.WAIT_CONFIRM.getCode().equals(status)) { + LOG.info("请点击微信确认按钮,进行登陆"); + } + + } catch (Exception e) { + LOG.error("微信登陆异常!", e); + } + } + LOCK.set(false); + } + LOG.info("登录,第【" + count + "】次跳出锁,isLogin:" + isLogin); + return isLogin; + } + + @Override + public String getUuid() { + // 组装参数和URL + List params = new ArrayList(); + params.add(new BasicNameValuePair(UUIDParaEnum.APP_ID.para(), UUIDParaEnum.APP_ID.value())); + params.add(new BasicNameValuePair(UUIDParaEnum.FUN.para(), UUIDParaEnum.FUN.value())); + params.add(new BasicNameValuePair(UUIDParaEnum.LANG.para(), UUIDParaEnum.LANG.value())); + params.add(new BasicNameValuePair(UUIDParaEnum._.para(), String.valueOf(System.currentTimeMillis()))); + + HttpEntity entity = httpClient.doGet(URLEnum.UUID_URL.getUrl(), params, true, null); + + try { + String result = EntityUtils.toString(entity); + String regEx = "window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";"; + Matcher matcher = CommonTools.getMatcher(regEx, result); + if (matcher.find()) { + if ((ResultEnum.SUCCESS.getCode().equals(matcher.group(1)))) { + core.setUuid(matcher.group(2)); + } + } + } catch (Exception e) { + LOG.error(e.getMessage(), e); + } + + return core.getUuid(); + } + + @Override + public boolean getQR(String qrPath) { + qrPath = qrPath + File.separator + "QR.jpg"; + String qrUrl = URLEnum.QRCODE_URL.getUrl() + core.getUuid(); + HttpEntity entity = myHttpClient.doGet(qrUrl, null, true, null); + try { + OutputStream out = new FileOutputStream(qrPath); + byte[] bytes = EntityUtils.toByteArray(entity); + out.write(bytes); + out.flush(); + out.close(); + try { + CommonTools.printQr(qrPath); // 打开登陆二维码图片 + } catch (Exception e) { + LOG.info(e.getMessage()); + } + + } catch (Exception e) { + LOG.info(e.getMessage()); + return false; + } + + return true; + } + + @Override + public boolean webWxInit() { + core.setAlive(true); + core.setLastNormalRetcodeTime(System.currentTimeMillis()); + // 组装请求URL和参数 + String url = String.format(URLEnum.INIT_URL.getUrl(), + core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), + String.valueOf(System.currentTimeMillis() / 3158L), + core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); + + Map paramMap = core.getParamMap(); + + // 请求初始化接口 + HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); + try { + String result = EntityUtils.toString(entity, Consts.UTF_8); + JSONObject obj = JSON.parseObject(result); + + JSONObject user = obj.getJSONObject(StorageLoginInfoEnum.User.getKey()); + JSONObject syncKey = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()); + + core.getLoginInfo().put(StorageLoginInfoEnum.InviteStartCount.getKey(), + obj.getInteger(StorageLoginInfoEnum.InviteStartCount.getKey())); + core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), syncKey); + + JSONArray syncArray = syncKey.getJSONArray("List"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < syncArray.size(); i++) { + sb.append(syncArray.getJSONObject(i).getString("Key") + "_" + + syncArray.getJSONObject(i).getString("Val") + "|"); + } + // 1_661706053|2_661706420|3_661706415|1000_1494151022| + String synckey = sb.toString(); + + // 1_661706053|2_661706420|3_661706415|1000_1494151022 + core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(), synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192 + core.setUserName(user.getString("UserName")); + core.setNickName(user.getString("NickName")); + core.setUserSelf(obj.getJSONObject("User")); + + String chatSet = obj.getString("ChatSet"); + String[] chatSetArray = chatSet.split(","); + for (int i = 0; i < chatSetArray.length; i++) { + if (chatSetArray[i].indexOf("@@") != -1) { + // 更新GroupIdList + core.getGroupIdList().add(chatSetArray[i]); // + } + } + // JSONArray contactListArray = obj.getJSONArray("ContactList"); + // for (int i = 0; i < contactListArray.size(); i++) { + // JSONObject o = contactListArray.getJSONObject(i); + // if (o.getString("UserName").indexOf("@@") != -1) { + // core.getGroupIdList().add(o.getString("UserName")); // + // // 更新GroupIdList + // core.getGroupList().add(o); // 更新GroupList + // core.getGroupNickNameList().add(o.getString("NickName")); + // } + // } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public void wxStatusNotify() { + // 组装请求URL和参数 + String url = String.format(URLEnum.STATUS_NOTIFY_URL.getUrl(), + core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); + + Map paramMap = core.getParamMap(); + paramMap.put(StatusNotifyParaEnum.CODE.para(), StatusNotifyParaEnum.CODE.value()); + paramMap.put(StatusNotifyParaEnum.FROM_USERNAME.para(), core.getUserName()); + paramMap.put(StatusNotifyParaEnum.TO_USERNAME.para(), core.getUserName()); + paramMap.put(StatusNotifyParaEnum.CLIENT_MSG_ID.para(), System.currentTimeMillis()); + String paramStr = JSON.toJSONString(paramMap); + + try { + HttpEntity entity = httpClient.doPost(url, paramStr); + EntityUtils.toString(entity, Consts.UTF_8); + } catch (Exception e) { + LOG.error("微信状态通知接口失败!", e); + } + + } + + private static final ThreadPoolExecutor receivingExecutor = new ThreadPoolExecutor(1, 1, 5, + TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy()); + + @Override + public void startReceiving() { + core.setAlive(true); + receivingExecutor.execute(() -> { + int retryCount = 0; + + while (core.isAlive()) { + try { + Map resultMap = syncCheck(); + LOG.info(JSONObject.toJSONString(resultMap)); + String retcode = resultMap.get("retcode"); + String selector = resultMap.get("selector"); + if (retcode.equals(RetCodeEnum.UNKOWN.getCode())) { + LOG.info(RetCodeEnum.UNKOWN.getType()); + continue; + } else if (retcode.equals(RetCodeEnum.LOGIN_OUT.getCode())) { // 退出 + LOG.info(RetCodeEnum.LOGIN_OUT.getType()); + break; + } else if (retcode.equals(RetCodeEnum.LOGIN_OTHERWHERE.getCode())) { // 其它地方登陆 + LOG.info(RetCodeEnum.LOGIN_OTHERWHERE.getType()); + break; + } else if (retcode.equals(RetCodeEnum.MOBILE_LOGIN_OUT.getCode())) { // 移动端退出 + LOG.info(RetCodeEnum.MOBILE_LOGIN_OUT.getType()); + break; + } else if (retcode.equals(RetCodeEnum.NORMAL.getCode())) { + core.setLastNormalRetcodeTime(System.currentTimeMillis()); // 最后收到正常报文时间 + JSONObject msgObj = webWxSync(); + if (selector.equals("2")) { + if (msgObj != null) { + try { + JSONArray msgList = new JSONArray(); + msgList = msgObj.getJSONArray("AddMsgList"); + msgList = MsgCenter.produceMsg(msgList); + for (int j = 0; j < msgList.size(); j++) { + BaseMsg baseMsg = JSON.toJavaObject(msgList.getJSONObject(j), + BaseMsg.class); + core.getMsgList().add(baseMsg); + } + } catch (Exception e) { + LOG.info(e.getMessage()); + } + } + } else if (selector.equals("7")) { + webWxSync(); + } else if (selector.equals("4")) { + continue; + } else if (selector.equals("3")) { + continue; + } else if (selector.equals("6")) { + if (msgObj != null) { + try { + JSONArray msgList = new JSONArray(); + msgList = msgObj.getJSONArray("AddMsgList"); + JSONArray modContactList = msgObj.getJSONArray("ModContactList"); // 存在删除或者新增的好友信息 + msgList = MsgCenter.produceMsg(msgList); + for (int j = 0; j < msgList.size(); j++) { + JSONObject userInfo = modContactList.getJSONObject(j); + // 存在主动加好友之后的同步联系人到本地 + core.getContactList().add(userInfo); + } + } catch (Exception e) { + LOG.info(e.getMessage()); + } + } + + } + } else { + JSONObject obj = webWxSync(); + } + } catch (Exception e) { + LOG.info(e.getMessage()); + retryCount += 1; + if (core.getReceivingRetryCount() < retryCount) { + core.setAlive(false); + } else { + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + LOG.info(e.getMessage()); + } + } + } + } + + }); + } + + @Override + public void webWxGetContact() { + String url = String.format(URLEnum.WEB_WX_GET_CONTACT.getUrl(), + core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey())); + Map paramMap = core.getParamMap(); + HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); + + try { + String result = EntityUtils.toString(entity, Consts.UTF_8); + JSONObject fullFriendsJsonList = JSON.parseObject(result); + // 查看seq是否为0,0表示好友列表已全部获取完毕,若大于0,则表示好友列表未获取完毕,当前的字节数(断点续传) + long seq = 0; + long currentTime = 0L; + List params = new ArrayList(); + if (fullFriendsJsonList.get("Seq") != null) { + seq = fullFriendsJsonList.getLong("Seq"); + currentTime = new Date().getTime(); + } + core.setMemberCount(fullFriendsJsonList.getInteger(StorageLoginInfoEnum.MemberCount.getKey())); + JSONArray member = fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey()); + // 循环获取seq直到为0,即获取全部好友列表 ==0:好友获取完毕 >0:好友未获取完毕,此时seq为已获取的字节数 + while (seq > 0) { + // 设置seq传参 + params.add(new BasicNameValuePair("r", String.valueOf(currentTime))); + params.add(new BasicNameValuePair("seq", String.valueOf(seq))); + entity = httpClient.doGet(url, params, false, null); + + params.remove(new BasicNameValuePair("r", String.valueOf(currentTime))); + params.remove(new BasicNameValuePair("seq", String.valueOf(seq))); + + result = EntityUtils.toString(entity, Consts.UTF_8); + fullFriendsJsonList = JSON.parseObject(result); + + if (fullFriendsJsonList.get("Seq") != null) { + seq = fullFriendsJsonList.getLong("Seq"); + currentTime = new Date().getTime(); + } + + // 累加好友列表 + member.addAll(fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey())); + } + core.setMemberCount(member.size()); + for (Iterator iterator = member.iterator(); iterator.hasNext(); ) { + JSONObject o = (JSONObject) iterator.next(); + if ((o.getInteger("VerifyFlag") & 8) != 0) { // 公众号/服务号 + core.getPublicUsersList().add(o); + } else if (Config.API_SPECIAL_USER.contains(o.getString("UserName"))) { // 特殊账号 + core.getSpecialUsersList().add(o); + } else if (o.getString("UserName").indexOf("@@") != -1) { // 群聊 + if (!core.getGroupIdList().contains(o.getString("UserName"))) { + core.getGroupNickNameList().add(o.getString("NickName")); + core.getGroupIdList().add(o.getString("UserName")); + core.getGroupList().add(o); + } + } else if (o.getString("UserName").equals(core.getUserSelf().getString("UserName"))) { // 自己 + core.getContactList().remove(o); + } else { // 普通联系人 + core.getContactList().add(o); + } + } + return; + } catch (Exception e) { + LOG.error(e.getMessage(), e); + } + return; + } + + @Override + public void WebWxBatchGetContact() { + String url = String.format(URLEnum.WEB_WX_BATCH_GET_CONTACT.getUrl(), + core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), new Date().getTime(), + core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); + Map paramMap = core.getParamMap(); + paramMap.put("Count", core.getGroupIdList().size()); + List> list = new ArrayList>(); + for (int i = 0; i < core.getGroupIdList().size(); i++) { + HashMap map = new HashMap(); + map.put("UserName", core.getGroupIdList().get(i)); + map.put("EncryChatRoomId", ""); + list.add(map); + } + paramMap.put("List", list); + HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); + try { + String text = EntityUtils.toString(entity, Consts.UTF_8); + JSONObject obj = JSON.parseObject(text); + JSONArray contactList = obj.getJSONArray("ContactList"); + for (int i = 0; i < contactList.size(); i++) { // 群好友 + if (contactList.getJSONObject(i).getString("UserName").indexOf("@@") > -1) { // 群 + core.getGroupNickNameList().add(contactList.getJSONObject(i).getString("NickName")); // 更新群昵称列表 + core.getGroupList().add(contactList.getJSONObject(i)); // 更新群信息(所有)列表 + core.getGroupMemeberMap().put(contactList.getJSONObject(i).getString("UserName"), + contactList.getJSONObject(i).getJSONArray("MemberList")); // 更新群成员Map + } + } + } catch (Exception e) { + LOG.info(e.getMessage()); + } + } + + /** + * 检查登陆状态 + * + * @param result + * @return + */ + public String checklogin(String result) { + String regEx = "window.code=(\\d+)"; + Matcher matcher = CommonTools.getMatcher(regEx, result); + if (matcher.find()) { + return matcher.group(1); + } + return null; + } + + /** + * 处理登陆信息 + * + * @param + * @author https://github.com/yaphone + * @date 2017年4月9日 下午12:16:26 + */ + private void processLoginInfo(String loginContent) { + String regEx = "window.redirect_uri=\"(\\S+)\";"; + Matcher matcher = CommonTools.getMatcher(regEx, loginContent); + if (matcher.find()) { + String originalUrl = matcher.group(1); + String url = originalUrl.substring(0, originalUrl.lastIndexOf('/')); // https://wx2.qq.com/cgi-bin/mmwebwx-bin + core.getLoginInfo().put("url", url); + Map> possibleUrlMap = this.getPossibleUrlMap(); + Iterator>> iterator = possibleUrlMap.entrySet().iterator(); + Map.Entry> entry; + String fileUrl; + String syncUrl; + while (iterator.hasNext()) { + entry = iterator.next(); + String indexUrl = entry.getKey(); + fileUrl = "https://" + entry.getValue().get(0) + "/cgi-bin/mmwebwx-bin"; + syncUrl = "https://" + entry.getValue().get(1) + "/cgi-bin/mmwebwx-bin"; + if (core.getLoginInfo().get("url").toString().contains(indexUrl)) { + core.setIndexUrl(indexUrl); + core.getLoginInfo().put("fileUrl", fileUrl); + core.getLoginInfo().put("syncUrl", syncUrl); + break; + } + } + if (core.getLoginInfo().get("fileUrl") == null && core.getLoginInfo().get("syncUrl") == null) { + core.getLoginInfo().put("fileUrl", url); + core.getLoginInfo().put("syncUrl", url); + } + core.getLoginInfo().put("deviceid", "e" + String.valueOf(new Random().nextLong()).substring(1, 16)); // 生成15位随机数 + core.getLoginInfo().put("BaseRequest", new ArrayList()); + String text = ""; + + try { + HttpEntity entity = myHttpClient.doGet(originalUrl, null, false, null); + text = EntityUtils.toString(entity); + } catch (Exception e) { + LOG.info(e.getMessage()); + return; + } + //add by 默非默 2017-08-01 22:28:09 + //如果登录被禁止时,则登录返回的message内容不为空,下面代码则判断登录内容是否为空,不为空则退出程序 + String msg = getLoginMessage(text); + if (!"".equals(msg)) { + LOG.info(msg); + System.exit(0); + } + Document doc = CommonTools.xmlParser(text); + if (doc != null) { + core.getLoginInfo().put(StorageLoginInfoEnum.skey.getKey(), + doc.getElementsByTagName(StorageLoginInfoEnum.skey.getKey()).item(0).getFirstChild() + .getNodeValue()); + core.getLoginInfo().put(StorageLoginInfoEnum.wxsid.getKey(), + doc.getElementsByTagName(StorageLoginInfoEnum.wxsid.getKey()).item(0).getFirstChild() + .getNodeValue()); + core.getLoginInfo().put(StorageLoginInfoEnum.wxuin.getKey(), + doc.getElementsByTagName(StorageLoginInfoEnum.wxuin.getKey()).item(0).getFirstChild() + .getNodeValue()); + core.getLoginInfo().put(StorageLoginInfoEnum.pass_ticket.getKey(), + doc.getElementsByTagName(StorageLoginInfoEnum.pass_ticket.getKey()).item(0).getFirstChild() + .getNodeValue()); + } + + } + } + + private Map> getPossibleUrlMap() { + Map> possibleUrlMap = new HashMap>(); + possibleUrlMap.put("wx.qq.com", new ArrayList() { + /** + * + */ + private static final long serialVersionUID = 1L; + + { + add("file.wx.qq.com"); + add("webpush.wx.qq.com"); + } + }); + + possibleUrlMap.put("wx2.qq.com", new ArrayList() { + /** + * + */ + private static final long serialVersionUID = 1L; + + { + add("file.wx2.qq.com"); + add("webpush.wx2.qq.com"); + } + }); + possibleUrlMap.put("wx8.qq.com", new ArrayList() { + /** + * + */ + private static final long serialVersionUID = 1L; + + { + add("file.wx8.qq.com"); + add("webpush.wx8.qq.com"); + } + }); + + possibleUrlMap.put("web2.wechat.com", new ArrayList() { + /** + * + */ + private static final long serialVersionUID = 1L; + + { + add("file.web2.wechat.com"); + add("webpush.web2.wechat.com"); + } + }); + possibleUrlMap.put("wechat.com", new ArrayList() { + /** + * + */ + private static final long serialVersionUID = 1L; + + { + add("file.web.wechat.com"); + add("webpush.web.wechat.com"); + } + }); + return possibleUrlMap; + } + + /** + * 同步消息 sync the messages + * + * @return + * @author https://github.com/yaphone + * @date 2017年5月12日 上午12:24:55 + */ + private JSONObject webWxSync() { + JSONObject result = null; + String url = String.format(URLEnum.WEB_WX_SYNC_URL.getUrl(), + core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), + core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey()), + core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()), + core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); + Map paramMap = core.getParamMap(); + paramMap.put(StorageLoginInfoEnum.SyncKey.getKey(), + core.getLoginInfo().get(StorageLoginInfoEnum.SyncKey.getKey())); + paramMap.put("rr", -new Date().getTime() / 1000); + String paramStr = JSON.toJSONString(paramMap); + try { + HttpEntity entity = myHttpClient.doPost(url, paramStr); + String text = EntityUtils.toString(entity, Consts.UTF_8); + JSONObject obj = JSON.parseObject(text); + if (obj.getJSONObject("BaseResponse").getInteger("Ret") != 0) { + result = null; + } else { + result = obj; + core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), obj.getJSONObject("SyncCheckKey")); + JSONArray syncArray = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()).getJSONArray("List"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < syncArray.size(); i++) { + sb.append(syncArray.getJSONObject(i).getString("Key") + "_" + + syncArray.getJSONObject(i).getString("Val") + "|"); + } + String synckey = sb.toString(); + core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(), + synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192 + } + } catch (Exception e) { + LOG.info(e.getMessage()); + } + return result; + + } + + /** + * 检查是否有新消息 check whether there's a message + * + * @return + * @author https://github.com/yaphone + * @date 2017年4月16日 上午11:11:34 + */ + private Map syncCheck() { + Map resultMap = new HashMap(); + // 组装请求URL和参数 + String url = core.getLoginInfo().get(StorageLoginInfoEnum.syncUrl.getKey()) + URLEnum.SYNC_CHECK_URL.getUrl(); + List params = new ArrayList(); + for (BaseParaEnum baseRequest : BaseParaEnum.values()) { + params.add(new BasicNameValuePair(baseRequest.para().toLowerCase(), + core.getLoginInfo().get(baseRequest.value()).toString())); + } + params.add(new BasicNameValuePair("r", String.valueOf(new Date().getTime()))); + params.add(new BasicNameValuePair("synckey", (String) core.getLoginInfo().get("synckey"))); + params.add(new BasicNameValuePair("_", String.valueOf(new Date().getTime()))); + SleepUtils.sleep(7); + try { + HttpEntity entity = myHttpClient.doGet(url, params, true, null); + if (entity == null) { + resultMap.put("retcode", "9999"); + resultMap.put("selector", "9999"); + return resultMap; + } + String text = EntityUtils.toString(entity); + String regEx = "window.synccheck=\\{retcode:\"(\\d+)\",selector:\"(\\d+)\"\\}"; + Matcher matcher = CommonTools.getMatcher(regEx, text); + if (!matcher.find() || matcher.group(1).equals("2")) { + LOG.info(String.format("Unexpected sync check result: %s", text)); + } else { + resultMap.put("retcode", matcher.group(1)); + resultMap.put("selector", matcher.group(2)); + } + } catch (Exception e) { + e.printStackTrace(); + } + return resultMap; + } + + /** + * 解析登录返回的消息,如果成功登录,则message为空 + * + * @param result + * @return + */ + public String getLoginMessage(String result) { + String[] strArr = result.split(""); + String[] rs = strArr[1].split(""); + if (rs != null && rs.length > 1) { + return rs[0]; + } + return ""; + } } diff --git a/src/main/java/cn/zhouyafeng/itchat4j/thread/CheckLoginStatusThread.java b/src/main/java/cn/zhouyafeng/itchat4j/thread/CheckLoginStatusThread.java index 382f3e7..8de8a4e 100644 --- a/src/main/java/cn/zhouyafeng/itchat4j/thread/CheckLoginStatusThread.java +++ b/src/main/java/cn/zhouyafeng/itchat4j/thread/CheckLoginStatusThread.java @@ -1,5 +1,8 @@ package cn.zhouyafeng.itchat4j.thread; +import cn.zhouyafeng.itchat4j.service.ILoginService; +import cn.zhouyafeng.itchat4j.service.impl.LoginServiceImpl; +import cn.zhouyafeng.itchat4j.utils.EmailUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,26 +16,56 @@ * 微信会有心跳包,LoginServiceImpl.syncCheck()正常在线情况下返回的消息中retcode报文应该为"0",心跳间隔一般在25秒, * 那么可以通过最后收到正常报文的时间来作为判断是否在线的依据。若报文间隔大于60秒,则认为已掉线。 *

- * + * * @author https://github.com/yaphone - * @date 创建时间:2017年5月17日 下午10:53:15 * @version 1.0 - * + * @date 创建时间:2017年5月17日 下午10:53:15 */ public class CheckLoginStatusThread implements Runnable { - private static Logger LOG = LoggerFactory.getLogger(CheckLoginStatusThread.class); - private Core core = Core.getInstance(); - - @Override - public void run() { - while (core.isAlive()) { - long t1 = System.currentTimeMillis(); // 秒为单位 - if (t1 - core.getLastNormalRetcodeTime() > 60 * 1000) { // 超过60秒,判为离线 - core.setAlive(false); - LOG.info("微信已离线"); - } - SleepUtils.sleep(10 * 1000); // 休眠10秒 - } - } + private static Logger LOG = LoggerFactory.getLogger(CheckLoginStatusThread.class); + private Core core = Core.getInstance(); + + @Override + public void run() { + while (core.isAlive()) { + long t1 = System.currentTimeMillis(); // 秒为单位 + if (t1 - core.getLastNormalRetcodeTime() > 60 * 1000) { // 超过60秒,判为离线 + core.setAlive(false); + core.setLogin(false); + // 清除历史状态 + core.setMemberCount(0); + + core.getMemberList().clear(); + core.getContactList().clear(); + core.getGroupList().clear(); + + core.getGroupMemeberMap().clear(); + core.getPublicUsersList().clear(); + core.getSpecialUsersList().clear(); + + core.getGroupIdList().clear(); + core.getGroupNickNameList().clear(); + + core.getLoginInfo().clear(); + LOG.info("微信已离线"); + + if (null != core.getEmailUtils() && null != core.getEmails()) { + EmailUtils emailUtils = core.getEmailUtils(); + for (EmailUtils.Email email : core.getEmails()) { + boolean flag = emailUtils.setMail(email); + if (flag) { + try { + emailUtils.send(); + } catch (Exception e) { + e.printStackTrace(); + LOG.info("邮件发送失败"); + } + } + } + } + } + SleepUtils.sleep(10 * 1000); // 休眠10秒 + } + } } diff --git a/src/main/java/cn/zhouyafeng/itchat4j/utils/EmailUtils.java b/src/main/java/cn/zhouyafeng/itchat4j/utils/EmailUtils.java new file mode 100644 index 0000000..74aafe6 --- /dev/null +++ b/src/main/java/cn/zhouyafeng/itchat4j/utils/EmailUtils.java @@ -0,0 +1,199 @@ +package cn.zhouyafeng.itchat4j.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +import javax.mail.*; +import javax.mail.internet.*; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Properties; + +/** + *

+ * 发送Email工具类 + *

+ * + * @author zhu.yuancheng + * @create 2020-01-03 14:26 + * @package com.ustcinfo.mail.utils + */ +public class EmailUtils { + private static Logger LOG = LoggerFactory.getLogger(EmailUtils.class); + private EmailConfig config; + + private static final class MailAuthenticator extends Authenticator { + private String user; + private String pwd; + + public MailAuthenticator(String user, String pwd) { + this.user = user; + this.pwd = pwd; + } + + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(user, pwd); + } + } + + public static final class EmailConfig { + private String smtpHost; // 邮件服务器地址 + private String sendUserName; // 发件人的用户名 + private String sendUserPass; // 发件人密码 + + public EmailConfig(String smtpHost, String sendUserName, String sendUserPass) { + this.smtpHost = smtpHost; + this.sendUserName = sendUserName; + this.sendUserPass = sendUserPass; + } + } + + public static final class Email { + private String to; // 接收人 + private String[] cc; // 抄送人 + private String body; // 邮件正文 + private String subject; // 邮件标题 + private List attachments; // 附件:文件地址 + + public Email setTo(String to) { + this.to = to; + return this; + } + + public Email setCc(String[] cc) { + this.cc = cc; + return this; + } + + public Email setBody(String body) { + this.body = body; + return this; + } + + public Email setSubject(String subject) { + this.subject = subject; + return this; + } + + public Email setAttachments(List attachments) { + this.attachments = attachments; + return this; + } + } + + private MimeMessage mimeMsg; // 邮件对象 + private Session session; + private Properties props; + private Multipart mp;// 附件添加的组件 + + /** + * 初始化邮件配置 + * + * @param config + */ + public void init(EmailConfig config) { + this.config = config; + if (props == null) { + props = System.getProperties(); + } + props.put("mail.smtp.host", config.smtpHost); + props.put("mail.smtp.auth", "true"); // 需要身份验证 + session = Session.getDefaultInstance(props, new MailAuthenticator(config.sendUserName, config.sendUserPass)); + // 置true可以在控制台(console)上看到发送邮件的过程 + session.setDebug(false); + // 用session对象来创建并初始化邮件对象 + mimeMsg = new MimeMessage(session); + } + + /** + * 设置邮件内容 + * + * @param email + * @return + */ + public boolean setMail(Email email) { + if (null == config) { + LOG.warn("邮件内容设置失败,未找到邮件配置"); + return false; + } + try { + // 添加发送人 + mimeMsg.setFrom(new InternetAddress(config.sendUserName)); + + // 添加接收人 + if (null != email.to) { + mimeMsg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email.to)); + } + + // 添加抄送人 + if (null != email.cc && email.cc.length > 0) { + mimeMsg.setRecipients(Message.RecipientType.CC, toInternetAddressList(email.cc)); + } + + // 生成附件组件的实例 + mp = new MimeMultipart(); + + // 在组件上添加邮件文本 + BodyPart bp = new MimeBodyPart(); + bp.setContent("" + email.body, "text/html;charset=UTF-8"); + mp.addBodyPart(bp); + + // 添加邮件标题 + mimeMsg.setSubject(email.subject); + + if (null != email.attachments) { + for (String attachment : email.attachments) { + if (attachment != null && attachment.length() > 0) { + BodyPart attachment_bp = new MimeBodyPart(); + FileDataSource filed = new FileDataSource(attachment); + attachment_bp.setDataHandler(new DataHandler(filed)); + attachment_bp.setFileName(MimeUtility.encodeText(filed.getName(), "utf-8", null)); // 解决附件名称乱码 + mp.addBodyPart(attachment_bp);// 添加附件 + } + } + } + } catch (MessagingException e) { + e.printStackTrace(); + return false; + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 发送邮件 + * + * @return + * @throws Exception + */ + public boolean send() throws Exception { + if (null == config) { + LOG.warn("邮件发送失败,未找到邮件配置"); + return false; + } + mimeMsg.setContent(mp); + mimeMsg.saveChanges(); + LOG.info("正在发送邮件...."); + Transport transport = session.getTransport("smtp"); + // 连接邮件服务器并进行身份验证 + transport.connect(config.smtpHost, config.sendUserName, config.sendUserPass); + // 发送邮件 + transport.sendMessage(mimeMsg, mimeMsg.getRecipients(Message.RecipientType.TO)); + LOG.info("发送邮件成功!"); + transport.close(); + return true; + } + + private Address[] toInternetAddressList(String[] cc) throws AddressException { + Address[] addresses = new Address[cc.length]; + for (int i = 0; i < cc.length; i++) { + addresses[i] = new InternetAddress(cc[i]); + } + return addresses; + } +} diff --git a/src/main/java/cn/zhouyafeng/itchat4j/utils/StringUtils.java b/src/main/java/cn/zhouyafeng/itchat4j/utils/StringUtils.java new file mode 100644 index 0000000..8b8b30b --- /dev/null +++ b/src/main/java/cn/zhouyafeng/itchat4j/utils/StringUtils.java @@ -0,0 +1,27 @@ +package cn.zhouyafeng.itchat4j.utils; + +/** + * Created by loneyfall on 2019/5/16. + */ +public class StringUtils { + + /** + * 判断是否为空字符串 + * + * @param str + * @return + */ + public static boolean isEmpty(String str) { + return str == null || str.trim().length() == 0; + } + + /** + * 判断字符串是否非空 + * + * @param str + * @return + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } +}