9 Star 10 Fork 5

jncpp / wechat-node

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
weixin.js 35.26 KB
一键复制 编辑 原始数据 按行查看 历史
jncpp 提交于 2014-10-08 13:01 . 1. 增加成都巴适公交处理
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
/**
* @project weixin
* @author ddc <jncpp@qq.com>
*/
var EVENT_EMITTER = require("events").EventEmitter;
var SHA1 = require("crypto").createHash('sha1');
var UTIL = require("util");
var HTTP = require("http");
var XML2JS = require("xml2js").parseString;
var LOGGER = require("./config.js").Logger;
/**
* 微信类,处理消息和发送消息
*
* @constructor
*/
function Weixin() {}
UTIL.inherits(Weixin, EVENT_EMITTER); // 继承自EventEmitter的原型
exports.Weixin = Weixin;
/**
* 全局配置数据
*
* @namespace
* @name config
*/
Weixin.prototype.config = {
/**
* Weixin Token
*
* @type String
* @memberof config
*/
token: "kingddc314",
/**
* 关注后给用户发送的文本消息
*
* @type String
* @memberof config
*/
subscribeMsg: "欢迎关注wp8,请回复任意文字,机器人能自动回复的哟!/::$/::$\n" +
"/::B收听电台回复:\n" +
"\t\t电台\n" +
"\t\tfm\n" +
"/:8-)搜索歌曲回复v加任意歌名或歌手关键字:\n" +
"\t\tv光辉岁月\n" +
"\t\tv张杰 领悟\n" +
"\t\tv平凡之路 朴树\n" +
"/::P更多功能敬请期待.../:,@f/:,@f/:,@f",
/**
* 当前订阅号ID
*
* @type String
* @memberof config
*/
userId: null,
/**
* Weixin内部自定义的错误消息,一般用于结果查找不到情况
*
* @type String
* @memberof config
*/
errorMsg: {
meessage: "unknown error",
code: 9527
},
/**
* 百度音乐中的音质码率设置,支持128和320
*
* @type String
* @memberof config
*/
rate: "&rate=128",
/**
* 百度音乐电台的频道key值和名称
*
* @type Array
* @memberof config
*/
fmList: [
{ key: "public_tuijian_winter", name: "温暖冬日" },
{ key: "public_tuijian_zhongguohaoshengyin", name: "中国好声音" },
{ key: "public_tuijian_rege", name: "热歌" },
{ key: "public_tuijian_ktv", name: "KTV金曲" },
{ key: "public_tuijian_billboard", name: "Billboard" },
{ key: "public_tuijian_chengmingqu", name: "成名曲" },
{ key: "public_tuijian_wangluo", name: "网络红歌" },
{ key: "public_tuijian_kaiche", name: "开车" },
{ key: "public_tuijian_yingshi", name: "影视" },
{ key: "public_tuijian_suibiantingting", name: "随便听听" },
{ key: "public_shiguang_jingdianlaoge", name: "经典老歌" },
{ key: "public_shiguang_70hou", name: "70后" },
{ key: "public_shiguang_80hou", name: "80后" },
{ key: "public_shiguang_90hou", name: "90后" },
{ key: "public_shiguang_xinge", name: "火爆新歌" }
],
/**
* 关注用户列表,包含每个用户的状态和数据, ["default"]为默认数据
*
* @type Object
* @memberof config
*/
userList: {
"default": {
status: 0, // 状态0为默认状态,状态1为用户输入音乐搜索,状态2为用户输入电台列表。。。然后等待输入数字进行选择
data: [], // 状态改变携带的数据
fmKey: "public_tuijian_suibiantingting" // 频道
}
}
};
/**
* 获取用户状态和数据
*
* @param {String} user
* @return {Object}
*/
Weixin.prototype.getUser = function(user){
var self = this;
// 不能对undefined进行任何运算
if(!self.config.userList[user]){
self.config.userList[user] = self.config.userList["default"];
}
return self.config.userList[user];
};
/**
* 设置用户状态和数据
*
* @param {String} user
* @param {Number} status - 0,默认状态 1,音乐选择状态 2,电台选择状态 3,公交站点选择状态
* @param {Array } data
* @param {String} fmKey - fmKey为空时使用默认值
*/
Weixin.prototype.setUser = function(user, status, data, fmKey){
var self = this;
if(!fmKey){
if(self.config.userList[user]){
fmKey = self.config.userList[user].fmKey;
}else{
fmKey = self.config.userList["default"].fmKey;
}
}
self.config.userList[user] = {
status: status,
data: data,
fmKey: fmKey
};
LOGGER.trace("status:", self.config.userList[user].status);
};
/**
* 删除用户
*
* @param {String} user
*/
Weixin.prototype.delUser = function(user){
var self = this;
self.config.userList[user] = null;
};
/**
* 重置用户状态和用户数据
*
* @param {String} user
*/
Weixin.prototype.resetUser = function(user){
var self = this;
self.setUser(user, 0, null, null);
};
/**
* 检查微信接口申请
*
* @param {Object} value
* @return {Boolean}
*/
Weixin.prototype.checkSignature = function (value) {
var arr = [this.config.token, value.timestamp, value.nonce];
arr.sort();
var check_str = SHA1.update(arr.join("")).digest('hex');
return (check_str == value.signature);
};
/**
* 解析并处理接收到的微信消息
*
* @param {Object} body
* @param {Object} response
*/
Weixin.prototype.parse = function (body, response) {
var self = this;
var date = new Date();
date.setTime(body.CreateTime[0] * 1000); // setTime是向1970添加毫秒,所以此处需要乘以1000
// 解析接收消息
var msgType = body.MsgType[0] ? body.MsgType[0] : "text";
switch (msgType) {
case "text":{
LOGGER.trace(UTIL.format("[%s] [%s] %s:\n\t%s",
body.MsgType[0], date.toString(), body.FromUserName[0], body.Content[0]));
self.parseTextMsg(body.FromUserName[0], body.Content[0], response);
break;
}
case "image":{
LOGGER.trace(UTIL.format("[%s] [%s] %s:\n\t%s\n\t",
body.MsgType[0], date.toString(), body.FromUserName[0], body.PicUrl[0]), body.MediaId[0]);
self.parseImageMsg(body.FromUserName[0], body.MediaId[0], response);
break;
}
case "voice":{
LOGGER.trace(UTIL.format("[%s] [%s] %s:\n\t%s",
body.MsgType[0], date.toString(), body.FromUserName[0], body.MediaId[0]));
self.parseVoiceMsg(body.FromUserName[0], body.MediaId[0], response);
break;
}
case "video":
break;
case "location":
break;
case "link":
break;
case "event":{
// 处理订阅或取消订阅事件
LOGGER.trace(UTIL.format("[%s] [%s] %s: %s",
body.MsgType[0], date.toString(), body.FromUserName[0], body.Event[0]));
self.parseEventMsg(body.FromUserName[0], body.Event[0], response);
break;
}
default :
break;
}
};
/**
* 解析文本消息
*
* @param {String} toUser
* @param {String} content
* @param {Object} response
*/
Weixin.prototype.parseTextMsg = function (toUser, content, response) {
var self = this;
// 播放百度音乐电台
if(content === "电台" || content === "fm" || content === "f")
{
self.processFmMsg(toUser, response);
}else if(content === "电台列表" || content === "fmlist" || content === "fl") {
self.processFmList(toUser, response);
}else{
// 使用百度音乐搜索,格式如"v领悟 张杰"
var match = content.match(new RegExp("v(.+)"));
if(match){
var title = match[1];
self.processMusicSearch(toUser, title, response);
}else{
// 是**路的线路查询
// 这里先匹配**路,再匹配**
var match2 = content.match(new RegExp("\\b(\\d+)路"));
if(match2){
self.processBusSearch(toUser, match2[1], false, response);
}else{
// 数字选择结果
match2 = content.match(new RegExp("\\b(\\d+)"));
if(match2) {
self.processChoiceResult(toUser, self.getUser(toUser).status, Number(match2[1]), response);
}else{
// 使用小黄鸡接口
//self.processXiaohuangji(toUser, content, response);
// 使用百度新闻搜索接口
self.processNewsSearch(toUser, content, response);
}
}
}
}
};
/**
* 处理文字消息中的电台消息
*
* @param {String} toUser
* @param {Object} response
*/
Weixin.prototype.processFmMsg = function(toUser, response){
var self = this;
// 发送FM歌曲列表的图文消息
var sendFmSongsByPlayList = function(){
if(self.playlist.length >= 5){
var items = [];
for(var i = 0; i <5; i++){
var songInfo = self.playlist.shift();
var str_size = UTIL.format(" [%dM]", Math.round(songInfo.size / 1024 / 1024 * 100) / 100);
var str_time = UTIL.format(" [%d:%d]", Math.floor(songInfo.time / 60), songInfo.time % 60);
var str_hq = songInfo.rate == 320 ? " [HQ]" : "";
items.push({
title: songInfo.songName + " - " + songInfo.artistName + str_hq,
desc: songInfo.albumName + str_size + str_time,
picUrl: songInfo.songPicRadio,
url: "http://music.baidu.com/song/" + songInfo.songId
});
}
self.sendNewsMsg({
toUser: toUser,
fromUser: self.config.userId,
items: items,
response: response
});
}
};
// 电台消息通过图文发送
if(!self.playlist || self.playlist.length < 5){
self.getBaiduFmPlayList(toUser, function(err, songs) {
if (!err && songs) {
LOGGER.info(UTIL.format("get new douban.fm playlist [%d]", songs.length));
self.playlist = songs;
sendFmSongsByPlayList();
}
});
}else{
sendFmSongsByPlayList();
}
};
/**
* 处理文本消息中的电台列表消息
*
* @param {String} toUser
* @param {Object} response
*/
Weixin.prototype.processFmList = function(toUser, response){
var self = this;
var str = '电台列表如下,回复数字选择相应频道:\n';
for (var i in self.config.fmList) {
str += UTIL.format("[%d] %s", Number(i) + 1, // 这里i是string类型,需要转换成number
self.config.fmList[i].name);
if(i != self.config.fmList.length - 1){
str += "\n";
}
}
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: str,
response: response
});
self.setUser(toUser, 2, null, null);
};
/**
* 处理文本消息中的音乐搜索消息
*
* @param {String} toUser
* @param {String} title
* @param {Object} response
*/
Weixin.prototype.processMusicSearch = function(toUser, title, response){
var self = this;
self.getBaiduMusic(title, function(err, result){
if (!err) {
var str = '搜索"' + title + '"结果如下,回复数字选择播放:\n';
for (var i in result) {
if(Number(i) + 1 > 9){
break;
}
str += UTIL.format("[%d] %s", Number(i) + 1, // 这里i是string类型,需要转换成number
result[i].songName + " - " + result[i].artistName);
if(Number(i) + 1 < 9 && i != result.length - 1){
str += "\n";
}
}
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: str,
response: response
});
self.setUser(toUser, 1, result, null);
}else{
self.processXiaohuangji(toUser, title, response);
}
});
};
/**
* 处理文本消息中的公交线路搜索
*
* @param {String} toUser
* @param {String} xianlu
* @param {Boolean} isBack
* @param {Object} response
*/
Weixin.prototype.processBusSearch = function(toUser, xianlu, isBack, response){
var self = this;
self.getBusInfo(xianlu, isBack, function(err, list){
if(err || list.result.length === 0){
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: "公交线路[" + xianlu + "]查询结果不存在,请检查是否拼写有误",
response: response
});
}else{
var text = UTIL.format("%d路\n%s(%s)\n->\n%s(%s)\n\n换向请回复0\n\n", xianlu,
list.begin.name, list.begin.time, list.end.name, list.end.time);
for(var i in list.result){
var index = list.result[i].index.length === 1 ? (" " + list.result[i].index) : list.result[i].index;
text += (index + ": " + list.result[i].name + "\n");
}
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: text,
response: response
});
self.setUser(toUser, 3, list, null);
}
});
};
/**
* 处理文本消息中发生数字选择结果的消息
*
* @param {String} toUser
* @param {Number} status
* @param {String | Number} number
* @param {Object} response
*/
Weixin.prototype.processChoiceResult = function(toUser, status, number, response){
var self = this;
switch(status){
case 1 :{
// 处理当搜索音乐后,等待用户选择的状态
var result = self.getUser(toUser).data[Number(number) - 1];
if(result){
var str_size = UTIL.format(" [%dM]", Math.round(result.size / 1024 / 1024 * 100) / 100);
var str_time = UTIL.format(" [%d:%d]", Math.floor(result.time / 60), result.time % 60);
var str_hq = result.rate == 320 ? " [HQ]" : "";
self.sendMusicMsg({
toUser: toUser,
fromUser: self.config.userId,
title: result.songName + " - " + result.artistName + str_hq,
desc: result.albumName + str_size + str_time,
url: result.songLink,
urlHD: result.songLink,
response: response
});
LOGGER.trace(result.songName + " - " + result.artistName, result.albumName);
LOGGER.trace(result.songLink);
}else{
self.processXiaohuangji(toUser, number, response);
}
break;
}
case 2:{
// 处理当发送电台列表后,等待用户选择的状态
var result = self.config.fmList[Number(number) - 1];
if(result){
self.setUser(toUser, 2, null, result.key); // 状态不变
self.playlist = null; // 清空歌曲列表
self.processFmMsg(toUser, response);
}else{
self.processXiaohuangji(toUser, number, response);
}
break;
}
case 3:{
// 处理当发送线路后,等待用户选择线路站点
var list = self.getUser(toUser).data;
if(Number(number) === 0){
self.processBusSearch(toUser, list.id, true, response);
}else if(Number(number) <= list.result.length){
self.getBusArriveInfo(list.id, list.result[Number(number) - 1].id, function(err, body){
var text = "";
if(err){
text = "未知错误,请重试";
}else{
var res = JSON.parse(body);
if(res.status === "101"){
for(var i in res.busList){
text += UTIL.format("距今%s站,在%s,预计%s分钟到站\n",
res.busList[i].lineDist, res.busList[i].currentStation, res.busList[i].arriveTime);
}
}else{
LOGGER.trace("bus arrive code: %s", res.status);
text = res.resultdes;
}
}
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: text,
response: response});
});
}else{
self.processBusSearch(toUser, Number(number), false, response);
}
break;
}
default :{
//self.processXiaohuangji(toUser, number, response);
self.processBusSearch(toUser, number, false, response);
}
}
};
/**
* 处理文本消息中的其他消息,使用小黄鸡自动回复
*
* @param {String} toUser
* @param {String} content
* @param {Object} response
*/
Weixin.prototype.processXiaohuangji = function (toUser, content, response){
var self = this;
// 关键字预处理
if(content == "wp8"){ // wp8小黄鸡会自动回复垃圾,好吧,和谐一下
content = "WP8";
}
var addUrl = function(msg, url) {
return UTIL.format("<a href='%s'>%s</a>", url, msg);
};
self.getXiaohuangji(content, function(err, msg){
if(!err){
// 前置处理
if(content == "热门微博"){
msg = addUrl(msg, "http://m.weibo.cn/p/102803");
}else if(content == "微博"){
msg = addUrl(msg, "http://m.weibo.cn/");
}
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: msg,
response: response
});
}else{
LOGGER.error("xiaohuangji error:", err);
}
self.resetUser(toUser);
});
};
/**
* 处理文本消息中的新闻搜索
*
* @param {String} toUser
* @param {String} content
* @param {Object} response
*/
Weixin.prototype.processNewsSearch = function(toUser, content, response){
var self = this;
var addUrl = function(msg, url) {
return UTIL.format("<a href='%s'>%s</a>", url, msg);
};
//http://news.baidu.com/ns?word=%s&tn=news&from=news&cl=2&rn=20&ct=1
//http://news.baidu.com/ns?ct=1&rn=20&ie=utf-8&rsv_bp=1&sr=0&cl=2&f=8&prevct=no&word=%s&tn=news&inputT=0
self.get(UTIL.format("http://news.baidu.com/ns?word=%s&tn=news&from=news&cl=2&rn=20&ct=1", content,content), function(err, body){
if(err){
self.processXiaohuangji(toUser,content, response);
}else{
var reg = new RegExp('<li[\\s\\S]+?' +
'<a\\s*href="(.+?)"[\\s\\S]+?' + // 匹配url
'>([\\s\\S]+?)</a>[\\s\\S]+?' + // 匹配标题
'class="c-author">&nbsp;([\\s\\S]+?)&nbsp;' + // 匹配出处
'([\\s\\S]+?)</span>[\\s\\S]+?' + // 匹配时间
'</li>', "g");
var result;
var res = [];
while ((result = reg.exec(body)) != null) {
res.push({
url: result[1],
title: result[2].replace(/[<em>|<\/em>]/g, ""),
ref: result[3],
time: result[4]
});
}
if(res.length != 0){
var str = "";
for(var i in res){
if(Number(i) + 1 > 6){
break;
}
str += UTIL.format("[%d] %s\n\t\t[%s %s]", Number(i) + 1,
addUrl(res[i].title, res[i].url), res[i].ref, res[i].time
);
if(Number(i) + 1 < 6 && i != res.length - 1){
str += "\n\n";
}
}
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: str,
response: response
});
self.resetUser(toUser);
}else{
self.processXiaohuangji(toUser,content, response);
}
}
});
};
/**
* 解析图片消息
*
* @param {String} toUser
* @param {String} mediaId
* @param {Object} response
*/
Weixin.prototype.parseImageMsg = function (toUser, mediaId, response) {
var self = this;
self.sendImageMsg({
toUser: toUser,
fromUser: self.config.userId,
mediaId: mediaId,
response: response
});
self.resetUser(toUser);
};
/**
* 解析语音消息
*
* @param {String} toUser
* @param {String} mediaId
* @param {Object} response
*/
Weixin.prototype.parseVoiceMsg = function (toUser, mediaId, response) {
var self = this;
self.sendVoiceMsg({
toUser: toUser,
fromUser: self.config.userId,
mediaId: mediaId,
response: response
});
self.resetUser(toUser);
};
/**
* 解析事件消息
*
* @param {String} toUser
* @param {String} event
* @param {Object} response
*/
Weixin.prototype.parseEventMsg = function (toUser, event, response) {
var self = this;
if(event == "subscribe")
{
// 订阅
self.sendTextMsg({
toUser: toUser,
fromUser: self.config.userId,
text: this.config.subscribeMsg,
response: response
});
self.resetUser(toUser);
}else if(event == "unsubscribe"){
// 取消订阅
response.end();
self.delUser(toUser);
}else{
self.resetUser(toUser);
response.end();
}
};
/**
* 发送文本消息
*
* @param {Object} value
* @example
* self.sendTextMsg({
* toUser: "接收消息用户",
* fromUser: "发送者",
* text: "文本内容",
* response: "http响应对象"
* })
*/
Weixin.prototype.sendTextMsg = function (value) {
var createTime = Math.round(Date.now() / 1000);
var str = UTIL.format("<xml>\
<ToUserName><![CDATA[%s]]></ToUserName>\
<FromUserName><![CDATA[%s]]></FromUserName>\
<CreateTime>%d</CreateTime>\
<MsgType><![CDATA[text]]></MsgType>\
<Content><![CDATA[%s]]></Content>\
</xml>", value.toUser, value.fromUser, createTime, value.text);
value.response.write(str, "utf8");
value.response.end();
};
/**
* 发送音乐消息
*
* @param {Object} value
* @example
* self.sendMusicMsg({
* toUser: "接收消息用户",
* fromUser: "发送者",
* title: "音乐标题",
* desc: "音乐描述",
* url: "音乐链接",
* urlHD: "高质量音乐链接,WIFI下优选此链接",
* mediaId: "缩略图的媒体ID[可选参数]"
* response: "http响应对象"
* })
*/
Weixin.prototype.sendMusicMsg = function(value){
var createTime = Math.round(Date.now() / 1000);
var media_str = "";
if(value.mediaId){
media_str = "<ThumbMediaId><![CDATA[" + value.mediaId + "]]></ThumbMediaId>";
}
var str = UTIL.format(
"<xml>\
<ToUserName><![CDATA[%s]]></ToUserName>\
<FromUserName><![CDATA[%s]]></FromUserName>\
<CreateTime>%d</CreateTime>\
<MsgType><![CDATA[music]]></MsgType>\
<Music>\
<Title><![CDATA[%s]]></Title>\
<Description><![CDATA[%s]]></Description>\
<MusicUrl><![CDATA[%s]]></MusicUrl>\
<HQMusicUrl><![CDATA[%s]]></HQMusicUrl>\
%s\
</Music>\
</xml>", value.toUser, value.fromUser, createTime,
value.title, value.desc, value.url, value.urlHD, media_str
);
value.response.write(str, "utf8");
value.response.end();
};
/**
* 发送图片消息
*
* @param {Object} value
* @example
* self.sendImageMsg({
* toUser: "接收消息用户",
* fromUser: "发送者",
* mediaId: "多媒体文件ID",
* response: "http响应对象"
* })
*/
Weixin.prototype.sendImageMsg = function(value){
var createTime = Math.round(Date.now() / 1000);
var str = UTIL.format("<xml>\
<ToUserName><![CDATA[%s]]></ToUserName>\
<FromUserName><![CDATA[%s]]></FromUserName>\
<CreateTime>%d</CreateTime>\
<MsgType><![CDATA[image]]></MsgType>\
<Image>\
<MediaId><![CDATA[%s]]></MediaId>\
</Image>\
</xml>", value.toUser, value.fromUser, createTime, value.mediaId);
value.response.write(str, "utf8");
value.response.end();
};
/**
* 发送语音消息
*
* @param {Object} value
* @example
* self.sendVoiceMsg({
* toUser: "接收消息用户",
* fromUser: "发送者",
* mediaId: "多媒体文件ID",
* response: "http响应对象"
* })
*/
Weixin.prototype.sendVoiceMsg = function(value){
var createTime = Math.round(Date.now() / 1000);
var str = UTIL.format("<xml>\
<ToUserName><![CDATA[%s]]></ToUserName>\
<FromUserName><![CDATA[%s]]></FromUserName>\
<CreateTime>%d</CreateTime>\
<MsgType><![CDATA[voice]]></MsgType>\
<Voice>\
<MediaId><![CDATA[%s]]></MediaId>\
</Voice>\
</xml>", value.toUser, value.fromUser, createTime, value.mediaId);
value.response.write(str, "utf8");
value.response.end();
};
/**
* 发送图文消息
*
* @param {Object} value
* @example
* self.sendNewsMsg({
* toUser: "接收消息用户",
* fromUser: "发送者",
* items: [
* {
* title: "标题",
* desc: "副标题(描述)",
* picUrl: "图片链接",
* url: "点击图文消息跳转连接"
* },
* {
* title: "标题",
* desc: "副标题(描述)",
* picUrl: "图片链接",
* url: "点击图文消息跳转连接"
* }
* ],
* response: "http响应对象"
* })
*/
Weixin.prototype.sendNewsMsg = function(value){
var createTime = Math.round(Date.now() / 1000);
var newsItems = "";
var items = value.items;
for(var i in items){
newsItems += UTIL.format("<item>\
<Title><![CDATA[%s]]></Title>\
<Description><![CDATA[%s]]></Description>\
<PicUrl><![CDATA[%s]]></PicUrl>\
<Url><![CDATA[%s]]></Url>\
</item>", items[i].title, items[i].desc, items[i].picUrl, items[i].url);
}
var str = UTIL.format("<xml>\
<ToUserName><![CDATA[%s]]></ToUserName>\
<FromUserName><![CDATA[%s]]></FromUserName>\
<CreateTime>%d</CreateTime>\
<MsgType><![CDATA[news]]></MsgType>\
<ArticleCount>%d</ArticleCount>\
<Articles>\
%s\
</Articles>\
</xml> ", value.toUser, value.fromUser, createTime, items.length, newsItems);
value.response.write(str, "utf8");
value.response.end();
};
/**
* 获取豆瓣电台列表
*
* @param {Function} cb
* @example
* self.getDoubanFmPlayList(function(err, resultList){
* });
*/
Weixin.prototype.getDoubanFmPlayList = function(cb){
var self = this;
self.get("http://douban.fm/j/mine/playlist?type=n&pt=5.1&channel=1&pb=64&from=mainsite", function(err, res_body){
if(err){
cb(err);
}else{
var result = JSON.parse(res_body);
result.song.shift(); // 忽略第一首歌
cb(null, result.song);
}
});
};
/**
* 获取百度电台列表
*
* @param {String} toUser
* @param {Function} cb
* @example
* self.getBaiduFmPlayList(toUser, function(err, resultList){
* });
*/
Weixin.prototype.getBaiduFmPlayList = function(toUser, cb){
var self = this;
self.get(UTIL.format("http://fm.baidu.com/dev/api/?tn=playlist&id=%s&special=flash&prepend=&format=json", self.getUser(toUser).fmKey), function(err, res_body){
if(err){
cb(err);
}else{
var result = JSON.parse(res_body);
for(var i in result.list){
result.list[i] = result.list[i].id;
}
var idlist = result.list.join(",");
var songinfo = UTIL.format("http://music.baidu.com/data/music/fmlink?songIds=%s&type=mp3%s", idlist, self.config.rate);
self.get(songinfo, function(err2, res_body2){
if(err2){
cb(err2);
}else {
var result2 = JSON.parse(res_body2);
if(result2.errorCode == 22000) {
cb(null, result2.data.songList);
}else{
cb(self.config.errorMsg, null);
}
}
});
}
});
};
/**
* 获取小黄鸡自动回复结果
*
* @param {String} content
* @param {Function} cb
* @example
* self.getXiaohuangji(content, function(err, content){
* });
*/
Weixin.prototype.getXiaohuangji = function(content, cb) {
var options = {
hostname: 'www.xiaohuangji.com',
path: '/ajax.php',
method: 'POST',
headers: {
"Accept":"*/*",
"Accept-Encoding":"gzip,deflate,sdch",
"Accept-Language":"zh-CN,zh;q=0.8",
"Connection":"keep-alive",
//"Content-Length":"6",
"Content-Type":"application/x-www-form-urlencoded",
//"Cookie":"bdshare_firstime=1407140631155",
"Host":"www.xiaohuangji.com",
"Origin":"http://www.xiaohuangji.com",
"Referer":"http://www.xiaohuangji.com/",
"User-Agent":"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36",
"X-Requested-With":"XMLHttpRequest"
}
};
// 发送小黄鸡请求
var req = HTTP.request(options, function(res){
res.setEncoding("utf8");
var res_body = "";
res.on("data", function(chunk) {
res_body += chunk;
});
res.on("end", function(){
cb(null, res_body);
});
res.on("error", function(err){
cb(err);
});
});
req.on("error", function(err){
LOGGER.error("problem with request: ", err.message);
cb(err);
});
// 写入请求主体
var contents = "para=" + content;
req.write(contents);
req.end();
};
/**
* 获取百度音乐搜索结果
*
* @param {String} title
* @param {Function} cb
* @example
* self.getBaiduMusic(title, function(err, resultList){
* });
*/
Weixin.prototype.getBaiduMusic = function(title, cb){
var self = this;
var url = UTIL.format("http://sug.music.baidu.com/info/suggestion?format=json&version=2&from=0&word=%s",
title);
self.get(url, function(err, res_body){
if(err){
cb(err, null);
}else{
var result = JSON.parse(res_body);
if(result.data.song && result.data.song.length > 0){
for(var i in result.data.song){
result.data.song[i] = result.data.song[i].songid;
}
var idlist = result.data.song.join(",");
self.get("http://ting.baidu.com/data/music/links?songIds=" + idlist + self.config.rate, function(err2, res_body2){
if(err2){
cb(err2, null);
}else{
var result2 = JSON.parse(res_body2);
if(result2.errorCode == 22000){
// 部分链接中带有&src=""需去除才能正常播放
for(var i in result2.data.songList){
var match = result2.data.songList[i].songLink.match(new RegExp('\\b(.+)&src=.+\\b"'));
if(match){
result2.data.songList[i].songLink = match[1];
}
}
cb(null, result2.data.songList);
}else{
cb(self.config.errorMsg, null);
}
}
});
}else{
cb(self.config.errorMsg, null);
}
}
});
};
/**
* 获取线路搜索结果信息
*
* @param {Number} xianlu
* @param {Boolean} isBack 是否是返程线路
* @param {Function} cb
* @return {Object}
* @example
* self.getBusInfo(xianlu, function(err, resultList){
* });
*/
Weixin.prototype.getBusInfo = function(xianlu, isBack, cb){
var self = this;
var url = UTIL.format("http://www.basbus.cn/m/line?id=%d%s", xianlu, isBack ? "&t=2" : "");
self.get(url, function(err, body){
if(err){
cb(err, null);
}else{
var reg = new RegExp('"li_01">(.+?)</li>[\\s\\S]*?"yuanicon">(\\d+)</div>' +
'[\\s\\S]*?"display:none">(\\d+)</p>', "g");
var result;
var list = { id: xianlu, result: []};
while((result = reg.exec(body)) != null){
list.result.push({
name: result[1],
index: result[2],
id: result[3]
})
}
reg = new RegExp('"leftspan01">(.+?)</span>[\\s\\S]*?"leftspan02">(.+?)</span>' +
'[\\s\\S]*?"rightspan01">(.+?)</span>[\\s\\S]*?"rightspan02">(.+?)</span>');
var match = body.match(reg);
if(match){
list.begin = {name: match[1], time: match[2]};
list.end = {name: match[3], time: match[4]};
}
cb(null, list);
}
});
};
/**
* 获取某线路的某站点的距离站点信息
* @param {String} id
* @param {String} gps
* @param {Function} cb
*/
Weixin.prototype.getBusArriveInfo = function(id, gps, cb){
// 写入请求主体
var contents = "id=" + id + "&g=" + gps;
var options = {
hostname: 'www.basbus.cn',
path: '/m/station',
method: 'POST',
headers: {
"Accept":"*/*",
"Accept-Encoding":"gzip,deflate,sdch",
"Accept-Language":"zh-CN,zh;q=0.8",
"Cache-Control": "no-cache",
"Connection":"keep-alive",
"Content-Length": contents.length,
"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
"Host":"www.basbus.cn",
"Origin":"http://www.basbus.cn",
"Referer":"http://www.basbus.cn/m/line?id=" + id,
"User-Agent":"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36",
"X-Requested-With":"XMLHttpRequest"
}
};
// 发送小黄鸡请求
var req = HTTP.request(options, function(res){
res.setEncoding("utf8");
var res_body = "";
res.on("data", function(chunk) {
res_body += chunk;
});
res.on("end", function(){
cb(null, res_body);
});
res.on("error", function(err){
cb(err);
});
});
req.on("error", function(err){
LOGGER.error("problem with request: ", err.message);
cb(err);
});
req.write(contents);
req.end();
};
/**
* 简化http.get操作
*
* @param {String} url
* @param {Function} cb
* @example
* self.get(url, function(err, body){
* });
*/
Weixin.prototype.get = function(url, cb){
HTTP.get(url, function(res){
res.setEncoding("utf8");
var res_body = "";
res.on("data", function(chunk) {
res_body += chunk;
});
res.on("end", function() {
cb(null, res_body);
});
res.on("error", function(err){
cb(err);
});
});
};
JavaScript
1
https://gitee.com/jncpp/wechat-node.git
git@gitee.com:jncpp/wechat-node.git
jncpp
wechat-node
wechat-node
master

搜索帮助