├── 微信协议.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 | --------------------------------------------------------------------------------