nodejs實(shí)現(xiàn)微信公眾號(hào)基本事件推回復(fù)

微信公眾號(hào)開發(fā)蜡饵,微信官方只開放了個(gè)人訂閱號(hào)給個(gè)人開發(fā)者,很多事件推送接口都有限制黔夭,所以自己申請(qǐng)了一個(gè)測(cè)試號(hào)宏胯,相對(duì)于個(gè)人未認(rèn)證的訂閱號(hào),多了一些接口本姥,下面就微信開發(fā)者文檔肩袍,用Nodejs的express框架實(shí)現(xiàn)了基本的事件回復(fù),利用一些普通的api實(shí)現(xiàn)微信公眾號(hào)常見(jiàn)的功能婚惫。代碼方面沒(méi)有使用市面比較流行的wechat和wechat-api開發(fā)框架氛赐,純粹使用原生的消息模板進(jìn)行事件的回復(fù)。

  • 準(zhǔn)備工作:

    • 微信公眾號(hào)的開發(fā)需要一臺(tái)公網(wǎng)可以訪問(wèn)的服務(wù)器先舷,現(xiàn)在百度BAE艰管,騰訊的服務(wù)器,阿里云的ECS對(duì)于學(xué)生都具有優(yōu)惠活動(dòng)蒋川,國(guó)外的AWS更有新用戶免費(fèi)一年的活動(dòng)牲芋。自己買的是阿里云學(xué)生優(yōu)惠ECS,價(jià)格是¥9.9尔破;關(guān)于阿里云服務(wù)器的配置可以直接參考其他教程街图。
    • 微信公眾號(hào)的域名驗(yàn)證,可以參考上一遍文件懒构,關(guān)于微信接入指南餐济。
  • 對(duì)于微信各種事件回復(fù)的代碼如下:

    • 對(duì)于微信事件回復(fù)的代碼如下:
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();
});
//對(duì)于各種微信服務(wù)器的事件推送進(jìn)行回復(fù)
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('歡迎關(guān)注聊天小喵;\n 1.發(fā)送地址定位信息可以獲得當(dāng)?shù)靥鞖馇闆r喲\n 2.發(fā)送歌曲名字可以獲取想要聽(tīng)的歌喲\n3.發(fā)送語(yǔ)音信息可以直接小喵聊天喲,她會(huì)很多東西喲\n圖片,視頻識(shí)別功能還在開發(fā)中\(zhòng)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是天行數(shù)據(jù)的新聞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;
- 各種事件回復(fù)函數(shù)模塊文件
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時(shí)間:'+data.data.sj+'\n天氣情況:'+data.data.list[0].tq1+'\n白天溫度:'+data.data.list[0].qw1+'度\n夜間溫度'+data.data.list[0].qw2+'度\n白天風(fēng)向 :'+data.data.list[0].fx1+'\n夜間風(fēng)向:'+data.data.list[0].fx2+'\n',res,result);
    });
}
//處理文本點(diǎn)歌
function requestSong(url,result,res){
    request(url, function (error, response, body) {
            if(error){
                dealText('出錯(cuò)請(qǐng)重試胆剧!',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('沒(méi)有資源絮姆,請(qǐng)選擇其他歌曲',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['+'悠悠音樂(lè),縷縷動(dòng)聽(tīng)'+']]></Description><MusicUrl><![CDATA['+url+']]></MusicUrl><HQMusicUrl><![CDATA['+url+']]></HQMusicUrl><ThumbMediaId><![CDATA['+'ojH9Q9iDl50J6PjyFmQcYYKg51COLtm2SJpFdDFzR0jYNYt4JOjtfee0LKNDYzQa'+']]></ThumbMediaId></Music></xml>';
                            res.send(str);
                        }
                        
                    });     
                    
                }else{
                   dealText('發(fā)送歌曲名字不合法,請(qǐng)重試',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;
  • 實(shí)現(xiàn)的功能效果圖如下所示:
微信公眾號(hào)效果圖
  • 微信測(cè)試號(hào)開發(fā)實(shí)例代碼如上所示現(xiàn)在已經(jīng)實(shí)現(xiàn)衛(wèi)星自定義菜單鸽照,文本消息回復(fù),語(yǔ)音消息回復(fù),地理位置事件回復(fù)颠悬,已經(jīng)圖靈機(jī)器人接入矮燎,語(yǔ)音識(shí)別功能的實(shí)現(xiàn)

源代碼地址微信公眾號(hào)開發(fā).覺(jué)得有幫助定血,點(diǎn)個(gè)star喲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诞外,一起剝皮案震驚了整個(gè)濱河市澜沟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌峡谊,老刑警劉巖茫虽,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異既们,居然都是意外死亡濒析,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門啥纸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悼枢,“玉大人,你說(shuō)我怎么就攤上這事脾拆。” “怎么了莹妒?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵名船,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旨怠,道長(zhǎng)渠驼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任鉴腻,我火速辦了婚禮迷扇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爽哎。我一直安慰自己蜓席,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布课锌。 她就那樣靜靜地躺著厨内,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渺贤。 梳的紋絲不亂的頭發(fā)上雏胃,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音志鞍,去河邊找鬼瞭亮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛固棚,可吹牛的內(nèi)容都是我干的统翩。 我是一名探鬼主播仙蚜,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼唆缴!你這毒婦竟也來(lái)了鳍征?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤面徽,失蹤者是張志新(化名)和其女友劉穎艳丛,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趟紊,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氮双,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霎匈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戴差。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铛嘱,靈堂內(nèi)的尸體忽然破棺而出暖释,到底是詐尸還是另有隱情,我是刑警寧澤墨吓,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布球匕,位于F島的核電站,受9級(jí)特大地震影響帖烘,放射性物質(zhì)發(fā)生泄漏亮曹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一秘症、第九天 我趴在偏房一處隱蔽的房頂上張望照卦。 院中可真熱鬧,春花似錦乡摹、人聲如沸役耕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蹄葱。三九已至,卻和暖如春锄列,著一層夾襖步出監(jiān)牢的瞬間图云,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工邻邮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竣况,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓筒严,卻偏偏與公主長(zhǎng)得像丹泉,于是被迫代替她去往敵國(guó)和親情萤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容