CNode

利用公众平台模拟登录发送微信消息给指定好友上(原创)

Yym1623发布于13 年前最后回复12 年前66 回复79311 浏览0 收藏

今天初来乍到cnode.js,也应该贡献贡献.看到微信公众平台,开始有点兴奋,能做个机器人玩玩,,随后用Node.js写了一个,觉得其实这没什么意思.很快就觉得腻了,于是有了做发送微信接口的想法.首先要做的我们就要模拟公众平台的登陆.对于微信的这些lib,当然不能直接写在routes里面,,那要怎么办呢?没错,就要封装起来,方便复用.你可以打开控制台看到公众平台的登录请求,还有所需的参数,其中密码它是用它本身的md5进行加密的,那么我们需要做的只是将它copy过来放在一个helpers/wx/md5.js文件里就可以直接用了,以下是微信公众平台解析后格式化的js提交代码

submit: function() {
      if (!n()) return;
        var e = d.getVal();
         t.post("/cgi-bin/login?lang=zh_CN", {
            username: e.account,
            pwd1: t.md5(e.password.substr(0, 15)),
            pwd2: t.md5(e.password),
            imgcode: f.data("isHide") ? "": e.verify,
            register: e.isRegister,
            f: "json"
         },

我们要建立一个login的方法

request = require 'superagent'
require __basename + '/helpers/wx/md5'
config = require __basename + '/config/config'
module.exports = 
  login: (fn) ->
    wx_usr = config.wx.user
    wx_pwd = md5 config.wx.pwd.substr(0, 16)
    request
      .post('http://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN')
      .type('form')
      .send(
        username: wx_usr
        pwd: wx_pwd
        imgcode : ''
        f : 'json'
        register : 0
      )
      .end (res) ->
          //在这里你已经成功获取cookie了

但是经过分析我想你会发现,这里的cookie其实并非你想要的cookie,因为它包含一些没用的信息Path=,我们设置cookie的时候,事实上是不能用直接设置这样的cookie,应该是一个cookie里面不应该有其他的东西,而分号后面的path应该将它去掉,这里是返回的结果:

[
"mp_user=xxxxxx; Path=/",
"mp_sid=NlJ2Tm5hb1NXRGxOU3V1MzF2a25tSFVWRHhTNkhwek1nMXlEOVZzMnZMUG1lZ29nSkdENGt3WlgwUjBJZnhydndYNkZSd0ZsaHRHdEozSHBIa3QwT3FWTmdXc3RxVFhYUDBCR3dnWkxIRWVvRlZObG15UC83SzU1aEZPZWpocU8=; Path=/"
]

以下是完整的login代码:

login: (fn) ->
    wx_usr = config.wx.user
    wx_pwd = md5 config.wx.pwd
    request
      .post('http://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN')
      .type('form')
      .send(
        username: wx_usr
        pwd1: wx_pwd
        pwd2: wx_pwd
        imgcode : ''
        f : 'json'
      )
      .end (res) ->
        cookie = ''
        for rs in res.header['set-cookie']
          cookie += rs.replace(/Path=\//g, '')
        fn null, cookie

在这里,我们已经完成登录的操作了,接下来,我们要做的是进行发送,在发送的时候,要把这个cookie设置在请求的地址中,接下来的代码比较简单:(注意:这里面的fakeid是微信公众平台的id,我们可以用控制台去微信公众平台的用户管理中查看,如何获取用户好友的fakeid,接下来一章我会讲)

sender: (options, fn) ->
    msg = options.msg
    fakeid = options.fakeid

    unless msg
      fn error: 'missing msg'
      return

    unless fakeid
      fn error: 'missing fakeid'
      return

    psotParams =
      type: 1
      content: msg
      error: false
      tofakeid : fakeid
      ajax : 1

    request
      .post('http://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response&lang=zh_CN')
      .type('form')
      .send(psotParams)
      .set('Cookie', options.cookie)
      .end (res) ->
        fn null, JSON.parse res.text

这里,我们已经能完全发送了,因为返回的结果是一个json,所要最好先JSON.parse一下,里面的成功判断大家可以加上,返回的接口有个叫ret的参数,0为发送成功

{
ret: "0",
msg: "ok"
}

下一章,我会为大家深入讲解怎么发送信息给好友,及获取微信头像等等技术

查看回复

回复 (65)

Y
ym1623#1·13 年前

在调用的时候其实也比较简单,刚忘写了补上:

app.get '/wx/login', (req, res) ->
    msg = req.query.msg
    wx.login (err, cookie) ->
      res.json err if err
      data =
        msg    : msg
        fakeid : 'xxxx'
        cookie :  cookie
      wx.sender data, (err, results) ->
        res.json err if err
        res.json results
Z
zcl521ss#2·13 年前

收藏了

Z
zhihuzeye#3·13 年前

登录的时候有验证码

Y
ym1623#4·13 年前
引用 zhihuzeye登录的时候有验证码

你用的是新注册的qq账号绑定的吧,偶尔用qq登录下,过了一个星期就不会有这个问题了.

Y
ym1623#5·13 年前

sender目前只能实现文字text的发送,但这并不完善,也可以实现图文的发送,但是却必须要上传一张封面图片,上传后需要在发送图文信息的时候把返回结果中的formId拿到,http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=2&t=iframe-uploadfile&lang=zh_CN&formId=1其中的参数type是上传的类型,语音和视频是0,formId为null.上传封面图片接口如下:

uploadmaterial: (fn) ->
    @login (err, cookie) ->
      request
        .post('http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=2&t=iframe-uploadfile&lang=zh_CN&formId=1')
        .type('form')
        .set('Cookie', cookie)
        .end (res) ->
          results = JSON.parse(res.text).match(/formId, '(\d+)'/)[2]
          fn null, results

成功后,可以进行图文消息的发送了.

  send_appmsg: (options, fn) ->
    @login (err, cookie) ->
      psotParams =
        error       : false
        count       : 1
        AppMsgId    : null
        title0      : options.title
        digest0     : '正文内容'
        # content0    : '<p>te<img src="http://www.e-research-solutions.com/system/cms/themes/default/img/top_l.png" /></p><p><span style="color:red">测试标题</span></p>'
        content0    : options.msg
        fileid0     : '10000001' #此处的id即为封面图片的id
        preusername : options.username
        ajax        : 1
      request
        .post('http://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview')
        .type('form')
        .set('Cookie', cookie)
        .send(psotParams)
        .end (res) ->
          results = JSON.parse res.text
          fn null, results['msg']
W
wuyonghui0810#6·13 年前

看不太懂,似乎是高手。。。

Y
ym1623#7·13 年前
引用 wuyonghui0810看不太懂,似乎是高手。。。

可能是我用node.js中的coffeescript语法写的原因,很多人都向我反映,以后我会注意的.

Z
zrhhust#8·13 年前

preusername : options.username 这里应该填什么?微信用户名?

S
sipgear#10·13 年前

非常好的技术文章。在github也有类似的机器人。但不能群发信息给微信好友。

L
luxueyan2008#11·13 年前
引用 wuyonghui0810看不太懂,似乎是高手。。。

@ym1623 支持作者可以换js语法来写,个人很讨厌将js这么好的语言 用coffeescript写,我觉得大多数用node的人都是喜欢js的人 如果写成这样 那还不如去用rails 为嘛用nodejs呢

Y
ym1623#12·13 年前
引用 wuyonghui0810看不太懂,似乎是高手。。。

@luxueyan2008 谢谢你的建议,起初用coffeescript写是为了开发的效率,现在为了分享,我以后改用javascript语法写,希望能帮助到更多的人.

Z
zrhhust#13·13 年前
引用 ym1623sender目前只能实现文字text的发送,但这并不完善,也可以实现图文的发送,但是却必须要上传一张封面图片,上传后需要在发送图文信息的时候把返回结果中的formId拿到,http://mp....

文件上传怎么体现的?我看这里只有一个post请求,post的数据中并没有封面图片的数据信息?另外,我post了一个文件给微信的这个地址,得到的答案中没有formid,不知道为啥?大侠帮忙解答下?

Line-based text data: text/html document.domain = location.hostname.match(/[^.]?.[^.]?$/); var url = window.location.href, type = url.match(/[?&]type=([^&])/), formId = url.match(/[?&]formId=([^&])/);

  type = type[1] || 0;
  formId = formId[1];
      top.W.upload.err("\344\270\212\344\274\240\347\232\204\347\264\240\346\235\220\346\240\274\345\274\217\344\270\215\346\255\243\347\241\256", type, formId);  
  </script>
H
heyke2114#14·13 年前
引用 ym1623sender目前只能实现文字text的发送,但这并不完善,也可以实现图文的发送,但是却必须要上传一张封面图片,上传后需要在发送图文信息的时候把返回结果中的formId拿到,http://mp....

试着将楼主的js用Phpl来实现 不过上传文件一直返回 php代码是这样的

		$send_snoopy = new Snoopy();
	$send_snoopy->agent = "(Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0)"; //伪装浏览器  
	$send_snoopy->referer = "http://mp.weixin.qq.com/cgi-bin/indexpage?lang=zh_CN&t=wxm-upload&lang=zh_CN&type=0&fromId=file_from_1341151893625"; //伪装来源页地址 http_referer  
	$send_snoopy->rawheaders['Cookie'] = $cookie;
	$send_snoopy->rawheaders["Host"] = "mp.weixin.qq.com"; 
	$post = array();
	$post['uploadfile'] = $imgContent;
	$post['formId'] = '';
	$submit = "http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=2&t=iframe-uploadfile&lang=zh_CN&formId=null";
	$send_snoopy->submit($submit,$post);
	$result = $send_snoopy->results;
	preg_match('/formId, \'(\d*)\'/',$result,$imgId);
	print_r($result);
	echo $imgId[1];

`

这是为什么呢?

N
nodejsainy#15·13 年前

想請教一下,我在_ref = res.header['set-cookie'];這一段出了問題 log出來看res.header裡面沒有set-cookie 請問是哪裡出錯了嗎

Y
ym1623#16·13 年前
引用 nodejsainy想請教一下,我在 ref = res.header['set cookie'];這一段出了問題 log出來看res.header裡面沒有set cookie 請問是哪裡出錯了嗎

很有可能是因为有验证码,所以导致你登录不成功.代码我已经放在github中,你可以下载使用. https://github.com/ym1623/node_wx

N
nodejsainy#17·13 年前

原來的作法無解 應該是不熟悉coffee造成 都轉成js後已經可以了 謝謝

A
aven86#20·13 年前
Y
yutingzhao1991#21·13 年前

不错不错,希望什么时候搞得成熟了直接在npm上面发布为一个模块拿来就用 :) 拿来主义 哈哈

J
jkeen#22·13 年前

今天开始,好像 http://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response&lang=zh_CN 调用不成功了,返回{ret:'-1',msg:'need post'}

H
hewy0526#23·13 年前

我也发现这个方法已经不能推送了,虽然已经是 post 请求,同样不能推送,就是4月3号开始好像就不信了

T
toctan#25·13 年前

@hewy0526 @jkeen POST headers 加上 "Referer" => "http://mp.weixin.qq.com/cgi-bin/singlemsgpage" 就行了

Y
ym1623#26·13 年前

大家要注意一下,因为公众平台本身也有cookie过期时间,大家获取到cookie时应该放入session,另外还需要写个restart.js的脚本,意在解决登录不成功的问题,如果session过期,就运行restart文件,可以写成nodejs也可以写成shell调用.如果大家觉得麻烦的话,大家有时间的话也可以研究一下网页版的微信登录接口,有朋友说用的是line的登录验证方法,已经破解了,大家也可以尝试一下.

J
jkeen#27·13 年前
引用 toctan@hewy0526 @jkeen POST headers 加上 "Referer" = "http://mp.weixin.qq.com/cgi bin/singlemsgpage" 就行了

加上就可以了,多谢! 另:微信零点改版后,URL需要加上从Login的Response中得到的Token即可。

W
wengebin#28·13 年前
引用 toctan@hewy0526 @jkeen POST headers 加上 "Referer" = "http://mp.weixin.qq.com/cgi bin/singlemsgpage" 就行了

@jkeen 我也发现了返回有token,意思是需要连续发起两次登录请求?第一次返回token,第二次加上token再登录?但是经过尝试依旧无法登录,第二次照样返回一个token,而且如果cookie如果没有包含 sig / cert 两个参数,就会返回-6(根据JS的描述是需要验证码),明显太坑爹了!求解决办法

X
xing320#29·13 年前
引用 toctan@hewy0526 @jkeen POST headers 加上 "Referer" = "http://mp.weixin.qq.com/cgi bin/singlemsgpage" 就行了

@wengebin 先用http://mp.weixin.qq.com/cgi-bin/verifycode?username=,这个获得验证码,然后用login api去登录,参数里面输入刚才的imgcode,成功后会返回 { "Ret": 302, "ErrMsg": "/cgi-bin/indexpage?t=wxm-index&lang=zh_CN&token=xxxxxx", "ShowVerifyCode": 0, "ErrCode": 0 } 里面的token就是每次发送消息时,需要传给服务器的,每次登录token都会变

J
jkeen#30·13 年前

@wengebin 我是直接调用 login,从返回的result里取token,好像没有验证码也可以: .send({ username: wx_usr, pwd: wx_pwd, imgcode: '', f: 'json' } 返回: { "Ret": 302, "ErrMsg": "/cgi-bin/indexpage?t=wxm-index&lang=zh_CN&token=xxxxxx", "ShowVerifyCode": 0, "ErrCode": 0 }

var token = res.body.ErrMsg.match(new RegExp("[?&]token=([^&]+)","i"));

这个token保存起来,下次访问用保存的cookie和token访问。我的做法是在每次调用接口前调用 /cgi-bin/getcontactinfo? 测试一下cookie和token是否有效。 if (res.statusCode !=200 || res.text.indexOf('登录超时')>0){ //需要重新登录 }else{ 使用之前保存的cookie和token }

J
jkeen#31·13 年前

不知是否能从腾讯的post请求中获取原始请求者(微信用户)的ip地址呢?

X
xianglei1130#32·13 年前

{"ret":"-20000", "msg":"Request is denied! "} 返回了这样信息

X
xianglei1130#33·13 年前
引用 jkeen@wengebin 我是直接调用 login,从返回的result里取token,好像没有验证码也可以: .send({ username: wx usr, pwd: wx pwd, imgc...

不知php的这种功能已经成功? 我测试返回了{"ret":"-20000", "msg":"Request is denied! "} 该怎么解决?

W
wengebin#35·13 年前
引用 toctan@hewy0526 @jkeen POST headers 加上 "Referer" = "http://mp.weixin.qq.com/cgi bin/singlemsgpage" 就行了

@xing320 灰常感谢,之后找到了解决办法,此办法是完全可行的,目前的做法是将cookie和token一起保存在文件中

X
xyn0563#36·13 年前
引用 xianglei1130{"ret":" 20000", "msg":"Request is denied! "} 返回了这样信息

难道现在这种方法被微信给禁了?我也是这样的错误信息

L
libamboo#37·13 年前
引用 jkeen不知是否能从腾讯的post请求中获取原始请求者(微信用户)的ip地址呢?

@jkeen 我遇到点问题想请教下 现在调login接口返回值里已经没有cookies信息了 怎么获取头信息里的set_cookies字段值呢。

J
jkeen#38·13 年前
引用 jkeen不知是否能从腾讯的post请求中获取原始请求者(微信用户)的ip地址呢?

@LIbamboo 我用的是Node.js,不是PHP,不是很懂PHP,如果是Node.js的话,按楼主的方法是可以的。。。

Y
ym1623#41·13 年前

最新的代码已经更新至github中:https://github.com/ym1623/node_wx 由于我今天晚上11点钟开始去弄,弄好后马上更新,经测试,现已能正常发送微信 更新如下: 添加:token 添加发送referer防止发送不到信息 本来想弄下记住密码那个功能的,由于时间太晚就没有去更新,大家有谁有兴趣的话可以在git中提交分支给我,我会审阅,希望更完善.

L
lijiangen1986#44·13 年前

我之前没弄过模拟登陆,所以上面很多东西我都不明白呢?? submit: function(),这个是干啥用的???

Y
ym1623#45·13 年前

sublmit : function 是微信公众格式化后js登录代码,我们就是在nodejs中进行模拟过程,具体的模拟代码你可以移步到我的github/node_wx中

H
huangbz2007#47·13 年前
引用 ym1623sublmit : function 是微信公众格式化后js登录代码,我们就是在nodejs中进行模拟过程,具体的模拟代码你可以移步到我的github/node wx中

模拟登陆时: { "Ret": 302, "ErrMsg": "/cgi-bin/indexpage?t=wxm-index&lang=zh_CN&token=1741837296", "ShowVerifyCode": 0, "ErrCode": 0 }

发送消息时返回:{"ret":"-20000", "msg":"Request is denied! "},请问什么原因呀?

H
huangbz2007#48·13 年前
引用 ym1623sublmit : function 是微信公众格式化后js登录代码,我们就是在nodejs中进行模拟过程,具体的模拟代码你可以移步到我的github/node wx中

@huangbz2007 返回cookie:slave_sid=NnY5UjNvN2c5ZldnZWduZ2JJak5qS3N5Qm1zdkhBakw0c0I4UUltX1ZoZjd4QW5HaEh0em9UNF9md3VVNU9FOGZmZTNWQTBRU2RYbU5oMDJZckJrakZqeThMcmN3NWlNaXdOYTVOX1ZfVWhLYmZLNnNudlFZSGxmZ0xxMWp0Nmc=; Path=/; Secure; HttpOnly

cookieVal=slave_sid=NnY5UjNvN2c5ZldnZWduZ2JJak5qS3N5Qm1zdkhBakw0c0I4UUltX1ZoZjd4QW5HaEh0em9UNF9md3VVNU9FOGZmZTNWQTBRU2RYbU5oMDJZckJrakZqeThMcmN3NWlNaXdOYTVOX1ZfVWhLYmZLNnNudlFZSGxmZ0xxMWp0Nmc=; Path=/; Secure; resumeConnection.setRequestProperty("Cookie", cookieVal); resumeConnection .setRequestProperty("Referer", "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response&lang=zh_CN");

Y
ym1623#49·13 年前
引用 ym1623sublmit : function 是微信公众格式化后js登录代码,我们就是在nodejs中进行模拟过程,具体的模拟代码你可以移步到我的github/node wx中

@huangbz2007 这个是返回的结果,我们模拟登陆后其实是需要这个token和cookie,然后正则过滤一下cookie,得到真正的cookie,你可以在我的github中参考一下看我是怎么做的

Y
ym1623#50·13 年前
引用 jkeen今天开始,好像 http://mp.weixin.qq.com/cgi bin/singlesend?t=ajax response&lang=zh CN 调用不成功了,返回{ret:' 1'...

那是因为在发送的时候他们加了referer机制,在调用发送的时候你在请求头设置referer即可,具体请参照我在github中的写法

L
lzyzsd#52·13 年前

可以尝试webot试试哈

R
realeyes#53·13 年前

楼主辛苦了,请发给我一份完整代码吧,我的邮箱是:168177867@qq.com,谢谢!

Y
ye3312#54·13 年前

我用php写的模拟登录是正常的,发送也正常的,可是我想取得用户的列表就不行了返回 {"ret":"-20000", "msg":"Request is denied! "},昨天的时候还可以,估计是今天微信官方做了调整。大家有什么解决方式吗

T
timmit#55·13 年前
引用 ye3312我用php写的模拟登录是正常的,发送也正常的,可是我想取得用户的列表就不行了返回 {"ret":" 20000", "msg":"Request is denied! "},昨天的时候还可以,...

我今天早上也是出现这种情况,但是后来重新登录好了(我昨天用的时候改成错误的用户名和密码了,今天第一次登的时候出现了你这种情况)会不会是cookie失效了

8
835127729#56·13 年前

php模拟登陆可以上传图片素材,但是不能上传语音素材,谁能指教一下?@xing320 @heyke211 @ym1623

Y
ym1623#57·13 年前
引用 835127729php模拟登陆可以上传图片素材,但是不能上传语音素材,谁能指教一下?@xing320 @heyke211 @ym1623

如果你有成功模拟php上传图片素材可以把代码贡献出来,可能能帮助到更多人,至于上传语音素材还要研究一下,应该不会太难./

C
cifer#58·13 年前
引用 wuyonghui0810看不太懂,似乎是高手。。。

@ym1623 我说这js怎么看着这么怪,孤陋寡闻了。。。。

4
4796642#59·13 年前
引用 ym1623sender目前只能实现文字text的发送,但这并不完善,也可以实现图文的发送,但是却必须要上传一张封面图片,上传后需要在发送图文信息的时候把返回结果中的formId拿到,http://mp....

你好,我写的代码总返回登录超时,Cookie我 也从模拟登录中得到了,并且请求时也赋值了。还是我给的 formid参数值不对啊 ,你说的那个 formid发送图文时如何获到呢,希望帮忙,谢谢,我的QQ4796642

F
fanlu7311#61·13 年前

现在还好使么? headers.set("Referer", "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid=****&count=20&t=wxm-singlechat&token=****&lang=zh_CN"); headers.set("User-Agent","Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20100101 Firefox/22.0"); headers.set("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"); headers.set("Host","mp.weixin.qq.com"); headers.set("Origin","https://mp.weixin.qq.com");

我都加了这么多了,还是返回 {"ret":"-20000", "msg":"Request is denied! "}

F
fanlu7311#62·13 年前
引用 fanlu7311现在还好使么? headers.set("Referer", "https://mp.weixin.qq.com/cgi bin/singlemsgpage?fromfakeid= &coun...

忽略我吧,我把Cookie写成了Coolie 至目前来说,功能还是好使的

C
carly1987#64·12 年前
引用 wlpphper@cashlee 关于微信模拟登录可交流一下, 说不定有新的进展.

@wlpphper 模拟登陆 你有成功么。 我也在写

参与回复
登录后即可参与回复。登录