├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── cn │ │ └── zhouyafeng │ │ └── itchat4j │ │ ├── Wechat.java │ │ ├── api │ │ ├── AssistTools.java │ │ ├── MessageTools.java │ │ └── WechatTools.java │ │ ├── beans │ │ ├── AppInfo.java │ │ ├── BaseMsg.java │ │ └── RecommendInfo.java │ │ ├── controller │ │ └── LoginController.java │ │ ├── core │ │ ├── Core.java │ │ └── MsgCenter.java │ │ ├── face │ │ └── IMsgHandlerFace.java │ │ ├── service │ │ ├── ILoginService.java │ │ └── impl │ │ │ └── LoginServiceImpl.java │ │ ├── thread │ │ └── CheckLoginStatusThread.java │ │ └── utils │ │ ├── Config.java │ │ ├── ConstantConfigEnum.java │ │ ├── MsgKeywords.java │ │ ├── MyHttpClient.java │ │ ├── SleepUtils.java │ │ ├── enums │ │ ├── MsgCodeEnum.java │ │ ├── MsgTypeEnum.java │ │ ├── OsNameEnum.java │ │ ├── ResultEnum.java │ │ ├── RetCodeEnum.java │ │ ├── StorageLoginInfoEnum.java │ │ ├── URLEnum.java │ │ ├── VerifyFriendEnum.java │ │ └── parameters │ │ │ ├── BaseParaEnum.java │ │ │ ├── LoginParaEnum.java │ │ │ ├── StatusNotifyParaEnum.java │ │ │ └── UUIDParaEnum.java │ │ └── tools │ │ ├── CommonTools.java │ │ └── DownloadTools.java └── resources │ └── log4j.properties └── test └── java └── cn └── zhouyafeng └── itchat4j └── demo ├── demo1 ├── MyTest.java └── SimpleDemo.java ├── demo2 └── TulingRobot.java ├── demo3 └── PicYourFriends.java └── unuseful └── UnusefulDemo.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | /.idea 6 | /.DS_Store 7 | *.iml 8 | /login/ 9 | /bin/ 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # itchat4j -- 用Java扩展个人微信号的能力 2 | 3 | 4 | 5 | # uos 的参数更新了,又可以用了。 6 | ## ~~UOS已经失效了,不能用了。建议使用原版。~~ 7 | 8 | 9 | 欢迎star、fork、 pull requests、 issue。 10 | 11 | ### 原项目地址:[yaphone/itchat4j](https://github.com/yaphone/itchat4j),。 12 | 13 | ## 示例项目程序[yaphone/itchat4jdemo](https://github.com/yaphone/itchat4jdemo)。 14 | 15 | ## 22.08.09 16 | 参考itchat-uos 1.5版本,修改请求参数,增加uos参数,现在已经可以模拟uos访问。 17 | 18 | ## 21.12.25 19 | 参考itchat-uos 1.4.1版本,修改请求参数,增加uos参数,现在已经可以模拟uos访问。 20 | ### 来源 21 | 22 | [itchat](https://github.com/littlecodersh/ItChat)是一个非常优秀的开源微信个人号接口,使用Python语言开发,提供了简单易用的API,可以很方便地对个人微信号进行扩展,实现自动回复,微信挂机机器人等,一直在关注这个项目,基于itchat开发过[一个小项目](https://github.com/yaphone/RasWxNeteaseMusic),用来控制我的树莓派来播放音乐,效果还不错。 23 | 24 | 一直想实现一个java版本的itchat,由于工作太忙导致一拖再拖,这段时间稍微空闲了一些,仔细阅读了itchat的源码,终于完成了一个基础版本,由于主要灵感来源于itchat项目,所以这个项目的就暂时定名为[itchat4j](https://github.com/yaphone/itchat4j)吧。 25 | 26 | 27 | 28 | ## 项目介绍 29 | 30 | > itchat是一个开源的微信个人号接口,使用Python调用微信从未如此简单。使用短短的几十行代码,你就可以完成一个能够处理所有信息的微信机器人。当然,itchat的使用远不止一个机器人,更多的功能等着你来发现,如今微信已经成为了个人社交的很大一部分,希望这个项目能够帮助你扩展你的个人的微信号、方便自己的生活。(引自itchat项目) 31 | 32 | 你可以轻松将[itchat4j](https://github.com/yaphone/itchat4j)其集成在你个人的Java应用中,无论是SpringMVC、桌面程序还是嵌入式程序,只要使用的JDK是1.7以上的版本,都可以轻松接入。玩法很多,请打开你的脑洞,比如这些: 33 | 34 | - Just for fun,把个人微信号扩展为"公众号",在朋友面前装个X吧。 35 | - 集成在你的个人应用(SpringMVC、Servlet、GUI)中,为应用提供更强的服务能力。 36 | - 部署在你的服务器上,将监控信息、日志发送到你的微信号。 37 | - 微信机器人,专业陪聊工具 38 | - 控制树莓派、智能家居、智能硬件等具有开放接口的设备 39 | - Anything you want ... 40 | 41 | 42 | 43 | 44 | ## 更新日志 45 | 46 | - 2017-7-21:1.消息体封装为POJO类,更易操作。 2.增加处理文件消息接口。 47 | 48 | - 2017-6-28:增加被动添加好友功能。 49 | 50 | - 2017-6-23:增加获取群好友昵称功能,修复已知问题。 51 | 52 | - 2017-6-16:增加微信状态维护。 53 | 54 | - 2017-6-13:修复获取群列表为空问题,增加根据群ID获取群成员列表方法。 55 | 56 | ​ 57 | 58 | ## API说明 59 | 60 | *项目在不断更新中,API会有变动,请以具体代码为准* 61 | 62 | 目前在`package cn.zhouyafeng.itchat4j.api`包中有两个静态类,即`MessageTools`和`WechatTools`,目前对外暴露的方法有: 63 | 64 | #### 1.获取好友昵称列表 WechatTools.getContactNickNameList() 65 | 66 | 此方法会返回好友昵称列表,其函数声明为: 67 | 68 | ``` 69 | public static List getContactNickNameList() 70 | ``` 71 | 72 | #### 2.获取好友完整信息列表 WechatTools.getContactList() 73 | 74 | 此方法会返回好友的完整信息,如昵称、备注、地区、头像链接等,其函数声明为: 75 | 76 | ```java 77 | public static List getContactList() 78 | ``` 79 | 80 | #### 3.获取群列表 WechatTools.getGroupIdList() 81 | 82 | 群列表与好友列表不同,在登陆后群列表其实是空的,只有主动发送消息或者收到一条群消息时,才能获取到这个群的信息,群列表会记录这个群的id,其格式为`@@d052d34b9c9228830363013ee53deb461404f80ea353dbdd8fc9391cbf5f1c46`。调用此方法会返回已知的群列表。其声明函数为: 83 | 84 | ``` 85 | public static List getGroupIdList() 86 | ``` 87 | 88 | #### 4.根据群ID获取群成员WechatTools.getMemberListByGroupId() 89 | 90 | 此方法根据群ID(格式为`@@d052d34b9c9228830363013ee53deb461404f80ea353dbdd8fc9391cbf5f1c46`)获取群成员列表。其函数声明为: 91 | 92 | ```java 93 | public static JSONArray getMemberListByGroupId(String groupId) 94 | ``` 95 | 96 | #### 5.退出微信 WechatTools.logout() 97 | 98 | 退出itchat4j,不再处理消息,其函数声明为: 99 | 100 | ``` 101 | public static void logout() 102 | ``` 103 | 104 | #### 6.获取微信在线状态WechatTools.getWechatStatus() 105 | 106 | 查询微信在线状态,在线返回`true`,离线返回`false`,其函数声明为 107 | 108 | ```java 109 | public static boolean getWechatStatus() 110 | ``` 111 | 112 | #### 7.获取群昵称列表WechatTools.getGroupNickNameList() 113 | 114 | 获取群昵称列表,函数声明为: 115 | 116 | ```java 117 | public static List getGroupNickNameList() 118 | ``` 119 | 120 | #### 8.根据用户昵称修改用户备注MessageTools.remarkNameByNickName(String nickName, String remName) 121 | 122 | 根据用户昵称修改用户备注名称,其函数声明为: 123 | 124 | ``` 125 | public static void remarkNameByNickName(String nickName, String remName) 126 | ``` 127 | 128 | #### 9. 根据好友昵称发送文本消息,MessageTools.sendMsgByNickName(String text, String nickName) 129 | 130 | 此方法根据用户昵称发送文本消息,注意,用户需在你的好友列表里,否则发送失败,如果你的好友列表里有存在昵称一样的多个用户,则只会给第一个匹配的好友发送消息。方法接受两个参数,`text`为要发送的文本消息,`nickName`为要发送消息的好友昵称,成功发送时返回true,失败返回false。其函数声明为: 131 | 132 | ``` 133 | public static boolean sendMsgByNickName(String text, String nickName) 134 | ``` 135 | 136 | #### 10.根据ID发送文本消息, MessageTools.sendMsgById(String text, String id) 137 | 138 | 根据ID发送文本消息,发送者ID可以从`msg`里通过`msg.getString("FromUserName")`获取,格式为`@@d052d34b9c9228830363013ee53deb461404f80ea353dbdd8fc9391cbf5f1c46`(群消息)或`@a257b99314d8313862cd44ab02fe0f81`(非群消息),调用此方法可向指定id发送消息。其函数声明为: 139 | 140 | ``` 141 | public static void sendMsgById(String text, String id) 142 | ``` 143 | 144 | #### 11.根据好友昵称发送图片消息,MessageTools.sendPicMsgByNickName(String nickName, String filePath) 145 | 146 | 此方法根据好友昵称发送图片消息,`filePath`为图片文件路径,如`D:/itchat4j/pic/test.jpg`,成功返回true,失败返回false。其函数声明为: 147 | 148 | ``` 149 | public static boolean sendPicMsgByNickName(String nickName, String filePath) 150 | ``` 151 | 152 | #### 12.根据ID发送图片消息,MessageTools.sendPicMsgByUserId(String userId, String filePath) 153 | 154 | 此方法根据好友ID发送图片消息,filePath`为图片文件路径,如`D:/itchat4j/pic/test.jpg`,成功返回true,失败返回false。其函数声明为: 155 | 156 | ``` 157 | public static boolean sendPicMsgByUserId(String userId, String filePath) 158 | ``` 159 | 160 | #### 13.根据好友昵称发送文件消息,MessageTools.sendFileMsgByNickName(String nickName, String filePath) 161 | 162 | 此方法根据好友昵称发送文件消息,文件可以为多种类型,如txt、PDF、小视频、语音、excel、docx等,发送时请保证文件后缀名正确。成功返回true,失败返回false。其函数声明为: 163 | 164 | ``` 165 | public static boolean sendPicFileByNickName(String nickName, String filePath) 166 | ``` 167 | 168 | #### 14.根据ID发送文件消息,MessageTools.sendFileMsgByNickName(String nickName, String filePath) 169 | 170 | 此方法根据好友昵称发送文件消息,成功返回true,失败返回false。其函数声明为: 171 | 172 | ``` 173 | public static boolean sendFileMsgByUserId(String userId, String filePath) 174 | ``` 175 | 176 | #### 15.处理好友添加请求,MessageTools.addFriend(BaseMsg msg, boolean accept) 177 | 178 | 当收到好友添加请求时,可调用此函数进行处理,msg为收到的好友添加请求消息,accept传true为接受好友请求,false为拒绝,其函数声明为: 179 | 180 | ```java 181 | public static void addFriend(BaseMsg msg, boolean accept) 182 | ``` 183 | 184 | 185 | 186 | 187 | 188 | ## TODO List 即将支持/正在开发 189 | 190 | - 拉人进群功能 191 | 192 | 193 | ## 如何使用 194 | 195 | *项目在不断更新中,导入后的项目结构会有变动* 196 | 197 | itchat4j是一个Maven项目,下载源码后,可以以Maven项目的形式导入,导入后的项目结构如下图: 198 | 199 | ![itchat4j项目结构](http://oj5vdtyuu.bkt.clouddn.com/itchat4j%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%842.png) 200 | 201 | src/main/java是itchat4j的项目源码,在src/test/java目录下有两个小Demo:一个是基本功能的小示例,当前,itchat4j可以处理四类基本信息,文本、语音、图片和小视频,该示例在收到文本信息后自动回复,回复内容为收到的文本,当收到图片、语音、小视频时可以保存到指定的目录;一个是微信接入图灵机器人的小例子,如下图。 202 | 203 | ### 微信机器人使用截图 204 | 205 | ![Windows控制台](http://oj5vdtyuu.bkt.clouddn.com/windows%E5%8F%AF%E8%BF%90%E8%A1%8C%E7%A8%8B%E5%BA%8F.png) 206 | 207 | ![微信机器人](http://oj5vdtyuu.bkt.clouddn.com/%E5%BE%AE%E4%BF%A1%E6%9C%BA%E5%99%A8%E4%BA%BA.jpg) 208 | 209 | ![控制台收到的消息](http://oj5vdtyuu.bkt.clouddn.com/%E6%8E%A7%E5%88%B6%E5%8F%B0%E6%94%B6%E5%88%B0%E7%9A%84%E6%B6%88%E6%81%AF.png) 210 | 211 | 212 | 213 | ## 消息格式 214 | 215 | 对于收到的消息,itchat4j将其以POJO类的形式进行了封装,即在`package cn.zhouyafeng.itchat4j.beans`包中的`BaseMsg类`,其声明如下: 216 | 217 | ```java 218 | /** 219 | * 收到的微信消息 220 | * 221 | * @author https://github.com/yaphone 222 | * @date 创建时间:2017年7月3日 下午10:28:06 223 | * @version 1.0 224 | * 225 | */ 226 | public class BaseMsg implements Serializable { 227 | /** 228 | * 229 | */ 230 | private static final long serialVersionUID = 1L; 231 | private int subMsgType; 232 | private int voiceLength; 233 | private String fileName; 234 | private int imgHeight; 235 | private String toUserName; 236 | private int hasProductId; 237 | private int imgStatus; 238 | private String url; 239 | private int imgWidth; 240 | private int forwardFlag; 241 | private int status; 242 | private String Ticket; 243 | /** 推荐消息报文 **/ 244 | private RecommendInfo recommendInfo; 245 | private long createTime; 246 | private String newMsgId; 247 | /** 文本消息内容 **/ 248 | private String text; 249 | /** 消息类型 **/ 250 | private int msgType; 251 | /** 是否为群消息 **/ 252 | private boolean groupMsg; 253 | private String msgId; 254 | private int statusNotifyCode; 255 | private AppInfo appInfo; 256 | private int appMsgType; 257 | private String Type; 258 | private int playLength; 259 | private String mediaId; 260 | private String content; 261 | private String statusNotifyUserName; 262 | /** 消息发送者ID **/ 263 | private String fromUserName; 264 | private String oriContent; 265 | private String fileSize; 266 | 267 | //省略getter/setter方法 268 | 269 | } 270 | ``` 271 | 272 | 273 | 274 | ## 如何使用 275 | 276 | 如果你想引入自己的项目,请切换到`pom.xml`目录,执行`mvn package`命令,将生成的`itchat4j`的jar包引入即可。 277 | 278 | 279 | 280 | ## 简单入门教程 281 | 282 | *项目不断更新中,教程仅供参考* 283 | 284 | 接下来,通过两个小Demo来演示一下如何使用itchat4j来扩展你的个人微信号,入门教程的项目源码可以从[此处下载](https://github.com/yaphone/itchat4jdemo)。以下几个demo在项目的`src/test/java`路径下可以找到,项目不断更新,请以实际代码为准。 285 | 286 | ### Demo1: SimpleDemo 287 | 288 | 这个小Demo将会将收到的文本消息发送给发件人,如果是图片、语音或者小视频消息,将会保存在我们指定的路径下。 289 | 290 | 首先需要新建一个类来实现`IMsgHandlerFace`这个接口,这个类要做的就是我们需要完成的逻辑,该接口有若干个需要自己实现的方法,如`textMsgHandle`用于处理文本信息,`picMsgHandle`用于处理图片信息,`viedoMsgHandle`用于处理小视频信息,`voiceMsgHandle`用于处理语音信息等,代码如下: 291 | 292 | ```java 293 | package cn.zhouyafeng.itchat4j.face; 294 | 295 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 296 | 297 | /** 298 | * 消息处理接口 299 | * 300 | * @author https://github.com/yaphone 301 | * @date 创建时间:2017年4月20日 上午12:13:49 302 | * @version 1.0 303 | * 304 | */ 305 | public interface IMsgHandlerFace { 306 | /** 307 | * 308 | * @author https://github.com/yaphone 309 | * @date 2017年4月20日 上午12:15:00 310 | * @param msg 311 | * @return 312 | */ 313 | public String textMsgHandle(BaseMsg msg); 314 | 315 | /** 316 | * 处理图片消息 317 | * 318 | * @author https://github.com/yaphone 319 | * @date 2017年4月21日 下午11:07:06 320 | * @param msg 321 | * @return 322 | */ 323 | public String picMsgHandle(BaseMsg msg); 324 | 325 | /** 326 | * 处理声音消息 327 | * 328 | * @author https://github.com/yaphone 329 | * @date 2017年4月22日 上午12:09:44 330 | * @param msg 331 | * @return 332 | */ 333 | public String voiceMsgHandle(BaseMsg msg); 334 | 335 | /** 336 | * 处理小视频消息 337 | * 338 | * @author https://github.com/yaphone 339 | * @date 2017年4月23日 下午12:19:50 340 | * @param msg 341 | * @return 342 | */ 343 | public String viedoMsgHandle(BaseMsg msg); 344 | 345 | /** 346 | * 处理名片消息 347 | * 348 | * @author https://github.com/yaphone 349 | * @date 2017年5月1日 上午12:50:50 350 | * @param msg 351 | * @return 352 | */ 353 | public String nameCardMsgHandle(BaseMsg msg); 354 | 355 | /** 356 | * 处理系统消息 357 | * 358 | * @author Relyn 359 | * @date 2017年6月21日17:43:51 360 | * @param msg 361 | * @return 362 | */ 363 | public void sysMsgHandle(BaseMsg msg); 364 | 365 | /** 366 | * 处理确认添加好友消息 367 | * 368 | * @date 2017年6月28日 下午10:15:30 369 | * @param msg 370 | * @return 371 | */ 372 | public String verifyAddFriendMsgHandle(BaseMsg msg); 373 | 374 | /** 375 | * 处理收到的文件消息 376 | * 377 | * @date 2017年7月21日 下午11:59:14 378 | * @param msg 379 | * @return 380 | */ 381 | public String mediaMsgHandle(BaseMsg msg); 382 | 383 | } 384 | ``` 385 | 386 | 在每个接口方法中,需要处理的消息均为上面介绍的BaseMsg的POJO类,在`textMsgHandler`中,通过`msg.getText()`就可以获取收到的文本信息,然后作进一步处理,比如接入图灵机器人、消息自动回复等,我们需要在这个方法中返回一个字符串,即是需要回复给好友的消息,在SimpleDemo这个示例中,我们直接回复收到的原文本消息。 387 | 388 | 在`picMsgHandle`、`voiceMsgHandle`、`viedoMsgHandle`这三个方法中,我们需要将这些消息下载下来,然后再作进一步处理,所以需要为每种类型的消息提供一个保存路径,然后调用`DownloadTools.getDownloadFn`方法可以将这三种类型的消息下载下来。`DownloadTools.getDownloadFn`方法提供下载图片、语音、小视频的功能,需要三个参数,第一个参数为我们收到的msg,第二个参数为`MsgType`,也就是消息类型,图片、语音、小视频分别对应`MsgTypeEnum.PIC.getType()`、`MsgTypeEnum.VOICE.getType()`、`MsgTypeEnum.VIEDO.getType()`,然后第三个参数就是保存这些消息的路径了。 389 | 390 | 另外还有一些需要处理的其它消息,如系统消息、名片消息、处理好友请求消息等,按业务需求进行实现即可。 391 | 392 | 就不多说了,让代码和注释君自述吧,有不明白的地方,可以在Issue中提出来。 393 | 394 | ```java 395 | package cn.zhouyafeng.itchat4j.demo.demo1; 396 | 397 | import java.io.File; 398 | import java.text.SimpleDateFormat; 399 | import java.util.Date; 400 | 401 | import org.apache.log4j.Logger; 402 | 403 | import cn.zhouyafeng.itchat4j.api.MessageTools; 404 | import cn.zhouyafeng.itchat4j.api.WechatTools; 405 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 406 | import cn.zhouyafeng.itchat4j.beans.RecommendInfo; 407 | import cn.zhouyafeng.itchat4j.core.Core; 408 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 409 | import cn.zhouyafeng.itchat4j.utils.enums.MsgTypeEnum; 410 | import cn.zhouyafeng.itchat4j.utils.tools.DownloadTools; 411 | 412 | /** 413 | * 简单示例程序,收到文本信息自动回复原信息,收到图片、语音、小视频后根据路径自动保存 414 | * 415 | * @author https://github.com/yaphone 416 | * @date 创建时间:2017年4月25日 上午12:18:09 417 | * @version 1.0 418 | * 419 | */ 420 | public class SimpleDemo implements IMsgHandlerFace { 421 | Logger LOG = Logger.getLogger(SimpleDemo.class); 422 | 423 | @Override 424 | public String textMsgHandle(BaseMsg msg) { 425 | // String docFilePath = "D:/itchat4j/pic/1.jpg"; // 这里是需要发送的文件的路径 426 | if (!msg.isGroupMsg()) { // 群消息不处理 427 | // String userId = msg.getString("FromUserName"); 428 | // MessageTools.sendFileMsgByUserId(userId, docFilePath); // 发送文件 429 | // MessageTools.sendPicMsgByUserId(userId, docFilePath); 430 | String text = msg.getText(); // 发送文本消息,也可调用MessageTools.sendFileMsgByUserId(userId,text); 431 | LOG.info(text); 432 | if (text.equals("111")) { 433 | WechatTools.logout(); 434 | } 435 | if (text.equals("222")) { 436 | WechatTools.remarkNameByNickName("yaphone", "Hello"); 437 | } 438 | if (text.equals("333")) { // 测试群列表 439 | System.out.print(WechatTools.getGroupNickNameList()); 440 | System.out.print(WechatTools.getGroupIdList()); 441 | System.out.print(Core.getInstance().getGroupMemeberMap()); 442 | } 443 | return text; 444 | } 445 | return null; 446 | } 447 | 448 | @Override 449 | public String picMsgHandle(BaseMsg msg) { 450 | String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());// 这里使用收到图片的时间作为文件名 451 | String picPath = "D://itchat4j/pic" + File.separator + fileName + ".jpg"; // 调用此方法来保存图片 452 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.PIC.getType(), picPath); // 保存图片的路径 453 | return "图片保存成功"; 454 | } 455 | 456 | @Override 457 | public String voiceMsgHandle(BaseMsg msg) { 458 | String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 459 | String voicePath = "D://itchat4j/voice" + File.separator + fileName + ".mp3"; 460 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VOICE.getType(), voicePath); 461 | return "声音保存成功"; 462 | } 463 | 464 | @Override 465 | public String viedoMsgHandle(BaseMsg msg) { 466 | String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 467 | String viedoPath = "D://itchat4j/viedo" + File.separator + fileName + ".mp4"; 468 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VIEDO.getType(), viedoPath); 469 | return "视频保存成功"; 470 | } 471 | 472 | @Override 473 | public String nameCardMsgHandle(BaseMsg msg) { 474 | return "收到名片消息"; 475 | } 476 | 477 | @Override 478 | public void sysMsgHandle(BaseMsg msg) { // 收到系统消息 479 | String text = msg.getContent(); 480 | LOG.info(text); 481 | } 482 | 483 | @Override 484 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 485 | MessageTools.addFriend(msg, true); // 同意好友请求,false为不接受好友请求 486 | RecommendInfo recommendInfo = msg.getRecommendInfo(); 487 | String nickName = recommendInfo.getNickName(); 488 | String province = recommendInfo.getProvince(); 489 | String city = recommendInfo.getCity(); 490 | String text = "你好,来自" + province + city + "的" + nickName + ", 欢迎添加我为好友!"; 491 | return text; 492 | } 493 | 494 | } 495 | 496 | ``` 497 | 498 | 之后我们需要将实现`IMsgHandlerFace`接口的类【注入】到`Wechat`中来启动服务,`Wechat`是服务的主入口,其构造函数接受两个参数,一个是我们刚才实现`IMsgHandlerFace`接口的类,另一个是保存登陆二维码图片的路径。之后在`Wechat`对象上调用`start()`方法来启动服务,会在我们刚才传入的路径下生成一个`QR.jpg`文件,即是登陆二维码,通过手机微信扫描后即可登陆,服务启动,处理逻辑开始工作。这里有一点需要注意:*二维码图片如果超过一定时间未扫描会过期,过期时会自动更新,所以你可能需要重新打开图片*。 499 | 500 | 额,文字还是太苍白,让代码和注释君自述吧。 501 | 502 | ```Java 503 | 504 | /** 505 | * 506 | * @author https://github.com/yaphone 507 | * @date 创建时间:2017年4月28日 上午12:44:10 508 | * @version 1.0 509 | * 510 | */ 511 | public class MyTest { 512 | public static void main(String[] args) { 513 | String qrPath = "D://itchat4j//login"; // 保存登陆二维码图片的路径,这里需要在本地新建目录 514 | IMsgHandlerFace msgHandler = new SimpleDemo(); // 实现IMsgHandlerFace接口的类 515 | Wechat wechat = new Wechat(msgHandler, qrPath); // 【注入】 516 | wechat.start(); // 启动服务,会在qrPath下生成一张二维码图片,扫描即可登陆,注意,二维码图片如果超过一定时间未扫描会过期,过期时会自动更新,所以你可能需要重新打开图片 517 | } 518 | } 519 | ``` 520 | 521 | ### Demo2 图灵机器人 522 | 523 | > 图灵机器人大脑具备强大的中文语义分析能力,可准确理解中文含义并作出回应,是最擅长聊中文的机器人大脑,赋予软硬件产品自然流畅的人机对话能力。(引自百度百科) 524 | 525 | 这个示例中我们接入图灵机器人的API,将收到的好友的文本信息发送给图灵机器人,并将机器人回复的消息发送给好友,接下来还是把舞台交代码和注释君吧。 526 | 527 | ```Java 528 | package cn.zhouyafeng.itchat4j.demo.demo2; 529 | 530 | import java.io.File; 531 | import java.util.Date; 532 | import java.util.HashMap; 533 | import java.util.Map; 534 | import java.util.logging.Logger; 535 | 536 | import org.apache.http.HttpEntity; 537 | import org.apache.http.util.EntityUtils; 538 | 539 | import com.alibaba.fastjson.JSON; 540 | import com.alibaba.fastjson.JSONObject; 541 | 542 | import cn.zhouyafeng.itchat4j.Wechat; 543 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 544 | import cn.zhouyafeng.itchat4j.core.Core; 545 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 546 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 547 | import cn.zhouyafeng.itchat4j.utils.enums.MsgTypeEnum; 548 | import cn.zhouyafeng.itchat4j.utils.tools.DownloadTools; 549 | 550 | /** 551 | * 图灵机器人示例 552 | * 553 | * @author https://github.com/yaphone 554 | * @date 创建时间:2017年4月24日 上午12:13:26 555 | * @version 1.0 556 | * 557 | */ 558 | public class TulingRobot implements IMsgHandlerFace { 559 | Logger logger = Logger.getLogger("TulingRobot"); 560 | MyHttpClient myHttpClient = Core.getInstance().getMyHttpClient(); 561 | String url = "http://www.tuling123.com/openapi/api"; 562 | String apiKey = "597b34bea4ec4c85a775c469c84b6817"; // 这里是我申请的图灵机器人API接口,每天只能5000次调用,建议自己去申请一个,免费的:) 563 | 564 | @Override 565 | public String textMsgHandle(BaseMsg msg) { 566 | String result = ""; 567 | String text = msg.getText(); 568 | Map paramMap = new HashMap(); 569 | paramMap.put("key", apiKey); 570 | paramMap.put("info", text); 571 | paramMap.put("userid", "123456"); 572 | String paramStr = JSON.toJSONString(paramMap); 573 | try { 574 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 575 | result = EntityUtils.toString(entity, "UTF-8"); 576 | JSONObject obj = JSON.parseObject(result); 577 | if (obj.getString("code").equals("100000")) { 578 | result = obj.getString("text"); 579 | } else { 580 | result = "处理有误"; 581 | } 582 | } catch (Exception e) { 583 | logger.info(e.getMessage()); 584 | } 585 | return result; 586 | } 587 | 588 | @Override 589 | public String picMsgHandle(BaseMsg msg) { 590 | return "收到图片"; 591 | } 592 | 593 | @Override 594 | public String voiceMsgHandle(BaseMsg msg) { 595 | String fileName = String.valueOf(new Date().getTime()); 596 | String voicePath = "D://itchat4j/voice" + File.separator + fileName + ".mp3"; 597 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VOICE.getType(), voicePath); 598 | return "收到语音"; 599 | } 600 | 601 | @Override 602 | public String viedoMsgHandle(BaseMsg msg) { 603 | String fileName = String.valueOf(new Date().getTime()); 604 | String viedoPath = "D://itchat4j/viedo" + File.separator + fileName + ".mp4"; 605 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VIEDO.getType(), viedoPath); 606 | return "收到视频"; 607 | } 608 | 609 | public static void main(String[] args) { 610 | IMsgHandlerFace msgHandler = new TulingRobot(); 611 | Wechat wechat = new Wechat(msgHandler, "D://itchat4j/login"); 612 | wechat.start(); 613 | } 614 | 615 | @Override 616 | public String nameCardMsgHandle(BaseMsg msg) { 617 | // TODO Auto-generated method stub 618 | return null; 619 | } 620 | 621 | @Override 622 | public void sysMsgHandle(BaseMsg msg) { 623 | // TODO Auto-generated method stub 624 | } 625 | 626 | @Override 627 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 628 | // TODO Auto-generated method stub 629 | return null; 630 | } 631 | 632 | @Override 633 | public String mediaMsgHandle(BaseMsg msg) { 634 | // TODO Auto-generated method stub 635 | return null; 636 | } 637 | 638 | } 639 | 640 | ``` 641 | 642 | ### Demo3 获取好友头像图片示例 643 | 644 | 代码自释吧,需要注意的是需要自己设定保存头像文件的路径,收到`111`消息后,将会下载所有好友的头像。 645 | 646 | ```java 647 | package cn.zhouyafeng.itchat4j.demo.demo3; 648 | 649 | import java.io.File; 650 | import java.io.FileOutputStream; 651 | import java.io.OutputStream; 652 | import java.util.List; 653 | 654 | import org.apache.http.HttpEntity; 655 | import org.apache.http.util.EntityUtils; 656 | import org.slf4j.Logger; 657 | import org.slf4j.LoggerFactory; 658 | 659 | import com.alibaba.fastjson.JSONObject; 660 | 661 | import cn.zhouyafeng.itchat4j.Wechat; 662 | import cn.zhouyafeng.itchat4j.api.WechatTools; 663 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 664 | import cn.zhouyafeng.itchat4j.core.Core; 665 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 666 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 667 | import cn.zhouyafeng.itchat4j.utils.enums.StorageLoginInfoEnum; 668 | 669 | /** 670 | * 此示例演示如何获取所有好友的头像 671 | * 672 | * @author https://github.com/yaphone 673 | * @date 创建时间:2017年6月26日 下午11:27:46 674 | * @version 1.0 675 | * 676 | */ 677 | public class PicYourFriends implements IMsgHandlerFace { 678 | private static Logger LOG = LoggerFactory.getLogger(PicYourFriends.class); 679 | private static final Core core = Core.getInstance(); 680 | private static final MyHttpClient myHttpClient = core.getMyHttpClient(); 681 | private static final String path = "D://itchat4j//head"; // 这里需要设置保存头像的路径 682 | 683 | @Override 684 | public String textMsgHandle(BaseMsg msg) { 685 | 686 | if (!msg.isGroupMsg()) { // 群消息不处理 687 | String text = msg.getText(); // 发送文本消息,也可调用MessageTools.sendFileMsgByUserId(userId,text); 688 | String baseUrl = "https://" + core.getIndexUrl(); // 基础URL 689 | String skey = (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()); 690 | if (text.equals("111")) { 691 | LOG.info("开始下载好友头像"); 692 | List friends = WechatTools.getContactList(); 693 | for (int i = 0; i < friends.size(); i++) { 694 | JSONObject friend = friends.get(i); 695 | String url = baseUrl + friend.getString("HeadImgUrl") + skey; 696 | // String fileName = friend.getString("NickName"); 697 | String headPicPath = path + File.separator + i + ".jpg"; 698 | 699 | HttpEntity entity = myHttpClient.doGet(url, null, true, null); 700 | try { 701 | OutputStream out = new FileOutputStream(headPicPath); 702 | byte[] bytes = EntityUtils.toByteArray(entity); 703 | out.write(bytes); 704 | out.flush(); 705 | out.close(); 706 | 707 | } catch (Exception e) { 708 | LOG.info(e.getMessage()); 709 | } 710 | 711 | } 712 | } 713 | } 714 | return null; 715 | } 716 | 717 | @Override 718 | public String picMsgHandle(BaseMsg msg) { 719 | // TODO Auto-generated method stub 720 | return null; 721 | } 722 | 723 | @Override 724 | public String voiceMsgHandle(BaseMsg msg) { 725 | // TODO Auto-generated method stub 726 | return null; 727 | } 728 | 729 | @Override 730 | public String viedoMsgHandle(BaseMsg msg) { 731 | // TODO Auto-generated method stub 732 | return null; 733 | } 734 | 735 | @Override 736 | public String nameCardMsgHandle(BaseMsg msg) { 737 | // TODO Auto-generated method stub 738 | return null; 739 | } 740 | 741 | @Override 742 | public void sysMsgHandle(BaseMsg msg) { 743 | // TODO Auto-generated method stub 744 | 745 | } 746 | 747 | public static void main(String[] args) { 748 | String qrPath = "D://itchat4j//login"; // 保存登陆二维码图片的路径,这里需要在本地新建目录 749 | IMsgHandlerFace msgHandler = new PicYourFriends(); // 实现IMsgHandlerFace接口的类 750 | Wechat wechat = new Wechat(msgHandler, qrPath); // 【注入】 751 | wechat.start(); // 启动服务,会在qrPath下生成一张二维码图片,扫描即可登陆,注意,二维码图片如果超过一定时间未扫描会过期,过期时会自动更新,所以你可能需要重新打开图片 752 | } 753 | 754 | @Override 755 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 756 | // TODO Auto-generated method stub 757 | return null; 758 | } 759 | 760 | @Override 761 | public String mediaMsgHandle(BaseMsg msg) { 762 | // TODO Auto-generated method stub 763 | return null; 764 | } 765 | 766 | } 767 | 768 | ``` 769 | 770 | 771 | 772 | ### 其它 773 | 774 | 以上几个小Demo只是起到抛砖引玉的作用,请打开你的脑洞吧,朋友。 775 | 776 | ### itchat4j集成在SpringMVC应用中 777 | 778 | 若需要将itchat4j集成在自己的应用中,打开项目pom.xml文件所在目录,运行`mvn package`,将生成的itchat4j的jar包引入即可。 779 | 780 | 这个示例要讲起来就比较困难了,因为SpringMVC本身就是一个复杂的东西,先在这里挖个坑吧。其实在SpringMVC中集成与上面两个示例并没有太大的不同,我的个人博客是基于SpringMVC的,我已经将集成在这个项目里了,这样我就可以通过微信来更新我的博客了。详细的就不多说了,大家先看看这个项目结构吧。 781 | 782 | ![itchat4j集成在Blog项目](http://oj5vdtyuu.bkt.clouddn.com/itchat4j%E9%9B%86%E6%88%90%E5%9C%A8blog%E9%A1%B9%E7%9B%AE%E4%B8%AD%E4%BF%AE%E6%94%B9%E5%90%8E.jpg) 783 | 784 | 其中`MsgHandler`就是我处理微信消息的逻辑,略复杂,就不贴代码了。`WechatService`就是将`MsgHandler`“注入”到`Wechat`类中,与上面两个示例的作用是一样的,贴一下`WechatService`的代码: 785 | 786 | ```Java 787 | /** 788 | * Wechat服务实现类 789 | * 790 | * @author https://github.com/yaphone 791 | * @date 创建时间:2017年4月29日 下午7:44:01 792 | * @version 1.0 793 | * 794 | */ 795 | @Service("wechatService") 796 | public class WechatService implements IWechatServiceFace { 797 | @Value("${qrPath}") 798 | private String qrPath; 799 | 800 | @Override 801 | public void login() { 802 | MsgHandler msgHandler = new MsgHandler(); 803 | Wechat wechat = new Wechat(msgHandler, qrPath); 804 | wechat.start(); 805 | } 806 | } 807 | 808 | ``` 809 | 810 | 811 | 812 | ## 类似项目 813 | 814 | [itchat](https://github.com/littlecodersh/ItChat) :优秀的、基于Python的微信个人号API,同时也是本项目的灵感之源。 815 | 816 | [WeixinBot](https://github.com/Urinx/WeixinBot): 网页版微信API,包含终端版微信及微信机器人 817 | 818 | ## 致谢: 819 | 820 | itchat4j开源后,收到很多朋友的建议,对ithcat4j改进做出了很多帮助,在此表示感谢! 821 | 822 | [@jasonTangxd](https://github.com/jasonTangxd?tab=overview&from=2017-05-15),项目结构调整。 823 | 824 | [@libre818](https://github.com/libre818)。 825 | 826 | @QQ群好友(北极心 851668663),增加修改好友备注名方法。 827 | 828 | @QQ群好友(beyond_12345@126.com) 829 | 830 | 以及[每位PR的朋友](https://github.com/yaphone/itchat4j/graphs/contributors)! 831 | 832 | ## 问题和建议 833 | 834 | 本项目长期更新、维护,功能不断扩展与完善中,欢迎star。 835 | 836 | 项目使用过程中遇到问题,或者有好的建议,欢迎随时反馈。 837 | 838 | 任何问题或者建议都可以在Issue中提出来,也可以加入QQ群讨论:636365179 839 | 840 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | cn.zhouyafeng 6 | itchat4j 7 | 1.1.0 8 | jar 9 | 10 | 11 | itchat4j 12 | http://maven.apache.org 13 | 14 | 15 | UTF-8 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.httpcomponents 23 | httpclient 24 | 4.5.3 25 | 26 | 27 | 28 | org.apache.httpcomponents 29 | httpmime 30 | 4.5 31 | 32 | 33 | 34 | 35 | com.alibaba 36 | fastjson 37 | 1.2.31 38 | 39 | 40 | 41 | 42 | org.apache.commons 43 | commons-lang3 44 | 3.0 45 | 46 | 47 | 48 | 49 | com.vdurmont 50 | emoji-java 51 | 3.2.0 52 | 53 | 54 | 55 | 56 | javax.activation 57 | activation 58 | 1.1.1 59 | 60 | 61 | 62 | 63 | junit 64 | junit 65 | 4.12 66 | 67 | 68 | 69 | 70 | org.slf4j 71 | slf4j-api 72 | 1.6.6 73 | 74 | 75 | org.slf4j 76 | slf4j-log4j12 77 | 1.6.6 78 | 79 | 80 | log4j 81 | log4j 82 | 1.2.16 83 | 84 | 85 | 86 | 87 | com.squareup.okhttp3 88 | okhttp 89 | 3.8.0 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-compiler-plugin 103 | 3.0 104 | 105 | 1.8 106 | 1.8 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-source-plugin 113 | 3.0.1 114 | 115 | 116 | attach-sources 117 | package 118 | 119 | jar-no-fork 120 | 121 | 122 | 123 | 124 | 125 | 126 | maven-assembly-plugin 127 | 128 | 129 | package 130 | 131 | single 132 | 133 | 134 | 135 | 136 | 137 | jar-with-dependencies 138 | src 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/Wechat.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import cn.zhouyafeng.itchat4j.controller.LoginController; 7 | import cn.zhouyafeng.itchat4j.core.MsgCenter; 8 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 9 | 10 | public class Wechat { 11 | private static final Logger LOG = LoggerFactory.getLogger(Wechat.class); 12 | private IMsgHandlerFace msgHandler; 13 | 14 | public Wechat(IMsgHandlerFace msgHandler, String qrPath) { 15 | System.setProperty("jsse.enableSNIExtension", "false"); // 防止SSL错误 16 | this.msgHandler = msgHandler; 17 | 18 | // 登陆 19 | LoginController login = new LoginController(); 20 | login.login(qrPath); 21 | } 22 | 23 | public void start() { 24 | LOG.info("+++++++++++++++++++开始消息处理+++++++++++++++++++++"); 25 | new Thread(new Runnable() { 26 | @Override 27 | public void run() { 28 | MsgCenter.handleMsg(msgHandler); 29 | } 30 | }).start(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/api/AssistTools.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.api; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import okhttp3.Call; 7 | import okhttp3.MediaType; 8 | import okhttp3.MultipartBody; 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.Request; 11 | import okhttp3.RequestBody; 12 | import okhttp3.Response; 13 | 14 | /** 15 | * 辅助工具类,该类暂时未用,请忽略 16 | * 17 | * @author https://github.com/yaphone 18 | * @date 创建时间:2017年5月22日 下午10:34:46 19 | * @version 1.0 20 | * 21 | */ 22 | public class AssistTools { 23 | private static OkHttpClient client = new OkHttpClient(); 24 | private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); 25 | 26 | public static boolean sendQrPicToServer(String username, String password, String uploadUrl, String localPath) 27 | throws IOException { 28 | File file = new File(localPath); 29 | RequestBody requestBody = new MultipartBody.Builder().addFormDataPart("username", username) 30 | .addFormDataPart("password", password) 31 | .addFormDataPart("file", file.getName(), RequestBody.create(MEDIA_TYPE_PNG, file)).build(); 32 | Request request = new Request.Builder().url(uploadUrl).post(requestBody).build(); 33 | Call call = client.newCall(request); 34 | try { 35 | Response response = call.execute(); 36 | System.out.println(response.body().string()); 37 | } catch (IOException e) { 38 | e.printStackTrace(); 39 | } 40 | return true; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/api/MessageTools.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.api; 2 | 3 | import java.io.File; 4 | import java.text.SimpleDateFormat; 5 | import java.util.ArrayList; 6 | import java.util.Date; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Random; 11 | 12 | import javax.activation.MimetypesFileTypeMap; 13 | 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.apache.http.Consts; 16 | import org.apache.http.HttpEntity; 17 | import org.apache.http.entity.ContentType; 18 | import org.apache.http.entity.mime.HttpMultipartMode; 19 | import org.apache.http.entity.mime.MultipartEntityBuilder; 20 | import org.apache.http.util.EntityUtils; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import com.alibaba.fastjson.JSON; 25 | import com.alibaba.fastjson.JSONObject; 26 | 27 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 28 | import cn.zhouyafeng.itchat4j.beans.RecommendInfo; 29 | import cn.zhouyafeng.itchat4j.core.Core; 30 | import cn.zhouyafeng.itchat4j.utils.Config; 31 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 32 | import cn.zhouyafeng.itchat4j.utils.enums.StorageLoginInfoEnum; 33 | import cn.zhouyafeng.itchat4j.utils.enums.URLEnum; 34 | import cn.zhouyafeng.itchat4j.utils.enums.VerifyFriendEnum; 35 | 36 | /** 37 | * 消息处理类 38 | * 39 | * @author https://github.com/yaphone 40 | * @date 创建时间:2017年4月23日 下午2:30:37 41 | * @version 1.0 42 | * 43 | */ 44 | public class MessageTools { 45 | private static Logger LOG = LoggerFactory.getLogger(MessageTools.class); 46 | private static Core core = Core.getInstance(); 47 | private static MyHttpClient myHttpClient = core.getMyHttpClient(); 48 | 49 | /** 50 | * 根据UserName发送文本消息 51 | * 52 | * @author https://github.com/yaphone 53 | * @date 2017年5月4日 下午11:17:38 54 | * @param msg 55 | * @param toUserName 56 | */ 57 | private static void sendMsg(String text, String toUserName) { 58 | if (text == null) { 59 | return; 60 | } 61 | LOG.info(String.format("发送消息 %s: %s", toUserName, text)); 62 | webWxSendMsg(1, text, toUserName); 63 | } 64 | 65 | /** 66 | * 根据ID发送文本消息 67 | * 68 | * @author https://github.com/yaphone 69 | * @date 2017年5月6日 上午11:45:51 70 | * @param text 71 | * @param id 72 | */ 73 | public static void sendMsgById(String text, String id) { 74 | if (text == null) { 75 | return; 76 | } 77 | sendMsg(text, id); 78 | } 79 | 80 | /** 81 | * 根据NickName发送文本消息 82 | * 83 | * @author https://github.com/yaphone 84 | * @date 2017年5月4日 下午11:17:38 85 | * @param text 86 | * @param nickName 87 | */ 88 | public static boolean sendMsgByNickName(String text, String nickName) { 89 | if (nickName != null) { 90 | String toUserName = WechatTools.getUserNameByNickName(nickName); 91 | if (toUserName != null) { 92 | webWxSendMsg(1, text, toUserName); 93 | return true; 94 | } 95 | } 96 | return false; 97 | 98 | } 99 | 100 | /** 101 | * 消息发送 102 | * 103 | * @author https://github.com/yaphone 104 | * @date 2017年4月23日 下午2:32:02 105 | * @param msgType 106 | * @param content 107 | * @param toUserName 108 | */ 109 | public static void webWxSendMsg(int msgType, String content, String toUserName) { 110 | String url = String.format(URLEnum.WEB_WX_SEND_MSG.getUrl(), core.getLoginInfo().get("url")); 111 | Map msgMap = new HashMap(); 112 | msgMap.put("Type", msgType); 113 | msgMap.put("Content", content); 114 | msgMap.put("FromUserName", core.getUserName()); 115 | msgMap.put("ToUserName", toUserName == null ? core.getUserName() : toUserName); 116 | msgMap.put("LocalID", new Date().getTime() * 10); 117 | msgMap.put("ClientMsgId", new Date().getTime() * 10); 118 | Map paramMap = core.getParamMap(); 119 | paramMap.put("Msg", msgMap); 120 | paramMap.put("Scene", 0); 121 | try { 122 | String paramStr = JSON.toJSONString(paramMap); 123 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 124 | EntityUtils.toString(entity, Consts.UTF_8); 125 | } catch (Exception e) { 126 | LOG.error("webWxSendMsg", e); 127 | } 128 | } 129 | 130 | /** 131 | * 上传多媒体文件到 微信服务器,目前应该支持3种类型: 1. pic 直接显示,包含图片,表情 2.video 3.doc 显示为文件,包含PDF等 132 | * 133 | * @author https://github.com/yaphone 134 | * @date 2017年5月7日 上午12:41:13 135 | * @param filePath 136 | * @return 137 | */ 138 | private static JSONObject webWxUploadMedia(String filePath) { 139 | File f = new File(filePath); 140 | if (!f.exists() && f.isFile()) { 141 | LOG.info("file is not exist"); 142 | return null; 143 | } 144 | String url = String.format(URLEnum.WEB_WX_UPLOAD_MEDIA.getUrl(), core.getLoginInfo().get("fileUrl")); 145 | String mimeType = new MimetypesFileTypeMap().getContentType(f); 146 | String mediaType = ""; 147 | if (mimeType == null) { 148 | mimeType = "text/plain"; 149 | } else { 150 | mediaType = mimeType.split("/")[0].equals("image") ? "pic" : "doc"; 151 | } 152 | String lastModifieDate = new SimpleDateFormat("yyyy MM dd HH:mm:ss").format(new Date()); 153 | long fileSize = f.length(); 154 | String passTicket = (String) core.getLoginInfo().get("pass_ticket"); 155 | String clientMediaId = String.valueOf(new Date().getTime()) 156 | + String.valueOf(new Random().nextLong()).substring(0, 4); 157 | String webwxDataTicket = MyHttpClient.getCookie("webwx_data_ticket"); 158 | if (webwxDataTicket == null) { 159 | LOG.error("get cookie webwx_data_ticket error"); 160 | return null; 161 | } 162 | 163 | Map paramMap = core.getParamMap(); 164 | 165 | paramMap.put("ClientMediaId", clientMediaId); 166 | paramMap.put("TotalLen", fileSize); 167 | paramMap.put("StartPos", 0); 168 | paramMap.put("DataLen", fileSize); 169 | paramMap.put("MediaType", 4); 170 | 171 | MultipartEntityBuilder builder = MultipartEntityBuilder.create(); 172 | builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); 173 | 174 | builder.addTextBody("id", "WU_FILE_0", ContentType.TEXT_PLAIN); 175 | builder.addTextBody("name", filePath, ContentType.TEXT_PLAIN); 176 | builder.addTextBody("type", mimeType, ContentType.TEXT_PLAIN); 177 | builder.addTextBody("lastModifieDate", lastModifieDate, ContentType.TEXT_PLAIN); 178 | builder.addTextBody("size", String.valueOf(fileSize), ContentType.TEXT_PLAIN); 179 | builder.addTextBody("mediatype", mediaType, ContentType.TEXT_PLAIN); 180 | builder.addTextBody("uploadmediarequest", JSON.toJSONString(paramMap), ContentType.TEXT_PLAIN); 181 | builder.addTextBody("webwx_data_ticket", webwxDataTicket, ContentType.TEXT_PLAIN); 182 | builder.addTextBody("pass_ticket", passTicket, ContentType.TEXT_PLAIN); 183 | builder.addBinaryBody("filename", f, ContentType.create(mimeType), filePath); 184 | HttpEntity reqEntity = builder.build(); 185 | HttpEntity entity = myHttpClient.doPostFile(url, reqEntity); 186 | if (entity != null) { 187 | try { 188 | String result = EntityUtils.toString(entity, Consts.UTF_8); 189 | return JSON.parseObject(result); 190 | } catch (Exception e) { 191 | LOG.error("webWxUploadMedia 错误: ", e); 192 | } 193 | 194 | } 195 | return null; 196 | } 197 | 198 | /** 199 | * 根据NickName发送图片消息 200 | * 201 | * @author https://github.com/yaphone 202 | * @date 2017年5月7日 下午10:32:45 203 | * @param nackName 204 | * @return 205 | */ 206 | public static boolean sendPicMsgByNickName(String nickName, String filePath) { 207 | String toUserName = WechatTools.getUserNameByNickName(nickName); 208 | if (toUserName != null) { 209 | return sendPicMsgByUserId(toUserName, filePath); 210 | } 211 | return false; 212 | } 213 | 214 | /** 215 | * 根据用户id发送图片消息 216 | * 217 | * @author https://github.com/yaphone 218 | * @date 2017年5月7日 下午10:34:24 219 | * @param nickName 220 | * @param filePath 221 | * @return 222 | */ 223 | public static boolean sendPicMsgByUserId(String userId, String filePath) { 224 | JSONObject responseObj = webWxUploadMedia(filePath); 225 | if (responseObj != null) { 226 | String mediaId = responseObj.getString("MediaId"); 227 | if (mediaId != null) { 228 | return webWxSendMsgImg(userId, mediaId); 229 | } 230 | } 231 | return false; 232 | } 233 | 234 | /** 235 | * 发送图片消息,内部调用 236 | * 237 | * @author https://github.com/yaphone 238 | * @date 2017年5月7日 下午10:38:55 239 | * @return 240 | */ 241 | private static boolean webWxSendMsgImg(String userId, String mediaId) { 242 | String url = String.format("%s/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s", core.getLoginInfo().get("url"), 243 | core.getLoginInfo().get("pass_ticket")); 244 | Map msgMap = new HashMap(); 245 | msgMap.put("Type", 3); 246 | msgMap.put("MediaId", mediaId); 247 | msgMap.put("FromUserName", core.getUserSelf().getString("UserName")); 248 | msgMap.put("ToUserName", userId); 249 | String clientMsgId = String.valueOf(new Date().getTime()) 250 | + String.valueOf(new Random().nextLong()).substring(1, 5); 251 | msgMap.put("LocalID", clientMsgId); 252 | msgMap.put("ClientMsgId", clientMsgId); 253 | Map paramMap = core.getParamMap(); 254 | paramMap.put("BaseRequest", core.getParamMap().get("BaseRequest")); 255 | paramMap.put("Msg", msgMap); 256 | String paramStr = JSON.toJSONString(paramMap); 257 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 258 | if (entity != null) { 259 | try { 260 | String result = EntityUtils.toString(entity, Consts.UTF_8); 261 | return JSON.parseObject(result).getJSONObject("BaseResponse").getInteger("Ret") == 0; 262 | } catch (Exception e) { 263 | LOG.error("webWxSendMsgImg 错误: ", e); 264 | } 265 | } 266 | return false; 267 | 268 | } 269 | 270 | /** 271 | * 根据用户id发送文件 272 | * 273 | * @author https://github.com/yaphone 274 | * @date 2017年5月7日 下午11:57:36 275 | * @param userId 276 | * @param filePath 277 | * @return 278 | */ 279 | public static boolean sendFileMsgByUserId(String userId, String filePath) { 280 | String title = new File(filePath).getName(); 281 | Map data = new HashMap(); 282 | data.put("appid", Config.API_WXAPPID); 283 | data.put("title", title); 284 | data.put("totallen", ""); 285 | data.put("attachid", ""); 286 | data.put("type", "6"); // APPMSGTYPE_ATTACH 287 | data.put("fileext", title.split("\\.")[1]); // 文件后缀 288 | JSONObject responseObj = webWxUploadMedia(filePath); 289 | if (responseObj != null) { 290 | data.put("totallen", responseObj.getString("StartPos")); 291 | data.put("attachid", responseObj.getString("MediaId")); 292 | } else { 293 | LOG.error("sednFileMsgByUserId 错误: ", data); 294 | } 295 | return webWxSendAppMsg(userId, data); 296 | } 297 | 298 | /** 299 | * 根据用户昵称发送文件消息 300 | * 301 | * @author https://github.com/yaphone 302 | * @date 2017年5月10日 下午10:59:27 303 | * @param nickName 304 | * @param filePath 305 | * @return 306 | */ 307 | public static boolean sendFileMsgByNickName(String nickName, String filePath) { 308 | String toUserName = WechatTools.getUserNameByNickName(nickName); 309 | if (toUserName != null) { 310 | return sendFileMsgByUserId(toUserName, filePath); 311 | } 312 | return false; 313 | } 314 | 315 | /** 316 | * 内部调用 317 | * 318 | * @author https://github.com/yaphone 319 | * @date 2017年5月10日 上午12:21:28 320 | * @param userId 321 | * @param data 322 | * @return 323 | */ 324 | private static boolean webWxSendAppMsg(String userId, Map data) { 325 | String url = String.format("%s/webwxsendappmsg?fun=async&f=json&pass_ticket=%s", core.getLoginInfo().get("url"), 326 | core.getLoginInfo().get("pass_ticket")); 327 | String clientMsgId = String.valueOf(new Date().getTime()) 328 | + String.valueOf(new Random().nextLong()).substring(1, 5); 329 | String content = "" + data.get("title") 330 | + "6" 331 | + "" + data.get("totallen") + "" + data.get("attachid") 332 | + "" + data.get("fileext") + ""; 333 | Map msgMap = new HashMap(); 334 | msgMap.put("Type", data.get("type")); 335 | msgMap.put("Content", content); 336 | msgMap.put("FromUserName", core.getUserSelf().getString("UserName")); 337 | msgMap.put("ToUserName", userId); 338 | msgMap.put("LocalID", clientMsgId); 339 | msgMap.put("ClientMsgId", clientMsgId); 340 | /* 341 | * Map paramMap = new HashMap(); 342 | * 343 | * @SuppressWarnings("unchecked") Map> 344 | * baseRequestMap = (Map>) 345 | * core.getLoginInfo() .get("baseRequest"); paramMap.put("BaseRequest", 346 | * baseRequestMap.get("BaseRequest")); 347 | */ 348 | 349 | Map paramMap = core.getParamMap(); 350 | paramMap.put("Msg", msgMap); 351 | paramMap.put("Scene", 0); 352 | String paramStr = JSON.toJSONString(paramMap); 353 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 354 | if (entity != null) { 355 | try { 356 | String result = EntityUtils.toString(entity, Consts.UTF_8); 357 | return JSON.parseObject(result).getJSONObject("BaseResponse").getInteger("Ret") == 0; 358 | } catch (Exception e) { 359 | LOG.error("错误: ", e); 360 | } 361 | } 362 | return false; 363 | } 364 | 365 | /** 366 | * 被动添加好友 367 | * 368 | * @date 2017年6月29日 下午10:08:43 369 | * @param msg 370 | * @param accept 371 | * true 接受 false 拒绝 372 | */ 373 | public static void addFriend(BaseMsg msg, boolean accept) { 374 | if (!accept) { // 不添加 375 | return; 376 | } 377 | int status = VerifyFriendEnum.ACCEPT.getCode(); // 接受好友请求 378 | RecommendInfo recommendInfo = msg.getRecommendInfo(); 379 | String userName = recommendInfo.getUserName(); 380 | String ticket = recommendInfo.getTicket(); 381 | // 更新好友列表 382 | // TODO 此处需要更新好友列表 383 | // core.getContactList().add(msg.getJSONObject("RecommendInfo")); 384 | 385 | String url = String.format(URLEnum.WEB_WX_VERIFYUSER.getUrl(), core.getLoginInfo().get("url"), 386 | String.valueOf(System.currentTimeMillis() / 3158L), core.getLoginInfo().get("pass_ticket")); 387 | 388 | List> verifyUserList = new ArrayList>(); 389 | Map verifyUser = new HashMap(); 390 | verifyUser.put("Value", userName); 391 | verifyUser.put("VerifyUserTicket", ticket); 392 | verifyUserList.add(verifyUser); 393 | 394 | List sceneList = new ArrayList(); 395 | sceneList.add(33); 396 | 397 | JSONObject body = new JSONObject(); 398 | body.put("BaseRequest", core.getParamMap().get("BaseRequest")); 399 | body.put("Opcode", status); 400 | body.put("VerifyUserListSize", 1); 401 | body.put("VerifyUserList", verifyUserList); 402 | body.put("VerifyContent", ""); 403 | body.put("SceneListCount", 1); 404 | body.put("SceneList", sceneList); 405 | body.put("skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey())); 406 | 407 | String result = null; 408 | try { 409 | String paramStr = JSON.toJSONString(body); 410 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 411 | result = EntityUtils.toString(entity, Consts.UTF_8); 412 | } catch (Exception e) { 413 | LOG.error("webWxSendMsg", e); 414 | } 415 | 416 | if (StringUtils.isBlank(result)) { 417 | LOG.error("被动添加好友失败"); 418 | } 419 | 420 | LOG.debug(result); 421 | 422 | } 423 | 424 | } 425 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/api/WechatTools.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.api; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.apache.http.Consts; 9 | import org.apache.http.HttpEntity; 10 | import org.apache.http.message.BasicNameValuePair; 11 | import org.apache.http.util.EntityUtils; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.alibaba.fastjson.JSON; 16 | import com.alibaba.fastjson.JSONArray; 17 | import com.alibaba.fastjson.JSONObject; 18 | 19 | import cn.zhouyafeng.itchat4j.core.Core; 20 | import cn.zhouyafeng.itchat4j.utils.enums.StorageLoginInfoEnum; 21 | import cn.zhouyafeng.itchat4j.utils.enums.URLEnum; 22 | 23 | /** 24 | * 微信小工具,如获好友列表等 25 | * 26 | * @author https://github.com/yaphone 27 | * @date 创建时间:2017年5月4日 下午10:49:16 28 | * @version 1.0 29 | * 30 | */ 31 | public class WechatTools { 32 | private static Logger LOG = LoggerFactory.getLogger(WechatTools.class); 33 | 34 | private static Core core = Core.getInstance(); 35 | 36 | /** 37 | * 根据用户名发送文本消息 38 | * 39 | * @author https://github.com/yaphone 40 | * @date 2017年5月4日 下午10:43:14 41 | * @param msg 42 | * @param toUserName 43 | */ 44 | public static void sendMsgByUserName(String msg, String toUserName) { 45 | MessageTools.sendMsgById(msg, toUserName); 46 | } 47 | 48 | /** 49 | *

50 | * 通过RealName获取本次UserName 51 | *

52 | *

53 | * 如NickName为"yaphone",则获取UserName= 54 | * "@1212d3356aea8285e5bbe7b91229936bc183780a8ffa469f2d638bf0d2e4fc63", 55 | * 可通过UserName发送消息 56 | *

57 | * 58 | * @author https://github.com/yaphone 59 | * @date 2017年5月4日 下午10:56:31 60 | * @param name 61 | * @return 62 | */ 63 | public static String getUserNameByNickName(String nickName) { 64 | for (JSONObject o : core.getContactList()) { 65 | if (o.getString("NickName").equals(nickName)) { 66 | return o.getString("UserName"); 67 | } 68 | } 69 | return null; 70 | } 71 | 72 | /** 73 | * 返回好友昵称列表 74 | * 75 | * @author https://github.com/yaphone 76 | * @date 2017年5月4日 下午11:37:20 77 | * @return 78 | */ 79 | public static List getContactNickNameList() { 80 | List contactNickNameList = new ArrayList(); 81 | for (JSONObject o : core.getContactList()) { 82 | contactNickNameList.add(o.getString("NickName")); 83 | } 84 | return contactNickNameList; 85 | } 86 | 87 | /** 88 | * 返回好友完整信息列表 89 | * 90 | * @date 2017年6月26日 下午9:45:39 91 | * @return 92 | */ 93 | public static List getContactList() { 94 | return core.getContactList(); 95 | } 96 | 97 | /** 98 | * 返回群列表 99 | * 100 | * @author https://github.com/yaphone 101 | * @date 2017年5月5日 下午9:55:21 102 | * @return 103 | */ 104 | public static List getGroupList() { 105 | return core.getGroupList(); 106 | } 107 | 108 | /** 109 | * 获取群ID列表 110 | * 111 | * @date 2017年6月21日 下午11:42:56 112 | * @return 113 | */ 114 | public static List getGroupIdList() { 115 | return core.getGroupIdList(); 116 | } 117 | 118 | /** 119 | * 获取群NickName列表 120 | * 121 | * @date 2017年6月21日 下午11:43:38 122 | * @return 123 | */ 124 | public static List getGroupNickNameList() { 125 | return core.getGroupNickNameList(); 126 | } 127 | 128 | /** 129 | * 根据groupIdList返回群成员列表 130 | * 131 | * @date 2017年6月13日 下午11:12:31 132 | * @param groupId 133 | * @return 134 | */ 135 | public static JSONArray getMemberListByGroupId(String groupId) { 136 | return core.getGroupMemeberMap().get(groupId); 137 | } 138 | 139 | /** 140 | * 退出微信 141 | * 142 | * @author https://github.com/yaphone 143 | * @date 2017年5月18日 下午11:56:54 144 | */ 145 | public static void logout() { 146 | webWxLogout(); 147 | } 148 | 149 | private static boolean webWxLogout() { 150 | String url = String.format(URLEnum.WEB_WX_LOGOUT.getUrl(), 151 | core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey())); 152 | List params = new ArrayList(); 153 | params.add(new BasicNameValuePair("redirect", "1")); 154 | params.add(new BasicNameValuePair("type", "1")); 155 | params.add( 156 | new BasicNameValuePair("skey", (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()))); 157 | try { 158 | HttpEntity entity = core.getMyHttpClient().doGet(url, params, false, null); 159 | String text = EntityUtils.toString(entity, Consts.UTF_8); // 无消息 160 | return true; 161 | } catch (Exception e) { 162 | LOG.debug(e.getMessage()); 163 | } 164 | return false; 165 | } 166 | 167 | public static void setUserInfo() { 168 | for (JSONObject o : core.getContactList()) { 169 | core.getUserInfoMap().put(o.getString("NickName"), o); 170 | core.getUserInfoMap().put(o.getString("UserName"), o); 171 | } 172 | } 173 | 174 | /** 175 | * 176 | * 根据用户昵称设置备注名称 177 | * 178 | * @date 2017年5月27日 上午12:21:40 179 | * @param userName 180 | * @param remName 181 | */ 182 | public static void remarkNameByNickName(String nickName, String remName) { 183 | String url = String.format(URLEnum.WEB_WX_REMARKNAME.getUrl(), core.getLoginInfo().get("url"), 184 | core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); 185 | Map msgMap = new HashMap(); 186 | Map msgMap_BaseRequest = new HashMap(); 187 | msgMap.put("CmdId", 2); 188 | msgMap.put("RemarkName", remName); 189 | msgMap.put("UserName", core.getUserInfoMap().get(nickName).get("UserName")); 190 | msgMap_BaseRequest.put("Uin", core.getLoginInfo().get(StorageLoginInfoEnum.wxuin.getKey())); 191 | msgMap_BaseRequest.put("Sid", core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey())); 192 | msgMap_BaseRequest.put("Skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey())); 193 | msgMap_BaseRequest.put("DeviceID", core.getLoginInfo().get(StorageLoginInfoEnum.deviceid.getKey())); 194 | msgMap.put("BaseRequest", msgMap_BaseRequest); 195 | try { 196 | String paramStr = JSON.toJSONString(msgMap); 197 | HttpEntity entity = core.getMyHttpClient().doPost(url, paramStr); 198 | // String result = EntityUtils.toString(entity, Consts.UTF_8); 199 | LOG.info("修改备注" + remName); 200 | } catch (Exception e) { 201 | LOG.error("remarkNameByUserName", e); 202 | } 203 | } 204 | 205 | /** 206 | * 获取微信在线状态 207 | * 208 | * @date 2017年6月16日 上午12:47:46 209 | * @return 210 | */ 211 | public static boolean getWechatStatus() { 212 | return core.isAlive(); 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/beans/AppInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.beans; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * AppInfo 7 | * 8 | * @author https://github.com/yaphone 9 | * @date 创建时间:2017年7月3日 下午10:38:14 10 | * @version 1.0 11 | * 12 | */ 13 | public class AppInfo implements Serializable { 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | private int type; 19 | private String appId; 20 | 21 | public int getType() { 22 | return type; 23 | } 24 | 25 | public void setType(int type) { 26 | this.type = type; 27 | } 28 | 29 | public String getAppId() { 30 | return appId; 31 | } 32 | 33 | public void setAppId(String appId) { 34 | this.appId = appId; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/beans/BaseMsg.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.beans; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 收到的微信消息 7 | * 8 | * @author https://github.com/yaphone 9 | * @date 创建时间:2017年7月3日 下午10:28:06 10 | * @version 1.0 11 | * 12 | */ 13 | public class BaseMsg implements Serializable { 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | private int subMsgType; 19 | private int voiceLength; 20 | private String fileName; 21 | private int imgHeight; 22 | private String toUserName; 23 | private int hasProductId; 24 | private int imgStatus; 25 | private String url; 26 | private int imgWidth; 27 | private int forwardFlag; 28 | private int status; 29 | private String Ticket; 30 | /** 推荐消息报文 **/ 31 | private RecommendInfo recommendInfo; 32 | private long createTime; 33 | private String newMsgId; 34 | /** 文本消息内容 **/ 35 | private String text; 36 | /** 消息类型 **/ 37 | private int msgType; 38 | /** 是否为群消息 **/ 39 | private boolean groupMsg; 40 | private String msgId; 41 | private int statusNotifyCode; 42 | private AppInfo appInfo; 43 | private int appMsgType; 44 | private String Type; 45 | private int playLength; 46 | private String mediaId; 47 | private String content; 48 | private String statusNotifyUserName; 49 | /** 消息发送者ID **/ 50 | private String fromUserName; 51 | private String oriContent; 52 | private String fileSize; 53 | 54 | public int getSubMsgType() { 55 | return subMsgType; 56 | } 57 | 58 | public void setSubMsgType(int subMsgType) { 59 | this.subMsgType = subMsgType; 60 | } 61 | 62 | public int getVoiceLength() { 63 | return voiceLength; 64 | } 65 | 66 | public void setVoiceLength(int voiceLength) { 67 | this.voiceLength = voiceLength; 68 | } 69 | 70 | public String getFileName() { 71 | return fileName; 72 | } 73 | 74 | public void setFileName(String fileName) { 75 | this.fileName = fileName; 76 | } 77 | 78 | public int getImgHeight() { 79 | return imgHeight; 80 | } 81 | 82 | public void setImgHeight(int imgHeight) { 83 | this.imgHeight = imgHeight; 84 | } 85 | 86 | public String getToUserName() { 87 | return toUserName; 88 | } 89 | 90 | public void setToUserName(String toUserName) { 91 | this.toUserName = toUserName; 92 | } 93 | 94 | public int getHasProductId() { 95 | return hasProductId; 96 | } 97 | 98 | public void setHasProductId(int hasProductId) { 99 | this.hasProductId = hasProductId; 100 | } 101 | 102 | public int getImgStatus() { 103 | return imgStatus; 104 | } 105 | 106 | public void setImgStatus(int imgStatus) { 107 | this.imgStatus = imgStatus; 108 | } 109 | 110 | public String getUrl() { 111 | return url; 112 | } 113 | 114 | public void setUrl(String url) { 115 | this.url = url; 116 | } 117 | 118 | public int getImgWidth() { 119 | return imgWidth; 120 | } 121 | 122 | public void setImgWidth(int imgWidth) { 123 | this.imgWidth = imgWidth; 124 | } 125 | 126 | public int getForwardFlag() { 127 | return forwardFlag; 128 | } 129 | 130 | public void setForwardFlag(int forwardFlag) { 131 | this.forwardFlag = forwardFlag; 132 | } 133 | 134 | public int getStatus() { 135 | return status; 136 | } 137 | 138 | public void setStatus(int status) { 139 | this.status = status; 140 | } 141 | 142 | public String getTicket() { 143 | return Ticket; 144 | } 145 | 146 | public void setTicket(String ticket) { 147 | Ticket = ticket; 148 | } 149 | 150 | public RecommendInfo getRecommendInfo() { 151 | return recommendInfo; 152 | } 153 | 154 | public void setRecommendInfo(RecommendInfo recommendInfo) { 155 | this.recommendInfo = recommendInfo; 156 | } 157 | 158 | public long getCreateTime() { 159 | return createTime; 160 | } 161 | 162 | public void setCreateTime(long createTime) { 163 | this.createTime = createTime; 164 | } 165 | 166 | public String getNewMsgId() { 167 | return newMsgId; 168 | } 169 | 170 | public void setNewMsgId(String newMsgId) { 171 | this.newMsgId = newMsgId; 172 | } 173 | 174 | public String getText() { 175 | return text; 176 | } 177 | 178 | public void setText(String text) { 179 | this.text = text; 180 | } 181 | 182 | public int getMsgType() { 183 | return msgType; 184 | } 185 | 186 | public void setMsgType(int msgType) { 187 | this.msgType = msgType; 188 | } 189 | 190 | public boolean isGroupMsg() { 191 | return groupMsg; 192 | } 193 | 194 | public void setGroupMsg(boolean groupMsg) { 195 | this.groupMsg = groupMsg; 196 | } 197 | 198 | public String getMsgId() { 199 | return msgId; 200 | } 201 | 202 | public void setMsgId(String msgId) { 203 | this.msgId = msgId; 204 | } 205 | 206 | public int getStatusNotifyCode() { 207 | return statusNotifyCode; 208 | } 209 | 210 | public void setStatusNotifyCode(int statusNotifyCode) { 211 | this.statusNotifyCode = statusNotifyCode; 212 | } 213 | 214 | public AppInfo getAppInfo() { 215 | return appInfo; 216 | } 217 | 218 | public void setAppInfo(AppInfo appInfo) { 219 | this.appInfo = appInfo; 220 | } 221 | 222 | public int getAppMsgType() { 223 | return appMsgType; 224 | } 225 | 226 | public void setAppMsgType(int appMsgType) { 227 | this.appMsgType = appMsgType; 228 | } 229 | 230 | public String getType() { 231 | return Type; 232 | } 233 | 234 | public void setType(String type) { 235 | Type = type; 236 | } 237 | 238 | public int getPlayLength() { 239 | return playLength; 240 | } 241 | 242 | public void setPlayLength(int playLength) { 243 | this.playLength = playLength; 244 | } 245 | 246 | public String getMediaId() { 247 | return mediaId; 248 | } 249 | 250 | public void setMediaId(String mediaId) { 251 | this.mediaId = mediaId; 252 | } 253 | 254 | public String getContent() { 255 | return content; 256 | } 257 | 258 | public void setContent(String content) { 259 | this.content = content; 260 | } 261 | 262 | public String getStatusNotifyUserName() { 263 | return statusNotifyUserName; 264 | } 265 | 266 | public void setStatusNotifyUserName(String statusNotifyUserName) { 267 | this.statusNotifyUserName = statusNotifyUserName; 268 | } 269 | 270 | public String getFromUserName() { 271 | return fromUserName; 272 | } 273 | 274 | public void setFromUserName(String fromUserName) { 275 | this.fromUserName = fromUserName; 276 | } 277 | 278 | public String getOriContent() { 279 | return oriContent; 280 | } 281 | 282 | public void setOriContent(String oriContent) { 283 | this.oriContent = oriContent; 284 | } 285 | 286 | public String getFileSize() { 287 | return fileSize; 288 | } 289 | 290 | public void setFileSize(String fileSize) { 291 | this.fileSize = fileSize; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/beans/RecommendInfo.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.beans; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * RecommendInfo 7 | * 8 | * @author https://github.com/yaphone 9 | * @date 创建时间:2017年7月3日 下午10:35:14 10 | * @version 1.0 11 | * 12 | */ 13 | public class RecommendInfo implements Serializable { 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | 19 | private String ticket; 20 | private String userName; 21 | private int sex; 22 | private int attrStatus; 23 | private String city; 24 | private String nickName; 25 | private int scene; 26 | private String province; 27 | private String content; 28 | private String alias; 29 | private String signature; 30 | private int opCode; 31 | private int qQNum; 32 | private int verifyFlag; 33 | 34 | public String getTicket() { 35 | return ticket; 36 | } 37 | 38 | public void setTicket(String ticket) { 39 | this.ticket = ticket; 40 | } 41 | 42 | public String getUserName() { 43 | return userName; 44 | } 45 | 46 | public void setUserName(String userName) { 47 | this.userName = userName; 48 | } 49 | 50 | public int getSex() { 51 | return sex; 52 | } 53 | 54 | public void setSex(int sex) { 55 | this.sex = sex; 56 | } 57 | 58 | public int getAttrStatus() { 59 | return attrStatus; 60 | } 61 | 62 | public void setAttrStatus(int attrStatus) { 63 | this.attrStatus = attrStatus; 64 | } 65 | 66 | public String getCity() { 67 | return city; 68 | } 69 | 70 | public void setCity(String city) { 71 | this.city = city; 72 | } 73 | 74 | public String getNickName() { 75 | return nickName; 76 | } 77 | 78 | public void setNickName(String nickName) { 79 | this.nickName = nickName; 80 | } 81 | 82 | public int getScene() { 83 | return scene; 84 | } 85 | 86 | public void setScene(int scene) { 87 | this.scene = scene; 88 | } 89 | 90 | public String getProvince() { 91 | return province; 92 | } 93 | 94 | public void setProvince(String province) { 95 | this.province = province; 96 | } 97 | 98 | public String getContent() { 99 | return content; 100 | } 101 | 102 | public void setContent(String content) { 103 | this.content = content; 104 | } 105 | 106 | public String getAlias() { 107 | return alias; 108 | } 109 | 110 | public void setAlias(String alias) { 111 | this.alias = alias; 112 | } 113 | 114 | public String getSignature() { 115 | return signature; 116 | } 117 | 118 | public void setSignature(String signature) { 119 | this.signature = signature; 120 | } 121 | 122 | public int getOpCode() { 123 | return opCode; 124 | } 125 | 126 | public void setOpCode(int opCode) { 127 | this.opCode = opCode; 128 | } 129 | 130 | public int getqQNum() { 131 | return qQNum; 132 | } 133 | 134 | public void setqQNum(int qQNum) { 135 | this.qQNum = qQNum; 136 | } 137 | 138 | public int getVerifyFlag() { 139 | return verifyFlag; 140 | } 141 | 142 | public void setVerifyFlag(int verifyFlag) { 143 | this.verifyFlag = verifyFlag; 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.controller; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import cn.zhouyafeng.itchat4j.api.WechatTools; 7 | import cn.zhouyafeng.itchat4j.core.Core; 8 | import cn.zhouyafeng.itchat4j.service.ILoginService; 9 | import cn.zhouyafeng.itchat4j.service.impl.LoginServiceImpl; 10 | import cn.zhouyafeng.itchat4j.thread.CheckLoginStatusThread; 11 | import cn.zhouyafeng.itchat4j.utils.SleepUtils; 12 | import cn.zhouyafeng.itchat4j.utils.tools.CommonTools; 13 | 14 | /** 15 | * 登陆控制器 16 | * 17 | * @author https://github.com/yaphone 18 | * @date 创建时间:2017年5月13日 下午12:56:07 19 | * @version 1.0 20 | * 21 | */ 22 | public class LoginController { 23 | private static Logger LOG = LoggerFactory.getLogger(LoginController.class); 24 | private ILoginService loginService = new LoginServiceImpl(); 25 | private static Core core = Core.getInstance(); 26 | 27 | public void login(String qrPath) { 28 | if (core.isAlive()) { // 已登陆 29 | LOG.info("itchat4j已登陆"); 30 | return; 31 | } 32 | while (true) { 33 | for (int count = 0; count < 10; count++) { 34 | LOG.info("获取UUID"); 35 | while (loginService.getUuid() == null) { 36 | LOG.info("1. 获取微信UUID"); 37 | while (loginService.getUuid() == null) { 38 | LOG.warn("1.1. 获取微信UUID失败,两秒后重新获取"); 39 | SleepUtils.sleep(2000); 40 | } 41 | } 42 | LOG.info("2. 获取登陆二维码图片"); 43 | if (loginService.getQR(qrPath)) { 44 | break; 45 | } else if (count == 10) { 46 | LOG.error("2.2. 获取登陆二维码图片失败,系统退出"); 47 | System.exit(0); 48 | } 49 | } 50 | LOG.info("3. 请扫描二维码图片,并在手机上确认"); 51 | if (!core.isAlive()) { 52 | loginService.login(); 53 | core.setAlive(true); 54 | LOG.info(("登陆成功")); 55 | break; 56 | } 57 | LOG.info("4. 登陆超时,请重新扫描二维码图片"); 58 | } 59 | 60 | LOG.info("5. 登陆成功,微信初始化"); 61 | if (!loginService.webWxInit()) { 62 | LOG.info("6. 微信初始化异常"); 63 | System.exit(0); 64 | } 65 | 66 | LOG.info("6. 开启微信状态通知"); 67 | loginService.wxStatusNotify(); 68 | 69 | LOG.info("7. 清除。。。。"); 70 | CommonTools.clearScreen(); 71 | LOG.info(String.format("欢迎回来, %s", core.getNickName())); 72 | 73 | LOG.info("8. 开始接收消息"); 74 | loginService.startReceiving(); 75 | 76 | LOG.info("9. 获取联系人信息"); 77 | loginService.webWxGetContact(); 78 | 79 | LOG.info("10. 获取群好友及群好友列表"); 80 | loginService.WebWxBatchGetContact(); 81 | 82 | LOG.info("11. 缓存本次登陆好友相关消息"); 83 | WechatTools.setUserInfo(); // 登陆成功后缓存本次登陆好友相关消息(NickName, UserName) 84 | 85 | LOG.info("12.开启微信状态检测线程"); 86 | new Thread(new CheckLoginStatusThread()).start(); 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/core/Core.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.alibaba.fastjson.JSONArray; 9 | import com.alibaba.fastjson.JSONObject; 10 | 11 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 12 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 13 | import cn.zhouyafeng.itchat4j.utils.enums.parameters.BaseParaEnum; 14 | 15 | /** 16 | * 核心存储类,全局只保存一份,单例模式 17 | * 18 | * @author https://github.com/yaphone 19 | * @date 创建时间:2017年4月23日 下午2:33:56 20 | * @version 1.0 21 | * 22 | */ 23 | public class Core { 24 | 25 | private static Core instance; 26 | 27 | private Core() { 28 | 29 | } 30 | 31 | public static Core getInstance() { 32 | if (instance == null) { 33 | synchronized (Core.class) { 34 | instance = new Core(); 35 | } 36 | } 37 | return instance; 38 | } 39 | 40 | boolean alive = false; 41 | private int memberCount = 0; 42 | 43 | private String indexUrl; 44 | 45 | private String userName; 46 | private String nickName; 47 | private List msgList = new ArrayList(); 48 | 49 | private JSONObject userSelf; // 登陆账号自身信息 50 | private List memberList = new ArrayList(); // 好友+群聊+公众号+特殊账号 51 | private List contactList = new ArrayList();// 好友 52 | private List groupList = new ArrayList();; // 群 53 | private Map groupMemeberMap = new HashMap(); // 群聊成员字典 54 | private List publicUsersList = new ArrayList();;// 公众号/服务号 55 | private List specialUsersList = new ArrayList();;// 特殊账号 56 | private List groupIdList = new ArrayList(); // 群ID列表 57 | private List groupNickNameList = new ArrayList(); // 群NickName列表 58 | 59 | private Map userInfoMap = new HashMap(); 60 | 61 | Map loginInfo = new HashMap(); 62 | // CloseableHttpClient httpClient = HttpClients.createDefault(); 63 | MyHttpClient myHttpClient = MyHttpClient.getInstance(); 64 | String uuid = null; 65 | 66 | boolean useHotReload = false; 67 | String hotReloadDir = "itchat.pkl"; 68 | int receivingRetryCount = 5; 69 | 70 | private long lastNormalRetcodeTime; // 最后一次收到正常retcode的时间,秒为单位 71 | 72 | /** 73 | * 请求参数 74 | */ 75 | public Map getParamMap() { 76 | return new HashMap(1) { 77 | /** 78 | * 79 | */ 80 | private static final long serialVersionUID = 1L; 81 | 82 | { 83 | Map map = new HashMap(); 84 | for (BaseParaEnum baseRequest : BaseParaEnum.values()) { 85 | map.put(baseRequest.para(), getLoginInfo().get(baseRequest.value()).toString()); 86 | } 87 | put("BaseRequest", map); 88 | } 89 | }; 90 | } 91 | 92 | public boolean isAlive() { 93 | return alive; 94 | } 95 | 96 | public void setAlive(boolean alive) { 97 | this.alive = alive; 98 | } 99 | 100 | public List getMemberList() { 101 | return memberList; 102 | } 103 | 104 | public void setMemberList(List memberList) { 105 | this.memberList = memberList; 106 | } 107 | 108 | public Map getLoginInfo() { 109 | return loginInfo; 110 | } 111 | 112 | public void setLoginInfo(Map loginInfo) { 113 | this.loginInfo = loginInfo; 114 | } 115 | 116 | public String getUuid() { 117 | return uuid; 118 | } 119 | 120 | public void setUuid(String uuid) { 121 | this.uuid = uuid; 122 | } 123 | 124 | public int getMemberCount() { 125 | return memberCount; 126 | } 127 | 128 | public void setMemberCount(int memberCount) { 129 | this.memberCount = memberCount; 130 | } 131 | 132 | public boolean isUseHotReload() { 133 | return useHotReload; 134 | } 135 | 136 | public void setUseHotReload(boolean useHotReload) { 137 | this.useHotReload = useHotReload; 138 | } 139 | 140 | public String getHotReloadDir() { 141 | return hotReloadDir; 142 | } 143 | 144 | public void setHotReloadDir(String hotReloadDir) { 145 | this.hotReloadDir = hotReloadDir; 146 | } 147 | 148 | public int getReceivingRetryCount() { 149 | return receivingRetryCount; 150 | } 151 | 152 | public void setReceivingRetryCount(int receivingRetryCount) { 153 | this.receivingRetryCount = receivingRetryCount; 154 | } 155 | 156 | public MyHttpClient getMyHttpClient() { 157 | return myHttpClient; 158 | } 159 | 160 | public List getMsgList() { 161 | return msgList; 162 | } 163 | 164 | public void setMsgList(List msgList) { 165 | this.msgList = msgList; 166 | } 167 | 168 | public void setMyHttpClient(MyHttpClient myHttpClient) { 169 | this.myHttpClient = myHttpClient; 170 | } 171 | 172 | public List getGroupIdList() { 173 | return groupIdList; 174 | } 175 | 176 | public void setGroupIdList(List groupIdList) { 177 | this.groupIdList = groupIdList; 178 | } 179 | 180 | public List getContactList() { 181 | return contactList; 182 | } 183 | 184 | public void setContactList(List contactList) { 185 | this.contactList = contactList; 186 | } 187 | 188 | public List getGroupList() { 189 | return groupList; 190 | } 191 | 192 | public void setGroupList(List groupList) { 193 | this.groupList = groupList; 194 | } 195 | 196 | public List getPublicUsersList() { 197 | return publicUsersList; 198 | } 199 | 200 | public void setPublicUsersList(List publicUsersList) { 201 | this.publicUsersList = publicUsersList; 202 | } 203 | 204 | public List getSpecialUsersList() { 205 | return specialUsersList; 206 | } 207 | 208 | public void setSpecialUsersList(List specialUsersList) { 209 | this.specialUsersList = specialUsersList; 210 | } 211 | 212 | public String getUserName() { 213 | return userName; 214 | } 215 | 216 | public void setUserName(String userName) { 217 | this.userName = userName; 218 | } 219 | 220 | public String getNickName() { 221 | return nickName; 222 | } 223 | 224 | public void setNickName(String nickName) { 225 | this.nickName = nickName; 226 | } 227 | 228 | public JSONObject getUserSelf() { 229 | return userSelf; 230 | } 231 | 232 | public void setUserSelf(JSONObject userSelf) { 233 | this.userSelf = userSelf; 234 | } 235 | 236 | public Map getUserInfoMap() { 237 | return userInfoMap; 238 | } 239 | 240 | public void setUserInfoMap(Map userInfoMap) { 241 | this.userInfoMap = userInfoMap; 242 | } 243 | 244 | public synchronized long getLastNormalRetcodeTime() { 245 | return lastNormalRetcodeTime; 246 | } 247 | 248 | public synchronized void setLastNormalRetcodeTime(long lastNormalRetcodeTime) { 249 | this.lastNormalRetcodeTime = lastNormalRetcodeTime; 250 | } 251 | 252 | public List getGroupNickNameList() { 253 | return groupNickNameList; 254 | } 255 | 256 | public void setGroupNickNameList(List groupNickNameList) { 257 | this.groupNickNameList = groupNickNameList; 258 | } 259 | 260 | public Map getGroupMemeberMap() { 261 | return groupMemeberMap; 262 | } 263 | 264 | public void setGroupMemeberMap(Map groupMemeberMap) { 265 | this.groupMemeberMap = groupMemeberMap; 266 | } 267 | 268 | public String getIndexUrl() { 269 | return indexUrl; 270 | } 271 | 272 | public void setIndexUrl(String indexUrl) { 273 | this.indexUrl = indexUrl; 274 | } 275 | 276 | } 277 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/core/MsgCenter.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.core; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.regex.Matcher; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.alibaba.fastjson.JSONArray; 10 | import com.alibaba.fastjson.JSONObject; 11 | 12 | import cn.zhouyafeng.itchat4j.api.MessageTools; 13 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 14 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 15 | import cn.zhouyafeng.itchat4j.utils.enums.MsgCodeEnum; 16 | import cn.zhouyafeng.itchat4j.utils.enums.MsgTypeEnum; 17 | import cn.zhouyafeng.itchat4j.utils.tools.CommonTools; 18 | 19 | /** 20 | * 消息处理中心 21 | * 22 | * @author https://github.com/yaphone 23 | * @date 创建时间:2017年5月14日 下午12:47:50 24 | * @version 1.0 25 | * 26 | */ 27 | public class MsgCenter { 28 | private static Logger LOG = LoggerFactory.getLogger(MsgCenter.class); 29 | 30 | private static Core core = Core.getInstance(); 31 | 32 | /** 33 | * 接收消息,放入队列 34 | * 35 | * @author https://github.com/yaphone 36 | * @date 2017年4月23日 下午2:30:48 37 | * @param msgList 38 | * @return 39 | */ 40 | public static JSONArray produceMsg(JSONArray msgList) { 41 | JSONArray result = new JSONArray(); 42 | for (int i = 0; i < msgList.size(); i++) { 43 | JSONObject msg = new JSONObject(); 44 | JSONObject m = msgList.getJSONObject(i); 45 | m.put("groupMsg", false);// 是否是群消息 46 | if (m.getString("FromUserName").contains("@@") || m.getString("ToUserName").contains("@@")) { // 群聊消息 47 | if (m.getString("FromUserName").contains("@@") 48 | && !core.getGroupIdList().contains(m.getString("FromUserName"))) { 49 | core.getGroupIdList().add((m.getString("FromUserName"))); 50 | } else if (m.getString("ToUserName").contains("@@") 51 | && !core.getGroupIdList().contains(m.getString("ToUserName"))) { 52 | core.getGroupIdList().add((m.getString("ToUserName"))); 53 | } 54 | // 群消息与普通消息不同的是在其消息体(Content)中会包含发送者id及":
"消息,这里需要处理一下,去掉多余信息,只保留消息内容 55 | if (m.getString("Content").contains("
")) { 56 | String content = m.getString("Content").substring(m.getString("Content").indexOf("
") + 5); 57 | m.put("Content", content); 58 | m.put("groupMsg", true); 59 | } 60 | } else { 61 | CommonTools.msgFormatter(m, "Content"); 62 | } 63 | if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_TEXT.getCode())) { // words 64 | // 文本消息 65 | if (m.getString("Url").length() != 0) { 66 | String regEx = "(.+?\\(.+?\\))"; 67 | Matcher matcher = CommonTools.getMatcher(regEx, m.getString("Content")); 68 | String data = "Map"; 69 | if (matcher.find()) { 70 | data = matcher.group(1); 71 | } 72 | msg.put("Type", "Map"); 73 | msg.put("Text", data); 74 | } else { 75 | msg.put("Type", MsgTypeEnum.TEXT.getType()); 76 | msg.put("Text", m.getString("Content")); 77 | } 78 | m.put("Type", msg.getString("Type")); 79 | m.put("Text", msg.getString("Text")); 80 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_IMAGE.getCode()) 81 | || m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_EMOTICON.getCode())) { // 图片消息 82 | m.put("Type", MsgTypeEnum.PIC.getType()); 83 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VOICE.getCode())) { // 语音消息 84 | m.put("Type", MsgTypeEnum.VOICE.getType()); 85 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VERIFYMSG.getCode())) {// friends 86 | // 好友确认消息 87 | // MessageTools.addFriend(core, userName, 3, ticket); // 确认添加好友 88 | m.put("Type", MsgTypeEnum.VERIFYMSG.getType()); 89 | 90 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_SHARECARD.getCode())) { // 共享名片 91 | m.put("Type", MsgTypeEnum.NAMECARD.getType()); 92 | 93 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VIDEO.getCode()) 94 | || m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_MICROVIDEO.getCode())) {// viedo 95 | m.put("Type", MsgTypeEnum.VIEDO.getType()); 96 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_MEDIA.getCode())) { // 多媒体消息 97 | m.put("Type", MsgTypeEnum.MEDIA.getType()); 98 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_STATUSNOTIFY.getCode())) {// phone 99 | // init 100 | // 微信初始化消息 101 | 102 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_SYS.getCode())) {// 系统消息 103 | m.put("Type", MsgTypeEnum.SYS.getType()); 104 | } else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_RECALLED.getCode())) { // 撤回消息 105 | 106 | } else { 107 | LOG.info("Useless msg"); 108 | } 109 | LOG.info("收到消息一条,来自: " + m.getString("FromUserName")); 110 | result.add(m); 111 | } 112 | return result; 113 | } 114 | 115 | /** 116 | * 消息处理 117 | * 118 | * @author https://github.com/yaphone 119 | * @date 2017年5月14日 上午10:52:34 120 | * @param msgHandler 121 | */ 122 | public static void handleMsg(IMsgHandlerFace msgHandler) { 123 | while (true) { 124 | if (core.getMsgList().size() > 0 && core.getMsgList().get(0).getContent() != null) { 125 | if (core.getMsgList().get(0).getContent().length() > 0) { 126 | BaseMsg msg = core.getMsgList().get(0); 127 | if (msg.getType() != null) { 128 | try { 129 | if (msg.getType().equals(MsgTypeEnum.TEXT.getType())) { 130 | String result = msgHandler.textMsgHandle(msg); 131 | MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); 132 | } else if (msg.getType().equals(MsgTypeEnum.PIC.getType())) { 133 | 134 | String result = msgHandler.picMsgHandle(msg); 135 | MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); 136 | } else if (msg.getType().equals(MsgTypeEnum.VOICE.getType())) { 137 | String result = msgHandler.voiceMsgHandle(msg); 138 | MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); 139 | } else if (msg.getType().equals(MsgTypeEnum.VIEDO.getType())) { 140 | String result = msgHandler.viedoMsgHandle(msg); 141 | MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); 142 | } else if (msg.getType().equals(MsgTypeEnum.NAMECARD.getType())) { 143 | String result = msgHandler.nameCardMsgHandle(msg); 144 | MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); 145 | } else if (msg.getType().equals(MsgTypeEnum.SYS.getType())) { // 系统消息 146 | msgHandler.sysMsgHandle(msg); 147 | } else if (msg.getType().equals(MsgTypeEnum.VERIFYMSG.getType())) { // 确认添加好友消息 148 | String result = msgHandler.verifyAddFriendMsgHandle(msg); 149 | MessageTools.sendMsgById(result, 150 | core.getMsgList().get(0).getRecommendInfo().getUserName()); 151 | } else if (msg.getType().equals(MsgTypeEnum.MEDIA.getType())) { // 多媒体消息 152 | String result = msgHandler.mediaMsgHandle(msg); 153 | MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName()); 154 | } 155 | } catch (Exception e) { 156 | e.printStackTrace(); 157 | } 158 | } 159 | } 160 | core.getMsgList().remove(0); 161 | } 162 | try { 163 | TimeUnit.MILLISECONDS.sleep(1000); 164 | } catch (InterruptedException e) { 165 | e.printStackTrace(); 166 | } 167 | } 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/face/IMsgHandlerFace.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.face; 2 | 3 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 4 | 5 | /** 6 | * 消息处理接口 7 | * 8 | * @author https://github.com/yaphone 9 | * @date 创建时间:2017年4月20日 上午12:13:49 10 | * @version 1.0 11 | * 12 | */ 13 | public interface IMsgHandlerFace { 14 | /** 15 | * 16 | * @author https://github.com/yaphone 17 | * @date 2017年4月20日 上午12:15:00 18 | * @param msg 19 | * @return 20 | */ 21 | public String textMsgHandle(BaseMsg msg); 22 | 23 | /** 24 | * 处理图片消息 25 | * 26 | * @author https://github.com/yaphone 27 | * @date 2017年4月21日 下午11:07:06 28 | * @param msg 29 | * @return 30 | */ 31 | public String picMsgHandle(BaseMsg msg); 32 | 33 | /** 34 | * 处理声音消息 35 | * 36 | * @author https://github.com/yaphone 37 | * @date 2017年4月22日 上午12:09:44 38 | * @param msg 39 | * @return 40 | */ 41 | public String voiceMsgHandle(BaseMsg msg); 42 | 43 | /** 44 | * 处理小视频消息 45 | * 46 | * @author https://github.com/yaphone 47 | * @date 2017年4月23日 下午12:19:50 48 | * @param msg 49 | * @return 50 | */ 51 | public String viedoMsgHandle(BaseMsg msg); 52 | 53 | /** 54 | * 处理名片消息 55 | * 56 | * @author https://github.com/yaphone 57 | * @date 2017年5月1日 上午12:50:50 58 | * @param msg 59 | * @return 60 | */ 61 | public String nameCardMsgHandle(BaseMsg msg); 62 | 63 | /** 64 | * 处理系统消息 65 | * 66 | * @author Relyn 67 | * @date 2017年6月21日17:43:51 68 | * @param msg 69 | * @return 70 | */ 71 | public void sysMsgHandle(BaseMsg msg); 72 | 73 | /** 74 | * 处理确认添加好友消息 75 | * 76 | * @date 2017年6月28日 下午10:15:30 77 | * @param msg 78 | * @return 79 | */ 80 | public String verifyAddFriendMsgHandle(BaseMsg msg); 81 | 82 | /** 83 | * 处理收到的文件消息 84 | * 85 | * @date 2017年7月21日 下午11:59:14 86 | * @param msg 87 | * @return 88 | */ 89 | public String mediaMsgHandle(BaseMsg msg); 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/service/ILoginService.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.service; 2 | 3 | /** 4 | * 登陆服务接口 5 | * 6 | * @author https://github.com/yaphone 7 | * @date 创建时间:2017年5月13日 上午12:07:21 8 | * @version 1.0 9 | * 10 | */ 11 | public interface ILoginService { 12 | 13 | /** 14 | * 登陆 15 | * 16 | * @author https://github.com/yaphone 17 | * @date 2017年5月13日 上午12:14:07 18 | * @return 19 | */ 20 | boolean login(); 21 | 22 | /** 23 | * 获取UUID 24 | * 25 | * @author https://github.com/yaphone 26 | * @date 2017年5月13日 上午12:21:40 27 | * @param qrPath 28 | * @return 29 | */ 30 | String getUuid(); 31 | 32 | /** 33 | * 获取二维码图片 34 | * 35 | * @author https://github.com/yaphone 36 | * @date 2017年5月13日 上午12:13:51 37 | * @param qrPath 38 | * @return 39 | */ 40 | boolean getQR(String qrPath); 41 | 42 | /** 43 | * web初始化 44 | * 45 | * @author https://github.com/yaphone 46 | * @date 2017年5月13日 上午12:14:13 47 | * @return 48 | */ 49 | boolean webWxInit(); 50 | 51 | /** 52 | * 微信状态通知 53 | * 54 | * @author https://github.com/yaphone 55 | * @date 2017年5月13日 上午12:14:24 56 | */ 57 | void wxStatusNotify(); 58 | 59 | /** 60 | * 接收消息 61 | * 62 | * @author https://github.com/yaphone 63 | * @date 2017年5月13日 上午12:14:37 64 | */ 65 | void startReceiving(); 66 | 67 | /** 68 | * 获取微信联系人 69 | * 70 | * @author https://github.com/yaphone 71 | * @date 2017年5月13日 下午2:26:18 72 | */ 73 | void webWxGetContact(); 74 | 75 | /** 76 | * 批量获取联系人信息 77 | * 78 | * @date 2017年6月22日 下午11:24:35 79 | */ 80 | void WebWxBatchGetContact(); 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/service/impl/LoginServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.service.impl; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.OutputStream; 6 | import java.util.ArrayList; 7 | import java.util.Date; 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Map.Entry; 13 | import java.util.Random; 14 | import java.util.regex.Matcher; 15 | 16 | import org.apache.http.Consts; 17 | import org.apache.http.HttpEntity; 18 | import org.apache.http.message.BasicNameValuePair; 19 | import org.apache.http.util.EntityUtils; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.w3c.dom.Document; 23 | 24 | import com.alibaba.fastjson.JSON; 25 | import com.alibaba.fastjson.JSONArray; 26 | import com.alibaba.fastjson.JSONObject; 27 | 28 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 29 | import cn.zhouyafeng.itchat4j.core.Core; 30 | import cn.zhouyafeng.itchat4j.core.MsgCenter; 31 | import cn.zhouyafeng.itchat4j.service.ILoginService; 32 | import cn.zhouyafeng.itchat4j.utils.Config; 33 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 34 | import cn.zhouyafeng.itchat4j.utils.SleepUtils; 35 | import cn.zhouyafeng.itchat4j.utils.enums.ResultEnum; 36 | import cn.zhouyafeng.itchat4j.utils.enums.RetCodeEnum; 37 | import cn.zhouyafeng.itchat4j.utils.enums.StorageLoginInfoEnum; 38 | import cn.zhouyafeng.itchat4j.utils.enums.URLEnum; 39 | import cn.zhouyafeng.itchat4j.utils.enums.parameters.BaseParaEnum; 40 | import cn.zhouyafeng.itchat4j.utils.enums.parameters.LoginParaEnum; 41 | import cn.zhouyafeng.itchat4j.utils.enums.parameters.StatusNotifyParaEnum; 42 | import cn.zhouyafeng.itchat4j.utils.enums.parameters.UUIDParaEnum; 43 | import cn.zhouyafeng.itchat4j.utils.tools.CommonTools; 44 | 45 | /** 46 | * 登陆服务实现类 47 | * 48 | * @author https://github.com/yaphone 49 | * @date 创建时间:2017年5月13日 上午12:09:35 50 | * @version 1.0 51 | * 52 | */ 53 | public class LoginServiceImpl implements ILoginService { 54 | private static Logger LOG = LoggerFactory.getLogger(LoginServiceImpl.class); 55 | 56 | private Core core = Core.getInstance(); 57 | private MyHttpClient httpClient = core.getMyHttpClient(); 58 | 59 | private MyHttpClient myHttpClient = core.getMyHttpClient(); 60 | 61 | public LoginServiceImpl() { 62 | 63 | } 64 | 65 | @Override 66 | public boolean login() { 67 | 68 | boolean isLogin = false; 69 | // 组装参数和URL 70 | List params = new ArrayList(); 71 | params.add(new BasicNameValuePair(LoginParaEnum.LOGIN_ICON.para(), LoginParaEnum.LOGIN_ICON.value())); 72 | params.add(new BasicNameValuePair(LoginParaEnum.UUID.para(), core.getUuid())); 73 | params.add(new BasicNameValuePair(LoginParaEnum.TIP.para(), LoginParaEnum.TIP.value())); 74 | 75 | // long time = 4000; 76 | while (!isLogin) { 77 | // SleepUtils.sleep(time += 1000); 78 | long millis = System.currentTimeMillis(); 79 | params.add(new BasicNameValuePair(LoginParaEnum.R.para(), String.valueOf(millis / 1579L))); 80 | params.add(new BasicNameValuePair(LoginParaEnum._.para(), String.valueOf(millis))); 81 | HttpEntity entity = httpClient.doGet(URLEnum.LOGIN_URL.getUrl(), params, true, null); 82 | 83 | try { 84 | String result = EntityUtils.toString(entity); 85 | String status = checklogin(result); 86 | 87 | if (ResultEnum.SUCCESS.getCode().equals(status)) { 88 | processLoginInfo(result); // 处理结果 89 | isLogin = true; 90 | core.setAlive(isLogin); 91 | break; 92 | } 93 | if (ResultEnum.WAIT_CONFIRM.getCode().equals(status)) { 94 | LOG.info("请点击微信确认按钮,进行登陆"); 95 | } 96 | 97 | } catch (Exception e) { 98 | LOG.error("微信登陆异常!", e); 99 | } 100 | } 101 | return isLogin; 102 | } 103 | 104 | @Override 105 | public String getUuid() { 106 | // 组装参数和URL 107 | List params = new ArrayList(); 108 | params.add(new BasicNameValuePair(UUIDParaEnum.APP_ID.para(), UUIDParaEnum.APP_ID.value())); 109 | params.add(new BasicNameValuePair(UUIDParaEnum.REDIRECT_URL.para(), 110 | UUIDParaEnum.REDIRECT_URL.value())); 111 | params.add(new BasicNameValuePair(UUIDParaEnum.FUN.para(), UUIDParaEnum.FUN.value())); 112 | params.add(new BasicNameValuePair(UUIDParaEnum.LANG.para(), UUIDParaEnum.LANG.value())); 113 | params.add(new BasicNameValuePair(UUIDParaEnum._.para(), String.valueOf(System.currentTimeMillis()))); 114 | 115 | HttpEntity entity = httpClient.doGet(URLEnum.UUID_URL.getUrl(), params, true, null); 116 | 117 | try { 118 | String result = EntityUtils.toString(entity); 119 | String regEx = "window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";"; 120 | Matcher matcher = CommonTools.getMatcher(regEx, result); 121 | if (matcher.find()) { 122 | if ((ResultEnum.SUCCESS.getCode().equals(matcher.group(1)))) { 123 | core.setUuid(matcher.group(2)); 124 | } 125 | } 126 | } catch (Exception e) { 127 | LOG.error(e.getMessage(), e); 128 | } 129 | 130 | return core.getUuid(); 131 | } 132 | 133 | @Override 134 | public boolean getQR(String qrPath) { 135 | qrPath = qrPath + File.separator + "QR.jpg"; 136 | String qrUrl = URLEnum.QRCODE_URL.getUrl() + core.getUuid(); 137 | HttpEntity entity = myHttpClient.doGet(qrUrl, null, true, null); 138 | try { 139 | OutputStream out = new FileOutputStream(qrPath); 140 | byte[] bytes = EntityUtils.toByteArray(entity); 141 | out.write(bytes); 142 | out.flush(); 143 | out.close(); 144 | try { 145 | CommonTools.printQr(qrPath); // 打开登陆二维码图片 146 | } catch (Exception e) { 147 | LOG.info(e.getMessage()); 148 | } 149 | 150 | } catch (Exception e) { 151 | LOG.info(e.getMessage()); 152 | return false; 153 | } 154 | 155 | return true; 156 | } 157 | 158 | @Override 159 | public boolean webWxInit() { 160 | core.setAlive(true); 161 | core.setLastNormalRetcodeTime(System.currentTimeMillis()); 162 | // 组装请求URL和参数 163 | String url = String.format(URLEnum.INIT_URL.getUrl(), 164 | core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), 165 | String.valueOf(System.currentTimeMillis() / 3158L), 166 | core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); 167 | 168 | Map paramMap = core.getParamMap(); 169 | 170 | // 请求初始化接口 171 | HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); 172 | try { 173 | String result = EntityUtils.toString(entity, Consts.UTF_8); 174 | JSONObject obj = JSON.parseObject(result); 175 | 176 | JSONObject user = obj.getJSONObject(StorageLoginInfoEnum.User.getKey()); 177 | JSONObject syncKey = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()); 178 | 179 | core.getLoginInfo().put(StorageLoginInfoEnum.InviteStartCount.getKey(), 180 | obj.getInteger(StorageLoginInfoEnum.InviteStartCount.getKey())); 181 | core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), syncKey); 182 | 183 | JSONArray syncArray = syncKey.getJSONArray("List"); 184 | StringBuilder sb = new StringBuilder(); 185 | for (int i = 0; i < syncArray.size(); i++) { 186 | sb.append(syncArray.getJSONObject(i).getString("Key") + "_" 187 | + syncArray.getJSONObject(i).getString("Val") + "|"); 188 | } 189 | // 1_661706053|2_661706420|3_661706415|1000_1494151022| 190 | String synckey = sb.toString(); 191 | 192 | // 1_661706053|2_661706420|3_661706415|1000_1494151022 193 | 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 194 | core.setUserName(user.getString("UserName")); 195 | core.setNickName(user.getString("NickName")); 196 | core.setUserSelf(obj.getJSONObject("User")); 197 | 198 | String chatSet = obj.getString("ChatSet"); 199 | String[] chatSetArray = chatSet.split(","); 200 | for (int i = 0; i < chatSetArray.length; i++) { 201 | if (chatSetArray[i].indexOf("@@") != -1) { 202 | // 更新GroupIdList 203 | core.getGroupIdList().add(chatSetArray[i]); // 204 | } 205 | } 206 | // JSONArray contactListArray = obj.getJSONArray("ContactList"); 207 | // for (int i = 0; i < contactListArray.size(); i++) { 208 | // JSONObject o = contactListArray.getJSONObject(i); 209 | // if (o.getString("UserName").indexOf("@@") != -1) { 210 | // core.getGroupIdList().add(o.getString("UserName")); // 211 | // // 更新GroupIdList 212 | // core.getGroupList().add(o); // 更新GroupList 213 | // core.getGroupNickNameList().add(o.getString("NickName")); 214 | // } 215 | // } 216 | } catch (Exception e) { 217 | e.printStackTrace(); 218 | return false; 219 | } 220 | return true; 221 | } 222 | 223 | @Override 224 | public void wxStatusNotify() { 225 | // 组装请求URL和参数 226 | String url = String.format(URLEnum.STATUS_NOTIFY_URL.getUrl(), 227 | core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); 228 | 229 | Map paramMap = core.getParamMap(); 230 | paramMap.put(StatusNotifyParaEnum.CODE.para(), StatusNotifyParaEnum.CODE.value()); 231 | paramMap.put(StatusNotifyParaEnum.FROM_USERNAME.para(), core.getUserName()); 232 | paramMap.put(StatusNotifyParaEnum.TO_USERNAME.para(), core.getUserName()); 233 | paramMap.put(StatusNotifyParaEnum.CLIENT_MSG_ID.para(), System.currentTimeMillis()); 234 | String paramStr = JSON.toJSONString(paramMap); 235 | 236 | try { 237 | HttpEntity entity = httpClient.doPost(url, paramStr); 238 | EntityUtils.toString(entity, Consts.UTF_8); 239 | } catch (Exception e) { 240 | LOG.error("微信状态通知接口失败!", e); 241 | } 242 | 243 | } 244 | 245 | @Override 246 | public void startReceiving() { 247 | core.setAlive(true); 248 | new Thread(new Runnable() { 249 | int retryCount = 0; 250 | 251 | @Override 252 | public void run() { 253 | while (core.isAlive()) { 254 | try { 255 | Map resultMap = syncCheck(); 256 | LOG.info(JSONObject.toJSONString(resultMap)); 257 | String retcode = resultMap.get("retcode"); 258 | String selector = resultMap.get("selector"); 259 | if (retcode.equals(RetCodeEnum.UNKOWN.getCode())) { 260 | LOG.info(RetCodeEnum.UNKOWN.getType()); 261 | continue; 262 | } else if (retcode.equals(RetCodeEnum.LOGIN_OUT.getCode())) { // 退出 263 | LOG.info(RetCodeEnum.LOGIN_OUT.getType()); 264 | break; 265 | } else if (retcode.equals(RetCodeEnum.LOGIN_OTHERWHERE.getCode())) { // 其它地方登陆 266 | LOG.info(RetCodeEnum.LOGIN_OTHERWHERE.getType()); 267 | break; 268 | } else if (retcode.equals(RetCodeEnum.MOBILE_LOGIN_OUT.getCode())) { // 移动端退出 269 | LOG.info(RetCodeEnum.MOBILE_LOGIN_OUT.getType()); 270 | break; 271 | } else if (retcode.equals(RetCodeEnum.NORMAL.getCode())) { 272 | core.setLastNormalRetcodeTime(System.currentTimeMillis()); // 最后收到正常报文时间 273 | JSONObject msgObj = webWxSync(); 274 | if (selector.equals("2")) { 275 | if (msgObj != null) { 276 | try { 277 | JSONArray msgList = new JSONArray(); 278 | msgList = msgObj.getJSONArray("AddMsgList"); 279 | msgList = MsgCenter.produceMsg(msgList); 280 | for (int j = 0; j < msgList.size(); j++) { 281 | BaseMsg baseMsg = JSON.toJavaObject(msgList.getJSONObject(j), 282 | BaseMsg.class); 283 | core.getMsgList().add(baseMsg); 284 | } 285 | } catch (Exception e) { 286 | LOG.info(e.getMessage()); 287 | } 288 | } 289 | } else if (selector.equals("7")) { 290 | webWxSync(); 291 | } else if (selector.equals("4")) { 292 | continue; 293 | } else if (selector.equals("3")) { 294 | continue; 295 | } else if (selector.equals("6")) { 296 | if (msgObj != null) { 297 | try { 298 | JSONArray msgList = new JSONArray(); 299 | msgList = msgObj.getJSONArray("AddMsgList"); 300 | JSONArray modContactList = msgObj.getJSONArray("ModContactList"); // 存在删除或者新增的好友信息 301 | msgList = MsgCenter.produceMsg(msgList); 302 | for (int j = 0; j < msgList.size(); j++) { 303 | JSONObject userInfo = modContactList.getJSONObject(j); 304 | // 存在主动加好友之后的同步联系人到本地 305 | core.getContactList().add(userInfo); 306 | } 307 | } catch (Exception e) { 308 | LOG.info(e.getMessage()); 309 | } 310 | } 311 | 312 | } 313 | } else { 314 | JSONObject obj = webWxSync(); 315 | } 316 | } catch (Exception e) { 317 | LOG.info(e.getMessage()); 318 | retryCount += 1; 319 | if (core.getReceivingRetryCount() < retryCount) { 320 | core.setAlive(false); 321 | } else { 322 | try { 323 | Thread.sleep(1000); 324 | } catch (InterruptedException e1) { 325 | LOG.info(e.getMessage()); 326 | } 327 | } 328 | } 329 | 330 | } 331 | } 332 | }).start(); 333 | 334 | } 335 | 336 | @Override 337 | public void webWxGetContact() { 338 | String url = String.format(URLEnum.WEB_WX_GET_CONTACT.getUrl(), 339 | core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey())); 340 | Map paramMap = core.getParamMap(); 341 | HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); 342 | 343 | try { 344 | String result = EntityUtils.toString(entity, Consts.UTF_8); 345 | JSONObject fullFriendsJsonList = JSON.parseObject(result); 346 | // 查看seq是否为0,0表示好友列表已全部获取完毕,若大于0,则表示好友列表未获取完毕,当前的字节数(断点续传) 347 | long seq = 0; 348 | long currentTime = 0L; 349 | List params = new ArrayList(); 350 | if (fullFriendsJsonList.get("Seq") != null) { 351 | seq = fullFriendsJsonList.getLong("Seq"); 352 | currentTime = new Date().getTime(); 353 | } 354 | core.setMemberCount(fullFriendsJsonList.getInteger(StorageLoginInfoEnum.MemberCount.getKey())); 355 | JSONArray member = fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey()); 356 | // 循环获取seq直到为0,即获取全部好友列表 ==0:好友获取完毕 >0:好友未获取完毕,此时seq为已获取的字节数 357 | while (seq > 0) { 358 | // 设置seq传参 359 | params.add(new BasicNameValuePair("r", String.valueOf(currentTime))); 360 | params.add(new BasicNameValuePair("seq", String.valueOf(seq))); 361 | entity = httpClient.doGet(url, params, false, null); 362 | 363 | params.remove(new BasicNameValuePair("r", String.valueOf(currentTime))); 364 | params.remove(new BasicNameValuePair("seq", String.valueOf(seq))); 365 | 366 | result = EntityUtils.toString(entity, Consts.UTF_8); 367 | fullFriendsJsonList = JSON.parseObject(result); 368 | 369 | if (fullFriendsJsonList.get("Seq") != null) { 370 | seq = fullFriendsJsonList.getLong("Seq"); 371 | currentTime = new Date().getTime(); 372 | } 373 | 374 | // 累加好友列表 375 | member.addAll(fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey())); 376 | } 377 | core.setMemberCount(member.size()); 378 | for (Iterator iterator = member.iterator(); iterator.hasNext();) { 379 | JSONObject o = (JSONObject) iterator.next(); 380 | if ((o.getInteger("VerifyFlag") & 8) != 0) { // 公众号/服务号 381 | core.getPublicUsersList().add(o); 382 | } else if (Config.API_SPECIAL_USER.contains(o.getString("UserName"))) { // 特殊账号 383 | core.getSpecialUsersList().add(o); 384 | } else if (o.getString("UserName").indexOf("@@") != -1) { // 群聊 385 | if (!core.getGroupIdList().contains(o.getString("UserName"))) { 386 | core.getGroupNickNameList().add(o.getString("NickName")); 387 | core.getGroupIdList().add(o.getString("UserName")); 388 | core.getGroupList().add(o); 389 | } 390 | } else if (o.getString("UserName").equals(core.getUserSelf().getString("UserName"))) { // 自己 391 | core.getContactList().remove(o); 392 | } else { // 普通联系人 393 | core.getContactList().add(o); 394 | } 395 | } 396 | return; 397 | } catch (Exception e) { 398 | LOG.error(e.getMessage(), e); 399 | } 400 | return; 401 | } 402 | 403 | @Override 404 | public void WebWxBatchGetContact() { 405 | String url = String.format(URLEnum.WEB_WX_BATCH_GET_CONTACT.getUrl(), 406 | core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), new Date().getTime(), 407 | core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); 408 | Map paramMap = core.getParamMap(); 409 | paramMap.put("Count", core.getGroupIdList().size()); 410 | List> list = new ArrayList>(); 411 | for (int i = 0; i < core.getGroupIdList().size(); i++) { 412 | HashMap map = new HashMap(); 413 | map.put("UserName", core.getGroupIdList().get(i)); 414 | map.put("EncryChatRoomId", ""); 415 | list.add(map); 416 | } 417 | paramMap.put("List", list); 418 | HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap)); 419 | try { 420 | String text = EntityUtils.toString(entity, Consts.UTF_8); 421 | JSONObject obj = JSON.parseObject(text); 422 | JSONArray contactList = obj.getJSONArray("ContactList"); 423 | for (int i = 0; i < contactList.size(); i++) { // 群好友 424 | if (contactList.getJSONObject(i).getString("UserName").indexOf("@@") > -1) { // 群 425 | core.getGroupNickNameList().add(contactList.getJSONObject(i).getString("NickName")); // 更新群昵称列表 426 | core.getGroupList().add(contactList.getJSONObject(i)); // 更新群信息(所有)列表 427 | core.getGroupMemeberMap().put(contactList.getJSONObject(i).getString("UserName"), 428 | contactList.getJSONObject(i).getJSONArray("MemberList")); // 更新群成员Map 429 | } 430 | } 431 | } catch (Exception e) { 432 | LOG.info(e.getMessage()); 433 | } 434 | } 435 | 436 | /** 437 | * 检查登陆状态 438 | * 439 | * @param result 440 | * @return 441 | */ 442 | public String checklogin(String result) { 443 | String regEx = "window.code=(\\d+)"; 444 | Matcher matcher = CommonTools.getMatcher(regEx, result); 445 | if (matcher.find()) { 446 | return matcher.group(1); 447 | } 448 | return null; 449 | } 450 | 451 | /** 452 | * 处理登陆信息 453 | * 454 | * @author https://github.com/yaphone 455 | * @date 2017年4月9日 下午12:16:26 456 | * @param result 457 | */ 458 | private void processLoginInfo(String loginContent) { 459 | String regEx = "window.redirect_uri=\"(\\S+)\";"; 460 | Matcher matcher = CommonTools.getMatcher(regEx, loginContent); 461 | if (matcher.find()) { 462 | String originalUrl = matcher.group(1); 463 | String url = originalUrl.substring(0, originalUrl.lastIndexOf('/')); // https://wx2.qq.com/cgi-bin/mmwebwx-bin 464 | core.getLoginInfo().put("url", url); 465 | Map> possibleUrlMap = this.getPossibleUrlMap(); 466 | Iterator>> iterator = possibleUrlMap.entrySet().iterator(); 467 | Map.Entry> entry; 468 | String fileUrl; 469 | String syncUrl; 470 | while (iterator.hasNext()) { 471 | entry = iterator.next(); 472 | String indexUrl = entry.getKey(); 473 | fileUrl = "https://" + entry.getValue().get(0) + "/cgi-bin/mmwebwx-bin"; 474 | syncUrl = "https://" + entry.getValue().get(1) + "/cgi-bin/mmwebwx-bin"; 475 | if (core.getLoginInfo().get("url").toString().contains(indexUrl)) { 476 | core.setIndexUrl(indexUrl); 477 | core.getLoginInfo().put("fileUrl", fileUrl); 478 | core.getLoginInfo().put("syncUrl", syncUrl); 479 | break; 480 | } 481 | } 482 | if (core.getLoginInfo().get("fileUrl") == null && core.getLoginInfo().get("syncUrl") == null) { 483 | core.getLoginInfo().put("fileUrl", url); 484 | core.getLoginInfo().put("syncUrl", url); 485 | } 486 | core.getLoginInfo().put("deviceid", "e" + String.valueOf(new Random().nextLong()).substring(1, 16)); // 生成15位随机数 487 | core.getLoginInfo().put("BaseRequest", new ArrayList()); 488 | String text = ""; 489 | 490 | try { 491 | HttpEntity entity = myHttpClient.doGet(originalUrl, null, false, null); 492 | text = EntityUtils.toString(entity); 493 | } catch (Exception e) { 494 | LOG.info(e.getMessage()); 495 | return; 496 | } 497 | //add by 默非默 2017-08-01 22:28:09 498 | //如果登录被禁止时,则登录返回的message内容不为空,下面代码则判断登录内容是否为空,不为空则退出程序 499 | String msg = getLoginMessage(text); 500 | if (!"".equals(msg)){ 501 | LOG.info(msg); 502 | System.exit(0); 503 | } 504 | Document doc = CommonTools.xmlParser(text); 505 | if (doc != null) { 506 | core.getLoginInfo().put(StorageLoginInfoEnum.skey.getKey(), 507 | doc.getElementsByTagName(StorageLoginInfoEnum.skey.getKey()).item(0).getFirstChild() 508 | .getNodeValue()); 509 | core.getLoginInfo().put(StorageLoginInfoEnum.wxsid.getKey(), 510 | doc.getElementsByTagName(StorageLoginInfoEnum.wxsid.getKey()).item(0).getFirstChild() 511 | .getNodeValue()); 512 | core.getLoginInfo().put(StorageLoginInfoEnum.wxuin.getKey(), 513 | doc.getElementsByTagName(StorageLoginInfoEnum.wxuin.getKey()).item(0).getFirstChild() 514 | .getNodeValue()); 515 | core.getLoginInfo().put(StorageLoginInfoEnum.pass_ticket.getKey(), 516 | doc.getElementsByTagName(StorageLoginInfoEnum.pass_ticket.getKey()).item(0).getFirstChild() 517 | .getNodeValue()); 518 | } 519 | 520 | } 521 | } 522 | 523 | private Map> getPossibleUrlMap() { 524 | Map> possibleUrlMap = new HashMap>(); 525 | possibleUrlMap.put("wx.qq.com", new ArrayList() { 526 | /** 527 | * 528 | */ 529 | private static final long serialVersionUID = 1L; 530 | 531 | { 532 | add("file.wx.qq.com"); 533 | add("webpush.wx.qq.com"); 534 | } 535 | }); 536 | 537 | possibleUrlMap.put("wx2.qq.com", new ArrayList() { 538 | /** 539 | * 540 | */ 541 | private static final long serialVersionUID = 1L; 542 | 543 | { 544 | add("file.wx2.qq.com"); 545 | add("webpush.wx2.qq.com"); 546 | } 547 | }); 548 | possibleUrlMap.put("wx8.qq.com", new ArrayList() { 549 | /** 550 | * 551 | */ 552 | private static final long serialVersionUID = 1L; 553 | 554 | { 555 | add("file.wx8.qq.com"); 556 | add("webpush.wx8.qq.com"); 557 | } 558 | }); 559 | 560 | possibleUrlMap.put("web2.wechat.com", new ArrayList() { 561 | /** 562 | * 563 | */ 564 | private static final long serialVersionUID = 1L; 565 | 566 | { 567 | add("file.web2.wechat.com"); 568 | add("webpush.web2.wechat.com"); 569 | } 570 | }); 571 | possibleUrlMap.put("wechat.com", new ArrayList() { 572 | /** 573 | * 574 | */ 575 | private static final long serialVersionUID = 1L; 576 | 577 | { 578 | add("file.web.wechat.com"); 579 | add("webpush.web.wechat.com"); 580 | } 581 | }); 582 | return possibleUrlMap; 583 | } 584 | 585 | /** 586 | * 同步消息 sync the messages 587 | * 588 | * @author https://github.com/yaphone 589 | * @date 2017年5月12日 上午12:24:55 590 | * @return 591 | */ 592 | private JSONObject webWxSync() { 593 | JSONObject result = null; 594 | String url = String.format(URLEnum.WEB_WX_SYNC_URL.getUrl(), 595 | core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), 596 | core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey()), 597 | core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()), 598 | core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey())); 599 | Map paramMap = core.getParamMap(); 600 | paramMap.put(StorageLoginInfoEnum.SyncKey.getKey(), 601 | core.getLoginInfo().get(StorageLoginInfoEnum.SyncKey.getKey())); 602 | paramMap.put("rr", -new Date().getTime() / 1000); 603 | String paramStr = JSON.toJSONString(paramMap); 604 | try { 605 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 606 | String text = EntityUtils.toString(entity, Consts.UTF_8); 607 | JSONObject obj = JSON.parseObject(text); 608 | if (obj.getJSONObject("BaseResponse").getInteger("Ret") != 0) { 609 | result = null; 610 | } else { 611 | result = obj; 612 | core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), obj.getJSONObject("SyncCheckKey")); 613 | JSONArray syncArray = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()).getJSONArray("List"); 614 | StringBuilder sb = new StringBuilder(); 615 | for (int i = 0; i < syncArray.size(); i++) { 616 | sb.append(syncArray.getJSONObject(i).getString("Key") + "_" 617 | + syncArray.getJSONObject(i).getString("Val") + "|"); 618 | } 619 | String synckey = sb.toString(); 620 | core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(), 621 | synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192 622 | } 623 | } catch (Exception e) { 624 | LOG.info(e.getMessage()); 625 | } 626 | return result; 627 | 628 | } 629 | 630 | /** 631 | * 检查是否有新消息 check whether there's a message 632 | * 633 | * @author https://github.com/yaphone 634 | * @date 2017年4月16日 上午11:11:34 635 | * @return 636 | * 637 | */ 638 | private Map syncCheck() { 639 | Map resultMap = new HashMap(); 640 | // 组装请求URL和参数 641 | String url = core.getLoginInfo().get(StorageLoginInfoEnum.syncUrl.getKey()) + URLEnum.SYNC_CHECK_URL.getUrl(); 642 | List params = new ArrayList(); 643 | for (BaseParaEnum baseRequest : BaseParaEnum.values()) { 644 | params.add(new BasicNameValuePair(baseRequest.para().toLowerCase(), 645 | core.getLoginInfo().get(baseRequest.value()).toString())); 646 | } 647 | params.add(new BasicNameValuePair("r", String.valueOf(new Date().getTime()))); 648 | params.add(new BasicNameValuePair("synckey", (String) core.getLoginInfo().get("synckey"))); 649 | params.add(new BasicNameValuePair("_", String.valueOf(new Date().getTime()))); 650 | SleepUtils.sleep(7); 651 | try { 652 | HttpEntity entity = myHttpClient.doGet(url, params, true, null); 653 | if (entity == null) { 654 | resultMap.put("retcode", "9999"); 655 | resultMap.put("selector", "9999"); 656 | return resultMap; 657 | } 658 | String text = EntityUtils.toString(entity); 659 | String regEx = "window.synccheck=\\{retcode:\"(\\d+)\",selector:\"(\\d+)\"\\}"; 660 | Matcher matcher = CommonTools.getMatcher(regEx, text); 661 | if (!matcher.find() || matcher.group(1).equals("2")) { 662 | LOG.info(String.format("Unexpected sync check result: %s", text)); 663 | } else { 664 | resultMap.put("retcode", matcher.group(1)); 665 | resultMap.put("selector", matcher.group(2)); 666 | } 667 | } catch (Exception e) { 668 | e.printStackTrace(); 669 | } 670 | return resultMap; 671 | } 672 | 673 | /** 674 | * 解析登录返回的消息,如果成功登录,则message为空 675 | * @param result 676 | * @return 677 | */ 678 | public String getLoginMessage(String result){ 679 | String[] strArr = result.split(""); 680 | String[] rs = strArr[1].split(""); 681 | if (rs!=null && rs.length>1) { 682 | return rs[0]; 683 | } 684 | return ""; 685 | } 686 | } 687 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/thread/CheckLoginStatusThread.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.thread; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import cn.zhouyafeng.itchat4j.core.Core; 7 | import cn.zhouyafeng.itchat4j.utils.SleepUtils; 8 | 9 | /** 10 | * 检查微信在线状态 11 | *

12 | * 如何来感知微信状态? 13 | * 微信会有心跳包,LoginServiceImpl.syncCheck()正常在线情况下返回的消息中retcode报文应该为"0",心跳间隔一般在25秒, 14 | * 那么可以通过最后收到正常报文的时间来作为判断是否在线的依据。若报文间隔大于60秒,则认为已掉线。 15 | *

16 | * 17 | * @author https://github.com/yaphone 18 | * @date 创建时间:2017年5月17日 下午10:53:15 19 | * @version 1.0 20 | * 21 | */ 22 | public class CheckLoginStatusThread implements Runnable { 23 | private static Logger LOG = LoggerFactory.getLogger(CheckLoginStatusThread.class); 24 | private Core core = Core.getInstance(); 25 | 26 | @Override 27 | public void run() { 28 | while (core.isAlive()) { 29 | long t1 = System.currentTimeMillis(); // 秒为单位 30 | if (t1 - core.getLastNormalRetcodeTime() > 60 * 1000) { // 超过60秒,判为离线 31 | core.setAlive(false); 32 | LOG.info("微信已离线"); 33 | } 34 | SleepUtils.sleep(10 * 1000); // 休眠10秒 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/Config.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | 8 | import cn.zhouyafeng.itchat4j.utils.enums.OsNameEnum; 9 | 10 | /** 11 | * 配置信息 12 | * 13 | * @author https://github.com/yaphone 14 | * @date 创建时间:2017年4月23日 下午2:26:21 15 | * @version 1.0 16 | * 17 | */ 18 | public class Config { 19 | 20 | public static final String API_WXAPPID = "API_WXAPPID"; 21 | 22 | public static final String picDir = "D://itchat4j"; 23 | public static final String VERSION = "1.4.1"; 24 | public static final String BASE_URL = "https://login.weixin.qq.com"; 25 | public static final String REFERER = "https://wx.qq.com/?&lang=zh_CN&target=t"; 26 | public static final String OS = ""; 27 | public static final String DIR = ""; 28 | public static final String DEFAULT_QR = "QR.jpg"; 29 | public static final String USER_AGENT = "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"; 30 | public static final String UOS_PATCH_CLIENT_VERSION = "2.0.0"; 31 | 32 | public static final String UOS_PATCH_EXTSPAM = 33 | "Go8FCIkFEokFCggwMDAwMDAwMRAGGvAESySibk50w5Wb3uTl2c2h64jVVrV7gNs06GFlWplHQbY/5FfiO++1yH4ykCyNPWKXmco+wfQzK5R98D3so7rJ5LmGFvBLjGceleySrc3SOf2Pc1gVehzJgODeS0lDL3/I/0S2SSE98YgKleq6Uqx6ndTy9yaL9qFxJL7eiA/R3SEfTaW1SBoSITIu+EEkXff+Pv8NHOk7N57rcGk1w0ZzRrQDkXTOXFN2iHYIzAAZPIOY45Lsh+A4slpgnDiaOvRtlQYCt97nmPLuTipOJ8Qc5pM7ZsOsAPPrCQL7nK0I7aPrFDF0q4ziUUKettzW8MrAaiVfmbD1/VkmLNVqqZVvBCtRblXb5FHmtS8FxnqCzYP4WFvz3T0TcrOqwLX1M/DQvcHaGGw0B0y4bZMs7lVScGBFxMj3vbFi2SRKbKhaitxHfYHAOAa0X7/MSS0RNAjdwoyGHeOepXOKY+h3iHeqCvgOH6LOifdHf/1aaZNwSkGotYnYScW8Yx63LnSwba7+hESrtPa/huRmB9KWvMCKbDThL/nne14hnL277EDCSocPu3rOSYjuB9gKSOdVmWsj9Dxb/iZIe+S6AiG29Esm+/eUacSba0k8wn5HhHg9d4tIcixrxveflc8vi2/wNQGVFNsGO6tB5WF0xf/plngOvQ1/ivGV/C1Qpdhzznh0ExAVJ6dwzNg7qIEBaw+BzTJTUuRcPk92Sn6QDn2Pu3mpONaEumacjW4w6ipPnPw+g2TfywJjeEcpSZaP4Q3YV5HG8D6UjWA4GSkBKculWpdCMadx0usMomsSS/74QgpYqcPkmamB4nVv1JxczYITIqItIKjD35IGKAUwAA=="; 34 | 35 | 36 | public static final ArrayList API_SPECIAL_USER = new ArrayList(Arrays.asList("filehelper", "weibo", 37 | "qqmail", "fmessage", "tmessage", "qmessage", "qqsync", "floatbottle", "lbsapp", "shakeapp", "medianote", 38 | "qqfriend", "readerapp", "blogapp", "facebookapp", "masssendapp", "meishiapp", "feedsapp", "voip", 39 | "blogappweixin", "brandsessionholder", "weixin", "weixinreminder", "officialaccounts", "wxitil", 40 | "notification_messages", "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "userexperience_alarm")); 41 | 42 | /** 43 | * 获取文件目录 44 | * 45 | * @author https://github.com/yaphone 46 | * @date 2017年4月8日 下午10:27:42 47 | * @return 48 | */ 49 | public static String getLocalPath() { 50 | String localPath = null; 51 | try { 52 | localPath = new File("").getCanonicalPath(); 53 | } catch (IOException e) { 54 | // TODO Auto-generated catch block 55 | e.printStackTrace(); 56 | } 57 | return localPath; 58 | } 59 | 60 | /** 61 | * 获取系统平台 62 | * 63 | * @author https://github.com/yaphone 64 | * @date 2017年4月8日 下午10:27:53 65 | */ 66 | public static OsNameEnum getOsNameEnum() { 67 | String os = System.getProperty("os.name").toUpperCase(); 68 | if (os.indexOf(OsNameEnum.DARWIN.toString()) >= 0) { 69 | return OsNameEnum.DARWIN; 70 | } else if (os.indexOf(OsNameEnum.WINDOWS.toString()) >= 0) { 71 | return OsNameEnum.WINDOWS; 72 | } else if (os.indexOf(OsNameEnum.LINUX.toString()) >= 0) { 73 | return OsNameEnum.LINUX; 74 | } else if (os.indexOf(OsNameEnum.MAC.toString()) >= 0) { 75 | return OsNameEnum.MAC; 76 | } 77 | return OsNameEnum.OTHER; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/ConstantConfigEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils; 2 | 3 | /** 4 | * 常量 5 | * 6 | * @author https=//github.com/yaphone 7 | * @date 创建时间:2017年5月5日 下午11=29=04 8 | * @version 1.0 9 | * 10 | */ 11 | public class ConstantConfigEnum { 12 | public static final int APPMSGTYPE_TEXT = 1; 13 | public static final int APPMSGTYPE_IMG = 2; 14 | public static final int APPMSGTYPE_AUDIO = 3; 15 | public static final int APPMSGTYPE_VIDEO = 4; 16 | public static final int APPMSGTYPE_URL = 5; 17 | public static final int APPMSGTYPE_ATTACH = 6; 18 | public static final int APPMSGTYPE_OPEN = 7; 19 | public static final int APPMSGTYPE_EMOJI = 8; 20 | public static final int APPMSGTYPE_VOICE_REMIND = 9; 21 | public static final int APPMSGTYPE_SCAN_GOOD = 10; 22 | public static final int APPMSGTYPE_GOOD = 13; 23 | public static final int APPMSGTYPE_EMOTION = 15; 24 | public static final int APPMSGTYPE_CARD_TICKET = 16; 25 | public static final int APPMSGTYPE_REALTIME_SHARE_LOCATION = 17; 26 | // public static final int APPMSGTYPE_TRANSFERS = 2e3; 27 | public static final int APPMSGTYPE_RED_ENVELOPES = 2001; 28 | public static final int APPMSGTYPE_READER_TYPE = 100001; 29 | public static final int UPLOAD_MEDIA_TYPE_IMAGE = 1; 30 | public static final int UPLOAD_MEDIA_TYPE_VIDEO = 2; 31 | public static final int UPLOAD_MEDIA_TYPE_AUDIO = 3; 32 | public static final int UPLOAD_MEDIA_TYPE_ATTACHMENT = 4; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/MsgKeywords.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils; 2 | 3 | public class MsgKeywords { 4 | public static String newFriendStr = "我通过了你的朋友验证请求"; 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/MyHttpClient.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | import java.util.Set; 8 | import java.util.logging.Logger; 9 | 10 | import org.apache.http.Consts; 11 | import org.apache.http.HttpEntity; 12 | import org.apache.http.client.ClientProtocolException; 13 | import org.apache.http.client.CookieStore; 14 | import org.apache.http.client.config.RequestConfig; 15 | import org.apache.http.client.entity.UrlEncodedFormEntity; 16 | import org.apache.http.client.methods.CloseableHttpResponse; 17 | import org.apache.http.client.methods.HttpGet; 18 | import org.apache.http.client.methods.HttpPost; 19 | import org.apache.http.cookie.Cookie; 20 | import org.apache.http.entity.StringEntity; 21 | import org.apache.http.impl.client.BasicCookieStore; 22 | import org.apache.http.impl.client.CloseableHttpClient; 23 | import org.apache.http.impl.client.HttpClients; 24 | import org.apache.http.message.BasicNameValuePair; 25 | import org.apache.http.util.EntityUtils; 26 | 27 | /** 28 | * HTTP访问类,对Apache HttpClient进行简单封装,适配器模式 29 | * 30 | * @author https://github.com/yaphone 31 | * @date 创建时间:2017年4月9日 下午7:05:04 32 | * @version 1.0 33 | * 34 | */ 35 | public class MyHttpClient { 36 | private Logger logger = Logger.getLogger("MyHttpClient"); 37 | 38 | private static CloseableHttpClient httpClient = HttpClients.createDefault(); 39 | 40 | private static MyHttpClient instance = null; 41 | 42 | private static CookieStore cookieStore; 43 | 44 | static { 45 | cookieStore = new BasicCookieStore(); 46 | 47 | // 将CookieStore设置到httpClient中 48 | httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build(); 49 | } 50 | 51 | public static String getCookie(String name) { 52 | List cookies = cookieStore.getCookies(); 53 | for (Cookie cookie : cookies) { 54 | if (cookie.getName().equalsIgnoreCase(name)) { 55 | return cookie.getValue(); 56 | } 57 | } 58 | return null; 59 | 60 | } 61 | 62 | private MyHttpClient() { 63 | 64 | } 65 | 66 | /** 67 | * 获取cookies 68 | * 69 | * @author https://github.com/yaphone 70 | * @date 2017年5月7日 下午8:37:17 71 | * @return 72 | */ 73 | public static MyHttpClient getInstance() { 74 | if (instance == null) { 75 | synchronized (MyHttpClient.class) { 76 | if (instance == null) { 77 | instance = new MyHttpClient(); 78 | } 79 | } 80 | } 81 | return instance; 82 | } 83 | 84 | /** 85 | * 处理GET请求 86 | * 87 | * @author https://github.com/yaphone 88 | * @date 2017年4月9日 下午7:06:19 89 | * @param url 90 | * @param params 91 | * @return 92 | */ 93 | public HttpEntity doGet(String url, List params, boolean redirect, 94 | Map headerMap) { 95 | HttpEntity entity = null; 96 | HttpGet httpGet = new HttpGet(); 97 | 98 | try { 99 | if (params != null) { 100 | String paramStr = EntityUtils.toString(new UrlEncodedFormEntity(params, Consts.UTF_8)); 101 | httpGet = new HttpGet(url + "?" + paramStr); 102 | } else { 103 | httpGet = new HttpGet(url); 104 | } 105 | if (!redirect) { 106 | httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); // 禁止重定向 107 | } 108 | httpGet.setHeader("User-Agent", Config.USER_AGENT); 109 | httpGet.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION); 110 | httpGet.setHeader("extspam", Config.UOS_PATCH_EXTSPAM); 111 | httpGet.setHeader("referer", Config.REFERER); 112 | if (headerMap != null) { 113 | Set> entries = headerMap.entrySet(); 114 | for (Entry entry : entries) { 115 | httpGet.setHeader(entry.getKey(), entry.getValue()); 116 | } 117 | } 118 | CloseableHttpResponse response = httpClient.execute(httpGet); 119 | entity = response.getEntity(); 120 | } catch (ClientProtocolException e) { 121 | logger.info(e.getMessage()); 122 | } catch (IOException e) { 123 | logger.info(e.getMessage()); 124 | } 125 | 126 | return entity; 127 | } 128 | 129 | /** 130 | * 处理POST请求 131 | * 132 | * @author https://github.com/yaphone 133 | * @date 2017年4月9日 下午7:06:35 134 | * @param url 135 | * @param params 136 | * @return 137 | */ 138 | public HttpEntity doPost(String url, String paramsStr) { 139 | HttpEntity entity = null; 140 | HttpPost httpPost = new HttpPost(); 141 | try { 142 | StringEntity params = new StringEntity(paramsStr, Consts.UTF_8); 143 | httpPost = new HttpPost(url); 144 | httpPost.setEntity(params); 145 | httpPost.setHeader("Content-type", "application/json; charset=utf-8"); 146 | httpPost.setHeader("User-Agent", Config.USER_AGENT); 147 | httpPost.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION); 148 | httpPost.setHeader("extspam", Config.UOS_PATCH_EXTSPAM); 149 | httpPost.setHeader("referer", Config.REFERER); 150 | 151 | CloseableHttpResponse response = httpClient.execute(httpPost); 152 | entity = response.getEntity(); 153 | } catch (ClientProtocolException e) { 154 | logger.info(e.getMessage()); 155 | } catch (IOException e) { 156 | logger.info(e.getMessage()); 157 | } 158 | 159 | return entity; 160 | } 161 | 162 | /** 163 | * 上传文件到服务器 164 | * 165 | * @author https://github.com/yaphone 166 | * @date 2017年5月7日 下午9:19:23 167 | * @param url 168 | * @param reqEntity 169 | * @return 170 | */ 171 | public HttpEntity doPostFile(String url, HttpEntity reqEntity) { 172 | HttpEntity entity = null; 173 | HttpPost httpPost = new HttpPost(url); 174 | httpPost.setHeader("User-Agent", Config.USER_AGENT); 175 | httpPost.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION); 176 | httpPost.setHeader("extspam", Config.UOS_PATCH_EXTSPAM); 177 | httpPost.setHeader("referer", Config.REFERER); 178 | 179 | httpPost.setEntity(reqEntity); 180 | try { 181 | CloseableHttpResponse response = httpClient.execute(httpPost); 182 | entity = response.getEntity(); 183 | 184 | } catch (Exception e) { 185 | logger.info(e.getMessage()); 186 | } 187 | return entity; 188 | } 189 | 190 | public static CloseableHttpClient getHttpClient() { 191 | return httpClient; 192 | } 193 | 194 | } -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/SleepUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils; 2 | 3 | /** 4 | * Created by xiaoxiaomo on 2017/5/6. 5 | */ 6 | public class SleepUtils { 7 | 8 | /** 9 | * 毫秒为单位 10 | * @param time 11 | */ 12 | public static void sleep( long time ){ 13 | try { 14 | Thread.sleep( time ); 15 | } catch (InterruptedException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/MsgCodeEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | /** 4 | * 消息类型 5 | * 6 | * @author https://github.com/yaphone 7 | * @date 创建时间:2017年4月23日 下午12:15:00 8 | * @version 1.0 9 | * 10 | */ 11 | public enum MsgCodeEnum { 12 | 13 | // public static final int MSGTYPE_TEXT = 1; // 文本消息类型 14 | // public static final int MSGTYPE_IMAGE = 3; // 图片消息 15 | // public static final int MSGTYPE_VOICE = 34; // 语音消息 16 | // public static final int MSGTYPE_VIDEO = 43; // 小视频消息 17 | // public static final int MSGTYPE_MICROVIDEO = 62; // 短视频消息 18 | // public static final int MSGTYPE_EMOTICON = 47; // 表情消息 19 | // public static final int MSGTYPE_APP = 49; 20 | // public static final int MSGTYPE_VOIPMSG = 50; 21 | // public static final int MSGTYPE_VOIPNOTIFY = 52; 22 | // public static final int MSGTYPE_VOIPINVITE = 53; 23 | // public static final int MSGTYPE_LOCATION = 48; 24 | // public static final int MSGTYPE_STATUSNOTIFY = 51; 25 | // public static final int MSGTYPE_SYSNOTICE = 9999; 26 | // public static final int MSGTYPE_POSSIBLEFRIEND_MSG = 40; 27 | // public static final int MSGTYPE_VERIFYMSG = 37; 28 | // public static final int MSGTYPE_SHARECARD = 42; 29 | // public static final int MSGTYPE_SYS = 10000; 30 | // public static final int MSGTYPE_RECALLED = 10002; 31 | MSGTYPE_TEXT(1, "文本消息类型"), 32 | MSGTYPE_IMAGE(3, "图片消息"), 33 | MSGTYPE_VOICE(34, "语音消息"), 34 | MSGTYPE_VIDEO(43, "小视频消息"), 35 | MSGTYPE_MICROVIDEO(62, "短视频消息"), 36 | MSGTYPE_EMOTICON(47, "表情消息"), 37 | MSGTYPE_MEDIA(49, "多媒体消息"), 38 | MSGTYPE_VOIPMSG(50, ""), 39 | MSGTYPE_VOIPNOTIFY(52, ""), 40 | MSGTYPE_VOIPINVITE(53, ""), 41 | MSGTYPE_LOCATION(48, ""), 42 | MSGTYPE_STATUSNOTIFY(51, ""), 43 | MSGTYPE_SYSNOTICE(9999, ""), 44 | MSGTYPE_POSSIBLEFRIEND_MSG(40, ""), 45 | MSGTYPE_VERIFYMSG(37, "好友请求"), 46 | MSGTYPE_SHARECARD(42, ""), 47 | MSGTYPE_SYS(10000, "系统消息"), 48 | MSGTYPE_RECALLED(10002, "") 49 | 50 | ; 51 | 52 | private int code; 53 | private String type; 54 | 55 | MsgCodeEnum(int code, String type) { 56 | this.code = code; 57 | this.type = type; 58 | } 59 | 60 | public int getCode() { 61 | return code; 62 | } 63 | 64 | public String getType() { 65 | return type; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/MsgTypeEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | 4 | /** 5 | * 消息类型枚举类 6 | * 7 | * @author https://github.com/yaphone 8 | * @date 创建时间:2017年5月13日 下午11:53:00 9 | * @version 1.0 10 | * 11 | */ 12 | public enum MsgTypeEnum { 13 | TEXT("Text", "文本消息"), 14 | PIC("Pic", "图片消息"), 15 | VOICE("Voice", "语音消息"), 16 | VIEDO("Viedo", "小视频消息"), 17 | NAMECARD("NameCard", "名片消息"), 18 | SYS("Sys", "系统消息"), 19 | VERIFYMSG("VerifyMsg", "添加好友"), 20 | MEDIA("app", "文件消息"); 21 | 22 | private String type; 23 | private String code; 24 | 25 | MsgTypeEnum(String type, String code) { 26 | this.type = type; 27 | this.code = code; 28 | } 29 | 30 | public String getType() { 31 | return type; 32 | } 33 | 34 | public String getCode() { 35 | return code; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/OsNameEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | /** 4 | * 系统平台 5 | * 6 | * @author https://github.com/yaphone 7 | * @date 创建时间:2017年4月8日 下午10:36:28 8 | * @version 1.0 9 | * 10 | */ 11 | public enum OsNameEnum { 12 | WINDOWS, LINUX, DARWIN, MAC, OTHER 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/ResultEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | /** 4 | * 返回结构枚举类 5 | *

6 | * Created by xiaoxiaomo on 2017/5/6. 7 | */ 8 | public enum ResultEnum { 9 | 10 | SUCCESS("200", "成功"), 11 | WAIT_CONFIRM("201", "请在手机上点击确认"), 12 | WAIT_SCAN("400", "请扫描二维码"); 13 | 14 | private String code; 15 | private String msg; 16 | 17 | ResultEnum(String code, String msg) { 18 | this.code = code; 19 | this.msg = msg; 20 | } 21 | 22 | public String getCode() { 23 | return code; 24 | } 25 | 26 | // public static MsgInfoEnum getCode(String code) { 27 | // switch (code) { 28 | // case "Text": 29 | // return MsgInfoEnum.TEXT; 30 | // default: 31 | // return MsgInfoEnum.VIDEO; 32 | // } 33 | // } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/RetCodeEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | public enum RetCodeEnum { 4 | 5 | NORMAL("0", "普通"), 6 | LOGIN_OUT("1102", "退出"), 7 | LOGIN_OTHERWHERE("1101", "其它地方登陆"), 8 | MOBILE_LOGIN_OUT("1102", "移动端退出"), 9 | UNKOWN("9999", "未知") 10 | 11 | ; 12 | 13 | 14 | private String code; 15 | private String type; 16 | 17 | RetCodeEnum(String code, String type) { 18 | this.code = code; 19 | this.type = type; 20 | } 21 | 22 | public String getCode() { 23 | return code; 24 | } 25 | 26 | public String getType() { 27 | return type; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/StorageLoginInfoEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by xiaoxiaomo on 2017/5/7. 11 | */ 12 | public enum StorageLoginInfoEnum { 13 | 14 | //URL 15 | url("url",new String()), 16 | fileUrl("fileUrl",new String()), 17 | syncUrl("syncUrl",new String()), 18 | 19 | deviceid("deviceid",new String()), //生成15位随机数 20 | 21 | //baseRequest 22 | skey("skey",new String()), 23 | wxsid("wxsid",new String()), 24 | wxuin("wxuin",new String()), 25 | pass_ticket("pass_ticket",new String()), 26 | 27 | 28 | InviteStartCount("InviteStartCount",new Integer(0)), 29 | User("User",new JSONObject()), 30 | SyncKey("SyncKey",new JSONObject()), 31 | synckey("synckey",new String()), 32 | 33 | 34 | 35 | MemberCount("MemberCount",new String()), 36 | MemberList("MemberList",new JSONArray()), 37 | 38 | 39 | 40 | ; 41 | 42 | private String key; 43 | private Object type; 44 | 45 | StorageLoginInfoEnum(String key, Object type) { 46 | this.key = key; 47 | this.type = type; 48 | } 49 | 50 | public String getKey() { 51 | return key; 52 | } 53 | 54 | 55 | public Object getType() { 56 | return type; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/URLEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | /** 4 | * URL 5 | * Created by xiaoxiaomo on 2017/5/6. 6 | */ 7 | public enum URLEnum { 8 | 9 | 10 | 11 | BASE_URL("https://login.weixin.qq.com","基本的URL"), 12 | UUID_URL(BASE_URL.url+"/jslogin","UUIDLURL"), 13 | QRCODE_URL(BASE_URL.url+"/qrcode/","初始化URL"), 14 | STATUS_NOTIFY_URL(BASE_URL.url+"/webwxstatusnotify?lang=zh_CN&pass_ticket=%s","微信状态通知"), 15 | LOGIN_URL(BASE_URL.url+"/cgi-bin/mmwebwx-bin/login","登陆URL"), 16 | INIT_URL("%s/webwxinit?r=%s&pass_ticket=%s","初始化URL"), 17 | SYNC_CHECK_URL("/synccheck","检查心跳URL"), 18 | WEB_WX_SYNC_URL("%s/webwxsync?sid=%s&skey=%s&pass_ticket=%s","web微信消息同步URL"), 19 | WEB_WX_GET_CONTACT("%s/webwxgetcontact","web微信获取联系人信息URL"), 20 | WEB_WX_SEND_MSG("%s/webwxsendmsg","发送消息URL"), 21 | WEB_WX_UPLOAD_MEDIA("%s/webwxuploadmedia?f=json", "上传文件到服务器"), 22 | WEB_WX_GET_MSG_IMG("%s/webwxgetmsgimg", "下载图片消息"), 23 | WEB_WX_GET_VOICE("%s/webwxgetvoice", "下载语音消息"), 24 | WEB_WX_GET_VIEDO("%s/webwxgetvideo", "下载语音消息"), 25 | WEB_WX_PUSH_LOGIN("%s/webwxpushloginurl", "不扫码登陆"), 26 | WEB_WX_LOGOUT("%s/webwxlogout", "退出微信"), 27 | WEB_WX_BATCH_GET_CONTACT("%s/webwxbatchgetcontact?type=ex&r=%s&lang=zh_CN&pass_ticket=%s", "查询群信息"), 28 | WEB_WX_REMARKNAME("%s/webwxoplog?lang=zh_CN&pass_ticket=%s", "修改好友备注"), 29 | WEB_WX_VERIFYUSER("%s/webwxverifyuser?r=%s&lang=zh_CN&pass_ticket=%s", "被动添加好友"), 30 | WEB_WX_GET_MEDIA("%s/webwxgetmedia", "下载文件") 31 | 32 | 33 | 34 | 35 | ; 36 | 37 | private String url; 38 | private String msg; 39 | 40 | URLEnum(String url, String msg) { 41 | this.url = url; 42 | this.msg = msg; 43 | } 44 | 45 | 46 | public String getUrl() { 47 | return url; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/VerifyFriendEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums; 2 | 3 | /** 4 | * 确认添加好友Enum 5 | * 6 | * @author https://github.com/yaphone 7 | * @date 创建时间:2017年6月29日 下午9:47:14 8 | * @version 1.0 9 | * 10 | */ 11 | public enum VerifyFriendEnum { 12 | 13 | ADD(2, "添加"), 14 | ACCEPT(3, "接受"); 15 | 16 | private int code; 17 | private String desc; 18 | 19 | private VerifyFriendEnum(int code, String desc) { 20 | this.code = code; 21 | this.desc = desc; 22 | } 23 | 24 | public int getCode() { 25 | return code; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/parameters/BaseParaEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums.parameters; 2 | 3 | /** 4 | * 5 | * 基本请求参数 6 | * 1. webWxInit 初始化 7 | * 2. wxStatusNotify 微信状态通知 8 | * 9 | *

10 | * Created by xiaoxiaomo on 2017/5/7. 11 | */ 12 | public enum BaseParaEnum { 13 | 14 | Uin("Uin", "wxuin"), 15 | Sid("Sid", "wxsid"), 16 | Skey("Skey", "skey"), 17 | DeviceID("DeviceID", "pass_ticket"); 18 | 19 | private String para; 20 | private String value; 21 | 22 | BaseParaEnum(String para, String value) { 23 | this.para = para; 24 | this.value = value; 25 | } 26 | 27 | public String para() { 28 | return para; 29 | } 30 | 31 | 32 | public Object value() { 33 | return value; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/parameters/LoginParaEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums.parameters; 2 | 3 | /** 4 | * 登陆 5 | *

6 | * Created by xiaoxiaomo on 2017/5/7. 7 | */ 8 | public enum LoginParaEnum { 9 | 10 | LOGIN_ICON("loginicon", "true"), 11 | UUID("uuid", ""), 12 | TIP("tip", "0"), 13 | R("r", ""), 14 | _("_", ""); 15 | 16 | private String para; 17 | private String value; 18 | 19 | LoginParaEnum(String para, String value) { 20 | this.para = para; 21 | this.value = value; 22 | } 23 | 24 | public String para() { 25 | return para; 26 | } 27 | 28 | public String value() { 29 | return value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/parameters/StatusNotifyParaEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums.parameters; 2 | 3 | /** 4 | * 状态通知 5 | *

6 | * Created by xiaoxiaomo on 2017/5/7. 7 | */ 8 | public enum StatusNotifyParaEnum { 9 | 10 | CODE("Code", "3"), 11 | FROM_USERNAME("FromUserName", ""), 12 | TO_USERNAME("ToUserName", ""), 13 | CLIENT_MSG_ID("ClientMsgId", ""); //时间戳 14 | 15 | private String para; 16 | private String value; 17 | 18 | StatusNotifyParaEnum(String para, String value) { 19 | this.para = para; 20 | this.value = value; 21 | } 22 | 23 | public String para() { 24 | return para; 25 | } 26 | 27 | public String value() { 28 | return value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/enums/parameters/UUIDParaEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.enums.parameters; 2 | 3 | /** 4 | * UUID 5 | *

6 | * Created by xiaoxiaomo on 2017/5/7. 7 | */ 8 | public enum UUIDParaEnum { 9 | 10 | APP_ID("appid", "wx782c26e4c19acffb"), 11 | FUN("fun", "new"), 12 | LANG("lang", "zh_CN"), 13 | REDIRECT_URL("redirect_uri", 14 | "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop"), 15 | _("_", "时间戳"); 16 | 17 | 18 | private String para; 19 | private String value; 20 | 21 | UUIDParaEnum(String para, String value) { 22 | this.para = para; 23 | this.value = value; 24 | } 25 | 26 | public String para() { 27 | return para; 28 | } 29 | 30 | public String value() { 31 | return value; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/tools/CommonTools.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.tools; 2 | 3 | import java.io.StringReader; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | import javax.xml.parsers.DocumentBuilder; 13 | import javax.xml.parsers.DocumentBuilderFactory; 14 | 15 | import org.w3c.dom.Document; 16 | import org.xml.sax.InputSource; 17 | 18 | import com.alibaba.fastjson.JSON; 19 | import com.alibaba.fastjson.JSONArray; 20 | import com.alibaba.fastjson.JSONObject; 21 | import com.vdurmont.emoji.EmojiParser; 22 | 23 | import cn.zhouyafeng.itchat4j.utils.Config; 24 | import cn.zhouyafeng.itchat4j.utils.enums.OsNameEnum; 25 | 26 | /** 27 | * 常用工具类 28 | * 29 | * @author https://github.com/yaphone 30 | * @date 创建时间:2017年4月8日 下午10:59:55 31 | * @version 1.0 32 | * 33 | */ 34 | public class CommonTools { 35 | 36 | public static boolean printQr(String qrPath) { 37 | 38 | switch (Config.getOsNameEnum()) { 39 | case WINDOWS: 40 | if (Config.getOsNameEnum().equals(OsNameEnum.WINDOWS)) { 41 | Runtime runtime = Runtime.getRuntime(); 42 | try { 43 | runtime.exec("cmd /c start " + qrPath); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | break; 49 | case MAC: 50 | if (Config.getOsNameEnum().equals(OsNameEnum.MAC)) { 51 | Runtime runtime = Runtime.getRuntime(); 52 | try { 53 | runtime.exec("open " + qrPath); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | break; 59 | 60 | default: 61 | break; 62 | } 63 | return true; 64 | } 65 | 66 | public static boolean clearScreen() { 67 | switch (Config.getOsNameEnum()) { 68 | case WINDOWS: 69 | if (Config.getOsNameEnum().equals(OsNameEnum.WINDOWS)) { 70 | Runtime runtime = Runtime.getRuntime(); 71 | try { 72 | runtime.exec("cmd /c " + "cls"); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | break; 78 | 79 | default: 80 | break; 81 | } 82 | return true; 83 | } 84 | 85 | /** 86 | * 正则表达式处理工具 87 | * 88 | * @author https://github.com/yaphone 89 | * @date 2017年4月9日 上午12:27:10 90 | * @return 91 | */ 92 | public static Matcher getMatcher(String regEx, String text) { 93 | Pattern pattern = Pattern.compile(regEx); 94 | Matcher matcher = pattern.matcher(text); 95 | return matcher; 96 | } 97 | 98 | /** 99 | * xml解析器 100 | * 101 | * @author https://github.com/yaphone 102 | * @date 2017年4月9日 下午6:24:25 103 | * @param text 104 | * @return 105 | */ 106 | public static Document xmlParser(String text) { 107 | Document doc = null; 108 | StringReader sr = new StringReader(text); 109 | InputSource is = new InputSource(sr); 110 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 111 | try { 112 | DocumentBuilder builder = factory.newDocumentBuilder(); 113 | doc = builder.parse(is); 114 | } catch (Exception e) { 115 | e.printStackTrace(); 116 | } 117 | return doc; 118 | } 119 | 120 | public static JSONObject structFriendInfo(JSONObject userObj) { 121 | Map friendInfoTemplate = new HashMap(); 122 | friendInfoTemplate.put("UserName", ""); 123 | friendInfoTemplate.put("City", ""); 124 | friendInfoTemplate.put("DisplayName", ""); 125 | friendInfoTemplate.put("PYQuanPin", ""); 126 | friendInfoTemplate.put("RemarkPYInitial", ""); 127 | friendInfoTemplate.put("Province", ""); 128 | friendInfoTemplate.put("KeyWord", ""); 129 | friendInfoTemplate.put("RemarkName", ""); 130 | friendInfoTemplate.put("PYInitial", ""); 131 | friendInfoTemplate.put("EncryChatRoomId", ""); 132 | friendInfoTemplate.put("Alias", ""); 133 | friendInfoTemplate.put("Signature", ""); 134 | friendInfoTemplate.put("NickName", ""); 135 | friendInfoTemplate.put("RemarkPYQuanPin", ""); 136 | friendInfoTemplate.put("HeadImgUrl", ""); 137 | 138 | friendInfoTemplate.put("UniFriend", 0); 139 | friendInfoTemplate.put("Sex", 0); 140 | friendInfoTemplate.put("AppAccountFlag", 0); 141 | friendInfoTemplate.put("VerifyFlag", 0); 142 | friendInfoTemplate.put("ChatRoomId", 0); 143 | friendInfoTemplate.put("HideInputBarFlag", 0); 144 | friendInfoTemplate.put("AttrStatus", 0); 145 | friendInfoTemplate.put("SnsFlag", 0); 146 | friendInfoTemplate.put("MemberCount", 0); 147 | friendInfoTemplate.put("OwnerUin", 0); 148 | friendInfoTemplate.put("ContactFlag", 0); 149 | friendInfoTemplate.put("Uin", 0); 150 | friendInfoTemplate.put("StarFriend", 0); 151 | friendInfoTemplate.put("Statues", 0); 152 | 153 | friendInfoTemplate.put("MemberList", new ArrayList()); 154 | 155 | JSONObject r = new JSONObject(); 156 | Set keySet = friendInfoTemplate.keySet(); 157 | for (String key : keySet) { 158 | if (userObj.containsKey(key)) { 159 | r.put(key, userObj.get(key)); 160 | } else { 161 | r.put(key, friendInfoTemplate.get(key)); 162 | } 163 | } 164 | 165 | return r; 166 | } 167 | 168 | public static String getSynckey(JSONObject obj) { 169 | JSONArray obj2 = obj.getJSONArray("List"); 170 | StringBuilder sb = new StringBuilder(); 171 | for (int i = 0; i < obj2.size(); i++) { 172 | JSONObject obj3 = (JSONObject) JSON.toJSON(obj2.get(i)); 173 | sb.append(obj3.get("Val") + "|"); 174 | } 175 | return sb.substring(0, sb.length() - 1); // 656159784|656159911|656159873|1491905341 176 | 177 | } 178 | 179 | public static JSONObject searchDictList(List list, String key, String value) { 180 | JSONObject r = null; 181 | for (JSONObject i : list) { 182 | if (i.getString(key).equals(value)) { 183 | r = i; 184 | break; 185 | } 186 | } 187 | return r; 188 | } 189 | 190 | /** 191 | * 处理emoji表情 192 | * 193 | * @author https://github.com/yaphone 194 | * @date 2017年4月23日 下午2:39:04 195 | * @param d 196 | * @param k 197 | */ 198 | public static void emojiFormatter(JSONObject d, String k) { 199 | Matcher matcher = getMatcher("", d.getString(k)); 200 | StringBuilder sb = new StringBuilder(); 201 | String content = d.getString(k); 202 | int lastStart = 0; 203 | while (matcher.find()) { 204 | String str = matcher.group(1); 205 | if (str.length() == 6) { 206 | 207 | } else if (str.length() == 10) { 208 | 209 | } else { 210 | str = "&#x" + str + ";"; 211 | String tmp = content.substring(lastStart, matcher.start()); 212 | sb.append(tmp + str); 213 | lastStart = matcher.end(); 214 | } 215 | } 216 | if (lastStart < content.length()) { 217 | sb.append(content.substring(lastStart)); 218 | } 219 | if (sb.length() != 0) { 220 | d.put(k, EmojiParser.parseToUnicode(sb.toString())); 221 | } else { 222 | d.put(k, content); 223 | } 224 | 225 | } 226 | 227 | /** 228 | * 消息格式化 229 | * 230 | * @author https://github.com/yaphone 231 | * @date 2017年4月23日 下午4:19:08 232 | * @param d 233 | * @param k 234 | */ 235 | public static void msgFormatter(JSONObject d, String k) { 236 | d.put(k, d.getString(k).replace("
", "\n")); 237 | emojiFormatter(d, k); 238 | // TODO 与emoji表情有部分兼容问题,目前暂未处理解码处理 d.put(k, 239 | // StringEscapeUtils.unescapeHtml4(d.getString(k))); 240 | 241 | } 242 | 243 | } 244 | -------------------------------------------------------------------------------- /src/main/java/cn/zhouyafeng/itchat4j/utils/tools/DownloadTools.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.utils.tools; 2 | 3 | import java.io.FileOutputStream; 4 | import java.io.OutputStream; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.logging.Logger; 10 | 11 | import org.apache.http.HttpEntity; 12 | import org.apache.http.message.BasicNameValuePair; 13 | import org.apache.http.util.EntityUtils; 14 | 15 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 16 | import cn.zhouyafeng.itchat4j.core.Core; 17 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 18 | import cn.zhouyafeng.itchat4j.utils.enums.MsgTypeEnum; 19 | import cn.zhouyafeng.itchat4j.utils.enums.URLEnum; 20 | 21 | /** 22 | * 下载工具类 23 | * 24 | * @author https://github.com/yaphone 25 | * @date 创建时间:2017年4月21日 下午11:18:46 26 | * @version 1.0 27 | * 28 | */ 29 | public class DownloadTools { 30 | private static Logger logger = Logger.getLogger("DownloadTools"); 31 | private static Core core = Core.getInstance(); 32 | private static MyHttpClient myHttpClient = core.getMyHttpClient(); 33 | 34 | /** 35 | * 处理下载任务 36 | * 37 | * @author https://github.com/yaphone 38 | * @date 2017年4月21日 下午11:00:25 39 | * @param url 40 | * @param msgId 41 | * @param path 42 | * @return 43 | */ 44 | public static Object getDownloadFn(BaseMsg msg, String type, String path) { 45 | Map headerMap = new HashMap(); 46 | List params = new ArrayList(); 47 | String url = ""; 48 | if (type.equals(MsgTypeEnum.PIC.getType())) { 49 | url = String.format(URLEnum.WEB_WX_GET_MSG_IMG.getUrl(), (String) core.getLoginInfo().get("url")); 50 | } else if (type.equals(MsgTypeEnum.VOICE.getType())) { 51 | url = String.format(URLEnum.WEB_WX_GET_VOICE.getUrl(), (String) core.getLoginInfo().get("url")); 52 | } else if (type.equals(MsgTypeEnum.VIEDO.getType())) { 53 | headerMap.put("Range", "bytes=0-"); 54 | url = String.format(URLEnum.WEB_WX_GET_VIEDO.getUrl(), (String) core.getLoginInfo().get("url")); 55 | } else if (type.equals(MsgTypeEnum.MEDIA.getType())) { 56 | headerMap.put("Range", "bytes=0-"); 57 | url = String.format(URLEnum.WEB_WX_GET_MEDIA.getUrl(), (String) core.getLoginInfo().get("fileUrl")); 58 | params.add(new BasicNameValuePair("sender", msg.getFromUserName())); 59 | params.add(new BasicNameValuePair("mediaid", msg.getMediaId())); 60 | params.add(new BasicNameValuePair("filename", msg.getFileName())); 61 | } 62 | params.add(new BasicNameValuePair("msgid", msg.getNewMsgId())); 63 | params.add(new BasicNameValuePair("skey", (String) core.getLoginInfo().get("skey"))); 64 | HttpEntity entity = myHttpClient.doGet(url, params, true, headerMap); 65 | try { 66 | OutputStream out = new FileOutputStream(path); 67 | byte[] bytes = EntityUtils.toByteArray(entity); 68 | out.write(bytes); 69 | out.flush(); 70 | out.close(); 71 | // Tools.printQr(path); 72 | 73 | } catch (Exception e) { 74 | logger.info(e.getMessage()); 75 | return false; 76 | } 77 | return null; 78 | }; 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | 3 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern = %d %-5p => %m%n 6 | 7 | -------------------------------------------------------------------------------- /src/test/java/cn/zhouyafeng/itchat4j/demo/demo1/MyTest.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.demo.demo1; 2 | 3 | import java.io.File; 4 | import cn.zhouyafeng.itchat4j.Wechat; 5 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 6 | 7 | /** 8 | * 9 | * @author https://github.com/yaphone 10 | * @date 创建时间:2017年4月28日 上午12:44:10 11 | * @version 1.0 12 | * 13 | */ 14 | public class MyTest { 15 | public static void main(String[] args) { 16 | String qrPath = "login"; // 保存登陆二维码图片的路径,这里需要在本地新建目录 17 | File f = new File(qrPath); 18 | IMsgHandlerFace msgHandler = new SimpleDemo(); // 实现IMsgHandlerFace接口的类 19 | Wechat wechat = new Wechat(msgHandler, f.getAbsolutePath()); // 【注入】 20 | wechat.start(); // 启动服务,会在qrPath下生成一张二维码图片,扫描即可登陆,注意,二维码图片如果超过一定时间未扫描会过期,过期时会自动更新,所以你可能需要重新打开图片 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/cn/zhouyafeng/itchat4j/demo/demo1/SimpleDemo.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.demo.demo1; 2 | 3 | import java.io.File; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | import org.apache.log4j.Logger; 8 | 9 | import cn.zhouyafeng.itchat4j.api.MessageTools; 10 | import cn.zhouyafeng.itchat4j.api.WechatTools; 11 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 12 | import cn.zhouyafeng.itchat4j.beans.RecommendInfo; 13 | import cn.zhouyafeng.itchat4j.core.Core; 14 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 15 | import cn.zhouyafeng.itchat4j.utils.enums.MsgTypeEnum; 16 | import cn.zhouyafeng.itchat4j.utils.tools.DownloadTools; 17 | 18 | /** 19 | * 简单示例程序,收到文本信息自动回复原信息,收到图片、语音、小视频后根据路径自动保存 20 | * 21 | * @author https://github.com/yaphone 22 | * @date 创建时间:2017年4月25日 上午12:18:09 23 | * @version 1.0 24 | * 25 | */ 26 | public class SimpleDemo implements IMsgHandlerFace { 27 | Logger LOG = Logger.getLogger(SimpleDemo.class); 28 | 29 | @Override 30 | public String textMsgHandle(BaseMsg msg) { 31 | // String docFilePath = "D:/itchat4j/pic/1.jpg"; // 这里是需要发送的文件的路径 32 | if (!msg.isGroupMsg()) { // 群消息不处理 33 | // String userId = msg.getString("FromUserName"); 34 | // MessageTools.sendFileMsgByUserId(userId, docFilePath); // 发送文件 35 | // MessageTools.sendPicMsgByUserId(userId, docFilePath); 36 | String text = msg.getText(); // 发送文本消息,也可调用MessageTools.sendFileMsgByUserId(userId,text); 37 | LOG.info(text); 38 | if (text.equals("111")) { 39 | WechatTools.logout(); 40 | } 41 | if (text.equals("222")) { 42 | WechatTools.remarkNameByNickName("yaphone", "Hello"); 43 | } 44 | if (text.equals("333")) { // 测试群列表 45 | System.out.print(WechatTools.getGroupNickNameList()); 46 | System.out.print(WechatTools.getGroupIdList()); 47 | System.out.print(Core.getInstance().getGroupMemeberMap()); 48 | } 49 | return text; 50 | } 51 | return null; 52 | } 53 | 54 | @Override 55 | public String picMsgHandle(BaseMsg msg) { 56 | String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());// 这里使用收到图片的时间作为文件名 57 | String picPath = "D://itchat4j/pic" + File.separator + fileName + ".jpg"; // 调用此方法来保存图片 58 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.PIC.getType(), picPath); // 保存图片的路径 59 | return "图片保存成功"; 60 | } 61 | 62 | @Override 63 | public String voiceMsgHandle(BaseMsg msg) { 64 | String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 65 | String voicePath = "D://itchat4j/voice" + File.separator + fileName + ".mp3"; 66 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VOICE.getType(), voicePath); 67 | return "声音保存成功"; 68 | } 69 | 70 | @Override 71 | public String viedoMsgHandle(BaseMsg msg) { 72 | String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 73 | String viedoPath = "D://itchat4j/viedo" + File.separator + fileName + ".mp4"; 74 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VIEDO.getType(), viedoPath); 75 | return "视频保存成功"; 76 | } 77 | 78 | @Override 79 | public String nameCardMsgHandle(BaseMsg msg) { 80 | return "收到名片消息"; 81 | } 82 | 83 | @Override 84 | public void sysMsgHandle(BaseMsg msg) { // 收到系统消息 85 | String text = msg.getContent(); 86 | LOG.info(text); 87 | } 88 | 89 | @Override 90 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 91 | MessageTools.addFriend(msg, true); // 同意好友请求,false为不接受好友请求 92 | RecommendInfo recommendInfo = msg.getRecommendInfo(); 93 | String nickName = recommendInfo.getNickName(); 94 | String province = recommendInfo.getProvince(); 95 | String city = recommendInfo.getCity(); 96 | String text = "你好,来自" + province + city + "的" + nickName + ", 欢迎添加我为好友!"; 97 | return text; 98 | } 99 | 100 | @Override 101 | public String mediaMsgHandle(BaseMsg msg) { 102 | String fileName = msg.getFileName(); 103 | String filePath = "D://itchat4j/file" + File.separator + fileName; // 这里是需要保存收到的文件路径,文件可以是任何格式如PDF,WORD,EXCEL等。 104 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.MEDIA.getType(), filePath); 105 | return "文件" + fileName + "保存成功"; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/cn/zhouyafeng/itchat4j/demo/demo2/TulingRobot.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.demo.demo2; 2 | 3 | import java.io.File; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.logging.Logger; 8 | 9 | import org.apache.http.HttpEntity; 10 | import org.apache.http.util.EntityUtils; 11 | 12 | import com.alibaba.fastjson.JSON; 13 | import com.alibaba.fastjson.JSONObject; 14 | 15 | import cn.zhouyafeng.itchat4j.Wechat; 16 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 17 | import cn.zhouyafeng.itchat4j.core.Core; 18 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 19 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 20 | import cn.zhouyafeng.itchat4j.utils.enums.MsgTypeEnum; 21 | import cn.zhouyafeng.itchat4j.utils.tools.DownloadTools; 22 | 23 | /** 24 | * 图灵机器人示例 25 | * 26 | * @author https://github.com/yaphone 27 | * @date 创建时间:2017年4月24日 上午12:13:26 28 | * @version 1.0 29 | * 30 | */ 31 | public class TulingRobot implements IMsgHandlerFace { 32 | Logger logger = Logger.getLogger("TulingRobot"); 33 | MyHttpClient myHttpClient = Core.getInstance().getMyHttpClient(); 34 | String url = "http://www.tuling123.com/openapi/api"; 35 | String apiKey = "597b34bea4ec4c85a775c469c84b6817"; // 这里是我申请的图灵机器人API接口,每天只能5000次调用,建议自己去申请一个,免费的:) 36 | 37 | @Override 38 | public String textMsgHandle(BaseMsg msg) { 39 | String result = ""; 40 | String text = msg.getText(); 41 | Map paramMap = new HashMap(); 42 | paramMap.put("key", apiKey); 43 | paramMap.put("info", text); 44 | paramMap.put("userid", "123456"); 45 | String paramStr = JSON.toJSONString(paramMap); 46 | try { 47 | HttpEntity entity = myHttpClient.doPost(url, paramStr); 48 | result = EntityUtils.toString(entity, "UTF-8"); 49 | JSONObject obj = JSON.parseObject(result); 50 | if (obj.getString("code").equals("100000")) { 51 | result = obj.getString("text"); 52 | } else { 53 | result = "处理有误"; 54 | } 55 | } catch (Exception e) { 56 | logger.info(e.getMessage()); 57 | } 58 | return result; 59 | } 60 | 61 | @Override 62 | public String picMsgHandle(BaseMsg msg) { 63 | return "收到图片"; 64 | } 65 | 66 | @Override 67 | public String voiceMsgHandle(BaseMsg msg) { 68 | String fileName = String.valueOf(new Date().getTime()); 69 | String voicePath = "D://itchat4j/voice" + File.separator + fileName + ".mp3"; 70 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VOICE.getType(), voicePath); 71 | return "收到语音"; 72 | } 73 | 74 | @Override 75 | public String viedoMsgHandle(BaseMsg msg) { 76 | String fileName = String.valueOf(new Date().getTime()); 77 | String viedoPath = "D://itchat4j/viedo" + File.separator + fileName + ".mp4"; 78 | DownloadTools.getDownloadFn(msg, MsgTypeEnum.VIEDO.getType(), viedoPath); 79 | return "收到视频"; 80 | } 81 | 82 | public static void main(String[] args) { 83 | IMsgHandlerFace msgHandler = new TulingRobot(); 84 | Wechat wechat = new Wechat(msgHandler, "D://itchat4j/login"); 85 | wechat.start(); 86 | } 87 | 88 | @Override 89 | public String nameCardMsgHandle(BaseMsg msg) { 90 | // TODO Auto-generated method stub 91 | return null; 92 | } 93 | 94 | @Override 95 | public void sysMsgHandle(BaseMsg msg) { 96 | // TODO Auto-generated method stub 97 | } 98 | 99 | @Override 100 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 101 | // TODO Auto-generated method stub 102 | return null; 103 | } 104 | 105 | @Override 106 | public String mediaMsgHandle(BaseMsg msg) { 107 | // TODO Auto-generated method stub 108 | return null; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/cn/zhouyafeng/itchat4j/demo/demo3/PicYourFriends.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.demo.demo3; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.OutputStream; 6 | import java.util.List; 7 | 8 | import org.apache.http.HttpEntity; 9 | import org.apache.http.util.EntityUtils; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import com.alibaba.fastjson.JSONObject; 14 | 15 | import cn.zhouyafeng.itchat4j.Wechat; 16 | import cn.zhouyafeng.itchat4j.api.WechatTools; 17 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 18 | import cn.zhouyafeng.itchat4j.core.Core; 19 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 20 | import cn.zhouyafeng.itchat4j.utils.MyHttpClient; 21 | import cn.zhouyafeng.itchat4j.utils.enums.StorageLoginInfoEnum; 22 | 23 | /** 24 | * 此示例演示如何获取所有好友的头像 25 | * 26 | * @author https://github.com/yaphone 27 | * @date 创建时间:2017年6月26日 下午11:27:46 28 | * @version 1.0 29 | * 30 | */ 31 | public class PicYourFriends implements IMsgHandlerFace { 32 | private static Logger LOG = LoggerFactory.getLogger(PicYourFriends.class); 33 | private static final Core core = Core.getInstance(); 34 | private static final MyHttpClient myHttpClient = core.getMyHttpClient(); 35 | private static final String path = "D://itchat4j//head"; // 这里需要设置保存头像的路径 36 | 37 | @Override 38 | public String textMsgHandle(BaseMsg msg) { 39 | 40 | if (!msg.isGroupMsg()) { // 群消息不处理 41 | String text = msg.getText(); // 发送文本消息,也可调用MessageTools.sendFileMsgByUserId(userId,text); 42 | String baseUrl = "https://" + core.getIndexUrl(); // 基础URL 43 | String skey = (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()); 44 | if (text.equals("111")) { 45 | LOG.info("开始下载好友头像"); 46 | List friends = WechatTools.getContactList(); 47 | for (int i = 0; i < friends.size(); i++) { 48 | JSONObject friend = friends.get(i); 49 | String url = baseUrl + friend.getString("HeadImgUrl") + skey; 50 | // String fileName = friend.getString("NickName"); 51 | String headPicPath = path + File.separator + i + ".jpg"; 52 | 53 | HttpEntity entity = myHttpClient.doGet(url, null, true, null); 54 | try { 55 | OutputStream out = new FileOutputStream(headPicPath); 56 | byte[] bytes = EntityUtils.toByteArray(entity); 57 | out.write(bytes); 58 | out.flush(); 59 | out.close(); 60 | 61 | } catch (Exception e) { 62 | LOG.info(e.getMessage()); 63 | } 64 | 65 | } 66 | } 67 | } 68 | return null; 69 | } 70 | 71 | @Override 72 | public String picMsgHandle(BaseMsg msg) { 73 | // TODO Auto-generated method stub 74 | return null; 75 | } 76 | 77 | @Override 78 | public String voiceMsgHandle(BaseMsg msg) { 79 | // TODO Auto-generated method stub 80 | return null; 81 | } 82 | 83 | @Override 84 | public String viedoMsgHandle(BaseMsg msg) { 85 | // TODO Auto-generated method stub 86 | return null; 87 | } 88 | 89 | @Override 90 | public String nameCardMsgHandle(BaseMsg msg) { 91 | // TODO Auto-generated method stub 92 | return null; 93 | } 94 | 95 | @Override 96 | public void sysMsgHandle(BaseMsg msg) { 97 | // TODO Auto-generated method stub 98 | 99 | } 100 | 101 | public static void main(String[] args) { 102 | String qrPath = "D://itchat4j//login"; // 保存登陆二维码图片的路径,这里需要在本地新建目录 103 | IMsgHandlerFace msgHandler = new PicYourFriends(); // 实现IMsgHandlerFace接口的类 104 | Wechat wechat = new Wechat(msgHandler, qrPath); // 【注入】 105 | wechat.start(); // 启动服务,会在qrPath下生成一张二维码图片,扫描即可登陆,注意,二维码图片如果超过一定时间未扫描会过期,过期时会自动更新,所以你可能需要重新打开图片 106 | } 107 | 108 | @Override 109 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 110 | // TODO Auto-generated method stub 111 | return null; 112 | } 113 | 114 | @Override 115 | public String mediaMsgHandle(BaseMsg msg) { 116 | // TODO Auto-generated method stub 117 | return null; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/test/java/cn/zhouyafeng/itchat4j/demo/unuseful/UnusefulDemo.java: -------------------------------------------------------------------------------- 1 | package cn.zhouyafeng.itchat4j.demo.unuseful; 2 | 3 | import java.io.IOException; 4 | 5 | import cn.zhouyafeng.itchat4j.Wechat; 6 | import cn.zhouyafeng.itchat4j.api.AssistTools; 7 | import cn.zhouyafeng.itchat4j.beans.BaseMsg; 8 | import cn.zhouyafeng.itchat4j.face.IMsgHandlerFace; 9 | 10 | /** 11 | * 自用的测试类,请无视 12 | * 13 | * @author https://github.com/yaphone 14 | * @date 创建时间:2017年5月22日 下午10:41:44 15 | * @version 1.0 16 | * 17 | */ 18 | public class UnusefulDemo implements IMsgHandlerFace { 19 | 20 | @Override 21 | public String textMsgHandle(BaseMsg msg) { 22 | if (!msg.isGroupMsg()) { // 群消息不处理 23 | String text = msg.getText(); // 发送文本消息,也可调用MessageTools.sendFileMsgByUserId(userId,text); 24 | if (text.equals("111")) { 25 | String username = "yaphone"; 26 | String password = "123456"; 27 | String localPath = "D://itchat4j/pic/1.jpg"; 28 | String uploadUrl = "http://127.0.0.1/file/put"; 29 | try { 30 | AssistTools.sendQrPicToServer(username, password, uploadUrl, localPath); 31 | } catch (IOException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | return text; 36 | } 37 | return null; 38 | } 39 | 40 | @Override 41 | public String picMsgHandle(BaseMsg msg) { 42 | // TODO Auto-generated method stub 43 | return null; 44 | } 45 | 46 | @Override 47 | public String voiceMsgHandle(BaseMsg msg) { 48 | // TODO Auto-generated method stub 49 | return null; 50 | } 51 | 52 | @Override 53 | public String viedoMsgHandle(BaseMsg msg) { 54 | // TODO Auto-generated method stub 55 | return null; 56 | } 57 | 58 | @Override 59 | public String nameCardMsgHandle(BaseMsg msg) { 60 | // TODO Auto-generated method stub 61 | return null; 62 | } 63 | 64 | public static void main(String[] args) { 65 | IMsgHandlerFace msgHandler = new UnusefulDemo(); 66 | Wechat wechat = new Wechat(msgHandler, "D://itchat4j/login"); 67 | wechat.start(); 68 | } 69 | 70 | @Override 71 | public void sysMsgHandle(BaseMsg msg) { 72 | // TODO Auto-generated method stub 73 | } 74 | 75 | @Override 76 | public String verifyAddFriendMsgHandle(BaseMsg msg) { 77 | // TODO Auto-generated method stub 78 | return null; 79 | } 80 | 81 | @Override 82 | public String mediaMsgHandle(BaseMsg msg) { 83 | // TODO Auto-generated method stub 84 | return null; 85 | } 86 | 87 | } 88 | --------------------------------------------------------------------------------