程序員愛(ài)炫技,寫個(gè)公眾號(hào)文章,都想拿點(diǎn)技術(shù)整整【自動(dòng)回復(fù)】;程序員愛(ài)偷懶,什么都想做個(gè)【自動(dòng)化】,最好所有事情系統(tǒng)都給做了,點(diǎn)點(diǎn)手指頭就能達(dá)到目標(biāo)。
今天的主角是如何搭建一個(gè)公眾號(hào)自動(dòng)回復(fù)功能。整個(gè)流程如下:
- 利用 hapi 開(kāi)發(fā) web 程序,對(duì)接微信公眾號(hào)后臺(tái);
- 利用 LeanCloud SDK 將文章存儲(chǔ)于 LeanCloud 后端;
- 接收用戶發(fā)送的消息,獲取關(guān)鍵詞,查詢文章,并回復(fù)給用戶;
- 將 web 程序代碼托管于 coding.net;
- 使用 LeanEngine 部署 hapi 程序。
1. hapi
A rich framework for building applications and services
hapi enables developers to focus on writing reusable application logic instead of spending time building infrastructure.

選擇 hapi,主要被它的 logo 和讀音(與 happy 同音)吸引,也主要因?yàn)樽约簮?ài)折騰,老想著用不同的框架寫寫,重點(diǎn)是看看區(qū)別,你也可以看看 stackoverflow 上的帖子
https://stackoverflow.com/questions/30469767/how-do-express-and-hapi-compare-to-each-other
在用 hapi 寫代碼時(shí),有種代碼既文檔的感覺(jué),更重點(diǎn)的是 hapi 提供了一套 Plugins 方案,所有代碼都可以以這種方式進(jìn)行擴(kuò)展和分離組織,可讀性更高,如我將對(duì)接微信公眾后臺(tái)的 API 獨(dú)立為 Plugin 形式:
server.register([{
register: require('./api/Books'),
options: {}
}, {
register: require('./api/Articles'),
options: {}
},{
register: require('./api/Blogs'),
options: {}
}, {
register: require('./api/User'),
options: {}
}, {
register: require('./api/Comments'),
options: {}
}], {routes: {prefix: '/api'}}, (err) => {
if (err) {
throw err;
}
});
// wechat
server.register([{
register: require('./wechat/weixin'),
options: {}
}], (err) => {
if (err) {
throw err;
}
});
這樣就可以將微信公眾號(hào)后臺(tái)功能相關(guān)的代碼獨(dú)立到wechat/weixin.js中:
const wechat = require('./wechatserver');
const AV = require('leanengine');
const ModelBlog = require('../model/Blogs');
const config = {
token: '***',
appid: '***',
encodingAESKey: '***'
};
exports.register = function (server, options, next) {
server.route({
method: ['POST', 'GET'],
path: '/wechat',
handler: function (request, reply) {
wechat(config, request, reply, next, function (req, res, next) {
// 微信輸入信息都在req.weixin上
var message = req.weixin;
// 具體邏輯處理 和 自動(dòng)回復(fù)
});
}
});
return next();
};
exports.register.attributes = {
name: 'wechat-weixin'
};
具體如何配置開(kāi)發(fā) hapi 可以參考官網(wǎng)和 github
https://hapijs.com/
https://github.com/hapijs/hapi/
具體和微信后臺(tái)路由配置就好說(shuō)了,直接上圖:

2. LeanCloud
LeanCloud 領(lǐng)先的 BaaS 提供商,為移動(dòng)開(kāi)發(fā)提供強(qiáng)有力的后端支持
包括云存儲(chǔ)、數(shù)據(jù)分析、用戶關(guān)系、消息推送、即時(shí)通信等現(xiàn)代應(yīng)用基礎(chǔ)模塊,滿足移動(dòng)開(kāi)發(fā)所有需求
為什么選擇 LeanCloud
- 云存儲(chǔ)。 主要方便自動(dòng)回復(fù),不需要存儲(chǔ)大量數(shù)據(jù),完全沒(méi)必要購(gòu)買一臺(tái)【云服務(wù)器】或者【云數(shù)據(jù)庫(kù)服務(wù)器】,而且 LeanCloud 提供 10 GB 免費(fèi)空間,足夠前期使用;
- 數(shù)據(jù)。支持任意類型的 JSON 對(duì)象,以及對(duì)象之間的關(guān)聯(lián)映射,同時(shí)提供完整的增刪改查操作接口,方便開(kāi)發(fā);
- 統(tǒng)計(jì)。提供開(kāi)箱即用的移動(dòng)統(tǒng)計(jì),沒(méi)準(zhǔn)哪天粉絲數(shù)量增多了,也可以做一些統(tǒng)計(jì)分析,如分析分析哪個(gè)關(guān)鍵詞是大家關(guān)注的,哪個(gè)話題是大家樂(lè)于傳播分享的,等等。
- 全平臺(tái) SDK 。搭建公眾號(hào)后臺(tái)功能,主要也是折騰折騰,這回用 Javascipt SDK 寫寫接口,沒(méi)準(zhǔn)哪天想換換口味,也可以重新試試用 PHP SDK 來(lái)重構(gòu)復(fù)寫。滿足折騰需求。
JavaScript SDK
使用 SDK 主要三個(gè)作用:
- 存儲(chǔ)公眾號(hào)文章基本信息,便于查詢
根據(jù)微信公眾號(hào)文檔接口需要,主要存儲(chǔ) title, description, picurl, url 四要素;如果為了結(jié)合搜索功能,還需要存儲(chǔ)這篇文章的關(guān)鍵詞。

結(jié)合 LeanCloud SDK,存儲(chǔ)代碼就很簡(jiǎn)單了:
var blog = new Blog();
blog.set('title', request.payload.title);
blog.set('url', request.payload.url);
blog.set('desc', request.payload.desc);
blog.set('picurl', request.payload.picurl);
blog.set('tags', request.payload.tags);
blog.save().then(function (blog) {
// 成功保存之后,執(zhí)行其他邏輯.
console.log('成功保存:New object created with objectId: ' + blog.id);
reply(blog);
}, function (error) {
// 失敗之后執(zhí)行其他邏輯
console.log('Failed to create new object, with error message: ' + error.message);
return reply(Boom.wrap(error, 'error'));
});
- 獲取最新的一篇文章;
當(dāng)一個(gè)新粉絲首次進(jìn)入公眾號(hào),或者發(fā)了一些暫時(shí)沒(méi)處理的信息時(shí),可以直接回復(fù)最新的一篇文章:
const query = new AV.Query(Blog);
query.descending('createdAt');
query.first().then(function (data) {
res.reply([
{
title: data.get('title'),
description: data.get('desc'),
picurl: data.get('picurl'),
url: data.get('url')
}
]);
}, function (error) {
});
- 通過(guò)【關(guān)鍵詞】查詢相關(guān)文章。
搜索【關(guān)鍵詞】,主要搜索 title, description, tags等:
const titleQuery = new AV.Query(Blog)
titleQuery.contains('title', words);
const descQuery = new AV.Query(Blog)
descQuery.contains('desc', words);
const tagsQuery = new AV.Query(Blog)
tagsQuery.contains('tags', words);
const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery);
wordsQuery.descending('createdAt');
wordsQuery.limit(5);
wordsQuery.find().then(function (results) {
res(results);
}, function (error) {
res([]);
});
最后可以將查詢結(jié)果轉(zhuǎn)成【圖文信息】回復(fù)
ModelBlog.search(message.Content, results => {
if (results.length === 0) {
res.reply({
content: message.Content,
type: 'text'
});
} else {
let data = [];
results.forEach(function(v) {
let wrap = {};
wrap.title = v.get('title');
wrap.description = v.get('desc');
wrap.picurl = v.get('picurl');
wrap.url = v.get('url');
data.push(wrap);
});
res.reply(data);
}
});

當(dāng)然,根據(jù)情況需要,如果在找不到查詢結(jié)果時(shí),我們可以內(nèi)建一些【智能聊天】、【生活信息查詢】等等。
3. LeanEngine
云引擎(LeanEngine)是 LeanCloud 推出的服務(wù)端托管平臺(tái)。提供了多種運(yùn)行環(huán)境(Node.js, Python 等)來(lái)運(yùn)行服務(wù)端程序。你只需要提供服務(wù)端的業(yè)務(wù)邏輯(網(wǎng)站或云函數(shù)等),而服務(wù)端的多實(shí)例負(fù)載均衡,不中斷服務(wù)的平滑升級(jí)等都由云引擎提供支持。
當(dāng)寫了服務(wù)端代碼后,總要有個(gè)地方托管。對(duì)于大網(wǎng)站或者項(xiàng)目來(lái)說(shuō),找一家如阿里云、騰訊云等云服務(wù)平臺(tái), 但對(duì)于個(gè)人只是想簡(jiǎn)單的搭建一個(gè)公眾號(hào)管理——自動(dòng)回復(fù)功能,終究有些大材小用了;LeanEngine 結(jié)合 LeanCloud 使用相得映彰,而且 LeanEngine 可以根據(jù)需要升級(jí)擴(kuò)展。

部署
部署主要有兩種途徑:命令行部署和 Git 部署
- 命令行部署
只要在項(xiàng)目的根目錄運(yùn)行:
lean deploy
使用命令行工具可以方便的部署、發(fā)布應(yīng)用,查看應(yīng)用狀態(tài),查看日志,甚至進(jìn)行多應(yīng)用部署。具體可參考網(wǎng)站文檔:
https://leancloud.cn/docs/leanengine_cli.html#使用
- Git 部署
代碼都有一個(gè)版本控制,為了配合 LeanEngine 使用,我將代碼托管到 https://coding.net/ 進(jìn)行源碼的版本管理,只要在 LeanCloud 后臺(tái)一鍵部署即可,具體可參考網(wǎng)站文檔:
https://leancloud.cn/docs/leanengine_webhosting_guide-node.html#Git_部署
總結(jié)
寫代碼太無(wú)聊了,偶爾折騰折騰挺好,如何將不同的技術(shù)和工具,以及第三方服務(wù)結(jié)合在一起,做一些事情也挺好的。
- LeanCloud 負(fù)責(zé)后臺(tái)數(shù)據(jù)存儲(chǔ);
- LeanEngine 負(fù)責(zé)服務(wù)端托管平臺(tái),來(lái)運(yùn)行服務(wù)端程序;
- hapi 負(fù)責(zé)服務(wù)端 web 程序開(kāi)發(fā);
- coding.net 負(fù)責(zé)源碼的版本控制管理;
下一步要做:
- 搭建一個(gè)錄入微信文章的后臺(tái);
- 如何解析和加解密微信的消息體;
coding01 期待您關(guān)注
