diff --git a/doc/web-weixin-api.md b/doc/web-weixin-api.md
new file mode 100644
index 0000000..401def4
--- /dev/null
+++ b/doc/web-weixin-api.md
@@ -0,0 +1,993 @@
+# Web微信API文档
+
+## 1、登录
+
+Web微信登录页面是[https://wx.qq.com](https://wx.qq.com),以下内容以此页开始。
+
+### 1.1、获取登录uuid
+
+进入Web微信登录页面后,在开发者工具中查看网络请求情况,可以发现在二维码图片加载之前,浏览器发送了一次 GET 请求:
+
+```
+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&_=1532078129220
+```
+
+该请求用于获取登录uuid,分析发现有如下URL参数:
+
+|参数名|示例值|描述|
+|---|---|---|
+|appid|wx782c26e4c19acffb|这个值不变,表示来自Web版微信;|
+|redirect_uri|https%3A%2F%2Fwx2.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage|跳转页面;非必须值;|
+|fun|new|固定值;|
+|lang|zh_CN|语言;zh_CN表示中文;|
+|_|1532078129220|13位时间戳;|
+
+然后服务器返回数据:
+
+```
+window.QRLogin.code = 200; window.QRLogin.uuid = "wcXrINkB9Q==";
+```
+
+- window.QRLogin.code的值为200时表示成功,为其它值均表示失败。
+- window.QRLogin.uuid的值为获取到的uuid,每次刷新会获取到不同的uuid,实际上uuid是服务端用来标识每一次登录的。
+
+### 1.2、获取二维码
+
+接着会发现如下 GET 请求:
+
+```
+https://login.weixin.qq.com/qrcode/wcXrINkB9Q==
+```
+
+该url为二维码图片的链接,url尾部的字符串即是上面获取到的uuid。
+
+### 1.3、等待确认登录
+
+在获取二维码的请求之后,会发现如下 GET 请求:
+
+```
+https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=wcXrINkB9Q==&tip=0&r=1223005686&_=1532080293141
+```
+
+这是等待扫描登录并获取跳转url的请求。如果没有及时扫描二维码,一定时间后该请求会重复出现,表示浏览器在轮询请求。
+
+URL参数分析:
+
+|数名|示例值|描述|
+|---|---|---|
+|loginicon|true|固定值;|
+|uid|wcXrINkB9Q==|同前面的uuid;|
+|tip|0或1|0表示等待用户扫码确认;1表示等待用户直接确认,用于push登陆时;|
+|r|1223005686|10位随机数|
+|_|1532080293141|13位时间戳;|
+
+如果没有及时扫描二维码,服务器返回数据:
+
+```
+window.code=408;
+```
+
+- 表示没有轮询到认证信息,此时需要再次发送该请求。
+
+如果一直没有扫描二维码,服务器返回数据:
+
+```
+window.code=400;
+```
+
+- 表示该次登录uuid失效,即二维码失效,此时会停止轮询,需要重新获取uuid才能继续。
+
+及时扫码认证之后,在点击确认登录之前,服务器返回数据:
+
+```
+window.code=201;
+```
+
+- 表示微信APP端已经扫码,但还没有点击确认。
+
+及时扫码并点击确认之后,服务器返回:
+
+```
+window.code=200;
+window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A51bGY49nxxxxxxx1wNPC3@qrticket_0&uuid=wcXrINkB9Q==&lang=zh_CN&scan=1532083897";
+```
+
+- window.code的值为200表示APP端认证成功。
+- window.redirect_uri为需要跳转的url。
+
+### 1.4、获取登录认证码
+
+微信APP端认证后,会产生的新的 GET 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A51bGY49nxxxxxxx1wNPC3@qrticket_0&uuid=wcXrINkB9Q==&lang=zh_CN&scan=1532083897&fun=new&version=v2
+```
+
+我们发现该请求即是上一步认证成功之后得到的redirect_uri再拼上&fun=new&version=v2。
+该请求发送之后,其它web或pc端登录的微信会被踢下线。
+
+URL参数分析:
+
+|数名|示例值|描述|
+|---|---|---|
+|ticket|A51bGY49nxxxxxxx1wNPC3@qrticket_0|保存不变即可;|
+|uuid|wcXrINkB9Q==|同前面的uuid;|
+|lang|zh_CN|语言;zh_CN表示中文;|
+|scan|1532083897|用户扫描的时间戳;|
+|fun|new|固定值;|
+|version|v2|固定值;|
+
+服务器返回数据:
+
+```XML
+
+ 0
+
+ @crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
+ 8rwxxxxxxxxxHq2P
+ 26xxx7
+ AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=
+ 1
+
+```
+
+***(为了个人隐私,以上返回数据有打码)***
+
+- ret表示请求返回状态,0表示成功。
+- skey、wxsid和wxuin都是具体微信用户的信息,不会变化,在后续的通讯中需要用到。
+- pass_ticket在初始化页面时需要用到。
+
+### 1.5、退出登录
+
+如果点击退出,我们会发现先后出现如下两个 POST 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=0&skey=%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1&skey=%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
+```
+
+这两个请求唯一的区别就是type的值,先是0再是1。
+
+URL参数分析:
+
+|数名|示例值|描述|
+|---|---|---|
+|redirect|1|固定值;|
+|type|0或1|0或1,固定值;|
+|skey|%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|登录时获取的;需要urlencode。|
+
+FORM表单参数分析:
+
+|数名|示例值|描述|
+|sid||登录时获取的;|
+|uin||登录时获取的;|
+
+服务器返回数据:
+
+- 该请求会导致页面跳转,没有返回数据。
+
+### 1.6、push登录
+
+点击退出后,页面会出现点击直接登录的页面,点击登录之后,会发现产生一个叫webwxpushloginurl的 GET 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxpushloginurl?uin=26xxx7
+```
+
+该请求是不扫码获取uuid的方式,仅用在手动退出登录之后,uuid获取之后还是继续1.3的等待确认。
+
+URL参数分析:
+
+|数名|示例值|描述|
+|uin|26xxx7|同前面获取的uin;|
+
+服务器返回数据:
+
+```JavaScript
+{
+ "ret": "0",
+ "msg": "all ok",
+ "uuid": "A_1_xxxw=="
+}
+```
+
+- ret为0表示成功,为1表示uin错误等失败。
+- uuid为新的uuid。经过反复“退出-push登录-退出-push登录”测试发现,全程uin保持不变,uuid会变化。
+
+## 2、数据同步
+
+### 2.1、页面初始化
+
+上面已经拿到了用户认证码,接着需要登录并初始化页面。
+
+初始化页面就是向服务端请求常用的联系人等信息,如下 POST 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=1219172280&lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=
+```
+
+URL参数分析:
+
+|数名|示例值|描述|
+|r|1219172280|10位随机数;|
+|lang|zh_CN|语言;zh_CN表示中文;|
+|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=|上一步获取到的;|
+
+POST的数据(JSON):
+
+```JavaScript
+{
+ "BaseRequest": {
+ "DeviceID": "e659xxxxxxxx3006",
+ "Sid": "8rwxxxxxxxxxHq2P",
+ "Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "Uin": "26xxx7"
+ }
+}
+```
+
+POST参数分析:
+
+|数名|示例值|描述|
+|BaseRequest.DeviceID|e659xxxxxxxx3006|设备ID,e拼上15位随机串;|
+|BaseRequest.Sid|rwxxxxxxxxxHq2P|上一步获取到的wxsid;|
+|BaseRequest.Skey|crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|上一步获取到的skey;|
+|BaseRequest.Uin|6xxx7|上一步获取到的wxuin;|
+
+服务器返回数据:
+
+```JavaScript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "Count": 11,
+ "ContactList": [{
+ "Uin": 0,
+ "UserName": "filehelper",
+ "NickName": "...",
+ "HeadImgUrl": "...",
+ "ContactFlag": 5,
+ "MemberCount": 0,
+ "MemberList": [],
+ "RemarkName": "",
+ "HideInputBarFlag": 0,
+ "Sex": 0,
+ "Signature": "",
+ "VerifyFlag": 0,
+ "OwnerUin": 0,
+ "PYInitial": "WJCSZS",
+ "PYQuanPin": "wenjianchuanshuzhushou",
+ "RemarkPYInitial": "",
+ "RemarkPYQuanPin": "",
+ "StarFriend": 0,
+ "AppAccountFlag": 0,
+ "Statues": 0,
+ "AttrStatus": 0,
+ "Province": "",
+ "City": "",
+ "Alias": "",
+ "SnsFlag": 0,
+ "UniFriend": 0,
+ "DisplayName": "",
+ "ChatRoomId": 0,
+ "KeyWord": "fil",
+ "EncryChatRoomId": "",
+ "IsOwner": 0
+ }],
+ "SyncKey": {
+ "Count": 4,
+ "List": [{
+ "Key": 1,
+ "Val": 704152857
+ }, {
+ "Key": 2,
+ "Val": 704152894
+ }, {
+ "Key": 3,
+ "Val": 704152870
+ }, {
+ "Key": 1000,
+ "Val": 1532075042
+ }]
+ },
+ "User": {
+ ...
+ },
+ "ChatSet": "...",
+ "SKey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "ClientVersion": 3697797,
+ "SystemTime": 1132000000,
+ "GrayScale": 1,
+ "InviteStartCount": 40,
+ "MPSubscribeMsgCount": 0,
+ "MPSubscribeMsgList": [],
+ "ClickReportInterval": 600000
+}
+```
+
+- BaseResponse.Ret表示请求返回状态,0表示成功。
+- ContactList为最近联系人列表。联系人的UserName以一个@开头为好友,两个@为群组。
+- MPSubscribeMsg为公众号订阅消息。
+- User是当前登录账户信息。
+
+### 2.2、开启消息状态通知
+
+接着需要开启消息状态通知。我们可以看到如下 POST 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=
+```
+
+URL参数分析:
+
+|数名|示例值|描述|
+|lang|zh_CN|语言;zh_CN表示中文;|
+|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn=|同前面;|
+
+POST的数据(JSON):
+
+```JavaScript
+{
+ "BaseRequest": {
+ "DeviceID": "e81xxxxxxxxx7686",
+ "Sid": "8rwxxxxxxxxxHq2P",
+ "Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "Uin": "26xxx7"
+ },
+ "ClientMsgId": 1532xxxxx3374,
+ "Code": 3,
+ "FromUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab",
+ "ToUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab"
+}
+```
+
+POST参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|BaseRequestB|{
"DeviceID":"e81xxxxxxxxx7686",
"Sid":"8rwxxxxxxxxxHq2P",
"Skey":"@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin":"26xxx7"
}|每一次POST请求都会发送BaseRequest;其中DeviceID是字母e拼上15位随机字符串,其余三个都是前面获取到的认证信息。|
+|ClientMsgId|1532xxxxx3374|13位时间戳;|
+|Code|3|固定值;|
+|FromUserName|@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab|页面加载时获取到的User.UserName;|
+|ToUserName|@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab|同FromUserName;|
+
+服务器返回数据:
+
+```javascript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "MsgID": "2271xxxxxxxxxxxxx44"
+}
+```
+
+- BaseResponse.Ret表示请求返回状态,0表示成功。
+
+### 2.3、服务端状态同步
+
+继续观察,我们没间隔一定的时间,浏览器会想服务器发送一个重复的 GET 请求:
+
+```
+https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1532145313248&skey=%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp&sid=8rwxxxxxxxxxHq2P&uin=26xxx7&deviceid=e35077xxxx772148&synckey=1_704152857%7C2_704152996%7C3_704152955%7C1000_1532127722&_=1532145290777
+```
+
+该请求是轮询向服务器获取最新在线、消息等状态的。
+
+URL参数分析:
+
+|数名|示例值|描述|
+|---|---|---|
+|r|1532145313248|13位时间戳;|
+|skey|%40crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|同前面;需要urlencode;|
+|sid|8rwxxxxxxxxxHq2P|同前面;|
+|uin|26xxx7 同前面;|
+|deviceid|e35077xxxx772148|设备ID,e拼上15位随机串;|
+|synckey|1_704152857%7C2_704152996%7C3_704152955%7C1000_1532127722|由初始化登录页面信息返回串中Sync的list列表组成;需要urlencode;|
+|_|1532145290777|13位时间戳;|
+
+服务器返回数据:
+
+```
+window.synccheck={retcode:"0",selector:"2"}
+```
+
+返回数据分析:
+
+|名称|示例值|描述|
+|---|---|---|
+|retcode|0|表示当前在线状态。
0:正常
1100:失败/登出微信
1101:从其它设备登录微信|
+|selector|2|表示消息状态。
0:正常
2:有新消息
4:目前发现修改了联系人备注会出现
7:手机操作了微信|
+
+## 3、联系人管理
+
+###3.1、获取全部联系人列表
+
+接着可以看到获取全部联系人列表的 GET 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D&r=1532190636970&seq=0&skey=@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp
+```
+
+URL参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|lang|zh_CN|语言;|
+|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|同前面获取到的;|
+|r|1532190636970|13位时间戳;|
+|seq|0|固定值;|
+|skey|@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|同前面获取到的;|
+
+服务器返回数据:
+
+```javascript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "MemberCount": 1,
+ "MemberList": [{
+ "Uin": 0,
+ "UserName": "weixin",
+ "NickName": "微信团队",
+ "HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=1&username=weixin&skey=@crypt_4fb399da_91a345e0be9deca5d61b183fc36a3a93",
+ "ContactFlag": 1,
+ "MemberCount": 0,
+ "MemberList": [],
+ "RemarkName": "",
+ "HideInputBarFlag": 0,
+ "Sex": 0,
+ "Signature": "微信团队官方帕啷",
+ "VerifyFlag": 56,
+ "OwnerUin": 0,
+ "PYInitial": "WXTD",
+ "PYQuanPin": "weixintuandui",
+ "RemarkPYInitial": "",
+ "RemarkPYQuanPin": "",
+ "StarFriend": 0,
+ "AppAccountFlag": 0,
+ "Statues": 0,
+ "AttrStatus": 4,
+ "Province": "",
+ "City": "",
+ "Alias": "",
+ "SnsFlag": 0,
+ "UniFriend": 0,
+ "DisplayName": "",
+ "ChatRoomId": 0,
+ "KeyWord": "wei",
+ "EncryChatRoomId": "",
+ "IsOwner": 0
+ }],
+ "Seq": 0
+}
+```
+
+- BaseResponse.Ret为0表示请求成功。
+- MemberCount表示联系人总数。
+- MemberList表示联系人列表。这个列表中同时包含好友、群组、公众号。好友和公众号是通过ContactFlag来区分的,1是好友,2是群组,3是公众号。
+- User内部字段介绍:
+
+```javascript
+ "Uin": 0,
+ "UserName": 用户名称,一个"@"为好友,两个"@"为群组
+ "NickName": 昵称
+ "HeadImgUrl":头像图片链接地址
+ "ContactFlag": 1-好友, 2-群组, 3-公众号
+ "MemberCount": 成员数量,只有在群组信息中才有效,
+ "MemberList": 成员列表,
+ "RemarkName": 备注名称
+ "HideInputBarFlag": 0,
+ "Sex": 性别,0-未设置(公众号、保密),1-男,2-女
+ "Signature": 公众号的功能介绍 or 好友的个性签名
+ "VerifyFlag": 0,
+ "OwnerUin": 0,
+ "PYInitial": 用户名拼音缩写
+ "PYQuanPin": 用户名拼音全拼
+ "RemarkPYInitial":备注拼音缩写
+ "RemarkPYQuanPin": 备注拼音全拼
+ "StarFriend": 是否为星标朋友 0-否 1-是
+ "AppAccountFlag": 0,
+ "Statues": 0,
+ "AttrStatus": 119911,
+ "Province": 省
+ "City": 市
+ "Alias":
+ "SnsFlag": 17,
+ "UniFriend": 0,
+ "DisplayName": "",
+ "ChatRoomId": 0,
+ "KeyWord":
+ "EncryChatRoomId": ""
+```
+
+### 3.2、批量获取指定用户信息
+
+然后可以看到批量获取指定联系人信息的 POST 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=1532246093335&lang=zh_CN&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D
+```
+
+该请求用于批量获取好友信息,或者批量获取群聊中的成员信息。
+
+URL参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|type|ex|不知道什么意思;|
+|r|1532246093335|13位时间戳;|
+|lang|zh_CN|语言;|
+|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|前面获取到的;|
+
+POST的数据(JSON):
+
+```javascript
+{
+ "BaseRequest": {
+ "DeviceID": "e81xxxxxxxxx7686",
+ "Sid": "8rwxxxxxxxxxHq2P",
+ "Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "Uin": "26xxx7"
+ },
+ "Count": 2,
+ "List": [
+ {
+ "EncryChatRoomId": "",
+ "UserName": "@@76feaa87c63576aaxxxxxxxxeaaa7030c60a09d33"
+
+ },
+ {
+ "ChatRoomId": "",
+ "UserName": "@@55664b9ab69bbe5e6aaxxxxxxxxx778c17e11e9dab09c9a1"
+
+ }
+ ]
+}
+```
+
+POST参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|BaseRequest||基本请求参数,同前面;|
+|Count||要进行批量查询的用户数;|
+|List||批量指定要查询的UserName的列表;如果要获取好友信息,EncryChatRoomId或ChatRoomId为空,UserName为好友的UserName;如果要获取群聊成员的信息,EncryChatRoomId或ChatRoomId为群聊的UserName,UserName为成员的UserName。|
+
+服务器返回数据:
+
+```javascript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "Count": 2,
+ "ContactList": [{
+ "Uin": 0,
+ "UserName": "@@76feaa87c635763438059aaeaaa7030c60a09d33",
+ "NickName": "一啣群)",
+ "HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=704138898&username=@@76feaa87c635763438059aaeaaa7030c60a09d33&skey=",
+ "ContactFlag": 3,
+ "MemberCount": 80,
+ "MemberList": [{
+ "Uin": 0,
+ "UserName": "@3a13860046f9f5cac66faba26f2bd0537f535",
+ "NickName": "龎",
+ "AttrStatus": 2147437,
+ "PYInitial": "",
+ "PYQuanPin": "",
+ "RemarkPYInitial": "",
+ "RemarkPYQuanPin": "",
+ "MemberStatus": 0,
+ "DisplayName": "",
+ "KeyWord": ""
+ }, ...],
+ "RemarkName": "",
+ "HideInputBarFlag": 0,
+ "Sex": 0,
+ "Signature": "",
+ "VerifyFlag": 0,
+ "OwnerUin": 0,
+ "PYInitial": "",
+ "PYQuanPin": "",
+ "RemarkPYInitial": "",
+ "RemarkPYQuanPin": "",
+ "StarFriend": 0,
+ "AppAccountFlag": 0,
+ "Statues": 0,
+ "AttrStatus": 0,
+ "Province": "",
+ "City": "",
+ "Alias": "",
+ "SnsFlag": 0,
+ "UniFriend": 0,
+ "DisplayName": "",
+ "ChatRoomId": 0,
+ "KeyWord": "",
+ "EncryChatRoomId": "@5fd8877457351e3a697",
+ "IsOwner": 0
+ }, {
+ "Uin": 0,
+ "UserName": "@f7dee38d1c3626c0e421c1f66deac0906559ac",
+ "NickName": "尕娅",
+ "HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=686458815&username=@f7dee38d1c3626c0e421c1f66deac0906559ac&skey=",
+ "ContactFlag": 259,
+ "MemberCount": 0,
+ "MemberList": [],
+ "RemarkName": "啢潧",
+ "HideInputBarFlag": 0,
+ "Sex": 2,
+ "Signature": "ä½ è",
+ "VerifyFlag": 0,
+ "OwnerUin": 0,
+ "PYInitial": "XY",
+ "PYQuanPin": "",
+ "RemarkPYInitial": "",
+ "RemarkPYQuanPin": "",
+ "StarFriend": 0,
+ "AppAccountFlag": 0,
+ "Statues": 0,
+ "AttrStatus": 1047,
+ "Province": "å京",
+ "City": "",
+ "Alias": "",
+ "SnsFlag": 49,
+ "UniFriend": 0,
+ "DisplayName": "",
+ "ChatRoomId": 0,
+ "KeyWord": "",
+ "EncryChatRoomId": "0",
+ "IsOwner": 0
+ }]
+}
+```
+
+- ContactList即是获取到的指定的联系人信息。
+
+### 3.3、修改联系人备注
+
+当我们修改联系人备注的时候,会发现浏览器发送了如下 POST 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxoplog
+```
+
+POST的数据(JSON):
+
+```javascript
+{
+ "BaseRequest": {
+ "DeviceID": "e81xxxxxxxxx7686",
+ "Sid": "8rwxxxxxxxxxHq2P",
+ "Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "Uin": "26xxx7"
+ },
+ "CmdId": 2,
+ "RemarkName": "新的备注名",
+ "UserName": "@07a2e8b4a1f98eb78a7xxxxxxxxxxxx5b7bc4908376xxx05917b"
+}
+```
+
+POST参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|BaseRequest||基本请求参数,同其它;|
+|CmdId|2|固定值;有可能还有其它值来表示不同类型操作。|
+|RemarkName||新的备注名;|
+|UserName||要修改的联系人的UserName;|
+
+服务器返回数据:
+
+```javascript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ }
+}
+```
+
+- BaseResponse.Ret表示操作成功。
+
+## 4、收发消息
+
+### 4.1、从服务端拉取新消息
+
+我们发现2.4中的轮询请求每一次返回的数据中selector为2或者6时,都会发生如下的 POST 请求来从服务端获取新消息:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=8rwxxxxxxxxxHq2P&skey=@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp&pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D
+```
+
+URL参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|sid|8rwxxxxxxxxxHq2P|同前面获取的;|
+|skey|@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp|同前面获取的;|
+|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|同前面获取的;|
+
+POST的数据(JSON):
+
+```javascript
+{
+ "BaseRequest": {
+ "DeviceID": "e2089xxxxx902803",
+ "Sid": "8rwxxxxxxxxxHq2P",
+ "Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "Uin": 26xxx7
+ },
+ "SyncKey": {
+ "Count": 4,
+ "List": [{
+ "Key": 1,
+ "Val": 704153081
+ },
+ {
+ "Key": 2,
+ "Val": 704153167
+ },
+ {
+ "Key": 3,
+ "Val": 704153152
+ },
+ {
+ "Key": 1000,
+ "Val": 1532180042
+ }
+ ]
+ },
+ "rr": 1112687259
+}
+```
+
+POST参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|BaseRequest|{
"DeviceID": "e81xxxxxxxxx7686",
"Sid": "8rwxxxxxxxxxHq2P",
"Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
"Uin": "26xxx7"
}|每一次POST请求都会发送
BaseRequest;其中DeviceID是字母e拼上15位随机字符串,其余三个都是前面获取到的认证信息。|
+|SyncKey|{
"Count": ...,
"List": [...]
}|前一次获取到的SyncKey;首次是初始化数据时获取的,zhiy之后都是通过本请求更新;|
+
+服务端返回数据:
+
+```javascript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "AddMsgCount": 1,
+ "AddMsgList": [{
+ "MsgId": "462399962004142457",
+ "FromUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab",
+ "ToUserName": "@56a0a9xxxxxxxxxxxxx54w0dxxxxbfab",
+ "MsgType": 51,
+ "Content": "",
+ "Status": 3,
+ "ImgStatus": 1,
+ "CreateTime": 1532190642,
+ "VoiceLength": 0,
+ "PlayLength": 0,
+ "FileName": "",
+ "FileSize": "",
+ "MediaId": "",
+ "Url": "",
+ "AppMsgType": 0,
+ "StatusNotifyCode": 4,
+ "StatusNotifyUserName": "...",
+ "RecommendInfo": {
+ "UserName": "",
+ "NickName": "",
+ "QQNum": 0,
+ "Province": "",
+ "City": "",
+ "Content": "",
+ "Signature": "",
+ "Alias": "",
+ "Scene": 0,
+ "VerifyFlag": 0,
+ "AttrStatus": 0,
+ "Sex": 0,
+ "Ticket": "",
+ "OpCode": 0
+ },
+ "ForwardFlag": 0,
+ "AppInfo": {
+ "AppID": "",
+ "Type": 0
+ },
+ "HasProductId": 0,
+ "Ticket": "",
+ "ImgHeight": 0,
+ "ImgWidth": 0,
+ "SubMsgType": 0,
+ "NewMsgId": 462399962004142457,
+ "OriContent": "",
+ "EncryFileName": ""
+ }],
+ "ModContactCount": 0,
+ "ModContactList": [],
+ "DelContactCount": 0,
+ "DelContactList": [],
+ "ModChatRoomMemberCount": 0,
+ "ModChatRoomMemberList": [],
+ "Profile": {
+ "BitFlag": 0,
+ "UserName": {
+ "Buff": ""
+ },
+ "NickName": {
+ "Buff": ""
+ },
+ "BindUin": 0,
+ "BindEmail": {
+ "Buff": ""
+ },
+ "BindMobile": {
+ "Buff": ""
+ },
+ "Status": 0,
+ "Sex": 0,
+ "PersonalCard": 0,
+ "Alias": "",
+ "HeadImgUpdateFlag": 0,
+ "HeadImgUrl": "",
+ "Signature": ""
+ },
+ "ContinueFlag": 0,
+ "SyncKey": {
+ "Count": 7,
+ "List": [{
+ "Key": 1,
+ "Val": 704153081
+ }, {
+ "Key": 2,
+ "Val": 704153169
+ }, {
+ "Key": 3,
+ "Val": 704153152
+ }, {
+ "Key": 11,
+ "Val": 704152941
+ }, {
+ "Key": 201,
+ "Val": 1532190642
+ }, {
+ "Key": 1000,
+ "Val": 1532190561
+ }, {
+ "Key": 1001,
+ "Val": 1532180114
+ }]
+ },
+ "SKey": "",
+ "SyncCheckKey": {
+ "Count": 7,
+ "List": [{
+ "Key": 1,
+ "Val": 704153081
+ }, {
+ "Key": 2,
+ "Val": 704153169
+ }, {
+ "Key": 3,
+ "Val": 704153152
+ }, {
+ "Key": 11,
+ "Val": 704152941
+ }, {
+ "Key": 201,
+ "Val": 1532190642
+ }, {
+ "Key": 1000,
+ "Val": 1532190561
+ }, {
+ "Key": 1001,
+ "Val": 1532180114
+ }]
+ }
+}
+```
+
+- AddMsgCount为新消息数目。
+- AddMsgList为新消息列表。
+ - MsgType说明:
+ - 1:文本消息
+ - 3:图片消息
+ - 34:语音消息
+ - 37:VERIFYMSG(验证消息)
+ - 40:POSSIBLEFRIEND_MSG(可能的朋友的消息)
+ - 42:共享名片
+ - 43:视频通话消息
+ - 47:动画表情
+ - 48:位置消息
+ - 49:分享链接
+ - 50:VOIPMSG(VoIP消息)
+ - 51:微信初始化消息
+ - 52:VOIPNOTIFY(VoIP通知)
+ - 53:VOIPINVITE(VoIP邀请)
+ - 62:小视频
+ - 9999:SYSNOTICE(系统通知)
+ - 10000:系统消息
+ - 10002:撤回消息
+- SyncKey会暂存起来作为下一次请求的参数。
+
+### 4.2、发送消息
+
+打开一个聊天界面,向好友发送一条消息,会发现产生如下 POST 请求:
+
+```
+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D
+```
+
+URL参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|pass_ticket|AFsdZ7eHxxxxxxxxxxxxxxxxfr1vjHPn%253D|同前面获取的;|
+
+POST的数据(JSON):
+
+```javascript
+{
+ "BaseRequest": {
+ "DeviceID": "e57xxxxxxx94xx27",
+ "Sid": "8rwxxxxxxxxxHq2P",
+ "Skey": "@crypt_jkde99da_b4xxxxxxxxxxxxxxx76d9yualp",
+ "Uin": "26xxx7"
+ },
+ "Msg": {
+ "ClientMsgId": "15321964202620841",
+ "Content": "这里是消息内容",
+ "FromUserName": "@da9d7631177b3c54492954010eb7",
+ "LocalID": "15321964202620841",
+ "ToUserName": "@d946a2006c12e85deda45c26b8cd42dbd3f03fa67be",
+ "Type": 1
+ },
+ "Scene": 0
+}
+```
+
+POST参数分析:
+
+|Key|示例值|描述|
+|---|---|---|
+|BaseRequest||同上;|
+|Msg.ClientMsgId||同LocalID;|
+|Msg.Content||消息内容;消息为媒体时,该值为媒体ID。|
+|Msg.FromUserName||发送用户;|
+|Msg.LocalID||13位时间戳+4位随机数;|
+|Msg.ToUserName||接收用户;|
+|Msg.Type|1|消息类型;同3.1中接收的消息类型;|
+|Scene|0|固定值;|
+
+服务端返回数据:
+
+```javascript
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "MsgID": "7650884298732299102",
+ "LocalID": "15321964202620841"
+}
+```
+
+- LocalID是发送时指定的LocalID。
+- MsgID是服务端的消息ID。
+
+### 4.3、上传媒体到服务器
+
+如果我们发送一个图片、文件等媒体类消息,会看到在发送消息请求前有如下 POST 请求:
+
+```
+https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json
+```