分享

手把手教你编写--微信机器人

 思想年代 2017-12-08

一. 引言

      我们都知道,微信提供了多种登录的方式,包括手机端、电脑端以及web端。

      web端的登录,我们用Python程序完全可以模拟出来~~(如果你不知道,那也没关系,稍微了解下Python request session即可)

      而所谓的机器人实际上就是后台一个智能的程序,类似“微软小冰”,“iPhone siri”。今天我们要用的是一个开放的机器人API,“图灵机器人”

      下面就让我们一步步分析如何,通过模拟web端微信登录+“图灵机器人” 实现一个微信机器人


二. 深入分析

      1. web版微信不是用用户名密码而是用扫描二维码登录,如何实现的呢?

          让我们登录https://wx.qq.com/,查看此时的网络请求情况 如下图所示

          1). 实际上客户端会先发送一个js get请求,请求url为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&_=1469852355025

仔细分析这个请求,会发现有已下几个参数

appid: wx782c26e4c19acffb //这个值不变,表示来自微信网页版

redirect_uri: https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage //这个也是一个固定值

fun: new //固定值位new

lang: zh_CN //表示中文

_: 1469852355025 //13位时间戳

2).然后服务端返回数据,window.QRLogin.code = 200; window.QRLogin.uuid = "IatVataLfQ==";

2. 多刷新几次,你会发现服务端的返回值中window.QRLogin.uuid的值每次都在变化。

实际上uuid是服务端用来标识一次登录的通信id

       

       


        2. 当我们拿到uuid后,就需要获取二维码,继续查看当前的网络请求

            1). 客户端继续发送一个js get 请求,url为https://login.weixin.qq.com/qrcode/IatVataLfQ==

仔细分析这个请求,会发现qrcode后跟着的值就是从上一个请求拿到的uuid值

2). 当拿到二维码之后,还需用微信客户端进行扫描(god,都有客户端了为什么还需用用web登录~~~~)

        3. ok,拿出手机扫描屏幕的二维码,继续查看网络请求

           1).  当我们在APP上点击登录按钮之后,实际上客户端是向服务端发送了一个js的get 请求,url为https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=od5FW4ipFw==&tip=0&r=-979422099&_=1469857970642

仔细分析这个请求,会发现有以下几个参数

uuid: od5FW4ipFw== //从上面请求得到的数据

tip: 0 //表示等待用户扫描确认

r: -979422099 //随机9位数字

_: 1469857970642 //13位时间戳

2). 这个请求,返回结果如下所示

window.code=200;
window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AUyRV3zm5RBTWt-mEvvDz8oz@qrticket_0&uuid=od5FW4ipFw==&lang=zh_CN&scan=1469858238";

code=200表示的是成功,redirect_uri表示需要我们继续请求的url

      

      

       4. 继续上一个请求得到的redirect_uri

1). ok,分析这个uri请求https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AUyRV3zm5RBTWt-mEvvDz8oz@qrticket_0&uuid=od5FW4ipFw==&lang=zh_CN&scan=1469858238&fun=new&version=v2,对应参数如下所示

uuid: 同前面

scan: 1469858238 //表示用户扫描的时间戳,10位

其它参数保持不变即可

2). 这个请求会返回,我们登陆所需要的信息,返回值是一个xml数据,如下所示

<error><ret>0</ret><message>OK</message><skey>@crypt_b13bcf4_edeadfd5xxxxd5e6b289b614fac25e5ac</skey><wxsid>HON+SKvxxxxTihHV</wxsid><wxuin>8xxxx5640</wxuin><pass_ticket>um3UATy9MNzcwDDkVT4xxxxMn5B25G%2FcYIAVbpHnF8vU23yMflmUCFsZkMKbIJIP</pass_ticket><isgrayscale>1</isgrayscale></error>

(为了我的隐私,我把返回值做了一定的打码~~)

ret: 表示请求返回状态码,0表示成功

skey和wxsid以及wxuin都是具体微信用户的信息,不会变的,在后续的通信过程中需要用到

pass_ticket: 这个值在初始化登录页面的时候需要用到

      5. 现在我们已经拿到了用户认证相关的信息,包括skey和wxsid以及wxuin,需要初始化登录页面

什么是初始化登录页面,也就是我们平时登录APP客户端看到的那个页面,我们需要发送一个请求到服务端拿到数据,获取到常用的联系人和微信公众号

如下图所示,这个请求是一个post请求,需要我们带一些用户认证相关的信息到服务端

1). ok,让我们分析一下这个请求url: https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-979155549&pass_ticket=um3UATy9MNzcwDDkVT4xxxxMn5B25G%252FcYIAVbpHnF8vU23yMflmUCFsZkMKbIJIP

r: 随机的9位数据

pass_ticke: 从上一步请求返回值中获取的数据

2). post请求所需要的data,如下所示

{
          'BaseResponse': {
              'Uin': wxuin,
              'Sid': wxsid,
              'Skey': skey,
              'DeviceID': //15位随机串 'e'+str(random.random())[2:17]
          }
        }

3). 请求返回数据,如下所示

Ret: 0表示返回成功,ContactList表示的是联系人列表。

返回数据包含了当前登录账户的相关信息,比如wxuid,昵称 ...

  1. {  
  2. "BaseResponse": {  
  3. "Ret": 0,  
  4. "ErrMsg": ""  
  5. }  
  6. ,  
  7. "Count": 10,  
  8. "ContactList": [{  
  9. "Uin": 0,  
  10. "UserName": "filehelper",  
  11. "NickName": "xxxx",  
  12. "HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=653892799&username=filehelper&skey=@crypt_b13bcf4_edeadfd5615d5e6b289b614fac25e5ac",  
  13. "ContactFlag": 1,  
  14. "MemberCount": 0,  
  15. "MemberList": [],  
  16. "RemarkName": "",  
  17. "HideInputBarFlag": 0,  
  18. "Sex": 0,  
  19. "Signature": "",  
  20. "VerifyFlag": 0,  
  21. "OwnerUin": 0,  
  22. "PYInitial": "WJCSZS",  
  23. "PYQuanPin": "wenjianchuanshuzhushou",  
  24. "RemarkPYInitial": "",  
  25. "RemarkPYQuanPin": ""   
  26.  ...  

6. 登录成功,接下来我们要做的就是开启消息状态通知。

ok,继续看此时的网络请求,会发现客户端向服务端发送了一个post请求

https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?pass_ticket=um3UATy9MNzcwDDkVT42sVxMn5xxxx%252FcYIAVbpHnF8vU23yMflmUCFsZkMKbIJIP

1). 对于这个请求,相信大家都不会感到陌生,就只有一个参数pass_ticket,同上

2). post请求,需要的data模式如下

{
          'BaseResponse': {
              'Uin': wxuin,
              'Sid': wxsid,
              'Skey': skey,
              'DeviceID': //15位随机串 'e'+str(random.random())[2:17]
          }
          'ClientMsgId': 13位时间戳,
          'Code': 3  //固定值
          'FromUserName':  userNmae, //从初始化登录信息那边取到
          'ToUserName': userNmae, //从初始化登录信息那边取到
       }

3. 请求返回一个json数据

{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MsgID": "4049260553244269433"
}

Ret:0表示的是请求返回成功状态

     

        7. ok,到目前为止我们以及成功登录了微信并且开启了消息通知

            让我继续查看网页客户端,会发现有非常多如下图所示的请求。从请求名称中,我们知道这些请求在进行 同步刷新,轮询检查服务端的消息

            1). 分析下,当前get请求,https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1469858460705&skey=%40crypt_b13bcf4_edeadfdxxxxx5e6b289b614fac25e5ac&sid=HON%2BSKxxxxxxxxxV&uin=828xxxx40&deviceid=e477405243870570&synckey=1_653921573%7C2_653921810%7C3_653921702%7C11_653919729%7C13_653890051%7C201_1469858241%7C1000_1469856425%7C1001_1469851411&_=1469857970652

a. r --> 13位时间戳
       b. skey 同上,需要url quote
       c. sid 同上
       d. devicedid 同上
       e. synckey由 初始化登录页面信息返回串中Sync的list列表组成, 需要url quote
       f. _ 13位时间戳

2). 请求返回json数据,如下所示window.synccheck={retcode:"0",selector:"0"}

retcode:
        a. 0 正常
        b. 1100 失败/登出微信
        c. 1101 从其它设备登录微信网页版
       selector:
        a. 0 正常
        b. 2 新的消息
        c. 7 手机操作了微信

        

        

       8. ok,万事具备,只需要知道如何获取消息和发送消息即可了。

           让我们先看一下,当我们在网页上收到消息的时候,不断轮询的同步刷新请求会返回,window.synccheck={retcode:"0",selector:"2"} 或者是 window.synccheck={retcode:"0",selector:"6"}

           1). 这个时候,我们发现客户端会向服务端发送一个post请求,拉取新的消息数据,如下图所示

                post请求: https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=HONxxxxxqSwTihHV&skey=@crypt_b13bcf4_edeadfd5615d5xxxxx9b614fac25e5ac&pass_ticket=um3UATy9MNzcwDDkVTxxxxxMn5B25G%252FcYI

请求所带的参数同上

2). 请求会返回json数据,包含具体的消息数据和类型

  1. {  
  2. "BaseResponse": {  
  3. "Ret": 0,  
  4. "ErrMsg": ""  
  5. }  
  6. ,  
  7. "AddMsgCount": 1,  
  8. "AddMsgList": [{  
  9. "MsgId": "4937824389423381364",  
  10. "FromUserName": "@ca2e7ef4exxxxxd71ab5fd68ac405b70",  
  11. "ToUserName": "@c26fa48f87634xxxxxx1ada0a4fa30f",  
  12. "MsgType": 1,  
  13. "Content": "[抷é™]",  
  14. "Status": 3,  
  15. "ImgStatus": 1,  
  16. "CreateTime": 1469861910,  
  17. "VoiceLength": 0,  
  18. "PlayLength": 0,  
  19. "FileName": "",  
  20. "FileSize": "",  
  21. "MediaId": "",  
  22. "Url": "",  
  23. "AppMsgType": 0,  
  24. "StatusNotifyCode": 0,  
  25. "StatusNotifyUserName": "",  
  26. "RecommendInfo": {  
  27. "UserName": "",  
  28. "NickName": "",  
  29. "QQNum": 0,  
  30. "Province": "",  
  31. "City": "",  
  32. "Content": "",  
  33. "Signature": "",  
  34. "Alias": "",  
  35. "Scene": 0,  
  36. "VerifyFlag": 0,  
  37. "AttrStatus": 0,  
  38. "Sex": 0,  
  39. "Ticket": "",  
  40. "OpCode": 0  
  41. }  
  42. ,  
  43. "ForwardFlag": 0,  
  44. "AppInfo": {  
  45. "AppID": "",  
  46. "Type": 0  
  47. }  
  48. ,  
  49. "HasProductId": 0,  
  50. "Ticket": "",  
  51. "ImgHeight": 0,  
  52. "ImgWidth": 0,  
  53. "SubMsgType": 0,  
  54. "NewMsgId": 4937824389423381364  
  55. }  
  56. ],  
  57. "ModContactCount": 0,  
  58. "ModContactList": [],  
  59. "DelContactCount": 0,  
  60. "DelContactList": [],  
  61. "ModChatRoomMemberCount": 0,  
  62. "ModChatRoomMemberList": [],  
  63. "Profile": {  
  64. "BitFlag": 0,  
  65. "UserName": {  
  66. "Buff": ""  
  67. }  
  68. ,  
  69. "NickName": {  
  70. "Buff": ""  
  71. }  
  72. ,  
  73. "BindUin": 0,  
  74. "BindEmail": {  
  75. "Buff": ""  
  76. }  
  77. ,  
  78. "BindMobile": {  
  79. "Buff": ""  
  80. }  
  81. ,  
  82. "Status": 0,  
  83. "Sex": 0,  
  84. "PersonalCard": 0,  
  85. "Alias": "",  
  86. "HeadImgUpdateFlag": 0,  
  87. "HeadImgUrl": "",  
  88. "Signature": ""  
  89. }  
  90. ,  
  91. "ContinueFlag": 0,  
  92. "SyncKey": {  
  93. "Count": 8,  
  94. "List": [{  
  95. "Key": 1,  
  96. "Val": 653921573  
  97. }  
  98. ,{  
  99. "Key": 2,  
  100. "Val": 653921823  
  101. }  
  102. ,{  
  103. "Key": 3,  
  104. "Val": 653921702  
  105. }  
  106. ,{  
  107. "Key": 11,  
  108. "Val": 653919729  
  109. }  
  110. ,{  
  111. "Key": 13,  
  112. "Val": 653890051  
  113. }  
  114. ,{  
  115. "Key": 201,  
  116. "Val": 1469861910  
  117. }  
  118. ,{  
  119. "Key": 1000,  
  120. "Val": 1469856425  
  121. }  
  122. ,{  
  123. "Key": 1001,  
  124. "Val": 1469851411  
  125. }  
  126. ]  
  127. }  
  128. ,  
  129. "SKey": ""  
  130. }  
a. BaseResponse,Ret位0表示返回成功
       b. AddMsgCount 表示新消息个数
       c. AddMsgList 表示新消息列表
          MsgType   说明
          1     文本消息
          3     图片消息
          34    语音消息
          37    VERIFYMSG
          40    POSSIBLEFRIEND_MSG
          42    共享名片
          43    视频通话消息
          47    动画表情
          48    位置消息
          49    分享链接
          50    VOIPMSG
          51    微信初始化消息
          52    VOIPNOTIFY
          53    VOIPINVITE
          62    小视频
          9999  SYSNOTICE
          10000     系统消息
          10002     撤回消息



           10.  最后让我们来看一下,如何发送一个消息

                  1). 当我发送一个消息给好友的时候,实际是执行了一次post请求,如下图

                       url: https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=um3UATy9MNzcwDDkVT42sVxxxxxxxxG%252FcYIAVbpHnF8vU23yMflmUCFsZkMKbIJIP

2). post请求需要的data如下所示

{
          'BaseResponse': {
              'Uin': wxuin,
              'Sid': wxsid,
              'Skey': skey,
              'DeviceID': //15位随机串 'e'+str(random.random())[2:17]
          }
          'Type': //消息类型,同上
          'Content': //消息内容
          'FromUserName':  //发送用户
          'ToUserName': //接受用户
          'LocalID':  //13位时间戳+4位随机数
          'ClientMsgId': //同LocalId
        }

3). 当发送成功之后,服务端返回json数据

{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"MsgID": "2882629525509760458",
"LocalID": "14698626523310328"
}

Ret:0表示发送成功

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约