├── .gitignore
├── LICENSE
├── README.md
├── docs
└── imgs
│ └── demo.gif
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── thankjava
│ │ └── wqq
│ │ ├── SmartQQClient.java
│ │ ├── SmartQQClientBuilder.java
│ │ ├── WQQClient.java
│ │ ├── consts
│ │ ├── ConfigParams.java
│ │ ├── ConstsParams.java
│ │ ├── DataResRegx.java
│ │ ├── MsgType.java
│ │ └── RequestUrls.java
│ │ ├── core
│ │ ├── action
│ │ │ ├── GetInfoAction.java
│ │ │ ├── LoginAction.java
│ │ │ └── SendMsgAction.java
│ │ ├── event
│ │ │ └── MsgPollEvent.java
│ │ └── request
│ │ │ ├── RequestBuilder.java
│ │ │ ├── aop
│ │ │ └── DoRequest.java
│ │ │ └── api
│ │ │ ├── BaseHttpService.java
│ │ │ ├── CheckLoginQRcodeStatus.java
│ │ │ ├── CheckSig.java
│ │ │ ├── GetDiscusList.java
│ │ │ ├── GetGroupNameListMask2.java
│ │ │ ├── GetLoginQRcode.java
│ │ │ ├── GetOnlineBuddies2.java
│ │ │ ├── GetRecentList2.java
│ │ │ ├── GetSelfInfo2.java
│ │ │ ├── GetUserFriends2.java
│ │ │ ├── GetVfWebqq.java
│ │ │ ├── Login2.java
│ │ │ ├── Poll2.java
│ │ │ ├── SendBuddyMsg2.java
│ │ │ ├── SendDiscuMsg2.java
│ │ │ └── SendQunMsg2.java
│ │ ├── entity
│ │ ├── Session.java
│ │ ├── enums
│ │ │ ├── LoginResultStatus.java
│ │ │ └── PullMsgStatus.java
│ │ ├── msg
│ │ │ ├── Content.java
│ │ │ ├── Font.java
│ │ │ ├── PollMsg.java
│ │ │ ├── SendMsg.java
│ │ │ └── Value.java
│ │ ├── sys
│ │ │ ├── LoginResult.java
│ │ │ └── MonitoringData.java
│ │ └── wqq
│ │ │ ├── CategorieInfo.java
│ │ │ ├── DetailedInfo.java
│ │ │ ├── DiscuInfo.java
│ │ │ ├── DiscusList.java
│ │ │ ├── FriendInfo.java
│ │ │ ├── FriendsList.java
│ │ │ ├── GroupInfo.java
│ │ │ └── GroupsList.java
│ │ ├── extend
│ │ ├── ActionListener.java
│ │ ├── CallBackListener.java
│ │ └── NotifyListener.java
│ │ ├── factory
│ │ ├── ActionFactory.java
│ │ └── RequestFactory.java
│ │ └── util
│ │ ├── JSON2Entity.java
│ │ ├── RegexUtil.java
│ │ └── WqqEncryptor.java
└── resources
│ └── hash.js
└── test
├── java
└── com
│ └── thankjava
│ └── wqq
│ └── test
│ └── qq
│ ├── MessageListener.java
│ └── TestSmartQQ.java
└── resources
└── logback.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 | /target/
14 | .settings
15 | .project
16 | .classpath
17 | log
18 | .idea
19 | *.iml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | [](https://github.com/thankjava/)
3 | 
4 | > ### smartqq-agreement-core
5 | 
6 |
7 | > # 停止维护公告
8 |
9 | ```
10 | 2018/12/12 腾讯在https://web2.qq.com/上贴出公告表示2019/01/01开始届时QQWeb协议将永久下线,
11 | 所以相关依赖服务将不再可用,为此该项目也将不再能正常使用,所以需要停止维护。大家也不用再去花时间找webqq协议的版本,
12 | 服务端的关闭将使其均不可用。若后续提供类似代替品可以花时间再研究。
13 | ```
14 |
15 | ---
16 | - JDK >= 1.7
17 | - [更多介绍](https://www.thankjava.com/opensource/069239e5eee95a2299b804d9f98f1f9a)
18 |
19 | ---
20 | > ### 升级备注
21 | ```
22 | 1.1.3
23 | 升级了关键依赖版本
24 | 掉线事件回调函数返回了SmartQQClient实例,方便业务关闭实例
25 | 处理腾讯WebQQ协议变更,自己发送的好友信息会触发消息拉取事件
26 | 调整poll事件触发后使用异步线程方式去回调业务事件,防止业务事件干扰poll事件进行
27 | 1.1.2
28 | 升级部分依赖组件版本
29 | 关闭httpclient cookie检查的警告日志
30 | 登录相关信息查询使用async.http的异步请求方式,提高登录速度
31 | 1.1.1
32 | 移除了历史版本登录测试方法和历史登录的相关支持代码
33 | 调整了测试代码优化了大量代码注释
34 | 1.1.0
35 | 标注废除历史版本登录和应用初始化相关代码预计1.1.1将彻底移除)
36 | 新增提供基于Fluent Interface风格代码的初始化,并提供测试案例代码
37 | 调整部分代码注释,调整可配参数代码
38 | 废弃主动登录接口,合并到初始化自动完成
39 | 闭环登录环节的相关异常,各个需要业务控制的回调均提供反馈调用
40 | 增加稳定性,新增应用健康状态监控,提供优化掉线自动重连机制
41 | 1.0.5
42 | 升级依赖组件
43 | 调整代码结构
44 | 1.0.4
45 | 升级java-toolkit依赖的模块
46 | 1.0.3
47 | 配合java-toolkit升级,修复async.http模块稳定性
48 | 配合async.http参数变更结构调整等
49 | 解决由于腾讯协议bug导致的自己发送的群消息识别为别人的信息
50 | 1.0.2
51 | 调整代码易读性,增加稳定性等
52 | 代码结构调整
53 | 增加异常重试机制,增强稳定性
54 | 初始化SmartQQ的实现新增两个可选的构造参数
55 | 1.0.1
56 | 修复腾讯修改二维码校验流程带来的影响
57 | 1.0.0
58 | 版本初始
59 | ```
60 | ---
61 | > ### 获取
62 | ```xml
63 |
Function: SmartQQClient
11 | *Description: 基础应用接口
12 | * 13 | * @author acexy@thankjava.com 14 | * @version 1.0 15 | * @date 2016年12月9日 下午2:17:14 16 | */ 17 | public interface SmartQQClient { 18 | 19 | /** 20 | * 获取讨论组信息 21 | *Function: getDiscusList
22 | *Description:
23 | * 24 | * @param isFromServer 是否从服务器获取最新 25 | * @return 26 | * @author acexy@thankjava.com 27 | * @date 2017年1月4日 下午6:59:20 28 | * @version 1.0 29 | */ 30 | public DiscusList getDiscusList(boolean isFromServer); 31 | 32 | /** 33 | * 获取群组信息 34 | *Function: getGroupsList
35 | *Description:
36 | * 37 | * @param isFromServer 是否从服务器获取最新 38 | * @return 39 | * @author acexy@thankjava.com 40 | * @date 2017年1月4日 下午6:59:52 41 | * @version 1.0 42 | */ 43 | public GroupsList getGroupsList(boolean isFromServer); 44 | 45 | /** 46 | * 获取好友信息 47 | *Function: getFriendsList
48 | *Description:
49 | * 50 | * @param isFromServer 是否从服务器获取最新 51 | * @return 52 | * @author acexy@thankjava.com 53 | * @date 2017年1月4日 下午7:00:06 54 | * @version 1.0 55 | */ 56 | public FriendsList getFriendsList(boolean isFromServer); 57 | 58 | /** 59 | * 发送信息 60 | *Function: sendMsg
61 | *Description: 只要构造好正确的SendMsg对象就能自动发送好友|讨论组|群组信息
62 | * 63 | * @param sendMsg 64 | * @author acexy@thankjava.com 65 | * @date 2016年12月28日 下午11:43:45 66 | * @version 1.0 67 | * @see SendMsg 68 | */ 69 | public void sendMsg(SendMsg sendMsg); 70 | 71 | /** 72 | * 关闭SmartQQClient 73 | */ 74 | public void shutdown(); 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/SmartQQClientBuilder.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq; 2 | 3 | import com.thankjava.toolkit.core.reflect.ReflectUtil; 4 | import com.thankjava.wqq.consts.ConfigParams; 5 | import com.thankjava.wqq.core.action.LoginAction; 6 | import com.thankjava.wqq.entity.enums.LoginResultStatus; 7 | import com.thankjava.wqq.entity.sys.LoginResult; 8 | import com.thankjava.wqq.extend.ActionListener; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | import com.thankjava.wqq.extend.NotifyListener; 11 | import com.thankjava.wqq.factory.ActionFactory; 12 | 13 | /** 14 | * SmartQQClient 实力创建工具指南 15 | * 16 | * @author acexy 17 | */ 18 | public class SmartQQClientBuilder { 19 | 20 | private static SmartQQClient smartQQClient; 21 | 22 | private SmartQQClientBuilder() { 23 | } 24 | 25 | /** 26 | * 声明需要自定义参数化 SmartQQClient 27 | * 28 | * @param notifyListener 29 | * @return 30 | */ 31 | public static SmartQQClientBuilder custom(NotifyListener notifyListener) { 32 | 33 | smartQQClient = new WQQClient(); 34 | 35 | if (notifyListener == null) { 36 | throw new NullPointerException("notifyListener can not be null"); 37 | } 38 | 39 | ReflectUtil.setFieldVal(smartQQClient, "notifyListener", notifyListener); 40 | 41 | return new SmartQQClientBuilder(); 42 | } 43 | 44 | /** 45 | * 设置单次请求与异常的最大重试次数 46 | * 47 | * @param times 48 | * @return 49 | */ 50 | public SmartQQClientBuilder setExceptionRetryMaxTimes(int times) { 51 | if (times < 1) { 52 | throw new IllegalArgumentException("exceptionRetryMaxTimes should >= 1"); 53 | } 54 | ConfigParams.EXCEPTION_RETRY_MAX_TIME = times; 55 | return this; 56 | } 57 | 58 | /** 59 | * 登录成功后立即获取一些信息并缓存下来(自己的信息,讨论组,好友信息) 60 | * 61 | * @return 62 | */ 63 | public SmartQQClientBuilder setAutoGetInfoAfterLogin() { 64 | ConfigParams.INIT_LOGIN_INFO = true; 65 | return this; 66 | } 67 | 68 | /** 69 | * 设置若登录二维码过期则自动重新拉取下一张二维码 70 | * 71 | * @return 72 | */ 73 | public SmartQQClientBuilder setAutoRefreshQrcode() { 74 | ConfigParams.AUTO_REFRESH_QR_CODE = true; 75 | return this; 76 | } 77 | 78 | /** 79 | * 当系统计算出登陆状态异常时会重新登录,连续异常到限定阀值后放弃 80 | * 81 | * @param times 82 | * @return 83 | */ 84 | public SmartQQClientBuilder setAutoLoginMaxRetryTimes(int times) { 85 | if (times < 1) { 86 | throw new IllegalArgumentException("autoLoginMaxRetryTimes should >= 1"); 87 | } 88 | ConfigParams.AUTO_RE_LOGIN_RETRY_MAX_TIME = times; 89 | return this; 90 | } 91 | 92 | /** 93 | * 注册一个掉线通知 94 | * 95 | * @param offlineListener 96 | * @return 97 | */ 98 | public SmartQQClientBuilder setOffLineListener(CallBackListener offlineListener) { 99 | ReflectUtil.setFieldVal(smartQQClient, "offlineListener", offlineListener); 100 | return this; 101 | } 102 | 103 | /** 104 | * 创建SmartQQClient实例并登录 105 | * 106 | * @param getQrListener 获取到登录二维码后将进行回调 107 | * @param loginListener 登录完毕后的回调函数,函数会返回一个LoginResult的登录反馈值 108 | */ 109 | public void createAndLogin(final CallBackListener getQrListener, final CallBackListener loginListener) { 110 | 111 | if (getQrListener == null || loginListener == null) { 112 | throw new NullPointerException("listener can not be null"); 113 | } 114 | 115 | LoginAction loginAction = ActionFactory.getInstance(LoginAction.class); 116 | 117 | loginAction.login(getQrListener, new CallBackListener() { 118 | 119 | @Override 120 | public void onListener(ActionListener actionListener) { 121 | loginListener.onListener(new ActionListener(new LoginResult(smartQQClient, (LoginResultStatus) actionListener.getData()))); 122 | } 123 | 124 | }); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/WQQClient.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq; 2 | 3 | import com.thankjava.wqq.core.action.GetInfoAction; 4 | import com.thankjava.wqq.core.action.SendMsgAction; 5 | import com.thankjava.wqq.core.request.api.BaseHttpService; 6 | import com.thankjava.wqq.entity.Session; 7 | import com.thankjava.wqq.entity.msg.SendMsg; 8 | import com.thankjava.wqq.entity.wqq.DiscusList; 9 | import com.thankjava.wqq.entity.wqq.FriendsList; 10 | import com.thankjava.wqq.entity.wqq.GroupsList; 11 | import com.thankjava.wqq.extend.CallBackListener; 12 | import com.thankjava.wqq.extend.NotifyListener; 13 | import com.thankjava.wqq.factory.ActionFactory; 14 | 15 | 16 | public class WQQClient implements SmartQQClient { 17 | 18 | // 通知 19 | private static NotifyListener notifyListener = null; 20 | 21 | // 系统停止回调(掉线) 22 | private static CallBackListener offlineListener = null; 23 | 24 | private Session session = Session.getSession(); 25 | 26 | private SendMsgAction sendMsgAction = ActionFactory.getInstance(SendMsgAction.class); 27 | private GetInfoAction getInfo = ActionFactory.getInstance(GetInfoAction.class); 28 | 29 | private static WQQClient instance = null; 30 | 31 | public WQQClient() { 32 | instance = this; 33 | } 34 | 35 | public static NotifyListener getNotifyListener() { 36 | return notifyListener; 37 | } 38 | 39 | public static CallBackListener getOfflineListener() { 40 | return offlineListener; 41 | } 42 | 43 | public static SmartQQClient getInstance() { 44 | if (instance == null) { 45 | throw new NullPointerException("SmartQQClient is not instantiated"); 46 | } 47 | return instance; 48 | } 49 | 50 | @Override 51 | public void sendMsg(SendMsg sendMsg) { 52 | sendMsgAction.sendMsg(sendMsg); 53 | } 54 | 55 | @Override 56 | public void shutdown() { 57 | BaseHttpService.shutdownAsyncHttpClient(); 58 | } 59 | 60 | @Override 61 | public DiscusList getDiscusList(boolean isFromServer) { 62 | if (isFromServer) { 63 | session.setDiscusList(getInfo.getDiscusList()); 64 | } 65 | return session.getDiscusList(); 66 | } 67 | 68 | @Override 69 | public GroupsList getGroupsList(boolean isFromServer) { 70 | if (isFromServer) { 71 | session.setGroupsList(getInfo.getGroupsList()); 72 | } 73 | return session.getGroupsList(); 74 | } 75 | 76 | @Override 77 | public FriendsList getFriendsList(boolean isFromServer) { 78 | FriendsList friendsList = null; 79 | if (isFromServer) { 80 | friendsList = getInfo.getFriendsList(); 81 | if (friendsList == null) { 82 | return null; 83 | } else { 84 | friendsList = getInfo.getOnlineStatus(); 85 | if (friendsList == null) { 86 | return null; 87 | } 88 | } 89 | } 90 | friendsList = session.getFriendsList(); 91 | if (friendsList == null) { 92 | friendsList = getFriendsList(true); 93 | } 94 | return friendsList; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/consts/ConfigParams.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.consts; 2 | 3 | /** 4 | * SmartQQ的可配置参数化列表 5 | * @author acexy 6 | * 7 | */ 8 | public class ConfigParams { 9 | 10 | /** 11 | * 一些接口最大的异常尝试次数 12 | */ 13 | public static int EXCEPTION_RETRY_MAX_TIME = 1; 14 | 15 | /** 16 | * 登录完成后是否立即获取好友信息,群信息,讨论组信息 17 | */ 18 | public static boolean INIT_LOGIN_INFO = false; 19 | 20 | /** 21 | * 若获取的二维码超时未登录则自动重新获取 22 | */ 23 | public static boolean AUTO_REFRESH_QR_CODE = false; 24 | 25 | /** 26 | * 用于算法计算是否掉线的样本数据个数 27 | */ 28 | public static int MONITOR_THE_NUMBER_OF_DATA_SAMPLES = 10; 29 | 30 | /** 31 | * 计算出掉线情况的连续自动重连最大次数 32 | */ 33 | public static int AUTO_RE_LOGIN_RETRY_MAX_TIME = 1; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/consts/ConstsParams.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.consts; 2 | 3 | public class ConstsParams { 4 | 5 | /** 6 | * 每次检查二维码的时间间隔 7 | */ 8 | public static final long CHECK_QRCODE_WITE_TIME = 2000; 9 | 10 | /** 11 | * APPID 应该是腾讯内部webqq产品所属的APPID 12 | */ 13 | public static final int APP_ID = 501004106; 14 | 15 | /** 16 | * CLIENT_ID 同上 17 | */ 18 | public static final int CLIENT_ID = 53999199; 19 | 20 | /** 21 | * 初始化的消息ID 22 | */ 23 | public static final int INIT_MSG_ID = 1704000; 24 | 25 | /** 26 | * 生成图片二维码类型 27 | */ 28 | public static final String QR_CODE_TYPE = "png"; 29 | 30 | /** 31 | * 生成二维码的名称 32 | */ 33 | public static final String QR_CODE_NAME = "qrcode.png"; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/consts/DataResRegx.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.consts; 2 | 3 | 4 | /** 5 | * 解析腾讯返回的数据正则表达式 6 | *Function: DataResRegx
7 | *Description:
8 | * @author acexy@thankjava.com 9 | * @date 2016年12月15日 上午11:48:07 10 | * @version 1.0 11 | */ 12 | public enum DataResRegx { 13 | 14 | // 样本数据: ptuiCB('66','0','','0','二维码未失效。(997219492)', ''); 15 | check_login_qrcode_status("ptuiCB\\('(\\d+)','(\\d+)','(.*?)','(\\d+)','(.*?)', '(.*?)'\\)", "检查二维码状态"), 16 | 17 | ; 18 | 19 | public String regx; 20 | public String remark; 21 | 22 | private DataResRegx(String regx,String remark){ 23 | this.regx = regx; 24 | this.remark = remark; 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/consts/MsgType.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.consts; 2 | 3 | /** 4 | * 消息类型 5 | *Function: PollMsgType
6 | *Description:
7 | * @author acexy@thankjava.com 8 | * @date 2016年12月26日 下午7:04:54 9 | * @version 1.0 10 | */ 11 | public enum MsgType { 12 | 13 | message, // f2f 好友点对点聊天 14 | discu_message, // discu 讨论组消息 15 | group_message // group 群消息 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/consts/RequestUrls.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.consts; 2 | 3 | /** 4 | * 腾讯wqq url 汇总 5 | *Function: Urls
6 | *Description:
7 | * @author acexy@thankjava.com 8 | * @date 2016年12月9日 下午3:23:44 9 | * @version 1.0 10 | */ 11 | public enum RequestUrls { 12 | 13 | // === request url 14 | get_login_qrcode("https://ssl.ptlogin2.qq.com/ptqrshow", "获取登录的二维码"), 15 | check_qrcode_status("https://ssl.ptlogin2.qq.com/ptqrlogin", "校验登录二维码当前状态"), 16 | getvfwebqq("http://s.web2.qq.com/api/getvfwebqq", "验证登录状态"), 17 | login2("http://d1.web2.qq.com/channel/login2", "登录系统得到登录态"), 18 | get_user_friends2("http://s.web2.qq.com/api/get_user_friends2", "获取用户好友信息"), 19 | get_online_buddies2("http://d1.web2.qq.com/channel/get_online_buddies2", "获取好友状态"), 20 | get_group_name_list_mask2("http://s.web2.qq.com/api/get_group_name_list_mask2", "获取群列表信息"), 21 | get_discus_list("http://s.web2.qq.com/api/get_discus_list", "获取讨论组信息"), 22 | get_self_info2("http://s.web2.qq.com/api/get_self_info2", "获取用户自己的个人信息"), 23 | get_recent_list2("http://d1.web2.qq.com/channel/get_recent_list2", "获取会话列表"), 24 | poll2("https://d1.web2.qq.com/channel/poll2", "消息查询"), 25 | send_buddy_msg2("https://d1.web2.qq.com/channel/send_buddy_msg2", "发送个人信息"), 26 | send_qun_msg2("https://d1.web2.qq.com/channel/send_qun_msg2", "发送群消息"), 27 | send_discu_msg2("https://d1.web2.qq.com/channel/send_discu_msg2", "发送讨论组信息"), 28 | 29 | 30 | // ==== referer url 31 | referer_check_qrcode_status("https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&" 32 | + "target=self&style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0" 33 | + "&no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&" 34 | + "f_url=loginerroralert&strong_login=1&login_state=10&t=20131024001", "检查二维码的referer"), 35 | referer_getvfwebqq("http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1", "getvfwebqq的referer"), 36 | referer_common("http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2", "公共的referer"), 37 | referer_about_msg("https://d1.web2.qq.com/cfproxy.html?v=20151105001&callback=1", "msg信息相关的referer"), 38 | 39 | 40 | // ==== else url 41 | else_proxy("http://w.qq.com/proxy.html?login2qq=1&webqq_type=10", "二维码状态检查的u1参数"), 42 | ; 43 | 44 | public String url; 45 | public String remark; 46 | 47 | private RequestUrls(String url,String remark){ 48 | 49 | this.url = url; 50 | this.remark = remark; 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/action/GetInfoAction.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.action; 2 | 3 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 4 | import com.thankjava.wqq.consts.ConfigParams; 5 | import com.thankjava.wqq.core.request.RequestBuilder; 6 | import com.thankjava.wqq.core.request.api.*; 7 | import com.thankjava.wqq.entity.Session; 8 | import com.thankjava.wqq.entity.wqq.DetailedInfo; 9 | import com.thankjava.wqq.entity.wqq.DiscusList; 10 | import com.thankjava.wqq.entity.wqq.FriendsList; 11 | import com.thankjava.wqq.entity.wqq.GroupsList; 12 | import com.thankjava.wqq.extend.ActionListener; 13 | import com.thankjava.wqq.extend.CallBackListener; 14 | import com.thankjava.wqq.factory.RequestFactory; 15 | import com.thankjava.wqq.util.JSON2Entity; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | public class GetInfoAction { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(GetInfoAction.class); 22 | 23 | private static Session session = Session.getSession(); 24 | 25 | private RequestBuilder getUserFriends2 = RequestFactory.getInstance(GetUserFriends2.class); 26 | private RequestBuilder getGroupNameListMask2 = RequestFactory.getInstance(GetGroupNameListMask2.class); 27 | private RequestBuilder getDiscusList = RequestFactory.getInstance(GetDiscusList.class); 28 | private RequestBuilder getSelfInfo2 = RequestFactory.getInstance(GetSelfInfo2.class); 29 | private RequestBuilder getOnlineBuddies2 = RequestFactory.getInstance(GetOnlineBuddies2.class); 30 | private RequestBuilder getRecentList2 = RequestFactory.getInstance(GetRecentList2.class); 31 | 32 | /** 33 | * 获取所有好友信息 34 | *Function: getFriendsList
35 | *Description:
36 | * 37 | * @return 38 | * @author acexy@thankjava.com 39 | * @date 2016年12月22日 下午1:34:35 40 | * @version 1.0 41 | */ 42 | public FriendsList getFriendsList() { 43 | 44 | AsyncResponse response = null; 45 | FriendsList friendsList = null; 46 | 47 | for (int i = 1; i <= ConfigParams.EXCEPTION_RETRY_MAX_TIME; i++) { 48 | response = getUserFriends2.doRequest(null); 49 | if (!response.isEmptyDataString()) { 50 | friendsList = JSON2Entity.userFriends2(response.getDataString()); 51 | if (friendsList != null) { 52 | session.setFriendsList(friendsList); 53 | return friendsList; 54 | } 55 | } 56 | } 57 | logger.error("getUserFriends2 失败 (已尝试重试)"); 58 | return null; 59 | } 60 | 61 | /** 62 | * 异步获取好友列表 63 | * 64 | * @param callBackListener 65 | * @param tryTimes 66 | */ 67 | public void getFriendsList(final CallBackListener callBackListener, final Integer... tryTimes) { 68 | 69 | final int tryTime = tryTimes == null || tryTimes.length == 0 ? 1 : tryTimes[0] + 1; 70 | if (tryTime >= ConfigParams.EXCEPTION_RETRY_MAX_TIME) { 71 | logger.error("getUserFriends2 失败 (已尝试重试)"); 72 | callBackListener.onListener(new ActionListener(null)); 73 | return; 74 | } 75 | 76 | getUserFriends2.doRequest(new CallBackListener() { 77 | 78 | @Override 79 | public void onListener(ActionListener actionListener) { 80 | if (actionListener.getData() != null) { 81 | AsyncResponse asyncResponse = (AsyncResponse) actionListener.getData(); 82 | FriendsList friendsList = JSON2Entity.userFriends2(asyncResponse.getDataString()); 83 | if (friendsList != null) { 84 | session.setFriendsList(friendsList); 85 | callBackListener.onListener(new ActionListener(friendsList)); 86 | } else { 87 | getFriendsList(callBackListener, tryTime); 88 | } 89 | } else { 90 | getFriendsList(callBackListener, tryTime); 91 | } 92 | } 93 | 94 | }); 95 | } 96 | 97 | /** 98 | * 为当前好友列表数据查询在线状态 99 | *Function: getOnlineStatus
100 | *Description:
101 | * 102 | * @return 103 | * @author acexy@thankjava.com 104 | * @date 2017年6月14日 下午2:46:17 105 | * @version 1.0 106 | */ 107 | public FriendsList getOnlineStatus() { 108 | 109 | FriendsList friendsList = session.getFriendsList(); 110 | if (friendsList == null) { 111 | return null; 112 | } 113 | 114 | AsyncResponse response = null; 115 | 116 | for (int i = 1; i <= ConfigParams.EXCEPTION_RETRY_MAX_TIME; i++) { 117 | response = getOnlineBuddies2.doRequest(null); 118 | if (!response.isEmptyDataString()) { 119 | friendsList = JSON2Entity.onlineStatus(friendsList, response.getDataString()); 120 | if (friendsList != null) { 121 | session.setFriendsList(friendsList); 122 | return friendsList; 123 | } 124 | } 125 | } 126 | logger.error("getOnlineBuddies2 失败 (已尝试重试)"); 127 | 128 | return null; 129 | } 130 | 131 | 132 | public void getOnlineStatus(final CallBackListener callBackListener, final Integer... tryTimes) { 133 | 134 | final int tryTime = tryTimes == null || tryTimes.length == 0 ? 1 : tryTimes[0] + 1; 135 | if (tryTime >= ConfigParams.EXCEPTION_RETRY_MAX_TIME) { 136 | logger.error("getOnlineBuddies2 失败 (已尝试重试)"); 137 | callBackListener.onListener(new ActionListener(null)); 138 | return; 139 | } 140 | 141 | getOnlineBuddies2.doRequest(new CallBackListener() { 142 | 143 | @Override 144 | public void onListener(ActionListener actionListener) { 145 | FriendsList friendsList = session.getFriendsList(); 146 | if (friendsList == null) { 147 | callBackListener.onListener(new ActionListener(null)); 148 | return; 149 | } 150 | if (actionListener.getData() != null) { 151 | AsyncResponse asyncResponse = (AsyncResponse) actionListener.getData(); 152 | friendsList = JSON2Entity.onlineStatus(friendsList, asyncResponse.getDataString()); 153 | if (friendsList != null) { 154 | session.setFriendsList(friendsList); 155 | callBackListener.onListener(new ActionListener(friendsList)); 156 | } else { 157 | getOnlineStatus(callBackListener, tryTime); 158 | } 159 | } else { 160 | getOnlineStatus(callBackListener, tryTime); 161 | } 162 | } 163 | 164 | }); 165 | } 166 | 167 | /** 168 | * 获取讨论组列表 169 | *Function: getDiscusList
170 | *Description:
171 | * 172 | * @return 173 | * @author acexy@thankjava.com 174 | * @date 2016年12月22日 下午4:42:58 175 | * @version 1.0 176 | */ 177 | public DiscusList getDiscusList() { 178 | AsyncResponse response = null; 179 | DiscusList discusList = null; 180 | for (int i = 1; i <= ConfigParams.EXCEPTION_RETRY_MAX_TIME; i++) { 181 | response = getDiscusList.doRequest(null); 182 | if (!response.isEmptyDataString()) { 183 | discusList = JSON2Entity.getDiscusList(response.getDataString()); 184 | if (discusList != null) { 185 | session.setDiscusList(discusList); 186 | return discusList; 187 | } 188 | } 189 | } 190 | logger.error("getDiscusList 失败 (已尝试重试)"); 191 | return null; 192 | } 193 | 194 | /** 195 | * 异步获取讨论组信息 196 | * 197 | * @param callBackListener 198 | * @param tryTimes 199 | */ 200 | public void getDiscusList(final CallBackListener callBackListener, final Integer... tryTimes) { 201 | final int tryTime = tryTimes == null || tryTimes.length == 0 ? 1 : tryTimes[0] + 1; 202 | 203 | if (tryTime >= ConfigParams.EXCEPTION_RETRY_MAX_TIME) { 204 | logger.error("getGroupNameListMask2 失败 (已尝试重试)"); 205 | callBackListener.onListener(new ActionListener(null)); 206 | return; 207 | } 208 | 209 | getDiscusList.doRequest(new CallBackListener() { 210 | @Override 211 | public void onListener(ActionListener actionListener) { 212 | if (actionListener.getData() != null) { 213 | AsyncResponse asyncResponse = (AsyncResponse) actionListener.getData(); 214 | DiscusList discusList = JSON2Entity.getDiscusList(asyncResponse.getDataString()); 215 | if (discusList != null) { 216 | session.setDiscusList(discusList); 217 | callBackListener.onListener(new ActionListener(discusList)); 218 | } else { 219 | getDiscusList(callBackListener, tryTime); 220 | } 221 | } else { 222 | getDiscusList(callBackListener, tryTime); 223 | } 224 | } 225 | 226 | }); 227 | } 228 | 229 | /** 230 | * 获取群列表 231 | *Function: getGroupsList
232 | *Description:
233 | * 234 | * @return 235 | * @author acexy@thankjava.com 236 | * @date 2016年12月22日 下午1:56:17 237 | * @version 1.0 238 | */ 239 | public GroupsList getGroupsList() { 240 | AsyncResponse response = null; 241 | GroupsList groupList = null; 242 | for (int i = 1; i <= ConfigParams.EXCEPTION_RETRY_MAX_TIME; i++) { 243 | response = getGroupNameListMask2.doRequest(null); 244 | if (!response.isEmptyDataString()) { 245 | groupList = JSON2Entity.getGroupsList(response.getDataString()); 246 | if (groupList != null) { 247 | session.setGroupsList(groupList); 248 | return groupList; 249 | } 250 | } 251 | } 252 | logger.error("getGroupNameListMask2 失败 (已尝试重试)"); 253 | return null; 254 | } 255 | 256 | /** 257 | * 异步获取群组列表 258 | * 259 | * @param callBackListener 260 | * @param tryTimes 261 | */ 262 | public void getGroupsList(final CallBackListener callBackListener, final Integer... tryTimes) { 263 | 264 | final int tryTime = tryTimes == null || tryTimes.length == 0 ? 1 : tryTimes[0] + 1; 265 | 266 | if (tryTime >= ConfigParams.EXCEPTION_RETRY_MAX_TIME) { 267 | logger.error("getGroupNameListMask2 失败 (已尝试重试)"); 268 | callBackListener.onListener(new ActionListener(null)); 269 | return; 270 | } 271 | 272 | getGroupNameListMask2.doRequest(new CallBackListener() { 273 | @Override 274 | public void onListener(ActionListener actionListener) { 275 | if (actionListener.getData() != null) { 276 | AsyncResponse asyncResponse = (AsyncResponse) actionListener.getData(); 277 | GroupsList groupList = JSON2Entity.getGroupsList(asyncResponse.getDataString()); 278 | if (groupList != null) { 279 | session.setGroupsList(groupList); 280 | callBackListener.onListener(new ActionListener(groupList)); 281 | } else { 282 | getGroupsList(callBackListener, tryTime); 283 | } 284 | } else { 285 | getGroupsList(callBackListener, tryTime); 286 | } 287 | } 288 | 289 | }); 290 | } 291 | 292 | /** 293 | * 获取个人信息(登录人) 294 | *Function: getSelfInfo
295 | *Description:
296 | * 297 | * @return 298 | * @author acexy@thankjava.com 299 | * @date 2017年6月14日 下午4:13:01 300 | * @version 1.0 301 | */ 302 | public DetailedInfo getSelfInfo() { 303 | AsyncResponse response = null; 304 | DetailedInfo detailedInfo = null; 305 | for (int i = 1; i <= ConfigParams.EXCEPTION_RETRY_MAX_TIME; i++) { 306 | response = getSelfInfo2.doRequest(null); 307 | if (!response.isEmptyDataString()) { 308 | detailedInfo = JSON2Entity.getSelfInfo(response.getDataString()); 309 | if (detailedInfo != null) { 310 | session.setSelfInfo(detailedInfo); 311 | return detailedInfo; 312 | } 313 | } 314 | } 315 | logger.error("getSelfInfo2 失败 (已尝试重试)"); 316 | 317 | return null; 318 | } 319 | 320 | 321 | /** 322 | * 异步获取个人信息 323 | * 324 | * @param callBackListener 325 | * @param tryTimes 326 | */ 327 | public void getSelfInfo(final CallBackListener callBackListener, final Integer... tryTimes) { 328 | 329 | final int tryTime = tryTimes == null || tryTimes.length == 0 ? 1 : tryTimes[0] + 1; 330 | 331 | if (tryTime >= ConfigParams.EXCEPTION_RETRY_MAX_TIME) { 332 | logger.error("getSelfInfo2 失败 (已尝试重试)"); 333 | callBackListener.onListener(new ActionListener(null)); 334 | return; 335 | } 336 | 337 | getSelfInfo2.doRequest(new CallBackListener() { 338 | 339 | @Override 340 | public void onListener(ActionListener actionListener) { 341 | if (actionListener.getData() != null) { 342 | AsyncResponse asyncResponse = (AsyncResponse) actionListener.getData(); 343 | DetailedInfo detailedInfo = JSON2Entity.getSelfInfo(asyncResponse.getDataString()); 344 | if (detailedInfo != null) { 345 | session.setSelfInfo(detailedInfo); 346 | callBackListener.onListener(new ActionListener(detailedInfo)); 347 | } else { 348 | getSelfInfo(callBackListener, tryTime); 349 | } 350 | } else { 351 | getSelfInfo(callBackListener, tryTime); 352 | } 353 | } 354 | 355 | }); 356 | } 357 | 358 | /** 359 | * 获取最近联系的好友,没有实际意义未实现 360 | */ 361 | @Deprecated 362 | void getRecentList() { 363 | getRecentList2.doRequest(null).isEmptyDataString(); 364 | } 365 | } -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/action/LoginAction.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.action; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 5 | import com.thankjava.wqq.consts.ConfigParams; 6 | import com.thankjava.wqq.consts.ConstsParams; 7 | import com.thankjava.wqq.consts.DataResRegx; 8 | import com.thankjava.wqq.core.event.MsgPollEvent; 9 | import com.thankjava.wqq.core.request.RequestBuilder; 10 | import com.thankjava.wqq.core.request.api.*; 11 | import com.thankjava.wqq.entity.Session; 12 | import com.thankjava.wqq.entity.enums.LoginResultStatus; 13 | import com.thankjava.wqq.extend.ActionListener; 14 | import com.thankjava.wqq.extend.CallBackListener; 15 | import com.thankjava.wqq.factory.ActionFactory; 16 | import com.thankjava.wqq.factory.RequestFactory; 17 | import com.thankjava.wqq.util.RegexUtil; 18 | import org.apache.http.cookie.Cookie; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import javax.imageio.ImageIO; 23 | import java.io.ByteArrayInputStream; 24 | import java.io.IOException; 25 | 26 | public class LoginAction { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(LoginAction.class); 29 | 30 | private Session session = Session.getSession(); 31 | 32 | private RequestBuilder getLoginQRcode = RequestFactory.getInstance(GetLoginQRcode.class); 33 | private RequestBuilder checkSig = RequestFactory.getInstance(CheckSig.class); 34 | private RequestBuilder getVfWebqq = RequestFactory.getInstance(GetVfWebqq.class); 35 | private RequestBuilder login2 = RequestFactory.getInstance(Login2.class); 36 | private RequestBuilder checkLoginQRcodeStatus = RequestFactory.getInstance(CheckLoginQRcodeStatus.class); 37 | 38 | private GetInfoAction getInfo = ActionFactory.getInstance(GetInfoAction.class); 39 | 40 | public void login(final CallBackListener getQrListener, final CallBackListener loginListener) { 41 | 42 | AsyncResponse asyncResponse = getLoginQRcode.doRequest(null); 43 | if (asyncResponse == null) { 44 | logger.error("获取二维码失败,执行重试"); 45 | login(getQrListener, loginListener); 46 | } 47 | 48 | ActionListener actionListener = null; 49 | try { 50 | // 得到二维码数据 51 | actionListener = new ActionListener(ImageIO.read(new ByteArrayInputStream(asyncResponse.getDataByteArray()))); 52 | } catch (IOException e) { 53 | logger.error("获取二维码数据失败", e); 54 | loginListener.onListener(new ActionListener(LoginResultStatus.exception)); 55 | } 56 | 57 | getQrListener.onListener(actionListener); 58 | 59 | logger.debug("获取二维码完成,启动二维码状态检查"); 60 | 61 | checkLoginQRcodeStatus(getQrListener, loginListener); 62 | } 63 | 64 | /** 65 | * 检查当前的二维码的状态 66 | *Function: checkLoginQRcodeStatus
67 | *Description:
68 | * 69 | * @return 70 | * @author zhaoxy@thankjava.com 71 | * @date 2016年12月19日 下午4:19:00 72 | * @version 1.0 73 | */ 74 | private void checkLoginQRcodeStatus(CallBackListener getQRListener, CallBackListener loginListener) { 75 | 76 | try { 77 | Thread.sleep(ConstsParams.CHECK_QRCODE_WITE_TIME); 78 | } catch (InterruptedException e) { 79 | logger.error("检查当前的二维码的状态线程等待异常", e); 80 | loginListener.onListener(new ActionListener(LoginResultStatus.exception)); 81 | } 82 | 83 | String[] data = RegexUtil.doRegex(checkLoginQRcodeStatus.doRequest(null).getDataString(), DataResRegx.check_login_qrcode_status); 84 | 85 | if (data == null) { 86 | logger.error("解析二维码状态失败,重试二维码状态检查"); 87 | checkLoginQRcodeStatus(getQRListener, loginListener); 88 | } 89 | 90 | Integer statusCode = Integer.valueOf(data[0]); 91 | if (statusCode == null) { 92 | logger.error("无法解析出有效的二维码状态码,重试二维码状态检查"); 93 | checkLoginQRcodeStatus(getQRListener, loginListener); 94 | } 95 | 96 | switch (statusCode) { 97 | case 0: // 二维码认证成功 98 | logger.debug("二维码验证完成"); 99 | session.setCheckSigUrl(data[2]); 100 | if (beginLogin()) { 101 | loginListener.onListener(new ActionListener(LoginResultStatus.success)); 102 | } else { 103 | logger.error("登录失败"); 104 | loginListener.onListener(new ActionListener(LoginResultStatus.failed)); 105 | } 106 | break; 107 | case 65: // 二维码认证过期 108 | if (ConfigParams.AUTO_REFRESH_QR_CODE) { // 如果指定过期自动刷新二维码 109 | logger.debug("二维码已过期, 重新获取二维码..."); 110 | login(getQRListener, loginListener); 111 | break; 112 | } 113 | logger.debug("当前二维码已过期, 并且未设置自动重刷二维码, 登录失败"); 114 | loginListener.onListener(new ActionListener(LoginResultStatus.failed)); 115 | break; 116 | default: // 二维码处于认证中|等待认证 117 | logger.debug("二维码状态: " + data[4]); 118 | checkLoginQRcodeStatus(getQRListener, loginListener); 119 | break; 120 | } 121 | } 122 | 123 | /** 124 | * 扫码认证完成后执行登录态相关的认证登录 125 | *Function: beginLogin
126 | *Description:
127 | * 128 | * @author zhaoxy@thankjava.com 129 | * @date 2016年12月20日 下午2:49:21 130 | * @version 1.0 131 | */ 132 | private boolean beginLogin() { 133 | 134 | // checkSig 135 | AsyncResponse asyncResponse = checkSig.doRequest(null); 136 | 137 | // checkSig 成功后 从cookie中得到ptwebqq 确切的说其实这个cookie是检查qrcode成功后服务器下发的 138 | Cookie cookie = asyncResponse.getCookies().getCookie("ptwebqq"); 139 | if (cookie == null) { 140 | logger.error("未能获取到ptwebqq数据"); 141 | return false; 142 | } 143 | String ptwebqq = cookie.getValue(); 144 | if (ptwebqq == null || ptwebqq.length() == 0) { 145 | logger.error("未能成功获取ptwebqq数据"); 146 | return false; 147 | } 148 | 149 | // getvfWebqq 150 | session.setPtwebqq(ptwebqq); 151 | asyncResponse = getVfWebqq.doRequest(null); 152 | if (asyncResponse.isEmptyDataString()) { 153 | logger.error("未能成功获取vfwebqq数据"); 154 | return false; 155 | } 156 | String content = asyncResponse.getDataString(); 157 | JSONObject jsonObject, result; 158 | try { 159 | jsonObject = JSONObject.parseObject(content); 160 | result = (JSONObject) jsonObject.get("result"); 161 | session.setVfwebqq(result.get("vfwebqq").toString()); 162 | } catch (Exception e) { 163 | logger.error("未能成功解析vfwebqq数据", e); 164 | return false; 165 | } 166 | 167 | // login2 168 | asyncResponse = login2.doRequest(null); 169 | if (asyncResponse.isEmptyDataString()) { 170 | logger.error("未能成功获取登录数据"); 171 | return false; 172 | } 173 | content = asyncResponse.getDataString(); 174 | try { 175 | jsonObject = JSONObject.parseObject(content); 176 | result = (JSONObject) jsonObject.get("result"); 177 | session.setUin(result.getLongValue("uin")); 178 | session.setPsessionid(result.getString("psessionid")); 179 | } catch (Exception e) { 180 | logger.error("未能成功解析登录数据", e); 181 | return false; 182 | } 183 | 184 | if (ConfigParams.INIT_LOGIN_INFO) { 185 | 186 | getInfo.getFriendsList(new CallBackListener() { 187 | @Override 188 | public void onListener(ActionListener actionListener) { 189 | if (actionListener.getData() == null) { 190 | logger.error("获取好友列表失败"); 191 | } else { 192 | logger.debug("获取好友列表成功"); 193 | getInfo.getOnlineStatus(new CallBackListener() { 194 | @Override 195 | public void onListener(ActionListener actionListener) { 196 | if (actionListener.getData() == null) { 197 | logger.error("查询好友状态失败"); 198 | } else { 199 | logger.debug("查询好友状态成功"); 200 | } 201 | } 202 | }); 203 | } 204 | } 205 | }); 206 | 207 | getInfo.getGroupsList(new CallBackListener() { 208 | @Override 209 | public void onListener(ActionListener actionListener) { 210 | if (actionListener.getData() != null) { 211 | logger.debug("获取群列表成功"); 212 | } else { 213 | logger.error("获取群列表失败"); 214 | } 215 | } 216 | }); 217 | 218 | getInfo.getSelfInfo(new CallBackListener() { 219 | @Override 220 | public void onListener(ActionListener actionListener) { 221 | if (actionListener.getData() != null) { 222 | logger.debug("获取个人信息成功"); 223 | } else { 224 | logger.error("获取个人信息失败"); 225 | } 226 | } 227 | }); 228 | 229 | getInfo.getDiscusList(new CallBackListener() { 230 | @Override 231 | public void onListener(ActionListener actionListener) { 232 | if (actionListener.getData() != null) { 233 | logger.debug("获取讨论组列表成功"); 234 | } else { 235 | logger.error("获取讨论组列表失败"); 236 | 237 | } 238 | } 239 | }); 240 | 241 | } 242 | 243 | 244 | // 启动消息Poll 245 | new Thread(new Runnable() { 246 | @Override 247 | public void run() { 248 | ActionFactory.getInstance(MsgPollEvent.class).poll(); 249 | } 250 | }).start(); 251 | 252 | return true; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/action/SendMsgAction.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.action; 2 | 3 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 4 | import com.thankjava.wqq.core.request.api.SendBuddyMsg2; 5 | import com.thankjava.wqq.core.request.api.SendDiscuMsg2; 6 | import com.thankjava.wqq.core.request.api.SendQunMsg2; 7 | import com.thankjava.wqq.entity.msg.SendMsg; 8 | 9 | public class SendMsgAction { 10 | 11 | public boolean sendMsg(SendMsg sendMsg) { 12 | switch (sendMsg.getMsgType()) { 13 | case message: 14 | return sendFriendMsg(sendMsg); 15 | case group_message: 16 | return sendGroupMsg(sendMsg); 17 | case discu_message: 18 | return sendDiscuMsg(sendMsg); 19 | default: 20 | return false; 21 | } 22 | } 23 | 24 | private boolean sendFriendMsg(SendMsg sendMsg) { 25 | new SendBuddyMsg2(sendMsg).doRequest(null); 26 | return true; 27 | } 28 | 29 | private boolean sendGroupMsg(SendMsg sendMsg) { 30 | new SendQunMsg2(sendMsg).doRequest(null); 31 | return true; 32 | } 33 | 34 | private boolean sendDiscuMsg(SendMsg sendMsg) { 35 | new SendDiscuMsg2(sendMsg).doRequest(null); 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/event/MsgPollEvent.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.event; 2 | 3 | import com.thankjava.toolkit.core.reflect.ReflectUtil; 4 | import com.thankjava.toolkit.core.thread.ThreadPool; 5 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 6 | import com.thankjava.wqq.WQQClient; 7 | import com.thankjava.wqq.consts.ConfigParams; 8 | import com.thankjava.wqq.core.action.LoginAction; 9 | import com.thankjava.wqq.core.request.RequestBuilder; 10 | import com.thankjava.wqq.core.request.api.Poll2; 11 | import com.thankjava.wqq.entity.Session; 12 | import com.thankjava.wqq.entity.enums.PullMsgStatus; 13 | import com.thankjava.wqq.entity.msg.PollMsg; 14 | import com.thankjava.wqq.entity.sys.MonitoringData; 15 | import com.thankjava.wqq.extend.ActionListener; 16 | import com.thankjava.wqq.extend.CallBackListener; 17 | import com.thankjava.wqq.factory.ActionFactory; 18 | import com.thankjava.wqq.factory.RequestFactory; 19 | import com.thankjava.wqq.util.JSON2Entity; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.lang.reflect.Method; 24 | 25 | /** 26 | * 消息事件处理器 27 | */ 28 | public class MsgPollEvent { 29 | 30 | private static final Logger logger = LoggerFactory.getLogger(MsgPollEvent.class); 31 | private static Session session = Session.getSession(); 32 | private ThreadPool threadPool = new ThreadPool(); 33 | private RequestBuilder poll2 = RequestFactory.getInstance(Poll2.class); 34 | private LoginAction loginAction = ActionFactory.getInstance(LoginAction.class); 35 | 36 | public void poll() { 37 | 38 | poll2.doRequest(new CallBackListener() { 39 | 40 | @Override 41 | public void onListener(ActionListener actionListener) { 42 | 43 | PullMsgStatus pullMsgStatus; 44 | 45 | if (actionListener.getData() != null) { 46 | 47 | AsyncResponse response = (AsyncResponse) actionListener.getData(); 48 | if (response.getException() != null) { 49 | 50 | logger.error("MsgPollEvent Error", response.getException()); 51 | pullMsgStatus = PullMsgStatus.http_exception; 52 | 53 | } else { 54 | 55 | if (response.getHttpCode() == 200) { 56 | 57 | PollMsg pollMsg = JSON2Entity.pollMsg(response.getDataString()); 58 | 59 | if (pollMsg != null) { 60 | 61 | notifyMsgEvent(pollMsg); 62 | pullMsgStatus = PullMsgStatus.normal; 63 | 64 | } else { 65 | // 未能解析响应数据 66 | pullMsgStatus = PullMsgStatus.http_response_error; 67 | } 68 | 69 | } else { 70 | pullMsgStatus = PullMsgStatus.http_status_error; 71 | } 72 | } 73 | 74 | } else { 75 | logger.error("MsgPollEvent error"); 76 | pullMsgStatus = PullMsgStatus.http_exception; 77 | } 78 | 79 | if (doExceptionCheck(pullMsgStatus)) { 80 | poll(); 81 | } 82 | 83 | } 84 | }); 85 | } 86 | 87 | private void notifyMsgEvent(final PollMsg pollMsg) { 88 | 89 | threadPool.execute(new Runnable() { 90 | @Override 91 | public void run() { 92 | WQQClient.getNotifyListener().handler(WQQClient.getInstance(), pollMsg); 93 | } 94 | }); 95 | } 96 | 97 | // 用来监控&计算当前SmartQQClient的健康状态 98 | private boolean doExceptionCheck(PullMsgStatus pullMsgStatus) { 99 | 100 | MonitoringData monitoringData = session.getMonitoringData(pullMsgStatus); 101 | monitoringData.addData(); 102 | double avgValue = monitoringData.getAverageValueOfOneSecond(); 103 | 104 | if (avgValue >= 1) { 105 | 106 | logger.debug("MsgStatus: " + pullMsgStatus + " 类型平均值超出,可能处于登录异常,执行掉线重连"); 107 | 108 | // 重置监控数据 109 | session.resetMonitoringData(); 110 | try { 111 | Method method = ReflectUtil.getMethod(LoginAction.class, "beginLogin"); 112 | int retryTimes = ConfigParams.AUTO_RE_LOGIN_RETRY_MAX_TIME; 113 | while (retryTimes > 0) { 114 | boolean flag = (boolean) ReflectUtil.invokeMethod(loginAction, method); 115 | if (flag) { 116 | logger.debug("系统执行自动重连完成"); 117 | break; 118 | } 119 | retryTimes--; 120 | } 121 | if (retryTimes == 0) { 122 | // 计算是否处于连续重连失败的情况 123 | logger.debug("系统执行自动重连失败已达到上限,已放弃尝试"); 124 | CallBackListener callBackListener = WQQClient.getOfflineListener(); 125 | if (callBackListener != null) { 126 | callBackListener.onListener(new ActionListener(WQQClient.getInstance())); 127 | } 128 | } 129 | } catch (Exception e) { 130 | // TODO: 131 | } 132 | return false; 133 | } 134 | return true; 135 | } 136 | 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/RequestBuilder.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request; 2 | 3 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 4 | import com.thankjava.wqq.extend.CallBackListener; 5 | 6 | public interface RequestBuilder { 7 | 8 | /** 9 | * 请求腾讯服务器 得到请求结果 10 | *Function: doRequest
11 | *Description:
12 | * 13 | * @param listener 回调函数 如果回调函数为空 则通过 return 返回请求结果 14 | * @return 如果没有传入回调函数则结果将通过该参数返回, 如果传入了回调函数则返回值为null 15 | * @author acexy@thankjava.com 16 | * @date 2016年12月19日 下午3:05:37 17 | * @version 1.0 18 | */ 19 | public AsyncResponse doRequest(CallBackListener listener); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/aop/DoRequest.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.aop; 2 | 3 | import com.thankjava.toolkit.bean.aop.entity.AopArgs; 4 | import com.thankjava.toolkit.core.reflect.ReflectUtil; 5 | import com.thankjava.toolkit3d.bean.http.AsyncRequest; 6 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 7 | import com.thankjava.toolkit3d.bean.http.AsyncResponseCallback; 8 | import com.thankjava.toolkit3d.core.http.AsyncHttpClient; 9 | import com.thankjava.wqq.core.request.api.BaseHttpService; 10 | import com.thankjava.wqq.extend.ActionListener; 11 | import com.thankjava.wqq.extend.CallBackListener; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | public class DoRequest { 18 | 19 | private static final String proxyMethodName = "buildRequestParams"; 20 | 21 | private static Logger logger = LoggerFactory.getLogger(DoRequest.class); 22 | 23 | private static AsyncHttpClient asyncHttpClient = BaseHttpService.asyncHttpClient; 24 | 25 | public void doRequest(AopArgs aopArgs) { 26 | 27 | // 指定代理的函数不要被执行 28 | aopArgs.setInvokeProxyMethod(false); 29 | 30 | // 获取被代理的函数的参数列 31 | final CallBackListener listener = (CallBackListener) aopArgs.getInvokeArgs()[0]; 32 | 33 | // 执行buildRequestParams 得到请求的参数体 34 | Object proxyInstance = aopArgs.getProxyInstance(); 35 | 36 | Method method = ReflectUtil.getMethod(proxyInstance.getClass(), proxyMethodName); 37 | AsyncRequest asyncRequest = (AsyncRequest) ReflectUtil.invokeMethod(proxyInstance, method); 38 | 39 | if (listener != null) { 40 | 41 | // 如果传递了listener 则通过listener的方式回调返回 42 | try { 43 | 44 | asyncHttpClient.asyncRequestWithSession(asyncRequest, new AsyncResponseCallback() { 45 | 46 | @Override 47 | public void completed(AsyncResponse asyncResponse) { 48 | listener.onListener(new ActionListener(asyncResponse)); 49 | } 50 | 51 | @Override 52 | public void failed(Exception e) { 53 | listener.onListener(new ActionListener(null)); 54 | } 55 | 56 | @Override 57 | public void cancelled() { 58 | listener.onListener(new ActionListener(null)); 59 | } 60 | 61 | }); 62 | } catch (Throwable e) { 63 | logger.error("http request error", e); 64 | listener.onListener(new ActionListener()); 65 | } 66 | 67 | } else { 68 | 69 | try { 70 | aopArgs.setReturnResult(asyncHttpClient.syncRequestWithSession(asyncRequest)); 71 | } catch (Throwable e) { 72 | aopArgs.setReturnResult(null); 73 | logger.error("http request error", e); 74 | } 75 | 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/BaseHttpService.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit3d.bean.http.AsyncRequest; 4 | import com.thankjava.toolkit3d.bean.http.CookieCheckLevel; 5 | import com.thankjava.toolkit3d.core.http.AsyncHttpClient; 6 | import com.thankjava.toolkit3d.core.http.AsyncHttpClientBuilder; 7 | import com.thankjava.wqq.consts.ConstsParams; 8 | import com.thankjava.wqq.core.request.RequestBuilder; 9 | import com.thankjava.wqq.entity.Session; 10 | 11 | import java.util.concurrent.atomic.AtomicLong; 12 | 13 | public abstract class BaseHttpService implements RequestBuilder { 14 | 15 | public final static AsyncHttpClient asyncHttpClient = new AsyncHttpClientBuilder() 16 | .setWithoutSSLCheck() 17 | .setCookiePolicyLevel(CookieCheckLevel.BROWSER_COMPATIBILITY) 18 | .setCloseWarnLogger() 19 | .create(); 20 | protected static final AtomicLong msgId = new AtomicLong(ConstsParams.INIT_MSG_ID); 21 | protected final Session session = Session.getSession(); 22 | 23 | /** 24 | * 关闭AsyncHttpClient 25 | */ 26 | public static void shutdownAsyncHttpClient() { 27 | asyncHttpClient.shutdown(); 28 | } 29 | 30 | /** 31 | * 组装请求参数 32 | *Function: buildRequestParams
33 | *Description:
34 | * 35 | * @return 36 | * @author acexy@thankjava.com 37 | * @date 2016年12月19日 上午11:43:02 38 | * @version 1.0 39 | */ 40 | protected abstract AsyncRequest buildRequestParams(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/CheckLoginQRcodeStatus.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.RequestUrls; 6 | import com.thankjava.wqq.core.request.aop.DoRequest; 7 | import com.thankjava.wqq.extend.CallBackListener; 8 | import com.thankjava.wqq.util.WqqEncryptor; 9 | 10 | public class CheckLoginQRcodeStatus extends BaseHttpService { 11 | 12 | @Override 13 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 14 | public AsyncResponse doRequest(CallBackListener listener) { 15 | return null; 16 | } 17 | 18 | @Override 19 | protected AsyncRequest buildRequestParams() { 20 | 21 | Parameters params = new Parameters("webqq_type", "10"); 22 | params.append("ptqrtoken", WqqEncryptor.hashForCheckQrStatus(asyncHttpClient.getCookieFromClientContext("qrsig").getValue())); 23 | params.append("webqq_type", "10"); 24 | params.append("remember_uin", "1"); 25 | params.append("login2qq", "1"); 26 | params.append("aid", "501004106"); 27 | params.append("u1", RequestUrls.else_proxy.url); 28 | params.append("ptredirect", "0"); 29 | params.append("ptlang", "2052"); 30 | params.append("daid", "164"); 31 | params.append("from_ui", "1"); 32 | params.append("pttype", "1"); 33 | params.append("dumy", ""); 34 | params.append("fp", "loginerroralert"); 35 | params.append("action", "0-0-2177"); 36 | params.append("mibao_css", "m_webqq"); 37 | params.append("t", "undefined"); 38 | params.append("g", "1"); 39 | params.append("js_type", "0"); 40 | params.append("js_ver", "10185"); 41 | params.append("login_sig", ""); 42 | params.append("pt_randsalt", "0"); 43 | 44 | Headers headers = new Headers("Referer", RequestUrls.referer_check_qrcode_status.url); 45 | return new AsyncRequest( 46 | RequestUrls.check_qrcode_status.url, 47 | HttpMethod.get, 48 | params, 49 | headers 50 | ); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/CheckSig.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.AsyncRequest; 5 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 6 | import com.thankjava.toolkit3d.bean.http.HttpMethod; 7 | import com.thankjava.wqq.core.request.aop.DoRequest; 8 | import com.thankjava.wqq.extend.CallBackListener; 9 | 10 | public class CheckSig extends BaseHttpService { 11 | 12 | @Override 13 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 14 | public AsyncResponse doRequest(CallBackListener listener) { 15 | return null; 16 | } 17 | 18 | @Override 19 | protected AsyncRequest buildRequestParams() { 20 | return new AsyncRequest( 21 | session.getCheckSigUrl(), 22 | HttpMethod.get 23 | ); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetDiscusList.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.ConstsParams; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.core.request.aop.DoRequest; 8 | import com.thankjava.wqq.extend.CallBackListener; 9 | 10 | public class GetDiscusList extends BaseHttpService { 11 | 12 | @Override 13 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 14 | public AsyncResponse doRequest(CallBackListener listener) { 15 | return null; 16 | } 17 | 18 | @Override 19 | protected AsyncRequest buildRequestParams() { 20 | Parameters params = new Parameters("clientid", String.valueOf(ConstsParams.CLIENT_ID)); 21 | params.append("psessionid", session.getPsessionid()); 22 | params.append("vfwebqq", session.getVfwebqq()); 23 | params.append("t", String.valueOf(System.currentTimeMillis() / 1000)); 24 | 25 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 26 | return new AsyncRequest( 27 | RequestUrls.get_discus_list.url, 28 | HttpMethod.get, 29 | params, 30 | headers 31 | ); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetGroupNameListMask2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit.bean.aop.anno.Before; 5 | import com.thankjava.toolkit3d.bean.http.*; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.core.request.aop.DoRequest; 8 | import com.thankjava.wqq.extend.CallBackListener; 9 | import com.thankjava.wqq.util.WqqEncryptor; 10 | 11 | public class GetGroupNameListMask2 extends BaseHttpService { 12 | 13 | @Override 14 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 15 | public AsyncResponse doRequest(CallBackListener listener) { 16 | return null; 17 | } 18 | 19 | @Override 20 | protected AsyncRequest buildRequestParams() { 21 | JSONObject jsonObject = new JSONObject(); 22 | jsonObject.put("vfwebqq", session.getVfwebqq()); 23 | jsonObject.put("hash", WqqEncryptor.hash(String.valueOf(session.getUin()), session.getPtwebqq())); 24 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 25 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 26 | return new AsyncRequest(RequestUrls.get_group_name_list_mask2.url, HttpMethod.post, params, headers); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetLoginQRcode.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.AsyncRequest; 5 | import com.thankjava.toolkit3d.bean.http.AsyncResponse; 6 | import com.thankjava.toolkit3d.bean.http.HttpMethod; 7 | import com.thankjava.toolkit3d.bean.http.Parameters; 8 | import com.thankjava.wqq.consts.ConstsParams; 9 | import com.thankjava.wqq.consts.RequestUrls; 10 | import com.thankjava.wqq.core.request.aop.DoRequest; 11 | import com.thankjava.wqq.extend.CallBackListener; 12 | 13 | /** 14 | * 获取登录二维码 15 | *Function: GetLoginQRcode
16 | *Description:
17 | * 18 | * @author acexy@thankjava.com 19 | * @version 1.0 20 | * @date 2016年12月13日 下午5:53:53 21 | */ 22 | public class GetLoginQRcode extends BaseHttpService { 23 | 24 | 25 | @Override 26 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 27 | public AsyncResponse doRequest(CallBackListener listener) { 28 | return null; 29 | } 30 | 31 | @Override 32 | protected AsyncRequest buildRequestParams() { 33 | Parameters params = new Parameters("appid", String.valueOf(ConstsParams.APP_ID)); 34 | params.append("e", "0"); 35 | params.append("l", "M"); 36 | params.append("s", "5"); 37 | params.append("d", "72"); 38 | params.append("v", "4"); 39 | params.append("4", String.valueOf(Math.random())); 40 | 41 | return new AsyncRequest( 42 | RequestUrls.get_login_qrcode.url, 43 | HttpMethod.get, 44 | params 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetOnlineBuddies2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.ConstsParams; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.core.request.aop.DoRequest; 8 | import com.thankjava.wqq.extend.CallBackListener; 9 | 10 | public class GetOnlineBuddies2 extends BaseHttpService { 11 | 12 | @Override 13 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 14 | public AsyncResponse doRequest(CallBackListener listener) { 15 | return null; 16 | } 17 | 18 | @Override 19 | protected AsyncRequest buildRequestParams() { 20 | 21 | Parameters params = new Parameters("vfwebqq", session.getVfwebqq()); 22 | params.append("clientid", String.valueOf(ConstsParams.CLIENT_ID)); 23 | params.append("psessionid", session.getPsessionid()); 24 | params.append("t", String.valueOf(System.currentTimeMillis() / 1000)); 25 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 26 | return new AsyncRequest( 27 | RequestUrls.get_online_buddies2.url, 28 | HttpMethod.get, 29 | params, 30 | headers 31 | ); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetRecentList2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit.bean.aop.anno.Before; 5 | import com.thankjava.toolkit3d.bean.http.*; 6 | import com.thankjava.wqq.consts.ConstsParams; 7 | import com.thankjava.wqq.consts.RequestUrls; 8 | import com.thankjava.wqq.core.request.aop.DoRequest; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | 11 | public class GetRecentList2 extends BaseHttpService { 12 | 13 | @Override 14 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 15 | public AsyncResponse doRequest(CallBackListener listener) { 16 | return null; 17 | } 18 | 19 | @Override 20 | protected AsyncRequest buildRequestParams() { 21 | 22 | JSONObject jsonObject = new JSONObject(); 23 | jsonObject.put("vfwebqq", session.getVfwebqq()); 24 | jsonObject.put("clientid", ConstsParams.CLIENT_ID); 25 | jsonObject.put("psessionid", session.getPsessionid()); 26 | 27 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 28 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 29 | return new AsyncRequest( 30 | RequestUrls.get_recent_list2.url, 31 | HttpMethod.post, 32 | params, 33 | headers 34 | ); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetSelfInfo2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.RequestUrls; 6 | import com.thankjava.wqq.core.request.aop.DoRequest; 7 | import com.thankjava.wqq.extend.CallBackListener; 8 | 9 | public class GetSelfInfo2 extends BaseHttpService { 10 | 11 | @Override 12 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 13 | public AsyncResponse doRequest(CallBackListener listener) { 14 | return null; 15 | } 16 | 17 | @Override 18 | protected AsyncRequest buildRequestParams() { 19 | Parameters params = new Parameters("t", String.valueOf(System.currentTimeMillis() / 1000)); 20 | 21 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 22 | return new AsyncRequest( 23 | RequestUrls.get_self_info2.url, 24 | HttpMethod.get, 25 | params, 26 | headers 27 | ); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetUserFriends2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit.bean.aop.anno.Before; 5 | import com.thankjava.toolkit3d.bean.http.*; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.core.request.aop.DoRequest; 8 | import com.thankjava.wqq.extend.CallBackListener; 9 | import com.thankjava.wqq.util.WqqEncryptor; 10 | 11 | public class GetUserFriends2 extends BaseHttpService { 12 | 13 | @Override 14 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 15 | public AsyncResponse doRequest(CallBackListener listener) { 16 | return null; 17 | } 18 | 19 | @Override 20 | protected AsyncRequest buildRequestParams() { 21 | JSONObject jsonObject = new JSONObject(); 22 | jsonObject.put("vfwebqq", session.getVfwebqq()); 23 | jsonObject.put("hash", WqqEncryptor.hash(String.valueOf(session.getUin()), session.getPtwebqq())); 24 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 25 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 26 | return new AsyncRequest(RequestUrls.get_user_friends2.url, HttpMethod.post, params, headers); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/GetVfWebqq.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.thankjava.toolkit.bean.aop.anno.Before; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.RequestUrls; 6 | import com.thankjava.wqq.core.request.aop.DoRequest; 7 | import com.thankjava.wqq.extend.CallBackListener; 8 | 9 | public class GetVfWebqq extends BaseHttpService { 10 | 11 | @Override 12 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 13 | public AsyncResponse doRequest(CallBackListener listener) { 14 | return null; 15 | } 16 | 17 | @Override 18 | protected AsyncRequest buildRequestParams() { 19 | Parameters params = new Parameters("ptwebqq", session.getPtwebqq()); 20 | params.append("clientid", "53999199"); 21 | params.append("psessionid", ""); 22 | params.append("t", String.valueOf(System.currentTimeMillis() / 1000)); 23 | Headers header = new Headers("Referer", RequestUrls.referer_getvfwebqq.url); 24 | return new AsyncRequest(RequestUrls.getvfwebqq.url, HttpMethod.get, params, header); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/Login2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit.bean.aop.anno.Before; 5 | import com.thankjava.toolkit3d.bean.http.*; 6 | import com.thankjava.wqq.consts.ConstsParams; 7 | import com.thankjava.wqq.consts.RequestUrls; 8 | import com.thankjava.wqq.core.request.aop.DoRequest; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | 11 | public class Login2 extends BaseHttpService { 12 | 13 | @Override 14 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 15 | public AsyncResponse doRequest(CallBackListener listener) { 16 | return null; 17 | } 18 | 19 | @Override 20 | protected AsyncRequest buildRequestParams() { 21 | JSONObject jsonObject = new JSONObject(); 22 | jsonObject.put("ptwebqq", session.getPtwebqq()); 23 | jsonObject.put("clientid", ConstsParams.CLIENT_ID); 24 | jsonObject.put("psessionid", ""); 25 | jsonObject.put("status", "online"); 26 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 27 | Headers headers = new Headers("Referer", RequestUrls.referer_common.url); 28 | return new AsyncRequest(RequestUrls.login2.url, HttpMethod.post, params, headers); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/Poll2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit.bean.aop.anno.Before; 5 | import com.thankjava.toolkit3d.bean.http.*; 6 | import com.thankjava.wqq.consts.ConstsParams; 7 | import com.thankjava.wqq.consts.RequestUrls; 8 | import com.thankjava.wqq.core.request.aop.DoRequest; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | 11 | public class Poll2 extends BaseHttpService { 12 | 13 | @Override 14 | @Before(cutClass = DoRequest.class, cutMethod = "doRequest") 15 | public AsyncResponse doRequest(CallBackListener listener) { 16 | return null; 17 | } 18 | 19 | @Override 20 | protected AsyncRequest buildRequestParams() { 21 | JSONObject jsonObject = new JSONObject(); 22 | jsonObject.put("ptwebqq", session.getPtwebqq()); 23 | jsonObject.put("clientid", ConstsParams.CLIENT_ID); 24 | jsonObject.put("psessionid", session.getPsessionid()); 25 | jsonObject.put("key", ""); 26 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 27 | Headers headers = new Headers("Referer", RequestUrls.referer_about_msg.url); 28 | return new AsyncRequest(RequestUrls.poll2.url, HttpMethod.post, params, headers); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/SendBuddyMsg2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.ConstsParams; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.entity.msg.SendMsg; 8 | import com.thankjava.wqq.extend.ActionListener; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | 11 | public class SendBuddyMsg2 extends BaseHttpService { 12 | 13 | private SendMsg sendMsg; 14 | 15 | public SendBuddyMsg2(SendMsg sendMsg) { 16 | this.sendMsg = sendMsg; 17 | } 18 | 19 | @Override 20 | public AsyncResponse doRequest(CallBackListener listener) { 21 | if (listener != null) { 22 | listener.onListener(new ActionListener(asyncHttpClient.syncRequestWithSession(buildRequestParams()))); 23 | return null; 24 | } else { 25 | return asyncHttpClient.syncRequestWithSession(buildRequestParams()); 26 | } 27 | } 28 | 29 | @Override 30 | protected AsyncRequest buildRequestParams() { 31 | JSONObject jsonObject = new JSONObject(); 32 | jsonObject.put("to", sendMsg.getTo()); // 33 | jsonObject.put("content", sendMsg.getContent().toSendMsg()); // 34 | jsonObject.put("face", 546); // 这个其实没啥用 35 | jsonObject.put("clientid", ConstsParams.CLIENT_ID); 36 | jsonObject.put("msg_id", msgId.incrementAndGet()); 37 | jsonObject.put("psessionid", session.getPsessionid()); 38 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 39 | Headers headers = new Headers("Referer", RequestUrls.referer_about_msg.url); 40 | return new AsyncRequest(RequestUrls.send_buddy_msg2.url, HttpMethod.post, params, headers); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/SendDiscuMsg2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.ConstsParams; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.entity.msg.SendMsg; 8 | import com.thankjava.wqq.extend.ActionListener; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | 11 | public class SendDiscuMsg2 extends BaseHttpService { 12 | 13 | private SendMsg sendMsg; 14 | 15 | public SendDiscuMsg2(SendMsg sendMsg) { 16 | this.sendMsg = sendMsg; 17 | } 18 | 19 | @Override 20 | public AsyncResponse doRequest(CallBackListener listener) { 21 | if (listener != null) { 22 | listener.onListener(new ActionListener(asyncHttpClient.syncRequestWithSession(buildRequestParams()))); 23 | return null; 24 | } else { 25 | return asyncHttpClient.syncRequestWithSession(buildRequestParams()); 26 | } 27 | } 28 | 29 | @Override 30 | protected AsyncRequest buildRequestParams() { 31 | JSONObject jsonObject = new JSONObject(); 32 | jsonObject.put("did", sendMsg.getDid()); // 33 | jsonObject.put("content", sendMsg.getContent().toSendMsg()); // 34 | jsonObject.put("face", 546); // 这个其实没啥用 35 | jsonObject.put("clientid", ConstsParams.CLIENT_ID); 36 | jsonObject.put("msg_id", msgId.incrementAndGet()); 37 | jsonObject.put("psessionid", session.getPsessionid()); 38 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 39 | Headers headers = new Headers("Referer", RequestUrls.referer_about_msg.url); 40 | return new AsyncRequest(RequestUrls.send_discu_msg2.url, HttpMethod.post, params, headers); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/core/request/api/SendQunMsg2.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.core.request.api; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.thankjava.toolkit3d.bean.http.*; 5 | import com.thankjava.wqq.consts.ConstsParams; 6 | import com.thankjava.wqq.consts.RequestUrls; 7 | import com.thankjava.wqq.entity.msg.SendMsg; 8 | import com.thankjava.wqq.extend.ActionListener; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | 11 | public class SendQunMsg2 extends BaseHttpService { 12 | 13 | private SendMsg sendMsg; 14 | 15 | public SendQunMsg2(SendMsg sendMsg) { 16 | this.sendMsg = sendMsg; 17 | } 18 | 19 | @Override 20 | public AsyncResponse doRequest(CallBackListener listener) { 21 | if (listener != null) { 22 | ActionListener actionListener = new ActionListener(asyncHttpClient.syncRequestWithSession(buildRequestParams())); 23 | listener.onListener(actionListener); 24 | return null; 25 | } else { 26 | return asyncHttpClient.syncRequestWithSession(buildRequestParams()); 27 | } 28 | } 29 | 30 | @Override 31 | protected AsyncRequest buildRequestParams() { 32 | JSONObject jsonObject = new JSONObject(); 33 | jsonObject.put("group_uin", sendMsg.getGroupUin()); // 34 | jsonObject.put("content", sendMsg.getContent().toSendMsg()); // 35 | jsonObject.put("face", 546); // 这个其实没啥用 36 | jsonObject.put("clientid", ConstsParams.CLIENT_ID); 37 | jsonObject.put("msg_id", msgId.incrementAndGet()); 38 | jsonObject.put("psessionid", session.getPsessionid()); 39 | Parameters params = new Parameters("r", jsonObject.toJSONString()); 40 | Headers headers = new Headers("Referer", RequestUrls.referer_about_msg.url); 41 | return new AsyncRequest(RequestUrls.send_qun_msg2.url, HttpMethod.post, params, headers); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/Session.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity; 2 | 3 | import com.thankjava.wqq.entity.wqq.DiscusList; 4 | import com.thankjava.wqq.entity.wqq.FriendsList; 5 | import com.thankjava.wqq.entity.wqq.GroupsList; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import com.thankjava.wqq.entity.enums.PullMsgStatus; 11 | import com.thankjava.wqq.entity.sys.MonitoringData; 12 | import com.thankjava.wqq.entity.wqq.DetailedInfo; 13 | 14 | public class Session { 15 | 16 | private final static Session session = new Session(); 17 | 18 | private Session() { 19 | } 20 | 21 | ; 22 | 23 | public static Session getSession() { 24 | return session; 25 | } 26 | 27 | // ---- 28 | private String checkSigUrl; 29 | private String ptwebqq; 30 | private String vfwebqq; 31 | private String psessionid; 32 | private long uin; 33 | 34 | // 好友列表 35 | private FriendsList friendsList; 36 | // 群列表 37 | private GroupsList groupsList; 38 | // 讨论组列表 39 | private DiscusList discusList; 40 | // 个人信息 41 | private DetailedInfo selfInfo; 42 | 43 | // --- 44 | private static MapTitle:
17 | *Description:
18 | * @param content 19 | */ 20 | Content(String content){ 21 | msg = new Object[]{content}; 22 | } 23 | 24 | /** 25 | * 发送一条信息 26 | *Title:
27 | *Description: Object 里面必须是 String | Integer, Integer 代表的是QQ默认表情的编号
28 | * @param msg 29 | */ 30 | public Content(Object[] msg){ 31 | this.msg = msg; 32 | } 33 | 34 | public Font getFont() { 35 | return font; 36 | } 37 | public void setFont(Font font) { 38 | this.font = font; 39 | } 40 | public Object[] getMsg() { 41 | return msg; 42 | } 43 | public void setMsg(Object[] msg) { 44 | this.msg = msg; 45 | } 46 | 47 | // ========= 48 | 49 | public String toSendMsg(){ 50 | JSONArray array = new JSONArray(); 51 | 52 | if(msg == null || msg.length == 0){ 53 | array.add(""); 54 | }else{ 55 | for (Object m : msg) { 56 | if(String.class == m.getClass()){ 57 | array.add(m); 58 | }else if (Integer.class == m.getClass() || int.class == m.getClass()){ 59 | JSONArray faceArray = new JSONArray(); 60 | faceArray.add("face"); 61 | faceArray.add((Integer)m); 62 | array.add(faceArray); 63 | } 64 | } 65 | } 66 | array.add(font.toFontArray()); 67 | return array.toJSONString(); 68 | } 69 | 70 | 71 | /** 72 | * 获取消息问题 73 | * @return 74 | */ 75 | public String getMsgContext(){ 76 | if(msg == null || msg.length == 0){ 77 | return ""; 78 | } 79 | StringBuffer sbf = new StringBuffer(); 80 | for (Object m : msg) { 81 | if(String.class == m.getClass()){ 82 | sbf.append(m); 83 | } 84 | } 85 | return sbf.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/msg/Font.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity.msg; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | public class Font{ 7 | 8 | private String color = "000000"; 9 | private String name = "宋体"; 10 | private int size = 10; 11 | 12 | public String getColor() { 13 | return color; 14 | } 15 | public void setColor(String color) { 16 | this.color = color; 17 | } 18 | public String getName() { 19 | return name; 20 | } 21 | public void setName(String name) { 22 | this.name = name; 23 | } 24 | public int getSize() { 25 | return size; 26 | } 27 | public void setSize(int size) { 28 | this.size = size; 29 | } 30 | 31 | public JSONArray toFontArray(){ 32 | JSONArray contentArray = new JSONArray(); 33 | contentArray.add("font"); 34 | JSONObject fontJson = new JSONObject(); 35 | fontJson.put("name", name); 36 | fontJson.put("color", color); 37 | fontJson.put("size", size); 38 | JSONArray styleArray = new JSONArray(); 39 | styleArray.add(0); 40 | styleArray.add(0); 41 | styleArray.add(0); 42 | fontJson.put("style", styleArray); 43 | contentArray.add(fontJson); 44 | return contentArray; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/msg/PollMsg.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity.msg; 2 | 3 | import com.thankjava.wqq.consts.MsgType; 4 | 5 | import com.alibaba.fastjson.annotation.JSONField; 6 | 7 | public class PollMsg { 8 | 9 | @JSONField(name = "poll_type") 10 | private MsgType msgType; 11 | private Value value; 12 | public Value getValue() { 13 | return value; 14 | } 15 | public MsgType getMsgType() { 16 | return msgType; 17 | } 18 | 19 | /** 20 | * 根据消息类型获取消息回复的目标id 21 | * @return 22 | */ 23 | public long getTargetFromId(){ 24 | switch (msgType) { 25 | case message: // 好友信息 26 | return value.getFromUin(); 27 | case discu_message: // 讨论组信息 28 | return value.getDid(); 29 | case group_message: // 群信息 30 | return value.getGroupCode(); 31 | } 32 | return 0; 33 | } 34 | 35 | /** 36 | * 获取消息所属发送用户的uin 37 | * @return 38 | */ 39 | public long getMsgUin(){ 40 | return value.getFromUin(); 41 | } 42 | 43 | /** 44 | * 获取对方发送的消息文本 45 | * @return 46 | */ 47 | public String getMsgContext() { 48 | if(value == null){ 49 | return ""; 50 | } 51 | if(value.getContent() == null){ 52 | return ""; 53 | } 54 | return value.getContent().getMsgContext(); 55 | } 56 | 57 | public void setMsgType(MsgType msgType) { 58 | this.msgType = msgType; 59 | } 60 | 61 | public void setValue(Value value) { 62 | this.value = value; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/msg/SendMsg.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity.msg; 2 | 3 | import com.thankjava.wqq.consts.MsgType; 4 | 5 | import com.alibaba.fastjson.annotation.JSONField; 6 | 7 | /** 8 | * 发送消息的组装对象 9 | */ 10 | public class SendMsg { 11 | 12 | private MsgType msgType; 13 | 14 | @JSONField(name = "group_uin") 15 | private Long groupUin; 16 | private Long to; 17 | private Long did; 18 | private Content content; 19 | 20 | /** 21 | * 发送消息构造 22 | * 23 | * @param to 根据 msgType 指定回复对象 24 | * @param msgType 25 | * @param content 回复内容 Content 26 | * @see Content 27 | */ 28 | public SendMsg(long to, MsgType msgType, Content content) { 29 | 30 | switch (msgType) { 31 | case message: // 好友信息 32 | this.to = to; 33 | break; 34 | case discu_message: // 讨论组信息 35 | this.did = to; 36 | break; 37 | case group_message: // 群信息 38 | this.groupUin = to; 39 | break; 40 | 41 | default: 42 | throw new RuntimeException("msgType can not be null"); 43 | } 44 | this.msgType = msgType; 45 | this.content = content; 46 | } 47 | 48 | public SendMsg(long to, MsgType msgType, String msg) { 49 | switch (msgType) { 50 | case message: // 好友信息 51 | this.to = to; 52 | break; 53 | case discu_message: // 讨论组信息 54 | this.did = to; 55 | break; 56 | case group_message: // 群信息 57 | this.groupUin = to; 58 | break; 59 | 60 | default: 61 | throw new RuntimeException("msgType can not be null"); 62 | } 63 | this.msgType = msgType; 64 | this.content = new Content(msg); 65 | } 66 | 67 | public SendMsg(PollMsg pollMsg, Content content) { 68 | long to = pollMsg.getTargetFromId(); 69 | msgType = pollMsg.getMsgType(); 70 | switch (msgType) { 71 | case message: // 好友信息 72 | this.to = to; 73 | break; 74 | case discu_message: // 讨论组信息 75 | this.did = to; 76 | break; 77 | case group_message: // 群信息 78 | this.groupUin = to; 79 | break; 80 | } 81 | this.content = content; 82 | } 83 | 84 | public SendMsg(PollMsg pollMsg, String msg) { 85 | long to = pollMsg.getTargetFromId(); 86 | msgType = pollMsg.getMsgType(); 87 | switch (msgType) { 88 | case message: // 好友信息 89 | this.to = to; 90 | break; 91 | case discu_message: // 讨论组信息 92 | this.did = to; 93 | break; 94 | case group_message: // 群信息 95 | this.groupUin = to; 96 | break; 97 | } 98 | this.content = new Content(msg); 99 | } 100 | 101 | public MsgType getMsgType() { 102 | return msgType; 103 | } 104 | 105 | public Long getGroupUin() { 106 | return groupUin; 107 | } 108 | 109 | public Long getTo() { 110 | return to; 111 | } 112 | 113 | public Long getDid() { 114 | return did; 115 | } 116 | 117 | public Content getContent() { 118 | return content; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/msg/Value.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity.msg; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | public class Value { 6 | 7 | private Content content; 8 | 9 | private Long did; 10 | 11 | @JSONField(name = "send_uin") 12 | private Long sendUin; 13 | 14 | @JSONField(name = "from_uin") 15 | private Long fromUin; 16 | 17 | @JSONField(name = "msg_id") 18 | private Long msgId; 19 | 20 | @JSONField(name = "msg_type") 21 | private Integer msgType; 22 | 23 | private Long time; 24 | 25 | @JSONField(name = "to_uin") 26 | private Long toUin; 27 | 28 | @JSONField(name = "group_code") 29 | private Long groupCode; 30 | 31 | public Long getDid() { 32 | return did; 33 | } 34 | public void setDid(Long did) { 35 | this.did = did; 36 | } 37 | public Long getSendUin() { 38 | return sendUin; 39 | } 40 | public void setSendUin(Long sendUin) { 41 | this.sendUin = sendUin; 42 | } 43 | public Long getFromUin() { 44 | return fromUin; 45 | } 46 | public void setFromUin(Long fromUin) { 47 | this.fromUin = fromUin; 48 | } 49 | public Long getMsgId() { 50 | return msgId; 51 | } 52 | public void setMsgId(Long msgId) { 53 | this.msgId = msgId; 54 | } 55 | public Integer getMsgType() { 56 | return msgType; 57 | } 58 | public void setMsgType(Integer msgType) { 59 | this.msgType = msgType; 60 | } 61 | public Long getTime() { 62 | return time; 63 | } 64 | public void setTime(Long time) { 65 | this.time = time; 66 | } 67 | public Long getToUin() { 68 | return toUin; 69 | } 70 | public void setToUin(Long toUin) { 71 | this.toUin = toUin; 72 | } 73 | public Content getContent() { 74 | return content; 75 | } 76 | public void setContent(Content content) { 77 | this.content = content; 78 | } 79 | public Long getGroupCode() { 80 | return groupCode; 81 | } 82 | public void setGroupCode(Long groupCode) { 83 | this.groupCode = groupCode; 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/sys/LoginResult.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity.sys; 2 | 3 | import com.thankjava.wqq.SmartQQClient; 4 | import com.thankjava.wqq.entity.enums.LoginResultStatus; 5 | 6 | public class LoginResult { 7 | 8 | private SmartQQClient client; 9 | private LoginResultStatus loginStatus; 10 | 11 | public LoginResult(SmartQQClient client, LoginResultStatus loginStatus) { 12 | this.client = client; 13 | this.loginStatus = loginStatus; 14 | } 15 | 16 | public SmartQQClient getClient() { 17 | return client; 18 | } 19 | public LoginResultStatus getLoginStatus() { 20 | return loginStatus; 21 | } 22 | 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/entity/sys/MonitoringData.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.entity.sys; 2 | 3 | import java.util.ArrayDeque; 4 | 5 | import com.thankjava.toolkit.core.utils.MathUtil; 6 | import com.thankjava.wqq.consts.ConfigParams; 7 | 8 | public class MonitoringData { 9 | 10 | private ArrayDequeFunction: getFriendInfo
17 | *Description:
18 | * @author acexy@thankjava.com 19 | * @date 2016年12月22日 上午11:40:50 20 | * @version 1.0 21 | * @param uin 22 | * @return 23 | */ 24 | public FriendInfo getFriendInfo(long uin){ 25 | return friendsInfo.get(uin); 26 | } 27 | 28 | public MapFunction: ActionListener
7 | *Description:
8 | * @author acexy@thankjava.com 9 | * @date 2016年12月20日 上午10:07:23 10 | * @version 1.0 11 | */ 12 | public class ActionListener { 13 | 14 | public ActionListener(){} 15 | 16 | public ActionListener(Object data){ 17 | this.data = data; 18 | } 19 | 20 | private Object data; 21 | 22 | public Object getData() { 23 | return data; 24 | } 25 | 26 | // public void setData(Object data) { 27 | // this.data = data; 28 | // } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/extend/CallBackListener.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.extend; 2 | 3 | /** 4 | * 声明CallBack函数 5 | *Function: CallBackListener
6 | *Description:
7 | * @author acexy@thankjava.com 8 | * @date 2016年12月20日 上午10:06:49 9 | * @version 1.0 10 | */ 11 | public interface CallBackListener { 12 | 13 | /** 14 | * 回调函数的执行 15 | *Function: onListener
16 | *Description:
17 | * @author acexy@thankjava.com 18 | * @date 2016年12月20日 上午10:07:07 19 | * @version 1.0 20 | * @param actionListener 21 | */ 22 | public void onListener(ActionListener actionListener); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/extend/NotifyListener.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.extend; 2 | 3 | import com.thankjava.wqq.SmartQQClient; 4 | import com.thankjava.wqq.entity.msg.PollMsg; 5 | 6 | /** 7 | * 声明通知处理函数 8 | */ 9 | public interface NotifyListener { 10 | 11 | 12 | /** 13 | * 消息通知处理定义 14 | * @param smartQQClient 15 | * @param pollMsg 16 | */ 17 | public void handler(SmartQQClient smartQQClient, PollMsg pollMsg); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/factory/ActionFactory.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.factory; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public class ActionFactory { 10 | 11 | private static final Logger logger = LoggerFactory.getLogger(ActionFactory.class); 12 | 13 | private ActionFactory(){} 14 | 15 | private static final MapFunction: JSON2Entity
21 | *Description:
22 | * 23 | * @author acexy@thankjava.com 24 | * @version 1.0 25 | * @date 2017年6月14日 下午2:27:34 26 | */ 27 | public class JSON2Entity { 28 | 29 | private static Logger logger = LoggerFactory.getLogger(JSON2Entity.class); 30 | 31 | /** 32 | * 解析所有好友信息 33 | *Function: userFriend2
34 | *Description:
35 | * 36 | * @param content 37 | * @return 38 | * @author acexy@thankjava.com 39 | * @date 2017年6月14日 下午2:33:31 40 | * @version 1.0 41 | */ 42 | public static FriendsList userFriends2(String content) { 43 | FriendsList friendsList = new FriendsList(); 44 | JSONObject userFriends2Json; 45 | try { 46 | userFriends2Json = FastJson.toJSONObject(content); 47 | JSONObject result = (JSONObject) userFriends2Json.get("result"); 48 | MapFunction: onlineStatus
117 | *Description:
118 | * 119 | * @param friendsList 120 | * @param content 121 | * @return 122 | * @author acexy@thankjava.com 123 | * @date 2017年6月14日 下午4:05:59 124 | * @version 1.0 125 | */ 126 | public static FriendsList onlineStatus(FriendsList friendsList, String content) { 127 | try { 128 | MapFunction: getDiscusList
152 | *Description:
153 | * 154 | * @param content 155 | * @return 156 | * @author acexy@thankjava.com 157 | * @date 2017年6月14日 下午4:06:12 158 | * @version 1.0 159 | */ 160 | public static DiscusList getDiscusList(String content) { 161 | DiscusList discusList = new DiscusList(); 162 | try { 163 | JSONObject discusListJson = FastJson.toJSONObject(content); 164 | JSONObject result = (JSONObject) discusListJson.get("result"); 165 | 166 | MapFunction: getGroupsList
186 | *Description:
187 | * 188 | * @param content 189 | * @return 190 | * @author acexy@thankjava.com 191 | * @date 2017年6月14日 下午4:08:47 192 | * @version 1.0 193 | */ 194 | public static GroupsList getGroupsList(String content) { 195 | GroupsList groupsList = new GroupsList(); 196 | try { 197 | JSONObject groupsListJson = FastJson.toJSONObject(content); 198 | JSONObject result = (JSONObject) groupsListJson.get("result"); 199 | 200 | MapFunction: getSelfInfo
229 | *Description:
230 | * 231 | * @param content 232 | * @return 233 | * @author acexy@thankjava.com 234 | * @date 2017年6月14日 下午4:11:42 235 | * @version 1.0 236 | */ 237 | public static DetailedInfo getSelfInfo(String content) { 238 | try { 239 | JSONObject discusListJson = FastJson.toJSONObject(content); 240 | JSONObject result = (JSONObject) discusListJson.get("result"); 241 | return FastJson.toObject(result.toJSONString(), DetailedInfo.class); 242 | } catch (Exception e) { 243 | logger.error("解析getSelfInfo返回数据失败", e); 244 | } 245 | return null; 246 | } 247 | 248 | /** 249 | * 消息拉取数据解析 250 | *Function: parseToPollMsg
251 | *Description:
252 | * 253 | * @param content 254 | * @return 255 | * @author acexy@thankjava.com 256 | * @date 2017年6月14日 下午4:21:38 257 | * @version 1.0 258 | */ 259 | public static PollMsg pollMsg(String content) { 260 | try { 261 | JSONObject contentJson = FastJson.toJSONObject(content); 262 | if (contentJson.getInteger("retcode") != 0) { 263 | return null; 264 | } 265 | // 处理数据 266 | JSONArray array = (JSONArray) contentJson.get("result"); 267 | JSONObject pollMsgJson = (JSONObject) array.get(0); 268 | 269 | 270 | JSONObject valueJson = pollMsgJson.getJSONObject("value"); 271 | 272 | // 由于腾讯协议bug造成群消息&讨论组消息,会将自己发送的消息poll识别成别人的消息 273 | if (Session.getSession().getUin() == valueJson.getLongValue("send_uin")) { 274 | return null; 275 | } 276 | 277 | // 由于腾讯协议变更,自己发送的消息会触发poll事件 278 | if (Session.getSession().getUin() == valueJson.getLong("from_uin")) { 279 | return null; 280 | } 281 | 282 | JSONArray contentArray = valueJson.getJSONArray("content"); 283 | 284 | valueJson.remove("content"); 285 | 286 | PollMsg pollMsg = FastJson.toObject(array.get(0).toString(), PollMsg.class); 287 | Value value = pollMsg.getValue(); 288 | int contentSize = contentArray.size(); 289 | Content contentMsg = new Content(); 290 | value.setContent(contentMsg); 291 | Font font = FastJson.toObject(contentArray.getJSONArray(0).get(1).toString(), Font.class); 292 | contentMsg.setFont(font); 293 | 294 | Object[] objs = new Object[contentSize - 1]; 295 | Object obj; 296 | for (int index = 1; index < contentSize; index++) { 297 | obj = contentArray.get(index); 298 | if (JSONArray.class == obj.getClass()) { 299 | objs[index - 1] = Integer.parseInt(((JSONArray) obj).get(1).toString()); 300 | } else if (String.class == obj.getClass()) { 301 | objs[index - 1] = obj.toString(); 302 | } 303 | } 304 | contentMsg.setMsg(objs); 305 | return pollMsg; 306 | } catch (Exception e) { 307 | return null; 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/util/RegexUtil.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.util; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | import com.thankjava.wqq.consts.DataResRegx; 7 | 8 | public class RegexUtil { 9 | 10 | /** 11 | * 使用正则去匹配返回的数据 12 | * 13 | * @param content 14 | * @param regx 15 | * @return 16 | */ 17 | public static String[] doRegex(String content, DataResRegx regx) { 18 | Pattern pattern = Pattern.compile(regx.regx); 19 | Matcher matcher = pattern.matcher(content); 20 | if (matcher.find()) { 21 | String[] values = new String[matcher.groupCount()]; 22 | for (int i = 1; i < matcher.groupCount(); i++) { 23 | values[i - 1] = matcher.group(i); 24 | } 25 | return values; 26 | } 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/thankjava/wqq/util/WqqEncryptor.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.util; 2 | 3 | import com.thankjava.toolkit.core.utils.SourceLoaderUtil; 4 | import com.thankjava.wqq.core.action.LoginAction; 5 | import org.apache.commons.io.IOUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.script.Invocable; 10 | import javax.script.ScriptEngine; 11 | import javax.script.ScriptEngineManager; 12 | 13 | public class WqqEncryptor { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(LoginAction.class); 16 | 17 | /** 18 | * 计算一些接口的hash值 19 | * Function: hash 20 | *Description:
21 | * 22 | * @param uin 23 | * @param ptwebqq 24 | * @return 25 | * @author acexy@thankjava.com 26 | * @date 2016年12月21日 下午6:25:04 27 | * @version 1.0 28 | */ 29 | public static String hash(String uin, String ptwebqq) { 30 | 31 | try { 32 | String jsSource = IOUtils.toString(SourceLoaderUtil.getResourceAsInputStream("hash.js"), "utf-8"); 33 | ScriptEngineManager scriptEMgr = new ScriptEngineManager(); 34 | ScriptEngine engine = scriptEMgr.getEngineByMimeType("application/javascript"); 35 | engine.eval(jsSource); 36 | Invocable invocable = (Invocable) engine; 37 | // 调用js 38 | return (String) invocable.invokeFunction("hash", uin, ptwebqq); 39 | } catch (Exception e) { 40 | logger.error("计算hash值异常", e); 41 | } 42 | 43 | return null; 44 | } 45 | 46 | /** 47 | * 检查二维码时,用于计算ptqrtoken 48 | *Function: hashForCheckQrStatus
49 | *Description:
50 | * 51 | * @param str 52 | * @return 53 | * @author acexy@thankjava.com 54 | * @date 2017年2月13日 下午6:11:06 55 | * @version 1.0 56 | */ 57 | public static String hashForCheckQrStatus(String str) { 58 | long hash = 0; 59 | for (int i = 0, length = str.length(); i < length; i++) { 60 | hash += hash * 32 + str.charAt(i); 61 | } 62 | return String.valueOf(2147483647 & hash); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/hash.js: -------------------------------------------------------------------------------- 1 | hash = function(x, K) { 2 | x += ""; 3 | for (var N = [], T = 0; T < K.length; T++) 4 | N[T % 4] ^= K.charCodeAt(T); 5 | var U = [ "EC", "OK" ], V = []; 6 | V[0] = x >> 24 & 255 ^ U[0].charCodeAt(0); 7 | V[1] = x >> 16 & 255 ^ U[0].charCodeAt(1); 8 | V[2] = x >> 8 & 255 ^ U[1].charCodeAt(0); 9 | V[3] = x & 255 ^ U[1].charCodeAt(1); 10 | U = []; 11 | for (T = 0; T < 8; T++) 12 | U[T] = T % 2 == 0 ? N[T >> 1] : V[T >> 1]; 13 | N = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", 14 | "E", "F" ]; 15 | V = ""; 16 | for (T = 0; T < U.length; T++) { 17 | V += N[U[T] >> 4 & 15]; 18 | V += N[U[T] & 15] 19 | } 20 | return V 21 | } -------------------------------------------------------------------------------- /src/test/java/com/thankjava/wqq/test/qq/MessageListener.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.test.qq; 2 | 3 | import com.thankjava.wqq.SmartQQClient; 4 | import com.thankjava.wqq.entity.msg.PollMsg; 5 | import com.thankjava.wqq.entity.msg.SendMsg; 6 | import com.thankjava.wqq.extend.NotifyListener; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class MessageListener implements NotifyListener { 11 | 12 | private static final Logger logger = LoggerFactory.getLogger(MessageListener.class); 13 | 14 | @Override 15 | public void handler(SmartQQClient smartQQClient, PollMsg pollMsg) { 16 | 17 | // 获取到的消息内容 18 | logger.debug("received msg : " + pollMsg.getMsgContext()); 19 | switch (pollMsg.getMsgType()) { 20 | case message: 21 | smartQQClient.sendMsg(new SendMsg(pollMsg, "I Have Got Your Msg: `Friend`")); 22 | break; 23 | case group_message: 24 | smartQQClient.sendMsg(new SendMsg(pollMsg, "I Have Got Your Msg: `Group`")); 25 | break; 26 | case discu_message: 27 | smartQQClient.sendMsg(new SendMsg(pollMsg, "I Have Got Your Msg: `Discu`")); 28 | break; 29 | } 30 | } 31 | 32 | // sendMsg 接口能通过pollMsg得到msg的类型,然后自动回复该类型的msg 33 | // @Override 34 | // public void handler(SmartQQClient smartQQClient, PollMsg pollMsg) { 35 | // logger.debug("received msg : " + pollMsg.getMsgContext()); 36 | // smartQQClient.sendMsg(new SendMsg(pollMsg, "I Have Got Your Msg")); 37 | // } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/thankjava/wqq/test/qq/TestSmartQQ.java: -------------------------------------------------------------------------------- 1 | package com.thankjava.wqq.test.qq; 2 | 3 | import com.thankjava.toolkit3d.core.fastjson.FastJson; 4 | import com.thankjava.wqq.SmartQQClient; 5 | import com.thankjava.wqq.SmartQQClientBuilder; 6 | import com.thankjava.wqq.entity.enums.LoginResultStatus; 7 | import com.thankjava.wqq.entity.sys.LoginResult; 8 | import com.thankjava.wqq.extend.ActionListener; 9 | import com.thankjava.wqq.extend.CallBackListener; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import javax.imageio.ImageIO; 14 | import java.awt.image.BufferedImage; 15 | import java.io.File; 16 | 17 | /** 18 | * 新版本SmartQQClient测试代码 version >= 1.1.x 19 | * 20 | * @author acexy 21 | */ 22 | public class TestSmartQQ { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(TestSmartQQ.class); 25 | 26 | public static void main(String[] args) { 27 | 28 | /** 29 | * step 1 > 利用指定使用SmartQQClientBuilder指南来构建SmartQQClient实例 30 | */ 31 | SmartQQClientBuilder builder = SmartQQClientBuilder.custom( 32 | // 注册一个通知事件的处理器,它将在SmartQQClient获得到相关信息时被调用执行 33 | new MessageListener() 34 | ); 35 | 36 | 37 | /** 38 | * step 2 > 自定义可选参数(为方便查看可选方法,设置参数的函数均以set关键字命名开始) 39 | */ 40 | builder 41 | .setAutoGetInfoAfterLogin() // 设置登录成功后立即拉取一些信息 42 | .setExceptionRetryMaxTimes(3) // 设置如果请求异常重试3次 43 | .setAutoRefreshQrcode() // 设置若发现登录二维码过期则自动重新拉取 44 | .setOffLineListener(new CallBackListener() { // 注册一个离线通知 掉线后将被调用执行 45 | @Override 46 | public void onListener(ActionListener actionListener) { 47 | logger.info("登录的QQ已掉线无法继续使用(系统已经尝试自动处理)"); 48 | SmartQQClient smartQQClient = (SmartQQClient) actionListener.getData(); 49 | smartQQClient.shutdown(); 50 | } 51 | }) 52 | ; 53 | 54 | /** 55 | * step 3 > create SmartQQClient 实例 并进行登录 56 | */ 57 | 58 | // A: 声明一个获取到登录二维码的回调函数,将返回二维码的byte数组数据 59 | CallBackListener getQrListener = new CallBackListener() { 60 | 61 | // login 接口在得到登录二维码时会调用CallBackListener 62 | // 并且二维码byte[] 数据会通过ListenerAction.data返回 63 | 64 | @Override 65 | public void onListener(ActionListener actionListener) { 66 | 67 | try { 68 | // 将返回的byte[]数据io处理成一张png图片 69 | // 位于项目log/qrcode.png 70 | ImageIO.write((BufferedImage) actionListener.getData(), "png", new File("./log/qrcode.png")); 71 | logger.debug("获取登录二维码完成,手机QQ扫描 ./log/qrcode.png 位置的二维码图片"); 72 | } catch (Exception e) { 73 | logger.error("将byte[]写为图片失败", e); 74 | } 75 | 76 | } 77 | }; 78 | 79 | // B: 声明一个登录结果的函数回调,在登录成功或者失败或异常时进行回调触发 80 | CallBackListener loginListener = new CallBackListener() { 81 | 82 | // ListenerAction.data 返回登录结果 com.thankjava.wqq.entity.sys.LoginResult 83 | @Override 84 | public void onListener(ActionListener actionListener) { 85 | 86 | LoginResult loginResult = (LoginResult) actionListener.getData(); 87 | logger.info("登录结果: " + loginResult.getLoginStatus()); 88 | 89 | if (loginResult.getLoginStatus() == LoginResultStatus.success) { 90 | 91 | SmartQQClient smartQQClient = loginResult.getClient(); 92 | 93 | // TODO: 后续就可以利用smartQQClient调用API 94 | logger.info("获取到的好友列表信息: " + FastJson.toJSONString(smartQQClient.getFriendsList(true))); 95 | 96 | 97 | // 业务处理 98 | // TODO: 99 | 100 | } 101 | } 102 | }; 103 | 104 | // C: 进行登录,启动服务 105 | builder.createAndLogin(getQrListener, loginListener); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 |