UNPKG

express-wx

Version:

Express Router for building Wechat Offical Account Message Server easily, supporting loading request (message) handlers dynamically. 快速构建微信公众号消息后端,支持动态加载请求处理逻辑代码。

460 lines (459 loc) 17.2 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var TextWXMessage_1, EventWXMessage_1, ImageWXMessage_1, VoiceWXMessage_1, VideoWXMessage_1, ShortVideoWXMessage_1, LocationWXMessage_1, LinkWXMessage_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseMessageXml = exports.MiniProgramPageWXMessage = exports.WXCardWXMessage = exports.MsgMenuWXMessage = exports.MPNewsWXMessage = exports.NoResponseWXMessage = exports.NewsWXMessage = exports.MusicWXMessage = exports.LinkWXMessage = exports.LocationWXMessage = exports.ShortVideoWXMessage = exports.VideoWXMessage = exports.VoiceWXMessage = exports.ImageWXMessage = exports.EventWXMessage = exports.TextWXMessage = exports.WXMessageBasicPart = exports.WXMessageReceiveClass = void 0; const utils_1 = require("./utils"); const request = require("request-promise"); const naming_style_1 = require("naming-style"); let _registeredWXTypes = {}; /** @decorator */ function WXMessageReceiveClass(typeName, cannotToXML = false) { return (target) => { if (target.hasOwnProperty("fromXML")) { _registeredWXTypes[typeName] = target; } else { target["fromXML"] = function () { throw Error("微信API未定义此种消息类型为可能接收到的消息类型!您很可能是收到了一个不是一个来自微信官方的请求。"); }; } if (!target.prototype.hasOwnProperty("toJson")) target.prototype.toJson = function () { throw Error("微信API未提供向用户发送此类型的消息的方法!"); }; if (cannotToXML) target.prototype.toXML = function () { throw Error("该类型的消息不能被回复给用户,只能通过客服消息发送!"); }; }; } exports.WXMessageReceiveClass = WXMessageReceiveClass; let _wxJsonToXmlKeyAdapter = { musicurl: "MusicUrl", hqmusicurl: "HQMusicUrl", picurl: "PicUrl" }; function addJSONtoXML(xml, json) { for (let k in json) { if (k === "touser" || k === "msgtype") continue; let v = json[k]; let type = typeof v; let parsedKey = _wxJsonToXmlKeyAdapter[k] || (0, naming_style_1.pascal)(k); if (type === "string" || type === "boolean" || type === "number") (0, utils_1.xmlSetText)(xml, parsedKey, v, true); else if (type === "object") { if (v instanceof Array) { xml[parsedKey] = { item: v.map((o) => addJSONtoXML({}, o)) }; } else xml[parsedKey] = addJSONtoXML({}, v); } } return xml; } class WXMessageBasicPart { constructor(type, openId) { if (!type) return; this.type = type; this.openId = openId; this.CreateTime = new Date(); } addBasicPartToXML(xml) { this.type = (0, utils_1.xmlGetKey)(xml, "MsgType"); this.openId = (0, utils_1.xmlGetKey)(xml, "FromUserName"); this.CreateTime = new Date(Number((0, utils_1.xmlGetKey)(xml, "CreateTime")) * 1000); this.MsgId = (0, utils_1.xmlGetKey)(xml, "MsgId"); return this; } static fromXML(xml) { return new WXMessageBasicPart(null).addBasicPartToXML(xml); } toJson() { return { touser: this.openId, msgtype: this.type }; } toXML() { // 在消息转xml阶段,是不加入FromUserName信息的,由内置中间件在之后根据req中提供的信息加入进来。 // 而如果openId已经指定,则会自动填入ToUserName;否则会留空ToUserName,之后由WXRouter上的逻辑手动填入。 let xml = {}; (0, utils_1.xmlSetText)(xml, "MsgType", this.type, true); (0, utils_1.xmlSetText)(xml, "CreateTime", String(Math.round(this.CreateTime.getTime() / 1000))); (0, utils_1.xmlSetText)(xml, "ToUserName", this.openId, true); addJSONtoXML(xml, this.toJson()); return xml; } toLog() { let o = Object.assign({}, this); delete o.openId; delete o.CreateTime; delete o.MsgId; return o; } } exports.WXMessageBasicPart = WXMessageBasicPart; let TextWXMessage = TextWXMessage_1 = class TextWXMessage extends WXMessageBasicPart { constructor(text) { super("text"); this.type = "text"; this.text = text; } static fromXML(xml) { let ins = new TextWXMessage_1((0, utils_1.xmlGetKey)(xml, "Content")).addBasicPartToXML(xml); ins.bizmsgmenuid = (0, utils_1.xmlGetKey)(xml, "bizmsgmenuid"); return ins; } toJson() { let s = super.toJson(); s.text = { content: this.text }; return s; } toXML() { let xml = super.toXML(); xml.Content = xml.Text.Content; delete xml.Text; return xml; } toLog() { if (!this.bizmsgmenuid) return this.text; else return super.toLog(); } }; TextWXMessage = TextWXMessage_1 = __decorate([ WXMessageReceiveClass("text") ], TextWXMessage); exports.TextWXMessage = TextWXMessage; let EventWXMessage = EventWXMessage_1 = class EventWXMessage extends WXMessageBasicPart { constructor() { super("event"); this.type = "event"; } static fromXML(xml) { let ins = new EventWXMessage_1().addBasicPartToXML(xml); ins.event = (0, utils_1.xmlGetKey)(xml, "Event"); ins.EventKey = (0, utils_1.xmlGetKey)(xml, "EventKey"); if (ins.event === "subscribe" || ins.event === "SCAN") ins.Ticket = (0, utils_1.xmlGetKey)(xml, "Ticket"); if (ins.event === "LOCATION") ins.positionInfo = { Latitude: (0, utils_1.xmlGetKey)(xml, "Latitude"), Longitude: (0, utils_1.xmlGetKey)(xml, "Longitude"), Precision: (0, utils_1.xmlGetKey)(xml, "Precision") }; if (ins.event === "TEMPLATESENDJOBFINISH") ins.Status = (0, utils_1.xmlGetKey)(xml, "Status"); return ins; } }; EventWXMessage = EventWXMessage_1 = __decorate([ WXMessageReceiveClass("event") ], EventWXMessage); exports.EventWXMessage = EventWXMessage; let ImageWXMessage = ImageWXMessage_1 = class ImageWXMessage extends WXMessageBasicPart { constructor(MediaId) { super("image"); this.type = "image"; this.MediaId = MediaId; } static fromXML(xml) { let ins = new ImageWXMessage_1((0, utils_1.xmlGetKey)(xml, "MediaId")).addBasicPartToXML(xml); ins.PicUrl = (0, utils_1.xmlGetKey)(xml, "PicUrl"); return ins; } toJson() { let s = super.toJson(); s.image = { "media_id": this.MediaId }; return s; } async requestBytes() { if (!this.PicUrl) return null; return request(this.PicUrl, { method: "GET", timeout: 3000, encoding: null, gzip: true }); } }; ImageWXMessage = ImageWXMessage_1 = __decorate([ WXMessageReceiveClass("image") ], ImageWXMessage); exports.ImageWXMessage = ImageWXMessage; let VoiceWXMessage = VoiceWXMessage_1 = class VoiceWXMessage extends WXMessageBasicPart { constructor(MediaId) { super("voice"); this.type = "voice"; this.MediaId = MediaId; } static fromXML(xml) { let ins = new VoiceWXMessage_1((0, utils_1.xmlGetKey)(xml, "MediaId")).addBasicPartToXML(xml); ins.Format = (0, utils_1.xmlGetKey)(xml, "Format"); ins.Recognition = (0, utils_1.xmlGetKey)(xml, "Recognition"); return ins; } toJson() { let s = super.toJson(); s.voice = { media_id: this.MediaId }; return s; } async requestBytes() { throw Error("利用素材接口获取该Media的方法暂未实现!"); } }; VoiceWXMessage = VoiceWXMessage_1 = __decorate([ WXMessageReceiveClass("voice") ], VoiceWXMessage); exports.VoiceWXMessage = VoiceWXMessage; let VideoWXMessage = VideoWXMessage_1 = class VideoWXMessage extends WXMessageBasicPart { constructor(MediaId, title, description) { super("video"); this.type = "video"; this.MediaId = MediaId; this.title = title; this.description = description; } static fromXML(xml) { let ins = new VideoWXMessage_1((0, utils_1.xmlGetKey)(xml, "MediaId")).addBasicPartToXML(xml); ins.ThumbMediaId = (0, utils_1.xmlGetKey)(xml, "ThumbMediaId"); return ins; } toJson() { let s = super.toJson(); s.video = { media_id: this.MediaId, thumb_media_id: this.ThumbMediaId, title: this.title, description: this.description }; return s; } async requestBytes() { throw Error("利用素材接口获取该Media的方法暂未实现!"); } async requestThumbBytes() { throw Error("利用素材接口获取该Media的方法暂未实现!"); } }; VideoWXMessage = VideoWXMessage_1 = __decorate([ WXMessageReceiveClass("video") ], VideoWXMessage); exports.VideoWXMessage = VideoWXMessage; let ShortVideoWXMessage = ShortVideoWXMessage_1 = class ShortVideoWXMessage extends WXMessageBasicPart { constructor() { super("shortvideo"); this.type = "shortvideo"; } static fromXML(xml) { let ins = new ShortVideoWXMessage_1().addBasicPartToXML(xml); ins.MediaId = (0, utils_1.xmlGetKey)(xml, "MediaId"); ins.ThumbMediaId = (0, utils_1.xmlGetKey)(xml, "ThumbMediaId"); return ins; } async requestBytes() { throw Error("利用素材接口获取该Media的方法暂未实现!"); } async requestThumbBytes() { throw Error("利用素材接口获取该Media的方法暂未实现!"); } }; ShortVideoWXMessage = ShortVideoWXMessage_1 = __decorate([ WXMessageReceiveClass("shortvideo") ], ShortVideoWXMessage); exports.ShortVideoWXMessage = ShortVideoWXMessage; let LocationWXMessage = LocationWXMessage_1 = class LocationWXMessage extends WXMessageBasicPart { constructor() { super("location"); this.type = "location"; } static fromXML(xml) { let ins = new LocationWXMessage_1().addBasicPartToXML(xml); ins.x = (0, utils_1.xmlGetKey)(xml, "Location_X"); ins.y = (0, utils_1.xmlGetKey)(xml, "Location_Y"); ins.scale = (0, utils_1.xmlGetKey)(xml, "Scale"); ins.label = (0, utils_1.xmlGetKey)(xml, "Label"); return ins; } }; LocationWXMessage = LocationWXMessage_1 = __decorate([ WXMessageReceiveClass("location") ], LocationWXMessage); exports.LocationWXMessage = LocationWXMessage; let LinkWXMessage = LinkWXMessage_1 = class LinkWXMessage extends WXMessageBasicPart { constructor() { super("link"); this.type = "link"; } static fromXML(xml) { let ins = new LinkWXMessage_1().addBasicPartToXML(xml); ins.url = (0, utils_1.xmlGetKey)(xml, "Url"); ins.title = (0, utils_1.xmlGetKey)(xml, "Title"); ins.description = (0, utils_1.xmlGetKey)(xml, "Description"); return ins; } }; LinkWXMessage = LinkWXMessage_1 = __decorate([ WXMessageReceiveClass("link") ], LinkWXMessage); exports.LinkWXMessage = LinkWXMessage; /** 此消息类型只会作为回复类型,不会出现在收到的消息中。 */ let MusicWXMessage = class MusicWXMessage extends WXMessageBasicPart { constructor(ThumbMediaId, MusicURL, HQMusicURL, title, description) { super("music"); this.type = "music"; this.ThumbMediaId = ThumbMediaId; this.MusicURL = MusicURL; this.HQMusicURL = HQMusicURL; this.title = title; this.description = description; } toJson() { let s = super.toJson(); s.music = { title: this.title, description: this.description, musicurl: this.MusicURL, hqmusicurl: this.HQMusicURL, thumb_media_id: this.ThumbMediaId }; return s; } }; MusicWXMessage = __decorate([ WXMessageReceiveClass("music") ], MusicWXMessage); exports.MusicWXMessage = MusicWXMessage; /** 图文消息类型。此消息类型只会作为回复类型,不会出现在收到的消息中。 */ let NewsWXMessage = class NewsWXMessage extends WXMessageBasicPart { constructor(articles) { super("news"); this.type = "news"; this.articles = articles; } toJson() { let s = super.toJson(); s.news = { articles: this.articles }; return s; } }; NewsWXMessage = __decorate([ WXMessageReceiveClass("news") ], NewsWXMessage); exports.NewsWXMessage = NewsWXMessage; /** 表示希望对客户端不予回复任何消息(即接口返回"success")。此消息类型只会作为回复类型,不会出现在收到的消息中。 */ class NoResponseWXMessage extends WXMessageBasicPart { constructor() { super("noresponse"); this.type = "noresponse"; } toXML(xml) { return null; } toLog() { return null; } } exports.NoResponseWXMessage = NoResponseWXMessage; /** 图文消息类型。此消息类型只会作为**客服消息**的回复类型,不会出现在收到的消息中、也不能通过被动回复返回给用户。 */ let MPNewsWXMessage = class MPNewsWXMessage extends WXMessageBasicPart { constructor(MediaId) { super("mpnews"); this.type = "mpnews"; this.MediaId = MediaId; } toJson() { let s = super.toJson(); s.mpnews = { media_id: this.MediaId }; return s; } }; MPNewsWXMessage = __decorate([ WXMessageReceiveClass("mpnews", true) ], MPNewsWXMessage); exports.MPNewsWXMessage = MPNewsWXMessage; /** 图文消息类型。此消息类型只会作为**客服消息**的回复类型,不会出现在收到的消息中、也不能通过被动回复返回给用户。 */ let MsgMenuWXMessage = class MsgMenuWXMessage extends WXMessageBasicPart { constructor(head_content, list, tail_content) { super("msgmenu"); this.type = "msgmenu"; this.head_content = head_content; this.list = list; this.tail_content = tail_content; } toJson() { let s = super.toJson(); s.msgmenu = { head_content: this.head_content, list: this.list, tail_content: this.tail_content }; return s; } }; MsgMenuWXMessage = __decorate([ WXMessageReceiveClass("msgmenu", true) ], MsgMenuWXMessage); exports.MsgMenuWXMessage = MsgMenuWXMessage; /** 图文消息类型。此消息类型只会作为**客服消息**的回复类型,不会出现在收到的消息中、也不能通过被动回复返回给用户。 */ let WXCardWXMessage = class WXCardWXMessage extends WXMessageBasicPart { constructor(card_id) { super("wxcard"); this.type = "wxcard"; this.card_id = card_id; } toJson() { let s = super.toJson(); s.wxcard = { card_id: this.card_id }; return s; } }; WXCardWXMessage = __decorate([ WXMessageReceiveClass("wxcard", true) ], WXCardWXMessage); exports.WXCardWXMessage = WXCardWXMessage; /** 图文消息类型。此消息类型只会作为**客服消息**的回复类型,不会出现在收到的消息中、也不能通过被动回复返回给用户。 */ let MiniProgramPageWXMessage = class MiniProgramPageWXMessage extends WXMessageBasicPart { constructor(titie, appid, pagepath, thumb_media_id) { super("mpnews"); this.type = "mpnews"; this.title = titie; this.appid = appid; this.pagepath = pagepath; this.thumb_media_id = thumb_media_id; } toJson() { let s = super.toJson(); s.miniprogrampage = { title: this.title, appid: this.appid, pagepath: this.pagepath, thumb_media_id: this.thumb_media_id }; return s; } }; MiniProgramPageWXMessage = __decorate([ WXMessageReceiveClass("miniprogrampage", true) ], MiniProgramPageWXMessage); exports.MiniProgramPageWXMessage = MiniProgramPageWXMessage; function parseMessageXml(xml) { let type = (0, utils_1.xmlGetKey)(xml, "MsgType"); let cls = _registeredWXTypes[type]; if (!cls) throw Error("收到了不支持的消息类型!"); return cls.fromXML(xml); } exports.parseMessageXml = parseMessageXml;