本文介绍如何为公众号添加一个菜单点击后给用户发送天气预报的功能
上一次介绍了如何为公众号添加关注自动回复的功能,这次我们来扩展一个比较实用的功能--天气查询
# 程序思路
- 用户进入公众号会话,上传经纬度信息,服务器进行缓存
- 用户点击菜单后,服务端获取相应菜单的点击事件并调用接口将缓存中的用户经纬度信息进行逆地址解析为城市名称
- 根据城市名称调用天气API获取即时天气数据并返回xml数据
# 开发准备
# 关键代码
app.js
const getRawBody = require('raw-body')
const xml2js = require('xml2js')
const rp = require('request-promise')
// 缓存使用node-localstorage
if (typeof localStorage === 'undefined' || localStorage === null) {
var LocalStorage = require('node-localstorage').LocalStorage
localStorage = new LocalStorage('./scratch')
}
router.post('/', async (ctx, next) => {
var data = await getRawBody(ctx.req, {
length: ctx.length,
limit: '1mb',
encoding: ctx.charset,
})
const xml = await parseXMLAsync(data)
const createTime = Date.parse(new Date())
const msgType = xml.xml.MsgType[0]
const toUserName = xml.xml.ToUserName[0]
const toFromName = xml.xml.FromUserName[0]
const event = xml.xml.Event ? xml.xml.Event[0] : ''
localStorage.clear()
let fromUserName = localStorage.getItem('fromUserName') || []
if (event == 'LOCATION') {
let latitude = xml.xml.Latitude ? xml.xml.Latitude[0] : ''
let longitude = xml.xml.Longitude ? xml.xml.Longitude[0] : ''
if (fromUserName.length > 0) {
if (fromUserName.findIndex((f) => f.id == toFromName) == -1) {
fromUserName.push({
id: toFromName,
latitude: latitude,
longitude: longitude,
})
localStorage.setItem('fromUserName', fromUserName)
}
} else {
fromUserName.push({
id: toFromName,
latitude: latitude,
longitude: longitude,
})
localStorage.setItem('fromUserName', fromUserName)
}
}
if (msgType == 'event') {
let replyMsg = '说些什么'
if (event == 'subscribe') {
replyMsg = '欢迎关注'
//关注后
ctx.body = `<xml>
<ToUserName><![CDATA[${toFromName}]]></ToUserName>
<FromUserName><![CDATA[${toUserName}]]></FromUserName>
<CreateTime>${createTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${replyMsg}]]></Content>
</xml>`
} else if (event == 'CLICK') {
let eventKey = xml.xml.EventKey ? xml.xml.EventKey[0] : ''
switch (eventKey) {
case 'weather':
let latitude = '31.467138'
let longitude = '120.286194'
if (fromUserName.length > 0) {
// 当前用户的经纬度
latitude = fromUserName.find((f) => f.id == toFromName).latitude
longitude = fromUserName.find((f) => f.id == toFromName).longitude
}
let options = {
method: 'get',
uri:
'http://api.map.baidu.com/geocoder?location=' +
latitude +
',' +
longitude +
'&output=json&key=' +
config.baiduAk,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
json: true,
}
let cityInfo = await rp(options),
city = cityInfo.result.addressComponent.city.replace('市', '')
let woptions = {
method: 'get',
url:
'https://tianqiapi.com/api?version=v6&appid=' +
config.weatherAppid +
'&appsecret=' +
config.weatherSecrect +
'&city=' +
encodeURI(city),
json: true,
}
let weather = await rp(woptions)
let weatherTip = `您当前的城市${weather.city}\n天气:${weather.wea}\n温度:${weather.tem2}~${weather.tem1}℃\n实时温度:${weather.tem}℃\n风力:${weather.win}${weather.win_speed}\n空气质量:${weather.air_tips}`
replyMsg = weatherTip
ctx.body = `<xml>
<ToUserName><![CDATA[${toFromName}]]></ToUserName>
<FromUserName><![CDATA[${toUserName}]]></FromUserName>
<CreateTime>${createTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${replyMsg}]]></Content>
</xml>`
break
default:
break
}
}
} else {
//其他情况
ctx.body = `<xml>
<ToUserName><![CDATA[${toFromName}]]></ToUserName>
<FromUserName><![CDATA[${toUserName}]]></FromUserName>
<CreateTime>${createTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你说啥?]]></Content>
</xml>`
}
})
完整代码请移步github仓库 (opens new window)
# 扫码体验
测试号二维码