nodejs实现微信公众号基本事件推回复

微信公众号开发,微信官方只开放了个人订阅号给个人开发者,很多事件推送接口都有限制,所以自己申请了一个测试号,相对于个人未认证的订阅号,多了一些接口,下面就微信开发者文档,用Nodejs的express框架实现了基本的事件回复,利用一些普通的api实现微信公众号常见的功能。代码方面没有使用市面比较流行的wechat和wechat-api开发框架,纯粹使用原生的消息模板进行事件的回复。

  • 准备工作:

    • 微信公众号的开发需要一台公网可以访问的服务器,现在百度BAE,腾讯的服务器,阿里云的ECS对于学生都具有优惠活动,国外的AWS更有新用户免费一年的活动。自己买的是阿里云学生优惠ECS,价格是¥9.9;关于阿里云服务器的配置可以直接参考其他教程。
    • 微信公众号的域名验证,可以参考上一遍文件,关于微信接入指南。
  • 对于微信各种事件回复的代码如下:

    • 对于微信事件回复的代码如下:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var xmlParse=require('xml2js').parseString;
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var sha1=require('sha1');
var routes = require('./routes/index');
var users = require('./routes/users');
var common=require('./common');
var request=require('request');
var app = express();
var data='';
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);
app.get('/validate',(req,res,next)=>{
    let token='wechat';
    let signature=req.query.signature;
    let timestamp=req.query.timestamp;
    let echostr=req.query.echostr;
    let nonce=req.query.nonce;
    let oriArray=new Array();
    oriArray.push(nonce);
    oriArray.push(timestamp);
    oriArray.push(token);
    let original=oriArray.sort().join('');
    let combineStr=sha1(original);
    if(signature==combineStr){
        res.send(echostr);
    }else{
        console.log('error');
    }
    next();
});
//对于各种微信服务器的事件推送进行回复
app.post('/validate',(req,res,next)=>{
    var data='';
    req.on('data',(chunk)=>{
        data+=chunk;
    });
    req.on('end',()=>{
        xmlParse(data,(err,result)=>{
            if(result.xml.MsgType=='event'){
                if(result.xml.Event=='subscribe'){
                    common.dealText('欢迎关注聊天小喵;\n 1.发送地址定位信息可以获得当地天气情况哟\n 2.发送歌曲名字可以获取想要听的歌哟\n3.发送语音信息可以直接小喵聊天哟,她会很多东西哟\n图片,视频识别功能还在开发中\n',res,result);
                }else if(result.xml.Event=='CLICK' && result.xml.EventKey=='V1001_IT_NEWS'){
                    common.requestMsg('https://api.tianapi.com/it/?key=APIkey&num=2',result,res);//API
key是天行数据的新闻apikey
                }else if(result.xml.Event=='CLICK' && result.xml.EventKey=='V1001_TRAVEL_NEWS'){
                    common.requestMsg('https://api.tianapi.com/travel/?key=APIkey&num=2',result,res);//如上
                }else if(result.xml.Event=='CLICK' && result.xml.EventKey=='V1001_VR_NEWS'){
                    common.requestMsg('https://api.tianapi.com/vr/?key=APIkey&num=2',result,res);//如上
                }else if(result.xml.Event=='CLICK' && result.xml.EventKey=='V1001_AMUSE_NEWS'){
                    common.requestMsg('https://api.tianapi.com/huabian/?key=APIkey&num=2',result,res);
                }
            }
            if(result.xml.MsgType=='location'){
                    let lat=(result.xml.Location_X);
                    let log=(result.xml.Location_Y);
                    const url='http://api.yytianqi.com/forecast7d?city='+lat+','+log+'&key=APIKEY';
                    common.requestWeather(url,result,res);                  
            }
            if(result.xml.MsgType=='image'){
                let str= '<xml><ToUserName><![CDATA['+result.xml.FromUserName+']]></ToUserName><FromUserName><![CDATA['+result.xml.ToUserName+']]></FromUserName><CreateTime>'+new Date().getTime()+'</CreateTime><MsgType><![CDATA['+'image'+']]></MsgType><Image><MediaId><![CDATA['+result.xml.MediaId+']]></MediaId></Image></xml>';
                res.send(str);
            }
            if(result.xml.MsgType=='text'){
                let responseMSg=(result.xml.Content).toString();
                let url=encodeURI("http://s.music.163.com/search/get?type=1&limit=10&offset=0&s="+responseMSg);
                common.requestSong(url,result,res);     
            }
            if(result.xml.MsgType=='voice'){
                let url='http://www.tuling123.com/openapi/api?key=APIKEY&info='+encodeURI(result.xml.Recognition.toString());
                common.requestRobot(url,result,res);    
            }

            if(result.xml.MsgType=='video'){
                let str='<xml><ToUserName><![CDATA['+result.xml.FromUserName+']]></ToUserName><FromUserName><![CDATA['+result.xml.ToUserName+']]></FromUserName><CreateTime>'+new Date().getTime()+'</CreateTime><MsgType><![CDATA['+'video'+']]></MsgType><Video><MediaId><![CDATA['+result.xml.MediaId+']]></MediaId><Title><![CDATA['+'video_info'+']]></Title><Description><![CDATA['+'information'+']]></Description></Video></xml>'
                res.send(str);
            }

        });
    });

});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;
- 各种事件回复函数模块文件
var request=require('request');
//click事件推送
function requestMsg(url,result,res){
    request(url,(err,response,body)=>{
        let data=JSON.parse(body);
        let arr=data.newslist;
        let str='<xml><ToUserName><![CDATA['+result.xml.FromUserName+']]></ToUserName><FromUserName><![CDATA['+result.xml.ToUserName+']]></FromUserName><CreateTime>'+new Date().getTime()+'</CreateTime><MsgType><![CDATA['+'news'+']]></MsgType><ArticleCount>'+'2'+'</ArticleCount><Articles><item><Title><![CDATA['+arr[0].title+']]></Title> <Description><![CDATA['+arr[0].description+']]></Description><PicUrl><![CDATA['+arr[0].picUrl+']]></PicUrl><Url><![CDATA['+arr[0].url+']]></Url></item><item><Title><![CDATA['+arr[1].title+']]></Title><Description><![CDATA['+arr[1].deacription+']]></Description><PicUrl><![CDATA['+arr[1].picUrl+']]></PicUrl><Url><![CDATA['+arr[1].url+']]></Url></item></Articles></xml>';
        res.send(str);
    });
}
//地理位置事件处理
function requestWeather(url,result,res){
    request(url,(err,response,body)=>{
        if(err){
            console.log(err);
        }

        data=JSON.parse(body);
        dealText('城市: '+data.data.cityName+'\n时间:'+data.data.sj+'\n天气情况:'+data.data.list[0].tq1+'\n白天温度:'+data.data.list[0].qw1+'度\n夜间温度'+data.data.list[0].qw2+'度\n白天风向 :'+data.data.list[0].fx1+'\n夜间风向:'+data.data.list[0].fx2+'\n',res,result);
    });
}
//处理文本点歌
function requestSong(url,result,res){
    request(url, function (error, response, body) {
            if(error){
                dealText('出错请重试!',res,result);
             }else if (!error && response.statusCode == 200) {
                var data=JSON.parse(body);
                if(data.result){
                    let url=data.result.songs[0].audio;
                    console.log(url);
                    request(url,(error, response, body)=>{
                        if(response["headers"]["content-type"]=='text/html'){
                            dealText('没有资源,请选择其他歌曲',res,result);
                        }else{
                            let picUrl=data.result.songs[0].album.picUrl;
                            let str='<xml><ToUserName><![CDATA['+result.xml.FromUserName+']]></ToUserName><FromUserName><![CDATA['+result.xml.ToUserName+']]></FromUserName><CreateTime>'+new Date().getTime()+'</CreateTime><MsgType><![CDATA['+'music'+']]></MsgType><Music><Title><![CDATA['+data.result.songs[0].name+']]></Title><Description><![CDATA['+'悠悠音乐,缕缕动听'+']]></Description><MusicUrl><![CDATA['+url+']]></MusicUrl><HQMusicUrl><![CDATA['+url+']]></HQMusicUrl><ThumbMediaId><![CDATA['+'ojH9Q9iDl50J6PjyFmQcYYKg51COLtm2SJpFdDFzR0jYNYt4JOjtfee0LKNDYzQa'+']]></ThumbMediaId></Music></xml>';
                            res.send(str);
                        }
                        
                    });     
                    
                }else{
                   dealText('发送歌曲名字不合法,请重试',res,result);
                }
            }

    });
}
function requestRobot(url,result,res){
    request(url,(error,response,body)=>{
        if(!error && response.statusCode==400){
            dealText('智能小喵我病了,病好了再和你聊天。。。',res,result);
        }
        let data=JSON.parse(body);
        if(data.url){
            dealText(data.text+data.url,res,result);
        }else{
            dealText(data.text,res,result);
        }
        
    });
}
function dealText(responseMSg,res,result){
        let str='<xml><ToUserName><![CDATA['+result.xml.FromUserName+']]></ToUserName><FromUserName><![CDATA['+result.xml.ToUserName+']]></FromUserName><CreateTime>'+new Date().getTime()+'</CreateTime><MsgType><![CDATA['+'text'+']]></MsgType><Content><![CDATA['+responseMSg+']]></Content></xml>';
        res.send(str);
}
exports.requestSong=requestSong;
exports.requestWeather=requestWeather;
exports.requestMsg=requestMsg;
exports.dealText=dealText;
exports.requestRobot=requestRobot;
  • 实现的功能效果图如下所示:
微信公众号效果图
  • 微信测试号开发实例代码如上所示现在已经实现卫星自定义菜单,文本消息回复,语音消息回复,地理位置事件回复,已经图灵机器人接入,语音识别功能的实现

源代码地址微信公众号开发.觉得有帮助,点个star哟。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容