wechatext.class.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. <?php
  2. /**
  3. * 微信公众平台PHP-SDK
  4. * Wechatext为非官方微信发送API
  5. * 注: 用户id为通过getMsg()方法获取的FakeId值
  6. * 主要实现如下功能:
  7. * send($id,$content) 向某用户id发送微信文字信息
  8. * sendNews($id,$msgid) 发送图文消息
  9. * getNewsList($page,$pagesize) 获取图文信息列表
  10. * uploadFile($filepath,$type) 上传附件,包括图片/音频/视频
  11. * getFileList($type,$page,$pagesize) 获取素材库文件列表
  12. * sendImage($id,$fid) 发送图片消息
  13. * sendAudio($id,$fid) 发送音频消息
  14. * sendVideo($id,$fid) 发送视频消息
  15. * getInfo($id) 根据id获取用户资料
  16. * getNewMsgNum($lastid) 获取从$lastid算起新消息的数目
  17. * getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据
  18. * getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数
  19. * 消息返回结构: {"id":"消息id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容"}
  20. * getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据
  21. * getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据
  22. * @author dodge <dodgepudding@gmail.com>
  23. * @link https://github.com/dodgepudding/wechat-php-sdk
  24. * @version 1.2
  25. *
  26. */
  27. include "snoopy.class.php";
  28. class Wechatext
  29. {
  30. private $cookie;
  31. private $_cookiename;
  32. private $_cookieexpired = 3600;
  33. private $_account;
  34. private $_password;
  35. private $_datapath = './data/cookie_';
  36. private $debug;
  37. private $_logcallback;
  38. private $_token;
  39. public function __construct($options)
  40. {
  41. $this->_account = isset($options['account'])?$options['account']:'';
  42. $this->_password = isset($options['password'])?$options['password']:'';
  43. $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;
  44. $this->debug = isset($options['debug'])?$options['debug']:false;
  45. $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;
  46. $this->_cookiename = $this->_datapath.$this->_account;
  47. $this->cookie = $this->getCookie($this->_cookiename);
  48. }
  49. /**
  50. * 主动发消息
  51. * @param string $id 用户的uid(即FakeId)
  52. * @param string $content 发送的内容
  53. */
  54. public function send($id,$content)
  55. {
  56. $send_snoopy = new Snoopy;
  57. $post = array();
  58. $post['tofakeid'] = $id;
  59. $post['type'] = 1;
  60. $post['token'] = $this->_token;
  61. $post['content'] = $content;
  62. $post['ajax'] = 1;
  63. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN";
  64. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  65. $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response";
  66. $send_snoopy->submit($submit,$post);
  67. $this->log($send_snoopy->results);
  68. return $send_snoopy->results;
  69. }
  70. /**
  71. * 获取图文信息列表
  72. * @param $page 页码(从0开始)
  73. * @param $pagesize 每页大小
  74. * @return array
  75. */
  76. public function getNewsList($page,$pagesize=10) {
  77. $send_snoopy = new Snoopy;
  78. $t = time().strval(mt_rand(100,999));
  79. $type=10;
  80. $post = array();
  81. $post['token'] = $this->_token;
  82. $post['ajax'] = 1;
  83. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1";
  84. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  85. $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?token=".$this->_token."&lang=zh_CN&sub=list&t=ajax-appmsgs-fileselect&type=$type&r=".str_replace(' ','',microtime())."&pageIdx=$page&pagesize=$pagesize&subtype=3&formid=file_from_".$t;
  86. $send_snoopy->submit($submit,$post);
  87. $result = $send_snoopy->results;
  88. $this->log('newslist:'.$result);
  89. return json_decode($result,true);
  90. }
  91. /**
  92. * 发送图文信息,必须从图文库里选取消息ID发送
  93. * @param string $id 用户的uid(即FakeId)
  94. * @param string $msgid 图文消息id
  95. */
  96. public function sendNews($id,$msgid)
  97. {
  98. $send_snoopy = new Snoopy;
  99. $post = array();
  100. $post['tofakeid'] = $id;
  101. $post['type'] = 10;
  102. $post['token'] = $this->_token;
  103. $post['fid'] = $msgid;
  104. $post['appmsgid'] = $msgid;
  105. $post['error'] = 'false';
  106. $post['ajax'] = 1;
  107. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN";
  108. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  109. $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response";
  110. $send_snoopy->submit($submit,$post);
  111. $this->log($send_snoopy->results);
  112. return $send_snoopy->results;
  113. }
  114. /**
  115. * 上传附件(图片/音频/视频)
  116. * @param string $filepath 本地文件地址
  117. * @param int $type 文件类型: 2:图片 3:音频 4:视频
  118. */
  119. public function uploadFile($filepath,$type=2) {
  120. $send_snoopy = new Snoopy;
  121. $send_snoopy->referer = "http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1";
  122. $t = time().strval(mt_rand(100,999));
  123. $post = array('formId'=>'');
  124. $postfile = array('uploadfile'=>$filepath);
  125. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  126. $send_snoopy->set_submit_multipart();
  127. $submit = "http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=$type&token=".$this->_token."&t=iframe-uploadfile&lang=zh_CN&formId= file_from_".$t;
  128. $send_snoopy->submit($submit,$post,$postfile);
  129. $tmp = $send_snoopy->results;
  130. $this->log('upload:'.$tmp);
  131. preg_match("/formId,.*?\'(\d+)\'/",$tmp,$matches);
  132. if (isset($matches[1])) {
  133. return $matches[1];
  134. }
  135. return false;
  136. }
  137. /**
  138. * 发送媒体文件
  139. * @param $id 用户的uid(即FakeId)
  140. * @param $fid 文件id
  141. * @param $type 文件类型
  142. */
  143. public function sendFile($id,$fid,$type) {
  144. $send_snoopy = new Snoopy;
  145. $post = array();
  146. $post['tofakeid'] = $id;
  147. $post['type'] = $type;
  148. $post['token'] = $this->_token;
  149. $post['fid'] = $fid;
  150. $post['fileid'] = $fid;
  151. $post['error'] = 'false';
  152. $post['ajax'] = 1;
  153. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN";
  154. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  155. $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response";
  156. $send_snoopy->submit($submit,$post);
  157. $result = $send_snoopy->results;
  158. $this->log('sendfile:'.$result);
  159. $json = json_decode($result,true);
  160. if ($json && $json['ret']==0)
  161. return true;
  162. else
  163. return false;
  164. }
  165. /**
  166. * 获取素材库文件列表
  167. * @param $type 文件类型: 2:图片 3:音频 4:视频
  168. * @param $page 页码(从0开始)
  169. * @param $pagesize 每页大小
  170. * @return array
  171. */
  172. public function getFileList($type,$page,$pagesize=10) {
  173. $send_snoopy = new Snoopy;
  174. $t = time().strval(mt_rand(100,999));
  175. $post = array();
  176. $post['token'] = $this->_token;
  177. $post['ajax'] = 1;
  178. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1";
  179. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  180. $submit = "https://mp.weixin.qq.com/cgi-bin/filemanagepage?token=".$this->_token."&lang=zh_CN&t=ajax-fileselect&type=$type&r=".str_replace(' ','',microtime())."&pageIdx=$page&pagesize=$pagesize&formid=file_from_".$t;
  181. $send_snoopy->submit($submit,$post);
  182. $result = $send_snoopy->results;
  183. $this->log('filelist:'.$result);
  184. return json_decode($result,true);
  185. }
  186. /**
  187. * 发送图文信息,必须从库里选取文件ID发送
  188. * @param string $id 用户的uid(即FakeId)
  189. * @param string $fid 文件id
  190. */
  191. public function sendImage($id,$fid)
  192. {
  193. return $this->sendFile($id,$fid,2);
  194. }
  195. /**
  196. * 发送语音信息,必须从库里选取文件ID发送
  197. * @param string $id 用户的uid(即FakeId)
  198. * @param string $fid 语音文件id
  199. */
  200. public function sendAudio($id,$fid)
  201. {
  202. return $this->sendFile($id,$fid,3);
  203. }
  204. /**
  205. * 发送视频信息,必须从库里选取文件ID发送
  206. * @param string $id 用户的uid(即FakeId)
  207. * @param string $fid 视频文件id
  208. */
  209. public function sendVideo($id,$fid)
  210. {
  211. return $this->sendFile($id,$fid,4);
  212. }
  213. /**
  214. * 发送预览图文消息
  215. * @param string $account 账户名称
  216. * @param string $title 标题
  217. * @param string $summary 摘要
  218. * @param string $content 内容
  219. * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取)
  220. * @param string $srcurl 原文链接
  221. * @return json
  222. */
  223. public function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') {
  224. $send_snoopy = new Snoopy;
  225. $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview";
  226. $send_snoopy->set_submit_normal();
  227. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  228. $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&lang=zh_CN';
  229. $post = array(
  230. 'AppMsgId'=>'',
  231. 'ajax'=>1,
  232. 'content0'=>$content,
  233. 'count'=>1,
  234. 'digest0'=>$summary,
  235. 'error'=>'false',
  236. 'fileid0'=>$photoid,
  237. 'preusername'=>$account,
  238. 'sourceurl0'=>$srcurl,
  239. 'title0'=>$title,
  240. );
  241. $post['token'] = $this->_token;
  242. $send_snoopy->submit($submit,$post);
  243. $tmp = $send_snoopy->results;
  244. $this->log('step2:'.$tmp);
  245. $json = json_decode($tmp,true);
  246. return $json;
  247. }
  248. /**
  249. * 获取用户的信息
  250. * @param string $id 用户的uid(即FakeId)
  251. * @return array {FakeId:100001,NickName:'昵称',Username:'用户名',Signature:'签名档',Country:'中国',Province:'广东',City:'广州',Sex:'1',GroupID:'0'}
  252. */
  253. public function getInfo($id)
  254. {
  255. $send_snoopy = new Snoopy;
  256. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  257. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token;
  258. $submit = "https://mp.weixin.qq.com/cgi-bin/getcontactinfo?t=ajax-getcontactinfo&lang=zh_CN&fakeid=".$id;
  259. $post = array('ajax'=>1,'token'=>$this->_token);
  260. $send_snoopy->submit($submit,$post);
  261. $this->log($send_snoopy->results);
  262. $result = json_decode($send_snoopy->results,1);
  263. if(!$result){
  264. return false;
  265. }
  266. return $result;
  267. }
  268. /**
  269. * 获取消息更新数目
  270. * @param int $lastid 最近获取的消息ID,为0时获取总消息数目
  271. * @return int 数目
  272. */
  273. public function getNewMsgNum($lastid=0){
  274. $send_snoopy = new Snoopy;
  275. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  276. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token;
  277. $submit = "https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=".$lastid;
  278. $post = array('ajax'=>1,'token'=>$this->_token);
  279. $send_snoopy->submit($submit,$post);
  280. $this->log($send_snoopy->results);
  281. $result = json_decode($send_snoopy->results,1);
  282. if(!$result){
  283. return false;
  284. }
  285. return intval($result['newTotalMsgCount']);
  286. }
  287. /**
  288. * 获取最新一条消息
  289. * @return array {"id":"最新一条id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容","playLength":"0","length":"0","source":"","starred":"0","status":"4"}
  290. */
  291. public function getTopMsg(){
  292. $send_snoopy = new Snoopy;
  293. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  294. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token;
  295. $lastid = $lastid===0 ? '':$lastid;
  296. $submit = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=ajax-message&lang=zh_CN&count=1&timeline=0&day=0&star=0&cgi=getmessage&offset=0";
  297. $post = array('ajax'=>1,'token'=>$this->_token);
  298. $send_snoopy->submit($submit,$post);
  299. $this->log($send_snoopy->results);
  300. $result = json_decode($send_snoopy->results,1);
  301. if($result && count($result)>0)
  302. return $result[0];
  303. else
  304. return false;
  305. }
  306. /**
  307. * 获取新消息
  308. * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取
  309. * @param $offset lastid起算第一条的偏移量
  310. * @param $perpage 每页获取多少条
  311. * @param $day 最近几天消息(1:昨天,2:前天,3:五天内)
  312. * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0
  313. * @param $star 是否星标组信息
  314. * @return array[] 同getTopMsg()返回的字段结构相同
  315. */
  316. public function getMsg($lastid=0,$offset=0,$perpage=50,$day=0,$today=0,$star=0){
  317. $send_snoopy = new Snoopy;
  318. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  319. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token;
  320. $lastid = $lastid===0 ? '':$lastid;
  321. $submit = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=ajax-message&lang=zh_CN&count=$perpage&timeline=$today&day=$day&star=$star&frommsgid=$lastid&cgi=getmessage&offset=$offset";
  322. $post = array('ajax'=>1,'token'=>$this->_token);
  323. $send_snoopy->submit($submit,$post);
  324. $this->log($send_snoopy->results);
  325. $result = json_decode($send_snoopy->results,1);
  326. if(!$result){
  327. return false;
  328. }
  329. return $result;
  330. }
  331. /**
  332. * 获取图片消息
  333. * @param int $msgid 消息id
  334. * @param string $mode 图片尺寸(large/small)
  335. * @return jpg二进制文件
  336. */
  337. public function getMsgImage($msgid,$mode='large'){
  338. $send_snoopy = new Snoopy;
  339. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  340. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token;
  341. $url = "https://mp.weixin.qq.com/cgi-bin/getimgdata?token=".$this->_token."&msgid=$msgid&mode=$mode&source=&fileId=0";
  342. $send_snoopy->fetch($url);
  343. $result = $send_snoopy->results;
  344. $this->log('msg image:'.$msgid.';length:'.strlen($result));
  345. if(!$result){
  346. return false;
  347. }
  348. return $result;
  349. }
  350. /**
  351. * 获取语音消息
  352. * @param int $msgid 消息id
  353. * @return mp3二进制文件
  354. */
  355. public function getMsgVoice($msgid){
  356. $send_snoopy = new Snoopy;
  357. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  358. $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token;
  359. $url = "https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=".$this->_token."&msgid=$msgid&fileId=0";
  360. $send_snoopy->fetch($url);
  361. $result = $send_snoopy->results;
  362. $this->log('msg voice:'.$msgid.';length:'.strlen($result));
  363. if(!$result){
  364. return false;
  365. }
  366. return $result;
  367. }
  368. /**
  369. * 模拟登录获取cookie
  370. * @return [type] [description]
  371. */
  372. /**
  373. * 模拟登录获取cookie
  374. * @return [type] [description]
  375. */
  376. public function login(){
  377. $snoopy = new Snoopy;
  378. $submit = "http://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN";
  379. $post["username"] = $this->_account;
  380. $post["pwd"] = md5($this->_password);
  381. $post["f"] = "json";
  382. $post["imgcode"] = "";
  383. $snoopy->referer = "https://mp.weixin.qq.com/";
  384. $snoopy->submit($submit,$post);
  385. $cookie = '';
  386. $this->log($snoopy->results);
  387. $result = json_decode($snoopy->results,true);
  388. if ($result['ErrCode']!=0) return false;
  389. foreach ($snoopy->headers as $key => $value) {
  390. $value = trim($value);
  391. if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $value,$match))
  392. $cookie .=$match[1].'='.$match[2].'; ';
  393. }
  394. preg_match("/token=(\d+)/i",$result['ErrMsg'],$matches);
  395. if($matches){
  396. $this->_token = $matches[1];
  397. $this->log('token:'.$this->_token);
  398. }
  399. $this->saveCookie($this->_cookiename,$cookie);
  400. return $cookie;
  401. }
  402. /**
  403. * 把cookie写入缓存
  404. * @param string $filename 缓存文件名
  405. * @param string $content 文件内容
  406. * @return bool
  407. */
  408. public function saveCookie($filename,$content){
  409. return file_put_contents($filename,$content);
  410. }
  411. /**
  412. * 读取cookie缓存内容
  413. * @param string $filename 缓存文件名
  414. * @return string cookie
  415. */
  416. public function getCookie($filename){
  417. if (file_exists($filename)) {
  418. $mtime = filemtime($filename);
  419. if ($mtime<time()-$this->_cookieexpired)
  420. $data = '';
  421. else
  422. $data = file_get_contents($filename);
  423. } else
  424. $data = '';
  425. if($data){
  426. $send_snoopy = new Snoopy;
  427. $send_snoopy->rawheaders['Cookie']= $data;
  428. $send_snoopy->maxredirs = 0;
  429. $url = "https://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-index&lang=zh_CN";
  430. $send_snoopy->fetch($url);
  431. $header = implode(',',$send_snoopy->headers);
  432. $this->log('header:'.print_r($send_snoopy->headers,true));
  433. preg_match("/token=(\d+)/i",$header,$matches);
  434. if(empty($matches)){
  435. return $this->login();
  436. }else{
  437. $this->_token = $matches[1];
  438. $this->log('token:'.$this->_token);
  439. return $data;
  440. }
  441. }else{
  442. return $this->login();
  443. }
  444. }
  445. /**
  446. * 验证cookie的有效性
  447. * @return bool
  448. */
  449. public function checkValid()
  450. {
  451. if (!$this->cookie || !$this->_token) return false;
  452. $send_snoopy = new Snoopy;
  453. $post = array('ajax'=>1,'token'=>$this->_token);
  454. $submit = "https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN";
  455. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  456. $send_snoopy->submit($submit,$post);
  457. $result = $send_snoopy->results;
  458. if(json_decode($result,1)){
  459. return true;
  460. }else{
  461. return false;
  462. }
  463. }
  464. private function log($log){
  465. if ($this->debug && function_exists($this->_logcallback)) {
  466. if (is_array($log)) $log = print_r($log,true);
  467. return call_user_func($this->_logcallback,$log);
  468. }
  469. }
  470. }