├── 微信协议.docx
├── .gitignore
├── sort.php
├── Robot365.class.php
├── README.md
└── WebWeiXin.php
/微信协议.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lbbniu/WebWechat/HEAD/微信协议.docx
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | key.key
2 | cookie.cookie
3 | saved
4 | msg
5 | .DS_Store
6 | ~*
7 | .idea
--------------------------------------------------------------------------------
/sort.php:
--------------------------------------------------------------------------------
1 | $right)
45 | return;
46 | $temp = $arr[$left]; //temp中存的就是基准数
47 | $i = $left;
48 | $j = $right;
49 | while ($i != $j) {
50 | //顺序很重要,要先从右边开始找
51 | while ($arr[$j] >= $temp && $i < $j)
52 | $j--;
53 | //再找右边的
54 | while ($arr[$i] <= $temp && $i < $j)
55 | $i++;
56 | //交换两个数在数组中的位置
57 | if ($i < $j) {
58 | $t = $arr[$i];
59 | $arr[$i] = $arr[$j];
60 | $arr[$j] = $t;
61 | }
62 | }
63 | //最终将基准数归位
64 | $arr[$left] = $arr[$i];
65 | $arr[$i] = $temp;
66 | lbb_quicksort($arr, $left, $i - 1);//继续处理左边的,这里是一个递归的过程
67 | lbb_quicksort($arr, $i + 1, $right);//继续处理右边的 ,这里是一个递归的过程
68 | }
69 |
70 | $arr = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8];
71 | echo "输出排序前的结果:";
72 | foreach ($arr as $val)
73 | printf("%d ", $val);
74 | print("\n");
75 | lbb_quicksort($arr, 0, count($arr) - 1); //快速排序调用
76 |
77 | echo "输出排序后的结果:";
78 | foreach ($arr as $val)
79 | printf("%d ", $val);
80 | print("\n");
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Robot365.class.php:
--------------------------------------------------------------------------------
1 | url = 'http://k2vc.app/test.php';//"http://www.365rj.net/rjservlet?Command=talk_{$id}";
12 | $this->url = "http://www.365rj.net/rjservlet?Command=talk_{$id}";
13 | }
14 |
15 | public function search($content, $FromUserName)
16 | {
17 | if (strpos($content, '刘兵兵') !== false || strpos($content, 'lbbniu') !== false) {
18 | return '刘兵兵,本机器人开发者(项目地址:http://github.com/lbbniu/WebWechat),个人技术博客:http://www.lbbniu.com';
19 | }
20 |
21 | $xml = sprintf('
22 |
23 |
24 | %s
25 |
26 |
27 | %s
28 | ',
29 | 'gh_fe5b36b12c61',
30 | $FromUserName,
31 | time(),
32 | $content,
33 | time() . mt_rand(1000, 9999));
34 | $data = $this->_post($this->url, $xml);
35 | //var_dump($data);
36 | $array = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
37 | //var_dump($array);
38 | if (empty($array) || !isset($array['Content']) || strpos($array['Content'], '无法') !== false || strpos($array['Content'], '未找到') !== false)
39 | return '';
40 | return $array['Content'];
41 | }
42 |
43 | private function _post($url, $param, $post_file = false)
44 | {
45 | $oCurl = curl_init();
46 | if (stripos($url, "https://") !== FALSE) {
47 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
48 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
49 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
50 | }
51 | if (PHP_VERSION_ID >= 50500 && class_exists('\CURLFile')) {
52 | $is_curlFile = true;
53 | } else {
54 | $is_curlFile = false;
55 | if (defined('CURLOPT_SAFE_UPLOAD')) {
56 | curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false);
57 | }
58 | }
59 | if (is_string($param)) {
60 | $strPOST = $param;
61 | } elseif ($post_file) {
62 | if ($is_curlFile) {
63 | foreach ($param as $key => $val) {
64 | if (substr($val, 0, 1) == '@') {
65 | $param[$key] = new \CURLFile(realpath(substr($val, 1)));
66 | }
67 | }
68 | }
69 | $strPOST = $param;
70 | } else {
71 | $aPOST = array();
72 | foreach ($param as $key => $val) {
73 | $aPOST[] = $key . "=" . urlencode($val);
74 | }
75 | $strPOST = implode("&", $aPOST);
76 | }
77 | $header = [
78 | 'Content-Type: text/html; charset=UTF-8',
79 | 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/2008052906 Firefox/3.0',
80 | ];
81 | curl_setopt($oCurl, CURLOPT_HEADER, 0);
82 | //curl_setopt ($oCurl, CURLOPT_PROXY, 'http://127.0.0.1:8888');
83 | curl_setopt($oCurl, CURLOPT_HTTPHEADER, $header);
84 | curl_setopt($oCurl, CURLOPT_URL, $url);
85 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
86 | curl_setopt($oCurl, CURLOPT_POST, true);
87 | curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST);
88 | //curl_setopt($oCurl, CURLOPT_COOKIEFILE, $this->cookie);
89 | //curl_setopt($oCurl, CURLOPT_COOKIEJAR, $this->cookie);
90 | $sContent = curl_exec($oCurl);
91 | $aStatus = curl_getinfo($oCurl);
92 | curl_close($oCurl);
93 | //print_r($aStatus);
94 | if (intval($aStatus["http_code"]) == 200) {
95 | return $sContent;
96 | } else {
97 | return false;
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebWechat
2 | 网页微信PHP登录的实现
3 | 依赖扩展
4 | ---
5 | * curl
6 | * pcntl 或者 pthreads
7 | * gd
8 |
9 | # 微信网页版协议分析
10 |
11 | ## 微信网页版协议分析(1)-登录
12 |
13 | 最近研究了微信网页版的实现原理,接下来会通过一系列的文章来总结这次研究的结果,最终会通过PHP代码实现一个简单的微信机器人程序。
14 |
15 | ### 1.获取uuid(get)
16 | https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1476606163580
17 |
18 | #### 参数:
19 | ```
20 | appid:固定为wx782c26e4c19acffb
21 | redirect_rui:https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage经过url编码
22 | fun:固定值new
23 | lang:语言类型,中国zh_CN
24 | _:当前的unix时间戳
25 | ```
26 | #### 返回数据格式:
27 | ```
28 | window.QRLogin.code = 200; window.QRLogin.uuid = "gf5Gk61zEA==";
29 | window.QRLogin.uuid的值就是我们需要的uuid
30 | ```
31 | ### 2.获取二维码(get)
32 | ```
33 | https://login.weixin.qq.com/qrcode/gf5Gk61zEA==
34 | 固定格式:https://login.weixin.qq.com/qrcode/ 拼接上第一步的带的uuid
35 | ```
36 | #### 二维码中实际内容如下地址:
37 | ```
38 | https://login.weixin.qq.com/l/gf5Gk61zEA==
39 | 即:https://login.weixin.qq.com/l/ 加上那个uuid
40 | ```
41 | ### 3.等待登录扫描(get轮询):
42 | https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=gf5Gk61zEA==&tip=0&r=862560455&_=1476606163582
43 |
44 | #### 参数:
45 | ```
46 | loginicon:true
47 | uuid:第一步得到的uuid
48 | tip:0
49 | r:862560455 非必须参数
50 | _:同上为unix时间戳
51 | ```
52 | #### 返回数据格式:
53 | ```
54 | window.code=408;//登录超时code为408
55 | window.code=201;window.userAvatar = 'data:img/jpg;base64';//扫描成功 201,userAvatar为用户头像
56 | window.code=200;//确认登录code 200, 还有下面的redirect_uri的获取cookie的连接
57 | window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AYfheMIH6tt9EmcZ0DxCKF4a@qrticket_0&uuid=YeGrrvqmHQ==&lang=zh_CN&scan=1476606728";
58 | ```
59 | ### 4.登录后获取cookie信息(get):
60 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AYfheMIH6tt9EmcZ0DxCKF4a@qrticket_0&uuid=YeGrrvqmHQ==&lang=zh_CN&scan=1476606728&fun=new&version=v2&lang=zh_CN
61 | 此连接是上一步确认登录后获取的连接。
62 | #### 返回数据格式(xml,还有登录后设置的cookie数据):
63 | ```xml
64 |
65 | 0
66 |
67 | @crypt_14ae1b12_b73ba2673448154847d7007a2de3c53b
68 | jSsRlGGPyY7U8det
69 | 566148615
70 | kUY4PSgKNy4eOlWI%2FwIBMVULe3KHPVyvDqw1%2B4DVVu9McVvE2d5fL7LFOfa4iYnk
71 | 1
72 |
73 | xml中skey、wxsid、wxuin、pass_ticket重要参数,
74 | 要在接来的请求中使用,需要记下来,还有返回的cookie信息,在接下来的请求中,都要去携带上才可以。
75 | ```
76 | ## 微信网页版协议分析(2)-获取信息
77 |
78 | ### 1.微信初始化请求(post):
79 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=862039733&lang=zh_CN&pass_ticket=kUY4PSgKNy4eOlWI%252FwIBMVULe3KHPVyvDqw1%252B4DVVu9McVvE2d5fL7LFOfa4iYnk
80 | 这个请求会获取联系人信息、公众号信息、自己的账号信息
81 | #### 请求头:
82 | Content-Type:application/json;charset=UTF-8
83 | #### 提交数据(json):
84 | ```
85 | {
86 | "BaseRequest": {
87 | "Uin": "566148615",
88 | "Sid": "jSsRlGGPyY7U8det",
89 | "Skey": "@crypt_14ae1b12_b73ba2673448154847d7007a2de3c53b",
90 | "DeviceID": "e119795675188164"
91 | }
92 | }
93 | Uin、Sid、Skey 登录后获取的xml中的数据
94 | DeviceID 是e + 随机数
95 | ```
96 | #### 返回数据(json,用户的好友信息):
97 | ```
98 | {
99 | "BaseResponse": {
100 | "Ret": 0,
101 | "ErrMsg": ""
102 | },
103 | "Count": 11,
104 | "ContactList": [...], //联系人信息、公众号信息、群
105 | "SyncKey": {
106 | "Count": 4,
107 | "List": [
108 | {
109 | "Key": 1,
110 | "Val": 635705559
111 | },
112 | ... //同步key值,下次请求时要写到上
113 | ]
114 | },
115 | "User": {
116 | "Uin": xxx,
117 | "UserName": xxx,
118 | "NickName": xxx,
119 | "HeadImgUrl": xxx,
120 | "RemarkName": "",
121 | "PYInitial": "",
122 | "PYQuanPin": "",
123 | "RemarkPYInitial": "",
124 | "RemarkPYQuanPin": "",
125 | "HideInputBarFlag": 0,
126 | "StarFriend": 0,
127 | "Sex": 1,
128 | "Signature": "Apt-get install B",
129 | "AppAccountFlag": 0,
130 | "VerifyFlag": 0,
131 | "ContactFlag": 0,
132 | "WebWxPluginSwitch": 0,
133 | "HeadImgFlag": 1,
134 | "SnsFlag": 17
135 | },
136 | "ChatSet": "xxx",
137 | "SKey": "@crypt_14ae1b12_b73ba2673448154847d7007a2de3c53b",
138 | "ClientVersion": 369302288,
139 | "SystemTime": 1476608977,
140 | "GrayScale": 1,
141 | "InviteStartCount": 40,
142 | "MPSubscribeMsgCount": 7,
143 | "ClickReportInterval": 600000
144 | }
145 | ```
146 |
147 | ### 2.webwxstatusnotify通知消息已读(post):
148 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?pass_ticket=ZDJfLCa0EAKrLn2CdD7MDl%252B54GwlW0IEiwYOsm6II%252F8W57y0pF1F8fqS%252B5z4INU5
149 | 客户端读取消息后要发起请求,告诉服务器消息已经读取,从而通知手机客户端
150 | #### 参数
151 | ```
152 | pass_ticket 登录请求返回的xml中的值
153 | ```
154 | #### 请求头:
155 | Content-Type:application/json;charset=UTF-8
156 | #### 提交数据(json):
157 | ```
158 | {
159 | "BaseRequest": {
160 | "Uin": 566148615,
161 | "Sid": "EKjoZCMRIvrY7NIP",
162 | "Skey": "@crypt_14ae1b12_f59314a579c67b15f838d09feb79c17f",
163 | "DeviceID": "e098855372553243"
164 | },
165 | "Code": 3,
166 | "FromUserName": 自己ID,
167 | "ToUserName": 自己ID,
168 | "ClientMsgId": 时间戳
169 | }
170 | ```
171 | #### 返回数据(json):
172 | ```
173 | {
174 | "BaseResponse": {
175 | "Ret": 0,
176 | "ErrMsg": ""
177 | },
178 | "MsgID": "1525762281689643050"
179 | }
180 | ```
181 |
182 | ### 3.获取联系人信息列表(get):
183 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket=ZDJfLCa0EAKrLn2CdD7MDl%252B54GwlW0IEiwYOsm6II%252F8W57y0pF1F8fqS%252B5z4INU5&r=1476608979549&seq=0&skey=@crypt_14ae1b12_f59314a579c67b15f838d09feb79c17f
184 | #### 参数
185 | ```
186 | pass_ticket 登录请求中返回xml中获取
187 | seq=0 固定值即可
188 | skey 初始化请求中获取
189 | ```
190 |
191 | #### 返回数据(json):
192 | ```
193 | {
194 | "BaseResponse": {
195 | "Ret": 0,
196 | "ErrMsg": ""
197 | },
198 | "MemberCount": 637,
199 | "MemberList": [
200 | {
201 | "Uin": 0,
202 | "UserName": xxx,
203 | "NickName": "LbbNiu",
204 | "HeadImgUrl": xxx,
205 | "ContactFlag": 3,
206 | "MemberCount": 0,
207 | "MemberList": [],
208 | "RemarkName": "",
209 | "HideInputBarFlag": 0,
210 | "Sex": 0,
211 | "Signature": "IT全才-LbbNiu",
212 | "VerifyFlag": 8,
213 | "OwnerUin": 0,
214 | "PYInitial": "LbbNiu",
215 | "PYQuanPin": "LbbNiu",
216 | "RemarkPYInitial": "",
217 | "RemarkPYQuanPin": "",
218 | "StarFriend": 0,
219 | "AppAccountFlag": 0,
220 | "Statues": 0,
221 | "AttrStatus": 0,
222 | "Province": "",
223 | "City": "",
224 | "Alias": "Urinxs",
225 | "SnsFlag": 0,
226 | "UniFriend": 0,
227 | "DisplayName": "",
228 | "ChatRoomId": 0,
229 | "KeyWord": "gh_",
230 | "EncryChatRoomId": ""
231 | }
232 | ....//联系人列表
233 | ],
234 | "Seq": 0
235 | }
236 | ```
237 |
238 | ### 4.webwxbatchgetcontact获取聊天会话列表信息(post):
239 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=1476608979648&pass_ticket=ZDJfLCa0EAKrLn2CdD7MDl%252B54GwlW0IEiwYOsm6II%252F8W57y0pF1F8fqS%252B5z4INU5
240 | #### 参数
241 | ```
242 | type=ex 固定值即可
243 | r 当前时间戳
244 | pass_ticket 登录请求中返回xml中获取
245 | ```
246 | #### 请求头:
247 | Content-Type:application/json;charset=UTF-8
248 | #### 提交数据(json):
249 | ```
250 | {
251 | "BaseRequest": {
252 | "Uin": 566148615,
253 | "Sid": "EKjoZCMRIvrY7NIP",
254 | "Skey": "@crypt_14ae1b12_f59314a579c67b15f838d09feb79c17f",
255 | "DeviceID": "e316575061818116"
256 | },
257 | "Count": 7,
258 | "List": [
259 | {
260 | "UserName": "@@e2da072e5beda58413f788fd2978b6f9fbde2ba337a71f02e1458958fcdb8371", //去要获取的群UserName
261 | "ChatRoomId": ""
262 | }…
263 | ]
264 | }
265 | ```
266 | #### 返回数据(json):
267 | ```
268 | {
269 | "BaseResponse": {
270 | "Ret": 0,
271 | "ErrMsg": ""
272 | },
273 | "Count": 7,
274 | "ContactList": [
275 | ]
276 | }
277 | ```
278 | ### 5.同步刷新(get轮询):
279 | https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1476611147442&skey=%40crypt_14ae1b12_f59314a579c67b15f838d09feb79c17f&sid=EKjoZCMRIvrY7NIP&uin=566148615&deviceid=e673682280871456&synckey=1_643606091%7C2_643606203%7C3_643606171%7C11_643605834%7C13_643590001%7C201_1476611120%7C1000_1476610779%7C1001_1476590971%7C1004_1475896795&_=1476611120940
280 | #### 参数
281 | ```
282 | r 时间戳
283 | skey 初始化信息中获取
284 | sid 登录请求中返回xml中获取
285 | uin 登录请求中返回xml中获取
286 | synckey 初始化信息中获取
287 | deviceid 设备id
288 | _ 时间戳
289 | ```
290 | #### 返回数据:
291 | ```
292 | window.synccheck={retcode:"0",selector:"2"}
293 | retcode:
294 | 0 正常
295 | 1100 失败/登出微信
296 | selector:
297 | 0 正常
298 | 2 新的消息
299 | 4 通过时发现,删除好友
300 | 6 删除时发现和对方通过好友验证
301 | 7 进入/离开聊天界面 (可能没有了)
302 | ```
303 | ### 6.获取消息(post,cookie):
304 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=2qui+CC4YTiiI2zk&skey=@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0&lang=zh_CN&pass_ticket=tM909DkHH8fXLR7hhwIgn9MqmSuCxx6%252BcdaA9qE203zxP7fMT%252FtxPlY7opbnnVE2
305 | #### 参数
306 | ```
307 | pass_ticket 登录请求中返回xml中获取
308 | r 时间戳
309 | skey 初始化信息中获取
310 | sid 登录请求中返回xml中获取
311 | lang=zh_CN 语言类型
312 | ```
313 | #### 请求头:
314 | Content-Type:application/json;charset=UTF-8
315 | #### 提交数据(json):
316 | ```
317 | {
318 | "BaseRequest":{
319 | "Uin":566148615,
320 | "Sid":"2qui+CC4YTiiI2zk",
321 | "Skey":"@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0",
322 | "DeviceID":"e151332185497293"
323 | },
324 | "SyncKey":{
325 | "Count":9,
326 | "List":[
327 | {"Key":1,"Val":643606091},
328 | {"Key":2,"Val":643606236},
329 | {"Key":3,"Val":643606171},
330 | {"Key":11,"Val":643606232},
331 | {"Key":13,"Val":643590001},
332 | {"Key":203,"Val":1476612126},
333 | {"Key":1000,"Val":1476610779},
334 | {"Key":1001,"Val":1476590971},
335 | {"Key":1004,"Val":1475896795}
336 | ]
337 | },
338 | "rr":856481324
339 | }
340 | ```
341 | #### 返回数据(json):
342 | ```
343 | {
344 | "BaseResponse": {
345 | "Ret": 0,
346 | "ErrMsg": ""
347 | },
348 | "AddMsgCount": 1,
349 | "AddMsgList": [
350 | {
351 | "MsgId": "8410419333108271670",
352 | "FromUserName": "@595d9f44c64e2f480baa0d6430ca58ef053a768daa6d7b3fcc4bece244bcbce3",
353 | "ToUserName": "@e5403f77c2193da671790c1a242d0b43ac6f570e5047993ef745d13d6376b57b",
354 | "MsgType": 51,
355 | "Content": "<msg>
<op id='5'>
<username>wxid_e602if1itlm821</username>
</op>
</msg>",
356 | "Status": 3,
357 | "ImgStatus": 1,
358 | "CreateTime": 1476612570,
359 | "VoiceLength": 0,
360 | "PlayLength": 0,
361 | "FileName": "",
362 | "FileSize": "",
363 | "MediaId": "",
364 | "Url": "",
365 | "AppMsgType": 0,
366 | "StatusNotifyCode": 5,
367 | "StatusNotifyUserName": "@e5403f77c2193da671790c1a242d0b43ac6f570e5047993ef745d13d6376b57b",
368 | "RecommendInfo": {
369 | "UserName": "",
370 | "NickName": "",
371 | "QQNum": 0,
372 | "Province": "",
373 | "City": "",
374 | "Content": "",
375 | "Signature": "",
376 | "Alias": "",
377 | "Scene": 0,
378 | "VerifyFlag": 0,
379 | "AttrStatus": 0,
380 | "Sex": 0,
381 | "Ticket": "",
382 | "OpCode": 0
383 | },
384 | "ForwardFlag": 0,
385 | "AppInfo": {
386 | "AppID": "",
387 | "Type": 0
388 | },
389 | "HasProductId": 0,
390 | "Ticket": "",
391 | "ImgHeight": 0,
392 | "ImgWidth": 0,
393 | "SubMsgType": 0,
394 | "NewMsgId": 8410419333108272000
395 | }
396 | ],
397 | "ModContactCount": 0,
398 | "ModContactList": [],
399 | "DelContactCount": 0,
400 | "DelContactList": [],
401 | "ModChatRoomMemberCount": 0,
402 | "ModChatRoomMemberList": [],
403 | "Profile": {
404 | "BitFlag": 0,
405 | "UserName": {
406 | "Buff": ""
407 | },
408 | "NickName": {
409 | "Buff": ""
410 | },
411 | "BindUin": 0,
412 | "BindEmail": {
413 | "Buff": ""
414 | },
415 | "BindMobile": {
416 | "Buff": ""
417 | },
418 | "Status": 0,
419 | "Sex": 0,
420 | "PersonalCard": 0,
421 | "Alias": "",
422 | "HeadImgUpdateFlag": 0,
423 | "HeadImgUrl": "",
424 | "Signature": ""
425 | },
426 | "ContinueFlag": 0,
427 | "SyncKey": {
428 | "Count": 10,
429 | "List": [
430 | {
431 | "Key": 1,
432 | "Val": 643606091
433 | }
434 | ..... //同步key
435 | ]
436 | },
437 | "SKey": "",
438 | "SyncCheckKey": {
439 | "Count": 10,
440 | "List": [
441 | {
442 | "Key": 1,
443 | "Val": 643606091
444 | }
445 | ..... //同步检测消息key
446 | ]
447 | }
448 | }
449 | ```
450 | ### 7.webwxstatreport(post):
451 |
452 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatreport?fun=new&lang=zh_CN
453 | 登录前和登录后都有,可能是用来统计用的,不影响其他获取信息,暂时不用对次请求进行处理
454 | #### 请求头:
455 | Content-Type:application/json;charset=UTF-8
456 |
457 |
458 |
459 |
460 |
461 | ## 微信网页版协议分析(3)-消息接口
462 |
463 | ### 1.发文字消息(post):
464 |
465 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=tM909DkHH8fXLR7hhwIgn9MqmSuCxx6%252BcdaA9qE203zxP7fMT%252FtxPlY7opbnnVE2
466 | #### 参数
467 | ```
468 | lang=zh_CN 语言
469 | pass_ticket 登录后返回信息中获取
470 | ```
471 | #### 请求头:
472 | Content-Type:application/json;charset=UTF-8
473 | #### 提交数据(json):
474 | ```
475 | {
476 | "BaseRequest": {
477 | "Uin": 566148615,
478 | "Sid": "2qui+CC4YTiiI2zk",
479 | "Skey": "@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0",
480 | "DeviceID": "e784205590563506"
481 | },
482 | "Msg": {
483 | "Type": 1,
484 | "Content": "@啊啊啊 啊啊啊啊啊啊",
485 | "FromUserName": "@595d9f44c64e2f480baa0d6430ca58ef053a768daa6d7b3fcc4bece244bcbce3",
486 | "ToUserName": "@@9e1c0ab62b5ab222835786c00192fb1e10c75f8082c245d360ac6a6bd2efe2df",
487 | "LocalID": "14766146756340722",
488 | "ClientMsgId": "14766146756340722"
489 | },
490 | "Scene": 0
491 | }
492 | Type: 1 文字消息,
493 | Content: 要发送的消息,
494 | FromUserName: 自己ID,
495 | ToUserName: 好友ID,
496 | LocalID: 与clientMsgId相同,
497 | ClientMsgId: 时间戳左移4位随后补上4位随机数
498 | ```
499 | #### 返回数据(json):
500 | ```
501 | {
502 | "BaseResponse": {
503 | "Ret": 0,
504 | "ErrMsg": ""
505 | },
506 | "MsgID": "4527210051674039705",
507 | "LocalID": "14766146756340722"
508 | }
509 | ```
510 | ### 2.发送表情消息(post):
511 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendemoticon?fun=sys&lang=zh_CN
512 | #### 请求头:
513 | Content-Type:application/json;charset=UTF-8
514 | #### 提交数据(json):
515 | ```
516 | {
517 | "BaseRequest": {
518 | "Uin": 566148615,
519 | "Sid": "2qui+CC4YTiiI2zk",
520 | "Skey": "@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0",
521 | "DeviceID": "e961665475339031"
522 | },
523 | "Msg": {
524 | "Type": 47,
525 | "EmojiFlag": 2,
526 | "EMoticonMd5": "44682e637b75a3f5d6747d61dbd23a15",
527 | "FromUserName": "@595d9f44c64e2f480baa0d6430ca58ef053a768daa6d7b3fcc4bece244bcbce3",
528 | "ToUserName": "@e5403f77c2193da671790c1a242d0b43ac6f570e5047993ef745d13d6376b57b",
529 | "LocalID": "14766150190560991",
530 | "ClientMsgId": "14766150190560991"
531 | },
532 | "Scene": 0
533 | }
534 | Type: 47 emoji消息,
535 | EmojiFlag: 2,
536 | MediaId: 表情上传后的媒体ID,
537 | FromUserName: 自己ID,
538 | ToUserName: 好友ID,
539 | LocalID: 与clientMsgId相同,
540 | ClientMsgId: 时间戳左移4位随后补上4位随机数
541 | ```
542 | #### 返回数据(json):
543 | ```
544 | {
545 | "BaseResponse": {
546 | "Ret": 0,
547 | "ErrMsg": ""
548 | },
549 | "MsgID": "1604548346807369725",
550 | "LocalID": "14766150190560991"
551 | }
552 | ```
553 | ### 3.发送图片消息:
554 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg?fun=async&f=json&lang=zh_CN
555 | #### 请求头:
556 | Content-Type:application/json;charset=UTF-8
557 | #### 提交数据(json):
558 | ```
559 | {
560 | "BaseRequest": {
561 | "Uin": 566148615,
562 | "Sid": "2qui+CC4YTiiI2zk",
563 | "Skey": "@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0",
564 | "DeviceID": "e374531581999650"
565 | },
566 | "Msg": {
567 | "Type": 3,
568 | "MediaId": "@crypt_4d268bd0",
569 | "FromUserName": "@595d9f44c64e2f480baa0d6430ca58ef053a768daa6d7b3fcc4bece244bcbce3",
570 | "ToUserName": "@e5403f77c2193da671790c1a242d0b43ac6f570e5047993ef745d13d6376b57b",
571 | "LocalID": "14766158484990338",
572 | "ClientMsgId": "14766158484990338"
573 | },
574 | "Scene": 0
575 | }
576 | Type: 3 媒体消息,
577 | MediaId: 表情上传后的媒体ID,
578 | FromUserName: 自己ID,
579 | ToUserName: 好友ID,
580 | LocalID: 与clientMsgId相同,
581 | ClientMsgId: 时间戳左移4位随后补上4位随机数
582 | ```
583 | #### 返回数据(json):
584 | ```
585 | {
586 | "BaseResponse": {
587 | "Ret": 0,
588 | "ErrMsg": ""
589 | },
590 | "MsgID": "6722400141243782346",
591 | "LocalID": "14766158484990338"
592 | }
593 | ```
594 | ### 4.发送文件消息(post):
595 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendappmsg?fun=async&f=json
596 | #### 请求头:
597 | Content-Type:application/json;charset=UTF-8
598 | #### 提交数据(json):
599 | ```
600 | {
601 | "BaseRequest": {
602 | "Uin": 566148615,
603 | "Sid": "NO0RlTCXvpBGaag7",
604 | "Skey": "@crypt_14ae1b12_3bdec7023595f68f9900b5d9d7d30b75",
605 | "DeviceID": "e984332143788024"
606 | },
607 | "Msg": {
608 | "Type": 6,
609 | "Content": "top10.csv61760@crypt_f672a24d_9d9c7d6a3b5ec058db1e6eb3d7c66917ede2e615535e9659365322de110ac41877d9a3b878ed82c0959f9b4602ef6e6d784ae853ab4e242d201cb19b9ee255a337f884eab998c5c43e7d433c01f14c91csv",
610 | "FromUserName": "@257b5aa7fb7b1672afb889be97197478cf618d9bab2b470cf767a61ae9f7f321",
611 | "ToUserName": "@@ab3e18c922f8ab162762f6da231236137ca52871419ec9cafc2421607e82e1ca",
612 | "LocalID": "14767614604200494",
613 | "ClientMsgId": "14767614604200494"
614 | },
615 | "Scene": 0
616 | }
617 | ```
618 | #### 返回数据(json):
619 | ```
620 | {
621 | "BaseResponse": {
622 | "Ret": 0,
623 | "ErrMsg": ""
624 | },
625 | "MsgID": "6923581840474342573",
626 | "LocalID": "14767614604200494"
627 | }
628 |
629 | ```
630 | ### 5.上传附件接口(post):
631 | https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json
632 | #### 请求头:
633 | Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryoWmrkW7MIGyBhlOt
634 | #### 提交参数(表单信息):
635 | ```
636 | id 文件id
637 | name 文件名
638 | type 文件类型
639 | lastModifieDate 最后修改时间
640 | size 文件大小
641 | mediatype 文件媒体类型 doc pic
642 | uploadmediarequest(json字符串)
643 | UploadType 上传类型 2
644 | BaseRequest
645 | Uin 登录中获得
646 | Sid 登录中获得
647 | Skey 登录中获得
648 | DeviceID 设备id
649 | ClientMediaId 文件本地消息id
650 | TotalLen 文件总大小
651 | StartPos 开始位置
652 | DataLen 数据长度
653 | MediaType 媒体类型 4
654 | FromUserName 发送用户id
655 | ToUserName 接受用户id
656 | FileMd5 文件md5
657 | webwx_data_ticket cookie中信息
658 | pass_ticket
659 | filename 文件流
660 | ```
661 | #### 返回数据(json):
662 | ```
663 | {
664 | "BaseResponse": {
665 | "Ret": 0,
666 | "ErrMsg": ""
667 | }
668 | ,
669 | "MediaId": "@crypt_6084141c_c73c046b750fd6e3e82d0aba4839b89b30851de7eace0446a4f73b524e3b43d41b24048e9c10cf7ba3387d04d63e6bd75bf8e3237acdddda5d4e3ede176617370573ebb2f820595076c55906a50a50a7",
670 | "StartPos": 25929,
671 | "CDNThumbImgHeight": 0,
672 | "CDNThumbImgWidth": 0
673 | }
674 | ```
675 | ### 6.图片接口:
676 | #### 获取消息图片(get):
677 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg?&MsgID=6829659093985341608&skey=%40crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0&type=slave
678 | ##### 参数
679 | ```
680 | MsgID: 消息ID
681 | type: slave 略缩图 or 为空时加载原图
682 | skey: xxx
683 | ```
684 | #### 获取联系人头像(get):
685 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgeticon?seq=637275253&username=@aa357b7fc1ccb2e77a2632a6251fb6de2c5dd1c0ec503f04b25cfd34c256956a&skey=@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0
686 | ##### 参数
687 | ```
688 | seq: 数字,可为空
689 | username: ID
690 | skey: xxx
691 | ```
692 | #### 获取群头像(get):
693 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=0&username=@@eca74ece82ad7947d262c59d53ab5d001e8776374141377cc025956b44bd1c03&skey=@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0
694 | ##### 参数
695 | ```
696 | seq: 数字,可为空
697 | username: 群ID
698 | skey: xxx
699 | ```
700 |
701 | ### 6.多媒体接口:
702 | #### 获取语音消息(get):
703 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvoice?msgid=6181125285738858128&skey=@crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0
704 | ##### 参数
705 | ```
706 | msgid: 消息ID
707 | skey: xxx
708 | ```
709 | #### 获取视频消息信息(get):
710 |
711 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetvideo?msgid=114757855447263581&skey=%40crypt_14ae1b12_1bb5e370393b8cd502919825fe9dbfc0
712 |
713 | ##### 参数
714 | ```
715 | msgid: 消息ID
716 | skey: xxx
717 | ```
718 | #### 获取其他文件:
719 | https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia?sender=@@aa4d90eee1984452cfacf8b510ce6547af58aa4fe34fe567787c716fd262d42c&mediaid=@crypt_3d1bb9c7_7f07116cc9b86e9d8237ad938293eee8a820acfa35e7bdaa41a03fc2fb17b60a4c6da7f967714259dec587505a1b55b10e61e301f733ec718167d8d355f8809257d3fbf382d775e2872e552957e894d52060d8766133214a58ea017e6416216c9f6f1d6056e5b22fe6c78a50efcf384969a7e1b96f348f581853b41be070c16ea4d510b8d10d747a3fc5a3909e1ed2deea43aa6db558c6dd58d453e8d6235d9f6a7c7bc0eb752fb5aee59568ab8de8ff38e0064b827765ae847d6a3503fba9a970006d18f0092a12811ccaec57c071bd&filename=51CTO%E4%B8%8B%E8%BD%BD-MySQL%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98%E4%B8%8E%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.pdf&fromuser=1443149207&pass_ticket=td%252BZEx1ANBEB8wz%252BxjYhotl3fChIto%252FyC4w%252FNHMopoqOP8Eya9%252FivEs2lsPEDqEj&webwx_data_ticket=gSc8PoV98Y4y98pOsP6hmcpj
720 | ##### 参数
721 | ```
722 | sender 发送者UserName
723 | mediaid 媒体id
724 | filename 文件名
725 | fromuser 接受者UserName
726 | pass_ticket 登录返回信息中
727 | webwx_data_ticket cookie中信息
728 | ```
729 |
730 |
731 |
732 | ### 7.账号类型
733 | #### 个人账号
734 | 以@开头,例如:@xxx
735 | #### 群聊
736 | 以@@开头,例如:@@xxx
737 | #### 公众号/服务号
738 | ```
739 | 以@开头,但其VerifyFlag & 8 != 0
740 | VerifyFlag:
741 | 一般公众号/服务号:8
742 | 微信自家的服务号:24
743 | 微信官方账号微信团队:56
744 | ```
745 | #### 特殊账号
746 | 像文件传输助手之类的账号,有特殊的ID,目前已知的有:
747 | filehelper, newsapp, fmessage, weibo, qqmail, fmessage, tmessage, qmessage, qqsync, floatbottle, lbsapp, shakeapp, medianote, qqfriend, readerapp, blogapp, facebookapp, masssendapp, meishiapp, feedsapp, voip, blogappweixin, weixin, brandsessionholder, weixinreminder, officialaccounts, notification_messages, wxitil, userexperience_alarm, notification_messages
748 |
749 | #### 消息类型一般格式:
750 | ```
751 | {
752 | "FromUserName": "",
753 | "ToUserName": "",
754 | "Content": "",
755 | "StatusNotifyUserName": "",
756 | "ImgWidth": 0,
757 | "PlayLength": 0,
758 | "RecommendInfo": {...},
759 | "StatusNotifyCode": 4,
760 | "NewMsgId": "",
761 | "Status": 3,
762 | "VoiceLength": 0,
763 | "ForwardFlag": 0,
764 | "AppMsgType": 0,
765 | "Ticket": "",
766 | "AppInfo": {...},
767 | "Url": "",
768 | "ImgStatus": 1,
769 | "MsgType": 1,
770 | "ImgHeight": 0,
771 | "MediaId": "",
772 | "MsgId": "",
773 | "FileName": "",
774 | "HasProductId": 0,
775 | "FileSize": "",
776 | "CreateTime": 1454602196,
777 | "SubMsgType": 0
778 | }
779 | ```
780 | #### MsgType说明:
781 | ```
782 | 1 文本消息
783 | 3 图片消息
784 | 34 语音消息
785 | 37 VERIFYMSG 好友验证消息
786 | 40 POSSIBLEFRIEND_MSG
787 | 42 共享名片
788 | 43 视频通话消息
789 | 47 动画表情
790 | 48 位置消息
791 | 49 分享链接
792 | 50 VOIPMSG
793 | 51 微信初始化消息
794 | 52 VOIPNOTIFY
795 | 53 VOIPINVITE
796 | 62 小视频
797 | 9999 SYSNOTICE
798 | 10000 系统消息
799 | 10002 撤回消息
800 | ```
801 | ##### 微信初始化消息
802 | ```
803 | MsgType: 51
804 | FromUserName: 自己ID
805 | ToUserName: 自己ID
806 | StatusNotifyUserName: 最近联系的联系人ID
807 | Content:
808 |
809 |
810 |
811 | // 最近联系的联系人
812 | filehelper,xxx@chatroom,wxid_xxx,xxx,...
813 |
814 |
815 |
816 |
817 | // 朋友圈
818 | MomentsUnreadMsgStatus
819 |
820 |
821 | 1454502365
822 |
823 |
824 |
825 |
826 | // 未读的功能账号消息,群发助手,漂流瓶等
827 |
828 |
829 |
830 | ```
831 | ##### 文本消息
832 | ```
833 | MsgType: 1
834 | FromUserName: 发送方ID
835 | ToUserName: 接收方ID
836 | Content: 消息内容
837 | 图片消息
838 | MsgType: 3
839 | FromUserName: 发送方ID
840 | ToUserName: 接收方ID
841 | MsgId: 用于获取图片
842 | Content:
843 |
844 |
845 |
846 |
847 | ```
848 | ##### 小视频消息
849 | ```
850 | MsgType: 62
851 | FromUserName: 发送方ID
852 | ToUserName: 接收方ID
853 | MsgId: 用于获取小视频
854 | Content:
855 |
856 |
857 |
858 |
859 | ```
860 | ##### 地理位置消息
861 | ```
862 | MsgType: 1
863 | FromUserName: 发送方ID
864 | ToUserName: 接收方ID
865 | Content: http://weixin.qq.com/cgi-bin/redirectforward?args=xxx
866 | // 属于文本消息,只不过内容是一个跳转到地图的链接
867 | ```
868 | ##### 名片消息
869 | ```
870 | MsgType: 42
871 | FromUserName: 发送方ID
872 | ToUserName: 接收方ID
873 | Content:
874 |
875 |
876 |
877 | RecommendInfo:
878 | {
879 | "UserName": "xxx", // ID
880 | "Province": "xxx",
881 | "City": "xxx",
882 | "Scene": 17,
883 | "QQNum": 0,
884 | "Content": "",
885 | "Alias": "xxx", // 微信号
886 | "OpCode": 0,
887 | "Signature": "",
888 | "Ticket": "",
889 | "Sex": 0, // 1:男, 2:女
890 | "NickName": "xxx", // 昵称
891 | "AttrStatus": 4293221,
892 | "VerifyFlag": 0
893 | }
894 | ```
895 | ##### 语音消息
896 | ```
897 | MsgType: 34
898 | FromUserName: 发送方ID
899 | ToUserName: 接收方ID
900 | MsgId: 用于获取语音
901 | Content:
902 |
903 |
904 |
905 | ```
906 | ##### 动画表情
907 | ```
908 | MsgType: 47
909 | FromUserName: 发送方ID
910 | ToUserName: 接收方ID
911 | Content:
912 |
913 |
927 |
928 |
929 |
930 | ```
931 | ##### 普通链接或应用分享消息
932 | ```
933 | MsgType: 49
934 | AppMsgType: 5
935 | FromUserName: 发送方ID
936 | ToUserName: 接收方ID
937 | Url: 链接地址
938 | FileName: 链接标题
939 | Content:
940 |
941 |
942 |
943 |
944 | 5
945 |
946 |
947 |
948 | ...
949 |
950 |
951 |
952 |
953 |
954 |
955 | ```
956 | ##### 音乐链接消息
957 | ```
958 | MsgType: 49
959 | AppMsgType: 3
960 | FromUserName: 发送方ID
961 | ToUserName: 接收方ID
962 | Url: 链接地址
963 | FileName: 音乐名
964 |
965 | AppInfo: // 分享链接的应用
966 | {
967 | Type: 0,
968 | AppID: wx485a97c844086dc9
969 | }
970 |
971 | Content:
972 |
973 |
974 |
975 |
976 |
977 | 3
978 | 0
979 |
980 |
981 |
982 |
983 | 0
984 |
985 |
986 |
987 | http://ws.stream.qqmusic.qq.com/C100003i9hMt1bgui0.m4a?vkey=6867EF99F3684&guid=ffffffffc104ea2964a111cf3ff3edaf&fromtag=46
988 |
989 |
990 | http://ws.stream.qqmusic.qq.com/C100003i9hMt1bgui0.m4a?vkey=6867EF99F3684&guid=ffffffffc104ea2964a111cf3ff3edaf&fromtag=46
991 |
992 |
993 | 0
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 | http://imgcache.qq.com/music/photo/album/63/180_albumpic_143163_0.jpg
1004 |
1005 |
1006 |
1007 |
1008 | 0
1009 |
1010 | 29
1011 | 摇一摇搜歌
1012 |
1013 |
1014 |
1015 | ```
1016 | ##### 群消息
1017 | ```
1018 | MsgType: 1
1019 | FromUserName: @@xxx
1020 | ToUserName: @xxx
1021 | Content:
1022 | @xxx:
xxx
1023 | ```
1024 | ##### 红包消息
1025 |
1026 | ```
1027 | MsgType: 49
1028 | AppMsgType: 2001
1029 | FromUserName: 发送方ID
1030 | ToUserName: 接收方ID
1031 | Content: 未知
1032 | 注:根据网页版的代码可以看到未来可能支持查看红包消息,但目前走的是系统消息,见下。
1033 | ```
1034 |
1035 | ##### 系统消息
1036 |
1037 | ```
1038 | MsgType: 10000
1039 | FromUserName: 发送方ID
1040 | ToUserName: 自己ID
1041 | Content:
1042 | "你已添加了 xxx ,现在可以开始聊天了。"
1043 | "如果陌生人主动添加你为朋友,请谨慎核实对方身份。"
1044 | "收到红包,请在手机上查看"
1045 | ```
1046 |
1047 |
1048 | ## 微信网页版协议分析(4)-好友操作
1049 |
1050 | ### 1.加好友和通过好友验证接口(post):
1051 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxverifyuser?r=1476779339614&lang=zh_CN&pass_ticket=td%252BZEx1ANBEB8wz%252BxjYhotl3fChIto%252FyC4w%252FNHMopoqOP8Eya9%252FivEs2lsPEDqEj
1052 | #### 请求头:
1053 | Content-Type:application/json;charset=UTF-8
1054 | #### 提交数据(json):
1055 | ```
1056 | 加好友提交的数据
1057 | {
1058 | "BaseRequest": {
1059 | "Uin": 1443149207,
1060 | "Sid": "HqU75NIXRJ6qqu8t",
1061 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1062 | "DeviceID": "e799452821014375"
1063 | },
1064 | "Opcode": 2,
1065 | "VerifyUserListSize": 1,
1066 | "VerifyUserList": [
1067 | {
1068 | "Value": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f", "VerifyUserTicket": ""
1069 | }
1070 | ],
1071 | "VerifyContent": "我是lbbniu.cn",
1072 | "SceneListCount": 1,
1073 | "SceneList": [
1074 | 33
1075 | ],
1076 | "skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c"
1077 | }
1078 | ```
1079 | 通过好友提交的数据,下面2中获取相关信息
1080 | ```
1081 | {
1082 | "BaseRequest": {
1083 | "Uin": 1443149207,
1084 | "Sid": "HqU75NIXRJ6qqu8t",
1085 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1086 | "DeviceID": "e606224692711308"
1087 | },
1088 | "Opcode": 3,
1089 | "VerifyUserListSize": 1,
1090 | "VerifyUserList": [
1091 | {
1092 | "Value": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f",
1093 | "VerifyUserTicket": "v2_9d2e1b01ee6de582a237f6369a6746c84d3a072b48bf36f152c5c947505e871189cafa09748e05bab27caf812a8f3a12d51da3dceda3cab5f52a406ec124a4a9@stranger"
1094 | }
1095 | ],
1096 | "VerifyContent": "",
1097 | "SceneListCount": 1,
1098 | "SceneList": [
1099 | 33
1100 | ],
1101 | "skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c"
1102 | }
1103 | ```
1104 | #### 返回数据(post):
1105 | ```
1106 | {
1107 | "BaseResponse":{
1108 | "Ret": 0,
1109 | "ErrMsg":""
1110 | }
1111 | }
1112 | ```
1113 | ### 2.好友验证消息体
1114 | ```
1115 |
1116 |
1117 |
1118 |
1119 | "RecommendInfo": {
1120 | "UserName": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f",
1121 | "NickName": "LbbNiu",
1122 | "QQNum": 0,
1123 | "Province": "",
1124 | "City": "æ",
1125 | "Content": "",
1126 | "Signature": "",
1127 | "Alias": "lbbniu",
1128 | "Scene": 14,
1129 | "VerifyFlag": 0,
1130 | "AttrStatus": 4305791,
1131 | "Sex": 1,
1132 | "Ticket": "v2_9d2e1b01ee6de582a237f6369a6746c84d3a072b48bf36f152c5c947505e871189cafa09748e05bab27caf812a8f3a12d51da3dceda3cab5f52a406ec124a4a9@stranger",
1133 | "OpCode": 2
1134 | },
1135 | ```
1136 | ### 3.创建群聊(post):
1137 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxcreatechatroom?r=1476618448303&lang=zh_CN
1138 | #### 请求头:
1139 | Content-Type:application/json;charset=UTF-8
1140 | #### 提交数据(json):
1141 | ```
1142 | {
1143 | "MemberCount": 3,
1144 | "MemberList": [
1145 | {
1146 | "UserName": "@fe20084fb8e3b488f49d3df2bf3ec2837daeb468abfe62a48ef39a6e407cab0e"
1147 | },
1148 | ....
1149 | ],
1150 | "Topic": "",
1151 | "BaseRequest": {
1152 | "Uin": 1443149207,
1153 | "Sid": "HqU75NIXRJ6qqu8t",
1154 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1155 | "DeviceID": "e280173725621646"
1156 | }
1157 | }
1158 | UserName 初始化接口中获取
1159 | ```
1160 | #### 返回数据(json):
1161 | ```
1162 | {
1163 | "BaseResponse": {
1164 | "Ret": 0,
1165 | "ErrMsg": "Everything is OK"
1166 | },
1167 | "Topic": "",
1168 | "PYInitial": "",
1169 | "QuanPin": "",
1170 | "MemberCount": 3,
1171 | "MemberList": [
1172 | {
1173 | "Uin": 0,
1174 | "UserName": "@fe20084fb8e3b488f49d3df2bf3ec2837daeb468abfe62a48ef39a6e407cab0e",
1175 | "NickName": "111111",
1176 | "AttrStatus": 0,
1177 | "PYInitial": "",
1178 | "PYQuanPin": "",
1179 | "RemarkPYInitial": "",
1180 | "RemarkPYQuanPin": "",
1181 | "MemberStatus": 0,
1182 | "DisplayName": "",
1183 | "KeyWord": ""
1184 | },
1185 | ...
1186 | ],
1187 | "ChatRoomName": "@@45340ba1e520285af196cef99d2f41a2bdb5cc568569efa178354f08eb863d00",
1188 | "BlackList": ""
1189 | }
1190 | ```
1191 | ### 4.群中踢出好友接口
1192 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom?fun=delmember
1193 | #### 请求头:
1194 | Content-Type:application/json;charset=UTF-8
1195 | #### 提交数据(json):
1196 | ```
1197 | {
1198 | "DelMemberList": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f",
1199 | "ChatRoomName": "@@45340ba1e520285af196cef99d2f41a2bdb5cc568569efa178354f08eb863d00",
1200 | "BaseRequest": {
1201 | "Uin": 1443149207,
1202 | "Sid": "HqU75NIXRJ6qqu8t",
1203 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1204 | "DeviceID": "e934789096108643"
1205 | }
1206 | }
1207 | DelMemberList 好友id
1208 | ChatRoomName 群组id
1209 | ```
1210 | #### 返回数据(json):
1211 | ```
1212 | {
1213 | "BaseResponse": {
1214 | "Ret": 0,
1215 | "ErrMsg": ""
1216 | }
1217 | ,
1218 | "MemberCount": 0,
1219 | "MemberList": []
1220 | }
1221 | ```
1222 | ### 5.邀请好友加入群
1223 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom?fun=addmember
1224 | #### 请求头
1225 | Content-Type:application/json;charset=UTF-8
1226 | #### 提交数据(json):
1227 | ```
1228 | {
1229 | "AddMemberList": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f",
1230 | "ChatRoomName": "@@45340ba1e520285af196cef99d2f41a2bdb5cc568569efa178354f08eb863d00",
1231 | "BaseRequest": {
1232 | "Uin": 1443149207,
1233 | "Sid": "HqU75NIXRJ6qqu8t",
1234 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1235 | "DeviceID": "e681854808526145"
1236 | }
1237 | }
1238 | AddMemberList 好友id列表
1239 | ChatRoomName 群组id
1240 | ```
1241 | #### 返回数据(json):
1242 | ```
1243 | {
1244 | "BaseResponse": {
1245 | "Ret": 0,
1246 | "ErrMsg": ""
1247 | },
1248 | "MemberCount": 1,
1249 | "MemberList": [
1250 | {
1251 | "Uin": 0,
1252 | "UserName": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f",
1253 | "NickName": "LbbNiu",
1254 | "AttrStatus": 0,
1255 | "PYInitial": "",
1256 | "PYQuanPin": "",
1257 | "RemarkPYInitial": "",
1258 | "RemarkPYQuanPin": "",
1259 | "MemberStatus": 0,
1260 | "DisplayName": "",
1261 | "KeyWord": ""
1262 | }
1263 | ...
1264 | ]
1265 | }
1266 | ```
1267 | ### 6.修改群名称
1268 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxupdatechatroom?fun=modtopic
1269 | #### 请求头
1270 | Content-Type:application/json;charset=UTF-8
1271 | #### 提交数据(json):
1272 | ```
1273 | {
1274 | "NewTopic": "lbbniu.com",
1275 | "ChatRoomName": "@@45340ba1e520285af196cef99d2f41a2bdb5cc568569efa178354f08eb863d00",
1276 | "BaseRequest": {
1277 | "Uin": 1443149207,
1278 | "Sid": "HqU75NIXRJ6qqu8t",
1279 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1280 | "DeviceID": "e326783138505913"
1281 | }
1282 | }
1283 | NewTopic 新群名称
1284 | ChatRoomName 群组id
1285 | ```
1286 | #### 返回数据(json):
1287 | ```
1288 | {
1289 | "BaseResponse": {
1290 | "Ret": 0,
1291 | "ErrMsg": ""
1292 | }
1293 | ,
1294 | "MemberCount": 0,
1295 | "MemberList": []
1296 | }
1297 | ```
1298 | ### 7.置顶聊天会话
1299 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog
1300 | #### 请求头
1301 | Content-Type:application/json;charset=UTF-8
1302 | #### 提交数据(json):
1303 | ```
1304 | {
1305 | "UserName": "@@45340ba1e520285af196cef99d2f41a2bdb5cc568569efa178354f08eb863d00",
1306 | "CmdId": 3,
1307 | "OP": 1,
1308 | "BaseRequest": {
1309 | "Uin": 1443149207,
1310 | "Sid": "HqU75NIXRJ6qqu8t",
1311 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1312 | "DeviceID": "e736741369963778"
1313 | }
1314 | }
1315 | ```
1316 | #### 返回数据(json):
1317 | ```
1318 | {
1319 | "BaseResponse": {
1320 | "Ret": 0,
1321 | "ErrMsg": ""
1322 | }
1323 | }
1324 | ```
1325 | ### 8.取消置顶聊天会话
1326 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog
1327 | #### 请求头
1328 | Content-Type:application/json;charset=UTF-8
1329 | #### 提交数据(json):
1330 | ```
1331 | {
1332 | "UserName": "@@45340ba1e520285af196cef99d2f41a2bdb5cc568569efa178354f08eb863d00",
1333 | "CmdId": 3,
1334 | "OP": 0,
1335 | "BaseRequest": {
1336 | "Uin": 1443149207,
1337 | "Sid": "HqU75NIXRJ6qqu8t",
1338 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1339 | "DeviceID": "e736741369963778"
1340 | }
1341 | }
1342 | ```
1343 | #### 返回数据(json):
1344 | ```
1345 | {
1346 | "BaseResponse": {
1347 | "Ret": 0,
1348 | "ErrMsg": ""
1349 | }
1350 | }
1351 | ```
1352 | ### 9.给好友添加备注
1353 | https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog
1354 | #### 请求头
1355 | Content-Type:application/json;charset=UTF-8
1356 | #### 提交数据(json):
1357 | ```
1358 | {
1359 | "UserName": "@2cbb226c459cc5203aa991942f41e19820f5ef3ccceb2dece598412f36406d1f",
1360 | "CmdId": 2,
1361 | "RemarkName": "lbbniu.com",
1362 | "BaseRequest": {
1363 | "Uin": 1443149207,
1364 | "Sid": "HqU75NIXRJ6qqu8t",
1365 | "Skey": "@crypt_d9da2d81_f3a62e80c16e61ad660dffd14e0ef72c",
1366 | "DeviceID": "e573447900268361"
1367 | }
1368 | }
1369 | ```
1370 | #### 返回数据(json):
1371 | ```
1372 | {
1373 | "BaseResponse": {
1374 | "Ret": 0,
1375 | "ErrMsg": ""
1376 | }
1377 | }
1378 | ```
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 |
1386 |
1387 |
1388 |
1389 |
1390 |
--------------------------------------------------------------------------------
/WebWeiXin.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | DEBUG}\n" .
20 | "[#] Uuid: {$this->uuid}\n" .
21 | "[#] Uin: {$this->uin}\n" .
22 | "[#] Sid: {$this->sid}\n" .
23 | "[#] Skey: {$this->skey}\n" .
24 | "[#] PassTicket: {$this->pass_ticket}\n" .
25 | "[#] DeviceId: {$this->deviceId}\n" .
26 | "[#] synckey: {$this->synckey}\n" .
27 | "[#] SyncKey: " . self::json_encode($this->SyncKey) . "\n" .
28 | "[#] syncHost: {$this->syncHost}\n" .
29 | "=========================\n";
30 | return $description;
31 | }
32 |
33 | private $NoReplyGroup = [
34 | '优才网全栈工程师',
35 | 'Laravel学院微信群',
36 | 'PHP和测试',
37 | '微明项目特工队',
38 | ];
39 |
40 | public function __construct()
41 | {
42 | $this->DEBUG = false;
43 | $this->uuid = '';
44 | $this->base_uri = 'https://wx.qq.com/cgi-bin/mmwebwx-bin';
45 | $this->redirect_uri = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage';//
46 | $this->uin = '';
47 | $this->sid = '';
48 | $this->skey = '';
49 | $this->pass_ticket = '';
50 | $this->deviceId = 'e' . substr(md5(uniqid()), 2, 15);
51 | $this->BaseRequest = [];
52 | $this->synckey = '';
53 | $this->SyncKey = [];
54 | $this->User = [];
55 | $this->MemberList = [];
56 | $this->ContactList = []; # 好友
57 | $this->GroupList = []; # 群
58 | $this->GroupMemeberList = []; # 群友
59 | $this->PublicUsersList = []; # 公众号/服务号
60 | $this->SpecialUsersList = []; # 特殊账号
61 | $this->autoReplyMode = false;
62 | $this->syncHost = '';
63 | $this->user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36';
64 | $this->interactive = false;
65 | $this->autoOpen = false;
66 | $this->saveFolder = getcwd() . "/saved/";
67 | $this->saveSubFolders = ['webwxgeticon' => 'icons', 'webwxgetheadimg' => 'headimgs',
68 | 'webwxgetmsgimg' => 'msgimgs', 'webwxgetvideo' => 'videos', 'webwxgetvoice' => 'voices',
69 | '_showQRCodeImg' => 'qrcodes'
70 | ];
71 | $this->appid = 'wx782c26e4c19acffb';
72 | $this->lang = 'zh_CN';
73 | $this->lastCheckTs = time();
74 | $this->memberCount = 0;
75 | $this->SpecialUsers = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail',
76 | 'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle', 'lbsapp', 'shakeapp',
77 | 'medianote', 'qqfriend', 'readerapp', 'blogapp', 'facebookapp', 'masssendapp',
78 | 'meishiapp', 'feedsapp', 'voip', 'blogappweixin', 'weixin', 'brandsessionholder',
79 | 'weixinreminder', 'wxid_novlwrv3lqwv11', 'gh_22b87fa7cb3c', 'officialaccounts',
80 | 'notification_messages', 'wxid_novlwrv3lqwv11', 'gh_22b87fa7cb3c', 'wxitil',
81 | 'userexperience_alarm', 'notification_messages'
82 | ];
83 | $this->TimeOut = 20; # 同步最短时间间隔(单位:秒)
84 | $this->media_count = -1;
85 |
86 | $this->cookie = "cookie.cookie";
87 | }
88 |
89 | public function loadConfig($config)
90 | {
91 | if (isset($config['DEBUG'])) {
92 | $this->DEBUG = $config['DEBUG'];
93 | }
94 | if (isset($config['autoReplyMode'])) {
95 | $this->autoReplyMode = $config['autoReplyMode'];
96 | }
97 | if (isset($config['user_agent'])) {
98 | $this->user_agent = $config['user_agent'];
99 | }
100 | if (isset($config['interactive'])) {
101 | $this->interactive = $config['interactive'];
102 | }
103 | if (isset($config['autoOpen'])) {
104 | $this->autoOpen = $config['autoOpen'];
105 | }
106 | }
107 |
108 | /**
109 | * 获取
110 | * @return bool
111 | */
112 | public function getUUID()
113 | {
114 | /**
115 | * https://login.weixin.qq.com/jslogin
116 | * https://login.wx.qq.com/jslogin
117 | * https://login.wx1.qq.com/jslogin
118 | * https://login.wx2.qq.com/jslogin
119 | */
120 | $url = 'https://login.wx.qq.com/jslogin';
121 | $params = [
122 | 'appid' => $this->appid,
123 | //'redirect_uri'=> $this->redirect_uri,
124 | 'fun' => 'new',
125 | 'lang' => $this->lang,
126 | '_' => time(),
127 | ];
128 | $data = $this->_get($url, $params);
129 | $regx = '/window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"/';
130 | if (preg_match($regx, $data, $pm)) {
131 | $code = $pm[1];
132 | $this->uuid = $pm[2];
133 | return $code == '200';
134 | }
135 | return false;
136 | }
137 |
138 | public function genQRCode()
139 | {
140 | if (PHP_OS != 'Darwin' && strpos(PHP_OS, 'win') !== false) {
141 | $this->_showQRCodeImg();
142 | } else {
143 | $this->_str2qr('https://login.weixin.qq.com/l/' . $this->uuid);
144 | }
145 | }
146 |
147 | public function _showQRCodeImg()
148 | {
149 | $url = 'https://login.weixin.qq.com/qrcode/' . $this->uuid;
150 | $params = [
151 | 't' => 'webwx',
152 | '_' => time()
153 | ];
154 |
155 | $data = $this->_post($url, $params, false);
156 | $QRCODE_PATH = $this->_saveFile('qrcode.jpg', $data, '_showQRCodeImg');
157 | //os.startfile(QRCODE_PATH)
158 | //TODO:没有完成
159 | system($QRCODE_PATH);
160 | }
161 |
162 | public function waitForLogin($tip = 1)
163 | {
164 | sleep($tip);
165 | $url = sprintf('https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s', $tip, $this->uuid, time());
166 | $data = $this->_get($url);
167 | preg_match('/window.code=(\d+);/', $data, $pm);
168 | $code = $pm[1];
169 |
170 | if ($code == '201')
171 | return true;
172 | elseif ($code == '200') {
173 | preg_match('/window.redirect_uri="(\S+?)";/', $data, $pm);
174 | $r_uri = $pm[1] . '&fun=new';
175 | $this->redirect_uri = $r_uri;
176 | $this->base_uri = substr($r_uri, 0, strrpos($r_uri, '/'));
177 | //var_dump($this->base_uri);
178 | return true;
179 | } elseif ($code == '408') {
180 | $this->_echo("[登陆超时]");
181 | } else {
182 | $this->_echo("[登陆异常]");
183 | }
184 | return false;
185 | }
186 |
187 | public function login()
188 | {
189 | $data = $this->_get($this->redirect_uri);
190 | $array = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
191 | //var_dump($array);
192 | if (!isset($array['skey']) || !isset($array['wxsid']) || !isset($array['wxuin']) || !isset($array['pass_ticket']))
193 | return False;
194 | $this->skey = $array['skey'];
195 | $this->sid = $array['wxsid'];
196 | $this->uin = $array['wxuin'];
197 | $this->pass_ticket = $array['pass_ticket'];
198 |
199 | $this->BaseRequest = [
200 | 'Uin' => intval($this->uin),
201 | 'Sid' => $this->sid,
202 | 'Skey' => $this->skey,
203 | 'DeviceID' => $this->deviceId
204 | ];
205 | $this->initSave();
206 | return true;
207 | }
208 |
209 | public function webwxinit($first = true)
210 | {
211 | $url = sprintf($this->base_uri . '/webwxinit?pass_ticket=%s&skey=%s&r=%s', $this->pass_ticket, $this->skey, time());
212 | $params = [
213 | 'BaseRequest' => $this->BaseRequest
214 | ];
215 | $dic = $this->_post($url, $params);
216 | $this->SyncKey = $dic['SyncKey'];
217 | $this->User = $dic['User'];
218 | # synckey for synccheck
219 | $tempArr = [];
220 | if (is_array($this->SyncKey['List'])) {
221 | foreach ($this->SyncKey['List'] as $val) {
222 | # code...
223 | $tempArr[] = "{$val['Key']}_{$val['Val']}";
224 | }
225 | } elseif ($first) {
226 | return $this->webwxinit(false);
227 | }
228 | //$this->skey = $dic['SKey'];
229 | $this->synckey = implode('|', $tempArr);
230 | //$this->initSave();
231 | //var_dump($this->synckey);
232 | return $dic['BaseResponse']['Ret'] == 0;
233 | }
234 |
235 | public function webwxstatusnotify()
236 | {
237 | $url = sprintf($this->base_uri . '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s', $this->pass_ticket);
238 | $params = [
239 | 'BaseRequest' => $this->BaseRequest,
240 | "Code" => 3,
241 | "FromUserName" => $this->User['UserName'],
242 | "ToUserName" => $this->User['UserName'],
243 | "ClientMsgId" => time()
244 | ];
245 | $dic = $this->_post($url, $params);
246 | return $dic['BaseResponse']['Ret'] == 0;
247 | }
248 |
249 | public function webwxgetcontact()
250 | {
251 | $SpecialUsers = $this->SpecialUsers;
252 | //print $this->base_uri;
253 | $url = sprintf($this->base_uri . '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s',
254 | $this->pass_ticket, $this->skey, time());
255 | $dic = $this->_post($url, []);
256 |
257 | $this->MemberCount = $dic['MemberCount'] - 1;//把自己减去
258 | $this->MemberList = $dic['MemberList'];
259 | $ContactList = $this->MemberList;
260 | $GroupList = $this->GroupList;
261 | $PublicUsersList = $this->PublicUsersList;
262 | $SpecialUsersList = $this->SpecialUsersList;
263 | //var_dump($ContactList);
264 | if (is_array($ContactList)) {
265 | foreach ($ContactList as $key => $Contact) {
266 | //$this->_echo(sprintf("%s--------%d-------%d----%s",$Contact['UserName'] ,$Contact['VerifyFlag'],$Contact['VerifyFlag']&8,$Contact['VerifyFlag'] & 8 != 0));
267 | if (in_array($Contact['UserName'], $SpecialUsers)) { # 特殊账号
268 | unset($ContactList[$key]);
269 | $this->SpecialUsersList[] = $Contact;
270 | } elseif (($Contact['VerifyFlag'] & 8) != 0) { # 公众号/服务号
271 | unset($ContactList[$key]);
272 | $this->PublicUsersList[] = $Contact;
273 | } elseif (strpos($Contact['UserName'], '@@') !== false) { # 群聊
274 | unset($ContactList[$key]);
275 | $this->GroupList[] = $Contact;
276 | } elseif ($Contact['UserName'] == $this->User['UserName']) { # 自己
277 | unset($ContactList[$key]);
278 | }
279 | }
280 | } else {
281 | return false;
282 | }
283 | $this->ContactList = $ContactList;
284 | return true;
285 | }
286 |
287 | public function webwxbatchgetcontact()
288 | {
289 | $url = sprintf($this->base_uri . '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s', time(), $this->pass_ticket);
290 | $List = [];
291 | foreach ($this->GroupList as $g) {
292 | # code...
293 | $List[] = ["UserName" => $g['UserName'], "EncryChatRoomId" => ""];
294 | }
295 | $params = [
296 | 'BaseRequest' => $this->BaseRequest,
297 | "Count" => count($this->GroupList),
298 | "List" => $List
299 | ];
300 | $dic = $this->_post($url, $params);
301 |
302 | # blabla ...
303 | $ContactList = $dic['ContactList'];
304 | $ContactCount = $dic['Count'];
305 | $this->GroupList = $ContactList;
306 |
307 | foreach ($ContactList as $key => $Contact) {
308 | $MemberList = $Contact['MemberList'];
309 | foreach ($MemberList as $member)
310 | $this->GroupMemeberList[] = $member;
311 | }
312 |
313 | return true;
314 | }
315 |
316 | public function getNameById($id)
317 | {
318 | $url = sprintf($this->base_uri .
319 | '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s',
320 | time(), $this->pass_ticket);
321 | $params = [
322 | 'BaseRequest' => $this->BaseRequest,
323 | "Count" => 1,
324 | "List" => [["UserName" => $id, "EncryChatRoomId" => ""]]
325 | ];
326 | $dic = $this->_post($url, $params);
327 |
328 | # blabla ...
329 | return $dic['ContactList'];
330 | }
331 |
332 | public function testsynccheck()
333 | {
334 | //TODO:
335 | $SyncHost = [
336 | 'webpush.weixin.qq.com',
337 | 'webpush2.weixin.qq.com',
338 | 'webpush.wechat.com',
339 | 'webpush1.wechat.com',
340 | 'webpush2.wechat.com',
341 | 'webpush1.wechatapp.com',
342 | 'webpush.wechatapp.com'
343 | ];
344 | $SyncHost = ['webpush.wx.qq.com'];
345 | foreach ($SyncHost as $host) {
346 | $this->syncHost = $host;
347 | list($retcode, $selector) = $this->synccheck();
348 | if ($retcode == '0')
349 | return true;
350 | }
351 | return false;
352 | }
353 |
354 | public function synccheck()
355 | {
356 | $params = [
357 | 'r' => time(),
358 | 'sid' => $this->sid,
359 | 'uin' => $this->uin,
360 | 'skey' => $this->skey,
361 | 'deviceid' => $this->deviceId,
362 | 'synckey' => $this->synckey,
363 | '_' => time(),
364 | ];
365 | $url = 'https://' . $this->syncHost . '/cgi-bin/mmwebwx-bin/synccheck?' . http_build_query($params);
366 | $data = $this->_get($url);
367 | if (preg_match('/window.synccheck={retcode:"(\d+)",selector:"(\d+)"}/', $data, $pm)) {
368 | $retcode = $pm[1];
369 | $selector = $pm[2];
370 | } else {
371 | //var_dump($data);
372 | $retcode = -1;
373 | $selector = -1;
374 | }
375 | return [$retcode, $selector];
376 | }
377 |
378 | public function webwxsync()
379 | {
380 | $url = sprintf($this->base_uri .
381 | '/webwxsync?sid=%s&skey=%s&pass_ticket=%s',
382 | $this->sid, $this->skey, $this->pass_ticket);
383 | $params = [
384 | 'BaseRequest' => $this->BaseRequest,
385 | 'SyncKey' => $this->SyncKey,
386 | 'rr' => time()
387 | ];
388 | $dic = $this->_post($url, $params);
389 | if ($this->DEBUG)
390 | var_dump($dic);
391 |
392 | if ($dic['BaseResponse']['Ret'] == 0) {
393 | $this->SyncKey = $dic['SyncKey'];
394 | $synckey = [];
395 | foreach ($this->SyncKey['List'] as $keyVal)
396 | $synckey[] = "{$keyVal['Key']}_{$keyVal['Val']}";
397 | $this->synckey = implode('|', $synckey);
398 | }
399 | return $dic;
400 | }
401 |
402 | public function webwxsendmsg($word, $to = 'filehelper')
403 | {
404 | $url = sprintf($this->base_uri .
405 | '/webwxsendmsg?pass_ticket=%s', $this->pass_ticket);
406 | $clientMsgId = (time() * 1000) . substr(uniqid(), 0, 5);
407 | $data = [
408 | 'BaseRequest' => $this->BaseRequest,
409 | 'Msg' => [
410 | "Type" => 1,
411 | "Content" => $this->_transcoding($word),
412 | "FromUserName" => $this->User['UserName'],
413 | "ToUserName" => $to,
414 | "LocalID" => $clientMsgId,
415 | "ClientMsgId" => $clientMsgId
416 | ]
417 | ];
418 | $dic = $this->_post($url, $data);
419 | return $dic['BaseResponse']['Ret'] == 0;
420 | }
421 |
422 | public function webwxuploadmedia($ToUserName, $image_name)
423 | {
424 | $url = 'https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json';
425 | # 计数器
426 | $this->media_count = $this->media_count + 1;
427 | # 文件名
428 | $file_name = $image_name;
429 | # MIME格式
430 | # mime_type = application/pdf, image/jpeg, image/png, etc.
431 | $mime_type = mime_content_type($image_name);
432 | # 微信识别的文档格式,微信服务器应该只支持两种类型的格式。pic和doc
433 | # pic格式,直接显示。doc格式则显示为文件。
434 | $media_type = explode('/', $mime_type)[0] == 'image' ? 'pic' : 'doc';
435 | $fTime = filemtime($image_name);
436 | # 上一次修改日期
437 | $lastModifieDate = gmdate('D M d Y H:i:s TO', $fTime) . ' (CST)';//'Thu Mar 17 2016 00:55:10 GMT+0800 (CST)';
438 | # 文件大小
439 | $file_size = filesize($file_name);
440 | # PassTicket
441 | $pass_ticket = $this->pass_ticket;
442 | # clientMediaId
443 | $client_media_id = (time() * 1000) . mt_rand(10000, 99999);
444 | # webwx_data_ticket
445 | $webwx_data_ticket = '';
446 | $fp = fopen('cookie.cookie', 'r');
447 | while ($line = fgets($fp)) {
448 | # code...
449 | if (strpos($line, 'webwx_data_ticket') !== false) {
450 | $arr = explode("\t", trim($line));
451 | //var_dump($arr);
452 | $webwx_data_ticket = $arr[6];
453 | break;
454 | }
455 | }
456 | fclose($fp);
457 |
458 | if ($webwx_data_ticket == '')
459 | return "None Fuck Cookie";
460 |
461 | $uploadmediarequest = self::json_encode([
462 | "BaseRequest" => $this->BaseRequest,
463 | "ClientMediaId" => $client_media_id,
464 | "TotalLen" => $file_size,
465 | "StartPos" => 0,
466 | "DataLen" => $file_size,
467 | "MediaType" => 4,
468 | "UploadType" => 2,
469 | "FromUserName" => $this->User["UserName"],
470 | "ToUserName" => $ToUserName,
471 | "FileMd5" => md5_file($image_name)
472 | ]);
473 |
474 | $multipart_encoder = [
475 | 'id' => 'WU_FILE_' . $this->media_count,
476 | 'name' => $file_name,
477 | 'type' => $mime_type,
478 | 'lastModifieDate' => $lastModifieDate,
479 | 'size' => $file_size,
480 | 'mediatype' => $media_type,
481 | 'uploadmediarequest' => $uploadmediarequest,
482 | 'webwx_data_ticket' => $webwx_data_ticket,
483 | 'pass_ticket' => $pass_ticket,
484 | 'filename' => '@' . $file_name
485 | ];
486 |
487 | $response_json = json_decode($this->_post($url, $multipart_encoder, false, true), true);
488 | if ($response_json['BaseResponse']['Ret'] == 0)
489 | return $response_json;
490 | return null;
491 | }
492 |
493 | public function webwxsendmsgimg($user_id, $media_id)
494 | {
495 | $url = sprintf($this->base_uri . '/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s', $this->pass_ticket);
496 | $clientMsgId = (time() * 1000) . substr(uniqid(), 0, 5);
497 | $data = [
498 | "BaseRequest" => $this->BaseRequest,
499 | "Msg" => [
500 | "Type" => 3,
501 | "MediaId" => $media_id,
502 | "FromUserName" => $this->User['UserName'],
503 | "ToUserName" => $user_id,
504 | "LocalID" => $clientMsgId,
505 | "ClientMsgId" => $clientMsgId
506 | ]
507 | ];
508 | $dic = $this->_post($url, $data);
509 | if ($this->DEBUG)
510 | var_dump($dic);
511 | return $dic['BaseResponse']['Ret'] == 0;
512 | }
513 |
514 | public function webwxsendmsgemotion($user_id, $media_id)
515 | {
516 | $url = sprintf($this->base_uri . '/webwxsendemoticon?fun=sys&f=json&pass_ticket=%s', $this->pass_ticket);
517 | $clientMsgId = (time() * 1000) . substr(uniqid(), 0, 5);
518 | $data = [
519 | "BaseRequest" => $this->BaseRequest,
520 | "Msg" => [
521 | "Type" => 47,
522 | "EmojiFlag" => 2,
523 | "MediaId" => $media_id,
524 | "FromUserName" => $this->User['UserName'],
525 | "ToUserName" => $user_id,
526 | "LocalID" => $clientMsgId,
527 | "ClientMsgId" => $clientMsgId
528 | ]
529 | ];
530 | $dic = $this->_post($url, $data);
531 | if ($this->DEBUG)
532 | var_dump($dic);
533 | return $dic['BaseResponse']['Ret'] == 0;
534 | }
535 |
536 | /**
537 | * 添加好友,或者通过好友验证消息
538 | * @Opcode 2 添加 3 通过
539 | */
540 | public function webwxverifyuser($user, $Opcode)
541 | {
542 | $url = sprintf($this->base_uri . '/webwxverifyuser?lang=zh_CN&r=%s&pass_ticket=%s', time() * 1000, $this->pass_ticket);
543 |
544 | $data = [
545 | "BaseRequest" => $this->BaseRequest,
546 | "Opcode" => 3,
547 | "VerifyUserListSize" => 1,
548 | "VerifyUserList" => [$user],
549 | "VerifyContent" => "",
550 | "SceneListCount" => 1,
551 | "SceneList" => [33],
552 | "skey" => $this->skey
553 | ];
554 | $dic = $this->_post($url, $data);
555 | if ($this->DEBUG)
556 | var_dump($dic);
557 | return $dic['BaseResponse']['Ret'] == 0;
558 | }
559 |
560 | public function _saveFile($filename, $data, $api = null)
561 | {
562 | $fn = $filename;
563 | if (isset($this->saveSubFolders[$api])) {
564 | $dirName = $this->saveFolder . $this->saveSubFolders[$api];
565 | umask(0);
566 | if (!is_dir($dirName)) {
567 | mkdir($dirName, 0777, true);
568 | chmod($dirName, 0777);
569 | }
570 | $fn = $dirName . '/' . $filename;
571 | $this->_echo(sprintf('Saved file: %s', $fn));
572 | //file_put_contents($fn, $data);
573 | $f = fopen($fn, 'wb');
574 | if ($f) {
575 | fwrite($f, $data);
576 | fclose($f);
577 | } else {
578 | $this->_echo('[*] 保存失败 - ' . $fn);
579 | }
580 | }
581 | return $fn;
582 | }
583 |
584 | public function webwxgeticon($id)
585 | {
586 | $url = sprintf($this->base_uri .
587 | '/webwxgeticon?username=%s&skey=%s', $id, $this->skey);
588 | $data = $this->_get($url);
589 | $fn = 'img_' . $id . '.jpg';
590 | return $this->_saveFile($fn, $data, 'webwxgeticon');
591 | }
592 |
593 | public function webwxgetheadimg()
594 | {
595 | $url = sprintf($this->base_uri .
596 | '/webwxgetheadimg?username=%s&skey=%s', $id, $this->skey);
597 | $data = $this->_get($url);
598 | $fn = 'img_' . $id . '.jpg';
599 | return $this->_saveFile($fn, $data, 'webwxgetheadimg');
600 | }
601 |
602 | public function webwxgetmsgimg($msgid)
603 | {
604 | $url = sprintf($this->base_uri .
605 | '/webwxgetmsgimg?MsgID=%s&skey=%s', $msgid, $this->skey);
606 | $data = $this->_get($url);
607 | $fn = 'img_' . $msgid . '.jpg';
608 | return $this->_saveFile($fn, $data, 'webwxgetmsgimg');
609 | }
610 |
611 | public function webwxgetvideo($msgid)
612 | {
613 | $url = sprintf($this->base_uri .
614 | '/webwxgetvideo?msgid=%s&skey=%s', $msgid, $this->skey);
615 | $data = $this->_get($url, [], 'webwxgetvideo');
616 | $fn = 'video_' . $msgid . '.mp4';
617 | return $this->_saveFile($fn, $data, 'webwxgetvideo');
618 | }
619 |
620 | public function webwxgetvoice($msgid)
621 | {
622 | $url = sprintf($this->base_uri .
623 | '/webwxgetvoice?msgid=%s&skey=%s', $msgid, $this->skey);
624 | $data = $this->_get($url, [], 'webwxgetvoice');
625 | $fn = 'voice_' . $msgid . '.mp3';
626 | return $this->_saveFile($fn, $data, 'webwxgetvoice');
627 | }
628 |
629 | public function getGroupName($id)
630 | {
631 | $name = '未知群';
632 | foreach ($this->GroupList as $member) {
633 | if ($member['UserName'] == $id) {
634 | $name = $member['NickName'];
635 | }
636 | }
637 | if ($name == '未知群') {
638 | # 现有群里面查不到
639 | $GroupList = $this->getNameById($id);
640 | foreach ($GroupList as $group) {
641 | $this->GroupList[] = $group;
642 | if ($group['UserName'] == $id) {
643 | $name = $group['NickName'];
644 | $MemberList = $group['MemberList'];
645 | foreach ($MemberList as $member)
646 | $this->GroupMemeberList[] = $member;
647 | }
648 | }
649 | }
650 | return $name;
651 | }
652 |
653 | public function getUserRemarkName($id)
654 | {
655 | $name = substr($id, 0, 2) == '@@' ? '未知群' : '陌生人';
656 | if ($id == $this->User['UserName']) {
657 | return $this->User['NickName']; # 自己
658 | }
659 | if (substr($id, 0, 2) == '@@') {
660 | # 群
661 | $name = $this->getGroupName($id);
662 | } else {
663 | # 特殊账号
664 | foreach ($this->SpecialUsersList as $member) {
665 | if ($member['UserName'] == $id) {
666 | $name = $member['RemarkName'] ? $member['RemarkName'] : $member['NickName'];
667 | }
668 | }
669 | # 公众号或服务号
670 | foreach ($this->PublicUsersList as $member) {
671 | if ($member['UserName'] == $id) {
672 | $name = $member['RemarkName'] ? $member['RemarkName'] : $member['NickName'];
673 | }
674 | }
675 | # 直接联系人
676 | foreach ($this->ContactList as $member) {
677 | if ($member['UserName'] == $id) {
678 | $name = $member['RemarkName'] ? $member['RemarkName'] : $member['NickName'];
679 | }
680 | }
681 | # 群友
682 | foreach ($this->GroupMemeberList as $member) {
683 | if ($member['UserName'] == $id) {
684 | $name = $member['DisplayName'] ? $member['DisplayName'] : $member['NickName'];
685 | }
686 | }
687 | }
688 | if ($name == '未知群' || $name == '陌生人') {
689 | var_dump($id);
690 | }
691 | return $name;
692 | }
693 |
694 | public function getUSerID($name)
695 | {
696 | foreach ($this->MemberList as $member) {
697 | if ($name == $member['RemarkName'] || $name == $member['NickName']) {
698 | return $member['UserName'];
699 | }
700 | }
701 | return null;
702 | }
703 |
704 | public function _showMsg($message)
705 | {
706 | $srcName = null;
707 | $dstName = null;
708 | $groupName = null;
709 | $content = null;
710 |
711 | $msg = $message;
712 | //$this->_echo($msg);
713 |
714 | if ($msg['raw_msg']) {
715 | $srcName = $this->getUserRemarkName($msg['raw_msg']['FromUserName']);
716 | $dstName = $this->getUserRemarkName($msg['raw_msg']['ToUserName']);
717 | $content = $msg['raw_msg']['Content'];//str_replace(['<','>'], ['<','>'], $msg['raw_msg']['Content']);
718 | $message_id = $msg['raw_msg']['MsgId'];
719 |
720 | if (strpos($content, 'http://weixin.qq.com/cgi-bin/redirectforward?args=') !== false) {
721 | # 地理位置消息 lbbniu 可能不对
722 | $data = iconv('gbk', 'utf-8', $this->_get($content));
723 | $pos = $this->_searchContent('title', $data, 'xml');
724 | $tree = simplexml_load_string($this->_get($content), 'SimpleXMLElement', LIBXML_NOCDATA);
725 | $url = $tree->xpath('//html/body/div/img')[0]->attributes('src');//TODO: 可能不对吧
726 | $query = parse_url($url)['query'];
727 | foreach (explode('&', $query) as $item) {
728 | if (explode('=', $item)[0] == 'center') {
729 | $loc = explode('=', $item);
730 | }
731 | }
732 | $content = sprintf('%s 发送了一个 位置消息 - 我在 [%s](%s) @ %s]', $srcName, $pos, $url, $loc);
733 | }
734 |
735 | if ($msg['raw_msg']['ToUserName'] == 'filehelper') {
736 | # 文件传输助手
737 | $dstName = '文件传输助手';
738 | }
739 |
740 | if (substr($msg['raw_msg']['FromUserName'], 0, 2) == '@@') {
741 | # 接收到来自群的消息
742 | if (stripos($content, ':' . PHP_EOL/*":
"*/) !== false) {
743 | list($people, $content) = explode(':' . PHP_EOL/*":
"*/, $content);
744 | $groupName = $srcName;
745 | $srcName = $this->getUserRemarkName($people);
746 | $dstName = 'GROUP';
747 | } else {
748 | $groupName = $srcName;
749 | $srcName = 'SYSTEM';
750 | }
751 | } elseif (substr($msg['raw_msg']['ToUserName'], 0, 2) == '@@') {
752 | # 自己发给群的消息
753 | $groupName = $dstName;
754 | $dstName = 'GROUP';
755 | }
756 |
757 | # 收到了红包
758 | if ($content == '收到红包,请在手机上查看') {
759 | $msg['message'] = $content;
760 | }
761 |
762 | # 指定了消息内容
763 | if (isset($msg['message'])) {
764 | $content = $msg['message'];
765 | }
766 | }
767 | if (!empty($groupName)) {
768 | $this->_echo(sprintf('%s |%s| %s -> %s: %s', $message_id, trim($groupName), trim($srcName), trim($dstName), str_replace("
", "\n", $content)));
769 | //不自动回复的群
770 | if (in_array(trim($groupName), $this->NoReplyGroup)) {
771 | return false;
772 | } else {
773 | return true;
774 | }
775 | } else {
776 | $this->_echo(sprintf('%s %s -> %s: %s', $message_id, trim($srcName), trim($dstName), str_replace("
", "\n", $content)));
777 | return true;
778 | }
779 | }
780 |
781 | public static function br2nl($string)
782 | {
783 | return preg_replace('/\
/i', PHP_EOL, $string);
784 | }
785 |
786 | public function handleMsg($r)
787 | {
788 | foreach ($r['AddMsgList'] as $msg) {
789 | $this->_echo('[*] 你有新的消息,请注意查收');
790 |
791 | $msgType = $msg['MsgType'];
792 | $name = $this->getUserRemarkName($msg['FromUserName']);
793 | $content = $msg['Content'] = self::br2nl(html_entity_decode($msg['Content']));//str_replace(['<','>'], ['<','>'], $msg['Content']);
794 | $msgid = $msg['MsgId'];
795 | if ($this->DEBUG || true) {
796 | if (!is_dir('msg')) {
797 | umask(0);
798 | mkdir('msg', 0777, true);
799 | }
800 | $fn = 'msg/msg' . $msgid . '.json';
801 | $f = fopen($fn, 'w');
802 | fwrite($f, self::json_encode($msg));
803 | $this->_echo('[*] 该消息已储存到文件: ' . $fn);
804 | fclose($f);
805 | }
806 |
807 | if ($msgType == 1) {
808 | $raw_msg = ['raw_msg' => $msg];
809 | $isReply = $this->_showMsg($raw_msg);
810 | //lbbniu 重要
811 | if ($this->autoReplyMode && $isReply) {
812 | if (substr($msg['FromUserName'], 0, 2) == '@@' && stripos($content, ':' . PHP_EOL/*":
"*/) !== false) {
813 | list($people, $content) = explode(':' . PHP_EOL/*":
"*/, $content);
814 | //continue;
815 | }
816 | //自己发的消息不自动回复
817 | if ($msg['FromUserName'] == $this->User['UserName']) {
818 | $this->_echo('[*] 自己发的消息不自动回复');
819 | continue;
820 | }
821 |
822 | /*$ans = $this->robo365($content,$msg['FromUserName']);
823 | //365机器人
824 | if(!$ans){
825 | $ans = $this->_weida($content);
826 | }*/
827 | //青云客机器人
828 | if (!$ans) {
829 | $ans = $this->qingyunke($content);
830 | }
831 | //小豆比机器人
832 | if (!$ans) {
833 | $ans = $this->_xiaodoubi($content);
834 | }
835 | if (!$ans) {
836 | $ans = $content;
837 | }
838 | $ans .= "\n[IT全才-LbbNiu]";
839 | if ($this->webwxsendmsg($ans, $msg['FromUserName'])) {
840 | $this->_echo('自动回复: ' . $ans);
841 | } else {
842 | $this->_echo('自动回复失败');
843 | }
844 | }
845 | } elseif ($msgType == 3) {
846 | $image = $this->webwxgetmsgimg($msgid);
847 | $raw_msg = [
848 | 'raw_msg' => $msg,
849 | 'message' => sprintf('%s 发送了一张图片: %s', $name, $image)
850 | ];
851 | $this->_showMsg($raw_msg);
852 | $this->_safe_open($image);
853 | } elseif ($msgType == 34) {
854 | $voice = $this->webwxgetvoice($msgid);
855 | $raw_msg = ['raw_msg' => $msg,
856 | 'message' => sprintf('%s 发了一段语音: %s', $name, $voice)];
857 | $this->_showMsg($raw_msg);
858 | $this->_safe_open($voice);
859 | } elseif ($msgType == 37) {
860 | //是否自动通过加好友验证
861 | if (true) {
862 | $data = [
863 | "Value" => $msg['RecommendInfo']['UserName'],
864 | "VerifyUserTicket" => $msg['RecommendInfo']['Ticket']
865 | ];
866 | if ($this->webwxverifyuser($data, 3)) {
867 | $raw_msg = ['raw_msg' => $msg,
868 | 'message' => sprintf('添加 %s 好友成功', $msg['RecommendInfo']['NickName'])];
869 | } else {
870 | $raw_msg = ['raw_msg' => $msg,
871 | 'message' => sprintf('添加 %s 好友失败', $msg['RecommendInfo']['NickName'])];
872 | }
873 |
874 | $this->_showMsg($raw_msg);
875 | }
876 | } elseif ($msgType == 42) {
877 | $info = $msg['RecommendInfo'];
878 | $this->_echo(sprintf('%s 发送了一张名片:', $name));
879 | $this->_echo('=========================');
880 | $this->_echo(sprintf('= 昵称: %s', $info['NickName']));
881 | $this->_echo(sprintf('= 微信号: %s', $info['Alias']));
882 | $this->_echo(sprintf('= 地区: %s %s', $info['Province'], $info['City']));
883 | $this->_echo(sprintf('= 性别: %s', ['未知', '男', '女'][$info['Sex']]));
884 | $this->_echo('=========================');
885 | $raw_msg = ['raw_msg' => $msg,
886 | 'message' => sprintf('%s 发送了一张名片: %s', $name, self::json_encode($info))];
887 | $this->_showMsg($raw_msg);
888 | } elseif ($msgType == 47) {
889 | $url = $this->_searchContent('cdnurl', $content);
890 | $raw_msg = ['raw_msg' => $msg,
891 | 'message' => sprintf('%s 发了一个动画表情,点击下面链接查看: %s', $name, $url)];
892 | $this->_showMsg($raw_msg);
893 | $this->_safe_open($url);
894 | } elseif ($msgType == 49) {
895 | $appMsgType = [5 => '链接', 3 => '音乐', 7 => '微博', 17 => '位置共享'];
896 | $this->_echo(sprintf('%s 分享了一个%s:', $name, $appMsgType[$msg['AppMsgType']]));
897 | $this->_echo('=========================');
898 | $this->_echo(sprintf('= 标题: %s', $msg['FileName']));
899 | $this->_echo(sprintf('= 描述: %s', $this->_searchContent('des', $content, 'xml')));
900 | $this->_echo(sprintf('= 链接: %s', $msg['Url']));
901 | $this->_echo(sprintf('= 来自: %s', $this->_searchContent('appname', $content, 'xml')));
902 | $this->_echo('=========================');
903 | $card = [
904 | 'title' => $msg['FileName'],
905 | 'description' => $this->_searchContent('des', $content, 'xml'),
906 | 'url' => $msg['Url'],
907 | 'appname' => $this->_searchContent('appname', $content, 'xml')
908 | ];
909 | $raw_msg = ['raw_msg' => $msg, 'message' => sprintf('%s 分享了一个%s: %s',
910 | $name, $appMsgType[$msg['AppMsgType']], self::json_encode($card))];
911 | $this->_showMsg($raw_msg);
912 | } elseif ($msgType == 51) {
913 | $raw_msg = ['raw_msg' => $msg, 'message' => '[*] 成功获取联系人信息'];
914 | $this->_showMsg($raw_msg);
915 | } elseif ($msgType == 62) {
916 | $video = $this->webwxgetvideo($msgid);
917 | $raw_msg = ['raw_msg' => $msg,
918 | 'message' => sprintf('%s 发了一段小视频: %s', $name, $video)];
919 | $this->_showMsg($raw_msg);
920 | $this->_safe_open($video);
921 | } elseif ($msgType == 10002) {//撤销消息
922 | $raw_msg = ['raw_msg' => $msg, 'message' => sprintf('%s 撤回了一条消息', $name)];
923 | $this->_showMsg($raw_msg);
924 | } else {
925 | $raw_msg = [
926 | 'raw_msg' => $msg,
927 | 'message' => sprintf('[*] 该消息类型为: %d,可能是表情,图片, 链接或红包', $msg['MsgType'])
928 | ];
929 | var_dump($msg);
930 | $this->_showMsg($raw_msg);
931 | }
932 | }
933 | }
934 |
935 | public function listenMsgMode()
936 | {
937 | $this->_echo('[*] 进入消息监听模式 ... 成功');
938 |
939 | $this->_run('[*] 进行同步线路测试 ... ', 'testsynccheck');
940 |
941 | $playWeChat = 0;
942 | $redEnvelope = 0;
943 |
944 | while (true) {
945 | $this->lastCheckTs = time();
946 | list($retcode, $selector) = $this->synccheck();
947 | if ($this->DEBUG) {
948 | $this->_echo(sprintf('retcode: %s, selector: %s', $retcode, $selector));
949 | }
950 | //TODO:debug
951 | $this->_echo(sprintf('retcode: %s, selector: %s', $retcode, $selector));
952 |
953 | if ($retcode == '1100') {
954 | $this->_echo('[*] 你在手机上登出了微信,债见');
955 | break;
956 | }
957 | if ($retcode == '1101') {
958 | $this->_echo('[*] 你在其他地方登录了 WEB 版微信,债见');
959 | break;
960 | } elseif ($retcode == '0') {
961 | if ($selector == '2') {
962 | $r = $this->webwxsync();
963 | if ($r) {
964 | $this->handleMsg($r);
965 | }
966 | } elseif ($selector == '3') {
967 | $r = $this->webwxsync();
968 | if ($r) {
969 | $this->handleMsg($r);
970 | }
971 | } elseif ($selector == '4') {//朋友圈有动态
972 | $r = $this->webwxsync();
973 | if ($r) {
974 | $this->handleMsg($r);
975 | }
976 | } elseif ($selector == '6') {//有消息返回结果
977 | # TODO
978 | $redEnvelope += 1;
979 | $this->_echo(sprintf('[*] 收到疑似红包消息 %d 次', $redEnvelope));
980 | $r = $this->webwxsync();
981 | if ($r) {
982 | $this->handleMsg($r);
983 | }
984 | } elseif ($selector == '7') {
985 | $playWeChat += 1;
986 | $this->_echo(sprintf('[*] 你在手机上玩微信被我发现了 %d 次', $playWeChat));
987 | $r = $this->webwxsync();
988 | if ($r) {
989 | $this->handleMsg($r);
990 | }
991 | } elseif ($selector == '0') {
992 | sleep(1);
993 | }
994 | }
995 | if ((time() - $this->lastCheckTs) <= 20) {
996 | sleep(time() - $this->lastCheckTs);
997 | }
998 | }
999 | }
1000 |
1001 | public function sendMsg($name, $word, $isfile = false)
1002 | {
1003 | $id = $this->getUSerID($name);
1004 | if ($id) {
1005 | if ($isfile) {
1006 | $f = fopen($word, 'r');
1007 | while ($line = fgets($f)) {
1008 | # code...
1009 | $line = str_replace('\n', '', $line);
1010 | $this->_echo('-> ' . $name . ': ' . $line);
1011 | if ($this->webwxsendmsg($line, $id)) {
1012 | $this->_echo(' [成功]');
1013 | } else {
1014 | $this->_echo(' [失败]');
1015 | }
1016 | sleep(1);
1017 | }
1018 | } else {
1019 | if ($this->webwxsendmsg($word, $id)) {
1020 | $this->_echo('[*] 消息发送成功');
1021 | } else {
1022 | $this->_echo('[*] 消息发送失败');
1023 | }
1024 | }
1025 | } else {
1026 | $this->_echo('[*] 此用户不存在');
1027 | }
1028 | }
1029 |
1030 | public function sendMsgToAll($word)
1031 | {
1032 | foreach ($this->ContactList as $contact) {
1033 | $name = $contact['RemarkName'] ? $contact['RemarkName'] : $contact['NickName'];
1034 | $id = $contact['UserName'];
1035 | echo('-> ' . $name . ': ' . $word);
1036 | if ($this->webwxsendmsg($word, $id)) {
1037 | $this->_echo(' [成功]');
1038 | } else {
1039 | $this->_echo(' [失败]');
1040 | }
1041 | sleep(1);
1042 | }
1043 | }
1044 |
1045 | public function sendImg($name, $file_name)
1046 | {
1047 | $user_id = $this->getUSerID($name);
1048 | $response = $this->webwxuploadmedia($user_id, $file_name);
1049 | $media_id = "";
1050 | if (!empty($response)) {
1051 | $media_id = $response['MediaId'];
1052 | } else {
1053 | $this->_echo("{$name}->{$file_name} 发送失败");
1054 | return;
1055 | }
1056 | $response = $this->webwxsendmsgimg($user_id, $media_id);
1057 | if ($response) {
1058 | $this->_echo("{$name}->{$file_name} 发送成功");
1059 | } else {
1060 | $this->_echo("{$name}->{$file_name} 发送失败");
1061 | }
1062 | }
1063 |
1064 | public function sendEmotion($name, $file_name)
1065 | {
1066 | $user_id = $this->getUSerID($name);
1067 | $response = $this->webwxuploadmedia($user_id, $file_name);
1068 | $media_id = "";
1069 | if (!empty($response)) {
1070 | $media_id = $response['MediaId'];
1071 | } else {
1072 | $this->_echo("{$name}->{$file_name} 发送失败");
1073 | return;
1074 | }
1075 | $response = $this->webwxsendmsgemotion($user_id, $media_id);
1076 | }
1077 |
1078 | //开始登录
1079 | public function start()
1080 | {
1081 | $this->_echo('[*] 微信网页版 ... 开动');
1082 | if (!$this->init()) {
1083 | QRCODE:
1084 | while (true) {
1085 | $this->_run('[*] 正在获取 uuid ... ', 'getUUID');
1086 | $this->_echo('[*] 正在获取二维码 ... 成功');
1087 | $this->genQRCode();
1088 | $this->_echo('[*] 请使用微信扫描二维码以登录 ... ');
1089 | if (!$this->waitForLogin()) {
1090 | continue;
1091 | $this->_echo('[*] 请在手机上点击确认以登录 ... ');
1092 | }
1093 | if (!$this->waitForLogin(0)) {
1094 | continue;
1095 | }
1096 | break;
1097 | }
1098 | $this->_run('[*] 正在登录 ... ', 'login');
1099 | }
1100 |
1101 |
1102 | if (!$this->_run('[*] 微信初始化 ... ', 'webwxinit')) {
1103 | goto QRCODE;
1104 | }
1105 | $this->_run('[*] 开启状态通知 ... ', 'webwxstatusnotify');
1106 | $this->_run('[*] 获取联系人 ... ', 'webwxgetcontact');
1107 | $this->_echo(sprintf('[*] 应有 %s 个联系人,读取到联系人 %d 个',
1108 | $this->MemberCount, count($this->MemberList)));
1109 | $this->_echo(sprintf('[*] 共有 %d 个群 | %d 个直接联系人 | %d 个特殊账号 | %d 公众号或服务号', count($this->GroupList),
1110 | count($this->ContactList), count($this->SpecialUsersList), count($this->PublicUsersList)));
1111 | $this->_run('[*] 获取群 ... ', 'webwxbatchgetcontact');
1112 | $this->_echo('[*] 微信网页版 ... 开动');
1113 | if ($this->DEBUG)
1114 | echo($this);
1115 |
1116 | if ($this->interactive and raw_input('[*] 是否开启自动回复模式(y/n): ') == 'y') {
1117 | $this->autoReplyMode = true;
1118 | $this->_echo('[*] 自动回复模式 ... 开启');
1119 | } else {
1120 | if ($this->autoReplyMode)
1121 | $this->_echo('[*] 自动回复模式 ... 开启');
1122 | else
1123 | $this->_echo('[*] 自动回复模式 ... 关闭');
1124 | }
1125 | if (extension_loaded("pcntl")) {
1126 | $pf = pcntl_fork();
1127 | if ($pf) { //父进程负责监听消息
1128 | $this->listenMsgMode();
1129 | exit();
1130 | }
1131 | } elseif (extension_loaded("pthreads")) {
1132 | return true;
1133 | } else {
1134 | $this->_echo('[*] 缺少扩展,暂时只能获取监听消息,不能发送消息');
1135 | $this->_echo('[*] 如果要发消息,请安装pcntl或者pthreads扩展');
1136 | $this->listenMsgMode();
1137 | }
1138 |
1139 | sleep(2);
1140 | $this->readRun();
1141 | return false;
1142 | }
1143 |
1144 | public function readRun()
1145 | {
1146 | $this->help();
1147 | while (true) {
1148 | $text = raw_input('');
1149 | if ($text == 'quit') {
1150 | //listenProcess.terminate()
1151 | $this->_echo('[*] 退出微信');
1152 | exit();
1153 | } elseif ($text == 'help') {
1154 | $this->help();
1155 | } elseif ($text == 'me') {
1156 | $this->_echo($this->User);
1157 | } elseif ($text == 'friend') {
1158 | foreach ($this->ContactList as $key => $value) {
1159 | # code...
1160 | $this->_echo("NickName:{$value['NickName']}----Alias:{$value['Alias']}----UserName:{$value['UserName']}");
1161 | }
1162 | } elseif ($text == 'qun') {
1163 | foreach ($this->GroupList as $key => $value) {
1164 | # code...
1165 | $this->_echo("NickName:{$value['NickName']}----MemberCount:{$value['UserName']}----UserName:{$value['UserName']}");
1166 | }
1167 | } elseif ($text == 'qunyou') {
1168 | foreach ($this->GroupMemeberList as $key => $value) {
1169 | # code...
1170 | $this->_echo("NickName:{$value['NickName']}----UserName:{$value['UserName']}");
1171 | }
1172 | } elseif ($text == 'gzh') {
1173 | foreach ($this->PublicUsersList as $key => $value) {
1174 | # code...
1175 | $this->_echo("NickName:{$value['NickName']}----Alias:{$value['Alias']}----UserName:{$value['UserName']}");
1176 | }
1177 | } elseif ($text == 'tsh') {
1178 | foreach ($this->SpecialUsersList as $key => $value) {
1179 | # code...
1180 | $this->_echo("NickName:{$value['NickName']}----UserName:{$value['UserName']}");
1181 | }
1182 | } elseif (substr($text, 0, 2) == '->') {
1183 | list($name, $word) = explode(':', substr($text, 2));
1184 | if ($name == 'all')
1185 | $this->sendMsgToAll($word);
1186 | else
1187 | $this->sendMsg($name, $word);
1188 | } elseif (substr($text, 0, 3) == 'm->') {
1189 | list($name, $file) = explode(':', substr($text, 3));
1190 | $this->sendMsg($name, $file, true);
1191 | } elseif (substr($text, 0, 3) == 'f->') {
1192 | $this->_echo('发送文件');
1193 | } elseif (substr($text, 0, 3) == 'i->') {
1194 | $this->_echo('发送图片');
1195 | list($name, $file_name) = explode(':', substr($text, 3));
1196 | $this->sendImg($name, $file_name);
1197 | } elseif (substr($text, 0, 3) == 'e->') {
1198 | $this->_echo('发送表情');
1199 | list($name, $file_name) = explode(':', substr($text, 3));
1200 | $this->sendEmotion($name, $file_name);
1201 | }
1202 | }
1203 | }
1204 |
1205 | public function help()
1206 | {
1207 | $help = '
1208 | ==============================================================
1209 | ==============================================================
1210 | ->[昵称或ID]:[内容] 给好友发送消息
1211 | m->[昵称或ID]:[文件路径] 给好友发送文件中的内容
1212 | f->[昵称或ID]:[文件路径] 给好友发送文件
1213 | i->[昵称或ID]:[图片路径] 给好友发送图片
1214 | e->[昵称或ID]:[文件路径] 给好友发送表情(jpg/gif)
1215 | quit 退出程序
1216 | help 帮助
1217 | me 查看自己的信息
1218 | friend 好友列表
1219 | qun 群列表
1220 | qunyou 群友列表
1221 | gzh 公众号列表
1222 | tsh 特殊号列表
1223 | ==============================================================
1224 | ==============================================================
1225 | ';
1226 | $this->_echo($help);
1227 | }
1228 |
1229 | public function init()
1230 | {
1231 | if (file_exists("key.key")) {
1232 | $array = json_decode(file_get_contents("key.key"), true);
1233 | if ($array) {
1234 | $this->skey = $array['skey'];
1235 | $this->sid = $array['sid'];
1236 | $this->uin = $array['uin'];
1237 | $this->pass_ticket = $array['pass_ticket'];
1238 | $this->deviceId = $array['deviceId'];
1239 |
1240 | $this->BaseRequest = [
1241 | 'Uin' => intval($this->uin),
1242 | 'Sid' => $this->sid,
1243 | 'Skey' => $this->skey,
1244 | 'DeviceID' => $this->deviceId
1245 | ];
1246 | return true;
1247 | }
1248 | }
1249 | return false;
1250 | }
1251 |
1252 | public function initSave()
1253 | {
1254 | file_put_contents("key.key", self::json_encode([
1255 | 'skey' => $this->skey,
1256 | 'sid' => $this->sid,
1257 | 'uin' => $this->uin,
1258 | 'pass_ticket' => $this->pass_ticket,
1259 | 'deviceId' => $this->deviceId
1260 | ]));
1261 | }
1262 |
1263 | public function _safe_open($path)
1264 | {
1265 | //lbbniu 有问题
1266 | if ($this->autoOpen) {
1267 | if (PHP_OS == "Linux") {
1268 | system(sprintf("xdg-open %s &", $path));
1269 | } elseif (PHP_OS == "Darwin") {
1270 | system(sprintf('open %s &', $path));
1271 | } else {
1272 | system($path);
1273 | }
1274 | }
1275 | }
1276 |
1277 | public function _run($msg, $func)
1278 | {
1279 | echo($msg);
1280 | if ($this->$func()) {
1281 | $this->_echo('成功');
1282 | return true;
1283 | } else {
1284 | if ($func == 'webwxinit') {
1285 | $this->_echo("失败\n");
1286 | return false;
1287 | }
1288 | $this->_echo("失败\n[*] 退出程序");
1289 | exit();
1290 | }
1291 | }
1292 |
1293 | public function _echo($data)
1294 | {
1295 | if (is_string($data)) {
1296 | echo $data . "\n";
1297 | } elseif (is_array($data)) {
1298 | print_r($data);
1299 | } elseif (is_object($data)) {
1300 | var_dump($data);
1301 | } else {
1302 | echo $data;
1303 | }
1304 | }
1305 |
1306 | public function _printQR($mat)
1307 | {
1308 | $black = "\033[40m \033[0m";
1309 | $white = "\033[47m \033[0m";
1310 | foreach ($mat as $v) {
1311 | # code...
1312 | for ($i = 0; $i < strlen($v); $i++) {
1313 | if ($v[$i]) {
1314 | print $black;
1315 | } else {
1316 | print $white;
1317 | }
1318 | }
1319 | print "\n";
1320 | }
1321 | }
1322 |
1323 | public function _str2qr($str)
1324 | {
1325 | //$errorCorrectionLevel = 'L';//容错级别
1326 | //$matrixPointSize = 190;//生成图片大小
1327 | //QRcode::png($str, false, $errorCorrectionLevel, $matrixPointSize, 2);
1328 | $mat = QRcode::text($str);
1329 | $this->_printQR($mat);
1330 | }
1331 |
1332 | //lbbniu 需要修改
1333 | public function _transcoding($data)
1334 | {
1335 | if (!$data) {
1336 | return $data;
1337 | }
1338 | $result = null;
1339 | if (gettype($data) == 'unicode') {
1340 | $result = $data;
1341 | } elseif (gettype($data) == 'string') {
1342 | $result = $data;
1343 | }
1344 | return $result;
1345 | }
1346 |
1347 | public static function json_encode($json)
1348 | {
1349 | return json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
1350 | }
1351 |
1352 | /**
1353 | * GET 请求
1354 | * @param string $url
1355 | */
1356 | private function _get($url, $params = [], $api = false)
1357 | {
1358 | $oCurl = curl_init();
1359 | if (stripos($url, "https://") !== FALSE) {
1360 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
1361 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
1362 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
1363 | }
1364 | $header = [
1365 | 'User-Agent: ' . $this->user_agent,
1366 | 'Referer: https://wx.qq.com/'
1367 | ];
1368 | if ($api == 'webwxgetvoice')
1369 | $header[] = 'Range: bytes=0-';
1370 | if ($api == 'webwxgetvideo')
1371 | $header[] = 'Range: bytes=0-';
1372 | curl_setopt($oCurl, CURLOPT_HTTPHEADER, $header);
1373 | if (!empty($params)) {
1374 | if (strpos($url, '?') !== false) {
1375 | $url .= "&" . http_build_query($params);
1376 | } else {
1377 | $url .= "?" . http_build_query($params);
1378 | }
1379 | }
1380 | curl_setopt($oCurl, CURLOPT_URL, $url);
1381 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
1382 | curl_setopt($oCurl, CURLOPT_TIMEOUT, 36);
1383 | curl_setopt($oCurl, CURLOPT_COOKIEFILE, $this->cookie);
1384 | curl_setopt($oCurl, CURLOPT_COOKIEJAR, $this->cookie);
1385 | $sContent = curl_exec($oCurl);
1386 | $aStatus = curl_getinfo($oCurl);
1387 | curl_close($oCurl);
1388 | if (intval($aStatus["http_code"]) == 200) {
1389 | return $sContent;
1390 | } else {
1391 | return false;
1392 | }
1393 | }
1394 |
1395 | /**
1396 | * POST 请求
1397 | * @param string $url
1398 | * @param array $param
1399 | * @param boolean $post_file 是否文件上传
1400 | * @return string content
1401 | */
1402 | private function _post($url, $param, $jsonfmt = true, $post_file = false)
1403 | {
1404 | $oCurl = curl_init();
1405 | if (stripos($url, "https://") !== FALSE) {
1406 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
1407 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
1408 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
1409 | }
1410 | if (PHP_VERSION_ID >= 50500 && class_exists('\CURLFile')) {
1411 | $is_curlFile = true;
1412 | } else {
1413 | $is_curlFile = false;
1414 | if (defined('CURLOPT_SAFE_UPLOAD')) {
1415 | curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false);
1416 | }
1417 | }
1418 | $header = [
1419 | 'User-Agent: ' . $this->user_agent
1420 | ];
1421 | if ($jsonfmt) {
1422 | $param = self::json_encode($param);
1423 | $header[] = 'Content-Type: application/json; charset=UTF-8';
1424 | //var_dump($param);
1425 | }
1426 | if (is_string($param)) {
1427 | $strPOST = $param;
1428 | } elseif ($post_file) {
1429 | if ($is_curlFile) {
1430 | foreach ($param as $key => $val) {
1431 | if (substr($val, 0, 1) == '@') {
1432 | $param[$key] = new \CURLFile(realpath(substr($val, 1)));
1433 | }
1434 | }
1435 | }
1436 | $strPOST = $param;
1437 | } else {
1438 | $aPOST = array();
1439 | foreach ($param as $key => $val) {
1440 | $aPOST[] = $key . "=" . urlencode($val);
1441 | }
1442 | $strPOST = implode("&", $aPOST);
1443 | }
1444 |
1445 | curl_setopt($oCurl, CURLOPT_HTTPHEADER, $header);
1446 | curl_setopt($oCurl, CURLOPT_URL, $url);
1447 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
1448 | curl_setopt($oCurl, CURLOPT_POST, true);
1449 | curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST);
1450 | curl_setopt($oCurl, CURLOPT_COOKIEFILE, $this->cookie);
1451 | curl_setopt($oCurl, CURLOPT_COOKIEJAR, $this->cookie);
1452 | $sContent = curl_exec($oCurl);
1453 | $aStatus = curl_getinfo($oCurl);
1454 | curl_close($oCurl);
1455 | if (intval($aStatus["http_code"]) == 200) {
1456 | if ($jsonfmt)
1457 | return json_decode($sContent, true);
1458 | return $sContent;
1459 | } else {
1460 | return false;
1461 | }
1462 | }
1463 |
1464 | public function _xiaodoubi($word)
1465 | {
1466 | $url = 'http://www.xiaodoubi.com/bot/chat.php';
1467 | try {
1468 | $r = $this->_post($url, ['chat' => $word], false);
1469 | return $r;
1470 | } catch (Exception $e) {
1471 | return "让我一个人静静 T_T...";
1472 | }
1473 | }
1474 |
1475 | public function _simsimi($word)
1476 | {
1477 | $key = '';
1478 | $url = sprintf('http://sandbox.api.simsimi.com/request.p?key=%s&lc=ch&ft=0.0&text=%s', $key, urlencode($word));
1479 | $ans = json_decode(file_get_contents($url), true);
1480 | if ($ans['result'] == '100') {
1481 | return $ans['response'];
1482 | } else {
1483 | return '你在说什么,风太大听不清列';
1484 | }
1485 | }
1486 |
1487 | public function _searchContent($key, $content, $fmat = 'attr')
1488 | {
1489 | if ($fmat == 'attr') {
1490 | if (preg_match('/' . $key . '\s?=\s?"([^"<]+)"/', $content, $pm)) {
1491 | return $pm[1];
1492 | }
1493 | } elseif ($fmat == 'xml') {
1494 | if (!preg_match("/<{$key}>([^<]+)<\/{$key}>/", $content, $pm)) {
1495 | preg_match("/<{$key}><\!\[CDATA\[(.*?)\]\]><\/{$key}>/", $content, $pm);
1496 | }
1497 | if (isset($pm[1])) {
1498 | return $pm[1];
1499 | }
1500 | }
1501 | return '未知';
1502 | }
1503 |
1504 | public function qingyunke($word)
1505 | {
1506 | $url = "http://api.qingyunke.com/api.php?key=free&appid=0&msg=" . urlencode($word);
1507 | $ans = json_decode($this->juhecurl($url), true);
1508 | if ($ans['result'] == 0) {
1509 | return $ans['content'];
1510 | } else {
1511 | return '你在说什么,风太大听不清列';
1512 | }
1513 | }
1514 |
1515 | public function robo365($word, $ToUserName)
1516 | {
1517 | $robo365 = new Robot365(6);
1518 | return $robo365->search($word, $ToUserName);
1519 | }
1520 |
1521 | /**
1522 | * 问答机器人
1523 | * @param $word
1524 | * @return string
1525 | */
1526 | public function _weida($word)
1527 | {
1528 | //配置您申请的appkey
1529 | $appkey = "1fca5eeb8e26b07baf8136981d4a6e1e";
1530 | //************1.问答************
1531 | $url = "http://op.juhe.cn/robot/index";
1532 | $params = array(
1533 | "key" => $appkey,//您申请到的本接口专用的APPKEY
1534 | "info" => $word,//要发送给机器人的内容,不要超过30个字符
1535 | "dtype" => "json",//返回的数据的格式,json或xml,默认为json
1536 | "loc" => "",//地点,如北京中关村
1537 | "lon" => "",//经度,东经116.234632(小数点后保留6位),需要写为116234632
1538 | "lat" => "",//纬度,北纬40.234632(小数点后保留6位),需要写为40234632
1539 | "userid" => "",//1~32位,此userid针对您自己的每一个用户,用于上下文的关联
1540 | );
1541 | $paramstring = http_build_query($params);
1542 | $content = $this->juhecurl($url, $paramstring);
1543 | $result = json_decode($content, true);
1544 | if ($result) {
1545 | if ($result['error_code'] == '0') {
1546 | //print_r($result);
1547 | return $result['result']['text'];
1548 | } else {
1549 | //echo $result['error_code'].":".$result['reason'];
1550 | return '';
1551 | }
1552 | } else {
1553 | //echo "请求失败";
1554 | return '';
1555 | }
1556 | }
1557 |
1558 | /**
1559 | * 请求接口返回内容
1560 | * @param string $url [请求的URL地址]
1561 | * @param string $params [请求的参数]
1562 | * @param int $ipost [是否采用POST形式]
1563 | * @return string
1564 | */
1565 | public function juhecurl($url, $params = false, $ispost = 0)
1566 | {
1567 | $httpInfo = array();
1568 | $ch = curl_init();
1569 |
1570 | curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1571 | curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent);
1572 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
1573 | curl_setopt($ch, CURLOPT_TIMEOUT, 60);
1574 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1575 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
1576 | if ($ispost) {
1577 | curl_setopt($ch, CURLOPT_POST, true);
1578 | curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
1579 | curl_setopt($ch, CURLOPT_URL, $url);
1580 | } else {
1581 | if ($params) {
1582 | curl_setopt($ch, CURLOPT_URL, $url . '?' . $params);
1583 | } else {
1584 | curl_setopt($ch, CURLOPT_URL, $url);
1585 | }
1586 | }
1587 | $response = curl_exec($ch);
1588 | if ($response === FALSE) {
1589 | //echo "cURL Error: " . curl_error($ch);
1590 | return false;
1591 | }
1592 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1593 | $httpInfo = array_merge($httpInfo, curl_getinfo($ch));
1594 | curl_close($ch);
1595 | return $response;
1596 | }
1597 | }
1598 |
1599 | if (!extension_loaded('pthreads')) {
1600 | class Thread
1601 | {
1602 | public function start()
1603 | {
1604 |
1605 | }
1606 | }
1607 | }
1608 |
1609 | class ListenMsg extends Thread
1610 | {
1611 | private $weixin;
1612 |
1613 | public function __construct(WebWeiXin $weixin)
1614 | {
1615 | # code...
1616 | $this->weixin = $weixin;
1617 | }
1618 |
1619 | public function run()
1620 | {
1621 | if ($this->weixin) {
1622 | $this->weixin->_echo("[*] 进入消息监听模式 ......ListenMsg...run");
1623 | $this->weixin->listenMsgMode();
1624 | }
1625 | }
1626 | }
1627 |
1628 | class ListenWrite extends Thread
1629 | {
1630 | public function __construct(WebWeiXin $weixin)
1631 | {
1632 | $this->weixin = $weixin;
1633 | }
1634 |
1635 | public function run()
1636 | {
1637 | if ($this->weixin) {
1638 | if (!defined('STDIN')) define('STDIN', fopen('php://stdin', 'r'));
1639 | if (!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'w'));
1640 | if (!defined('STDERR')) define('STDERR', fopen('php://stderr', 'w'));
1641 | $this->weixin->_echo("[*] 进入命令行等待输入模式 ......ListenWrite...run");
1642 | $this->weixin->readRun();
1643 | }
1644 | }
1645 | }
1646 |
1647 |
1648 | $weixin = new WebWeiXin();
1649 | //var_dump($weixin);
1650 | $weixin->loadConfig([
1651 | 'interactive' => true,
1652 | //'autoReplyMode'=>true,
1653 | //'DEBUG'=>true
1654 | ]);
1655 | //var_dump($weixin->robo365("不错",'1222'));
1656 | //exit();
1657 | if ($weixin->start()) {
1658 | $msg = new ListenMsg($weixin);
1659 | $write = new ListenWrite($weixin);
1660 | $msg->start();
1661 | sleep(2);
1662 | $write->start();
1663 | }
1664 |
1665 |
1666 |
1667 |
1668 |
1669 |
1670 |
1671 |
1672 |
1673 |
1674 |
--------------------------------------------------------------------------------