基于nodejs的http模块通过smartqq实现自动收发qq消息的程序

---恢复内容开始---

  背景:2月1日我们实验室的qq群引入了一个聊天机器人,可实现签到,打劫,玩游戏(如24点,猜字谜等)等的功能,签到,打劫成功,游戏胜利(如24点回答正确)可获得积分,寒假时未曾关注群所以开学时自己毫无积分,而其他同学都已为富一方,尤其是某学长积分竟达十万之巨,(签到一次100左右,24点回答正确100),遂疑惑,问之,学长曰:无他,刷分耳。于是在4月9日,参考了下学长的思路(http://www.zhangzaizai.com/2017/02/08/xiaozi-helper/),便开始试着开发一个刷分的辅助。

  技术:我的后端平台是nodejs,其他框架不是学得很深所以用最底层的http模块来发送post请求。用明文传输信息的smartqq来作为post的接受端。

  步骤

  1.   了解只需发送两种post请求,一是代表发送信息的send_qun_msg2,可以看到在F12下post信息十分详细,那么在nodejs中照本宣科即可。

    var http = require(‘http‘);
    exports.post = function (contents) {
        var options = {
            host: "d1.web2.qq.com",
            method: "POST",
            path: "/channel/send_qun_msg2",
            headers: {
                ‘Host‘: ‘d1.web2.qq.com‘,
                ‘User-Agent‘: ‘Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0‘,
                ‘Accept‘: ‘*/*‘,
                ‘Accept-Language‘: ‘en-US,en;q=0.5‘,
                ‘Content-Type‘: ‘application/x-www-form-urlencoded‘,
                ‘Referer‘: ‘https://d1.web2.qq.com/cfproxy.html?v=20151105001&callback=1‘,
                ‘Content-Length‘: contents.length,
                ‘Cookie‘: ‘‘,/*出于安全考虑隐去cookie*/
                ‘Connection‘: ‘keep-alive‘
            }
        };
        var req = http.request(options, function (res) {});
        req.write(contents);
        req.end();
    };

    我为了操作方便把它封装在了模块中。

  2. 然而这看似简单的代码实际上出过很大的问题,这是我已经整理过得代码,但是在初期我的content并不是外部的参数,而是自己定义的一个JSON(如下

    var rr = {
        "group_uin": 4111913875,
        "content": "[\"开始24点\",[\"font\",{\"name\":\"宋体\",\"size\":10,\"style\":[0,0,0],\"color\":\"000000\"}]]",
        "face": 210,
        "clientid": 53156849,/*出于安全考虑隐去真实数字,换以同长数字*/
        "msg_id": 62220001,
        "psessionid": ""/*出于安全考虑隐去psessionid*/
    };

    ),但是传输老失败,后来询问学长,发现实际传输的内容并不是直接JSON,而需要经过URLencode加密,于是我调用了nodejs自带的encodeURI函数把上述加密后传输(),后即成功

    var contents2 = encodeURI(‘r=‘ + JSON.stringify(rr));
     
  3. 但马上我就碰到了另一个问题,我的响应是一堆乱码,虽然给群里发送消息并不需要考虑响应,但是我post的方式肯定是一样的,也就意味着我接受群里消息时也是一顿乱码,就更别谈处理了。这个问题大概花了我两天时间,一开始怀疑是charset,utf-8的问题,于是在请求头里各种改(比如request.setCharacterEncoding("utf-8")),但是并没有用,又怀疑smartqq用的根本不是utf-8,一查是的(如下图)
  4. 然后继续想,这期间查阅了不少网站,比如(http://blog.csdn.net/zsr_251/article/details/49993911)于是猜测问题在buffer的拼接上,几经周折尝试各种方法(如
    
    
    
    var buf=Buffer.concat(chunks,size);
     var str=iconv.decode(buf,‘utf-8‘);
    
    
     

    )还是不行。

  5. 最后打算对于请求头信息一个一个分析发现了这个
    
    
    
    ‘Accept-Encoding‘: ‘gzip, deflate, br‘,
    
    
     

    于是寻求相关信息,发现(http://www.jb51.net/article/61721.htm)这个网站,尝试着解压了下响应体,如

    
    
    
    zlib.unzip(body, function (err, buffer) {
    console.log(buffer.toString());
    }) 
    
    
     

6. 然后就ok了,当屏幕上出现send ok时心情还是蛮激动的,然后十分兴奋的告诉学长,只听学长一声“你可以发请求头要求它不做gzip压缩的”,遂删去Accept-Encoding。发送消息至此就算成功了。在此向某个被我拿来做实验的群说声抱歉,我不是故意水的。。

  接下来是接受消息。

在smartqq上接受消息的原理是不断post poll2当有人发给你消息时post就会响应,响应内容即为接受内容。那么模拟它即可。

  此post跟上述的并没有太大的不同,但是要注意option的path要变了,以及content-length固定不变,option的代码如下,

var options = {
    host: "d1.web2.qq.com",
    method: "POST",
    path: "/channel/poll2",/*注意此处*/
    headers: {
        ‘Host‘: ‘d1.web2.qq.com‘,
        ‘User-Agent‘: ‘Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0‘,
        ‘Accept‘: ‘*/*‘,
        ‘Accept-Language‘: ‘en-US,en;q=0.5‘,
        ‘Content-Type‘: ‘application/x-www-form-urlencoded‘,
        ‘Referer‘: ‘https://d1.web2.qq.com/cfproxy.html?v=20151105001&callback=1‘,
        ‘Content-Length‘: ‘395‘,/*我认为由于参数不同你们的不一定是395,自己抓包即可获得长度*/
        ‘Cookie‘: ‘‘,/*出于安全考虑隐去cookie*/
        ‘Connection‘: ‘keep-alive‘
    }
};

   重点在于对响应的处理。当我输入“开始24点”后小紫(机器人)会返回这样一段话:"24点开始!数字(共有6种解法):8, 8, 8, 5将以上数字加减乘除(+,-,*,/)得出24,并列出算式即可。如发送:24点 (4-2)*12/1",当我回答正确后会返回:"很强悍!算式正确。增加100积分当前数字(共有20种解法):9, 9, 3, 5将以上数字加减乘除(+,-,*,/)得出24,并列出算式即可。如发送:24点 (4-2)*12/1",我要做的是正确获取四个数字并避免其他信息干扰,同时不能在没获得信息,即没人互动时程序整个挂掉(会返回一个HTML网页),还要根据4个数字计算得出正确的字符串。

  1. 从24点算法开始,我在(http://www.99cankao.com/numbers/24game.php)网页上直接找到了计算24点的代码(如下图),把输入的交互改成参数传入,并把返回值改成字符串后就可以直接用了,我把它封装在了calc模块中方便调用,代码如下。

    function tdisoper(f0, f1, f2, f3) {
    this[0] = f0;
    this[1] = f1;
    this[2] = f2;
    this[3] = f3;
    }
    disoper = new tdisoper("-", "+", "/", "*");
    function oper(f, m, n) {
    if (f === 3) return (m * n);
    if (f === 2) return (m / n);
    if (f === 1) return (parseFloat(m) + parseFloat(n));
    if (f === 0) return (m - n);
    }
    function tb(i1, i2, i4, i8) {
    this[1] = i1;
    this[2] = i2;
    this[4] = i4;
    this[8] = i8;
    }
    function valid(a,b,c,d) {
    var result = ‘‘;
    n = 1;
    b = new tb(a, b, c, d);
    k = 0;
    var output = "";
    for (i1 = 1; i1 <= 8; i1 *= 2)
    for (i2 = 1; i2 <= 8; i2 *= 2)
    for (i3 = 1; i3 <= 8; i3 *= 2)
    for (i4 = 1; i4 <= 8; i4 *= 2) {
    if ((i1 | i2 | i3 | i4) !== 0xf) continue;
    for (f1 = 0; f1 <= 3; f1++)
    for (f2 = 0; f2 <= 3; f2++)
    for (f3 = 0; f3 <= 3; f3++) {
    m = oper(f3, oper(f2, oper(f1, b[i1], b[i2]), b[i3]), b[i4]);
    if (Math.abs(m - 24) < 1e-5) {
    result = result + "((" + b[i1] + disoper[f1] + b[i2] + ")" + disoper[f2] + b[i3] + ")" + disoper[f3] + b[i4];
    if ((n !== 0) && (++k >= n)) return result;
    }
    m = oper(f1, b[i1], oper(f3, oper(f2, b[i2], b[i3]), b[i4]));
    if (Math.abs(m - 24) < 1e-5) {
    result = result + b[i1] + disoper[f1] + "((" + b[i2] + disoper[f2] + b[i3] + ")" + disoper[f3] + b[i4] + ")";
    if ((n !== 0) && (++k >= n)) return result;
    }
    m = oper(f3, oper(f1, b[i1], oper(f2, b[i2], b[i3])), b[i4]);
    if (Math.abs(m - 24) < 1e-5) {
    result = result + "(" + b[i1] + disoper[f1] + "(" + b[i2] + disoper[f2] + b[i3] + "))" + disoper[f3] + b[i4];
    if ((n !== 0) && (++k >= n)) return result;
    }
    m = oper(f1, b[i1], oper(f2, b[i2], oper(f3, b[i3], b[i4])));
    if (Math.abs(m - 24) < 1e-5) {
    result = result + b[i1] + disoper[f1] + "(" + b[i2] + disoper[f2] + "(" + b[i3] + disoper[f3] + b[i4] + "))";
    if ((n !== 0) && (++k >= n)) return result;
    }
    m = oper(f2, oper(f1, b[i1], b[i2]), oper(f3, b[i3], b[i4]));
    if (Math.abs(m - 24) < 1e-5) {
    result = result + "(" + b[i1] + disoper[f1] + b[i2] + ")" + disoper[f2] + "(" + b[i3] + disoper[f3] + b[i4] + ")";
    if ((n !== 0) && (++k >= n)) return result;
    }
    
                                }
                    }
    return result;
    
    exports.cal = function(a,b,c,d){
    return valid(a,b,c,d);
    };
  2. 处理响应体并发送,代码如下,注释基本解释清楚,如有疑问欢迎提问。

    var req = http.request(options, function (res) {
        res.on(‘data‘, function (body) {
            if (body.toString().search(‘<html>‘) === -1) {/*此处是为了避免长时间未收到数据导致程序直接崩溃*/
                var str;
                str = JSON.parse(body.toString());/*解析响应体为JSON格式*/
                console.log("接受消息  " + str.result[0].value.content);/*获得接受内容*/
                if (str.result[0].value.send_uin === 8888888/*此处出于安全考虑。。*/) {
                    var usethen;/*含有关键4个数字的字符串*/
                    if (str.result[0].value.content.length >= 6)/*避免无关信息干扰*/
                        usethen = str.result[0].value.content[5];
                    if (usethen!==undefined) {
                        var use = usethen.match(/\u89e3[^\n]*\u4ee5/g);/*利用正则表达式获取从‘解‘到‘以‘的字符串*/
                        var number;
                        if (use!==undefined)
                            number = use[0].match(/[0-9]/g);  /*利用正则获取4个重要数字*/
                        console.log("获得数字" + number);
                        if (number !== undefined) {
                            var result = calc.cal(number[0], number[1], number[2], number[3]);
                            console.log("得到结果" + result);
                            var reg = /["][^\n]*[",]/;
                            rr.content = rr.content.replace(‘开始24点‘, "24点 " + result);/*rr就是上面的rr*/
                            contents2 = encodeURI(‘r=‘ + JSON.stringify(rr));
                            contents2 = contents2.replace(‘+‘, ‘%2b‘);/*为什么有这四个,我要放在下面讲*/
                            contents2 = contents2.replace(‘+‘, ‘%2b‘);
                            contents2 = contents2.replace(‘+‘, ‘%2b‘);
                            contents2 = contents2.replace(‘+‘, ‘%2b‘);
                            post.post(contents2);
                        }
                    }
                }
            }
        });
        res.on(‘err‘, function (err) {
            console.log(err);
        })
    });
    req.write(contents);
    req.end();

    为什么我要写四个contents = contents.replace(‘+‘,‘%2b‘)呢,因为encodeURI函数不会转换加号等特殊字符,导致我发送出去的字符串一直没有加号,这里又卡了我一下。如有更好的方法,希望有大神教与我。

  3. 最后是轮询发送poll2的问题,我一开始一直有error显示write after end,后来发现是我没有给req重新赋值,相当于请求结束后继续write自然有错,于是把赋值写在了轮询中。

至此,程序编的差不多了,作为并不熟练的noder,自然是有很多瑕疵,也希望有大神能够指出。

时间: 2024-12-09 16:16:04

基于nodejs的http模块通过smartqq实现自动收发qq消息的程序的相关文章

基于NodeJS的全栈式开发

随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越来越被重视,后端负责业务/数据接口,前端负责展现/交互逻辑,同一份数据接口,我们可以定制开发多个版本. 这个话题最近被讨论得比较多,阿里有些BU也在进行一些尝试.讨论了很久之后,我们团队决定探索一套基于NodeJS的前后端分离方案,过程中有一些不断变化的认识以及思考,记录在这里,也希望看到的同学参

基于nodejs模拟浏览器post请求爬取json数据

今天想爬取某网站的后台传来的数据,中间遇到了很多阻碍,花了2个小时才请求到数据,所以我在此总结了一些经验. 首先,放上我所爬取的请求地址http://api.chuchujie.com/api/?v=1.0: 下面我们开始爬取数据. 一.写一个基于nodejs的爬虫 1.引入所需模块 这里需要引入http模块(nodejs用来向浏览器发送http请求的模块)和querystring模块(把前台传过来的对象形式的参数转化成字符串形式): var http = require("http"

NodeJS经常使用模块收集

收集了NodeJS开发中经常使用的一些模块. MVC框架 - Express Express 是轻量灵活的Nodejs Web应用框架.它能够高速地搭建站点. Express框架建立在Nodejs内置的Http模块上,并对Http模块再包装.从而实际Web请求处理的功能.它支持多种前端模板,如Jade, EJS等.它是T.J大神的作品,只是已经交由其他团队维护了. Koa是T.J大神的另外一个作品,号称下一代NodeJS web框架.使用 koa 编写 web 应用.通过组合不同的 genera

也谈基于NodeJS的全栈式开发(基于NodeJS的前后端分离)

随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越来越被重视,后端负责业务/数据接口,前端负责展现/交互逻辑,同一份数据接口,我们可以定制开发多个版本. 这个话题最近被讨论得比较多,阿里有些BU也在进行一些尝试.讨论了很久之后,我们团队决定探索一套基于NodeJS的前后端分离方案,过程中有一些不断变化的认识以及思考,记录在这里,也希望看到的同学参

基于Nodejs构建属于自己的微信公众号

摘要: 微信,庞大的用户基数,极强的用户粘性,在近两年吸引了无数的开发者注意力. Nodejs,近两年发展非常快的开发工具,尤其适合构建移动后台.本文就以笔者自己开发的实例,来描述如何基于Nodejs开发属于自己的微信公众号.在这个实例中,主要使用到了express, wechat, mongodb, monk等模块. 前期准备: 1.   申请微信公众号, 前往 https://mp.weixin.qq.com/  申请,这里不做过多阐述. 2.   购买服务器, 这里推荐Amazon的EC2

基于NodeJS的全栈式开发(基于NodeJS的前后端分离)

也谈基于NodeJS的全栈式开发(基于NodeJS的前后端分离) 前言 为了解决传统Web开发模式带来的各种问题,我们进行了许多尝试,但由于前/后端的物理鸿沟,尝试的方案都大同小异.痛定思痛,今天我们重新思考了“前后端”的定义,引入前端同学都熟悉的NodeJS,试图探索一条全新的前后端分离模式. 随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越

关于Nodejs的多进程模块Cluster

关于Nodejs的多进程模块Cluster 前述 我们都知道nodejs最大的特点就是单进程.无阻塞运行,并且是异步事件驱动的.Nodejs的这些特性能够很好的解决一些问题,例如在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会导致资源浪费和时间延迟.通过事件注册.异步函数,开发人员可以提高资源的利用率,性能也会改善.既 然Node.js采用单进程.单线程模式,那么在如今多核硬件流行的环境中,单核性能出色的Nodejs如何利用多核CPU呢?创始人Ryan Dahl建议,运行多个Nodejs

Nodejs中关于模块的总结

关于Nodejs中的模块 概念 Nodejs在ECMAScript的基础上扩展并封装了许多高级特性,如文件访问.网络访问等,使得Nodejs成为一个很好的Web开发平台.基于Nodejs这个平台将Web开发常用的一些功能进行封装,称为模块. 1.系统模块 1. 模块被加载后才能使用,Nodejs提供了全局的函数require加载模块. 1.os模块 获取当前操作系统信息 2.fs模块 文件读写操作 3.path模块 路径处理 2. require加载模块路径 相对路径: 如 ./ 或 ../ 绝

第二篇 基于微擎的模块开发—PHP

从陌生到如今能勉强完成第一个微网站模块的实现.也算是一个小小的进步,从设计数据库到,返回数据,前端模版渲染 每一点都是有点难度的.所以我想总结一下,我是如何实现一个微擎模块. 第一,首先得分析某个模块的想实现什么需求,根据需求设计合理的数据库结构. 第二,了解微擎的结构,运行流程,设计模块结构. 第三,重点就是site.php , 完成site.php 需要一定的php的编程能力, 第四,site.php 其中 通过 pdo 从数据库的获取我们想得到数据源. 微擎已封装其路由机制, doWeb