1 | "use strict";
|
2 | var __extends = (this && this.__extends) || (function () {
|
3 | var extendStatics = Object.setPrototypeOf ||
|
4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
6 | return function (d, b) {
|
7 | extendStatics(d, b);
|
8 | function __() { this.constructor = d; }
|
9 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
10 | };
|
11 | })();
|
12 | Object.defineProperty(exports, "__esModule", { value: true });
|
13 | var path = require("path");
|
14 | var crypto = require("crypto");
|
15 | var request = require("request");
|
16 | var channel_1 = require("./channel");
|
17 | var logger_1 = require("./logger");
|
18 | var recime_keyvalue_store_1 = require("recime-keyvalue-store");
|
19 | var MaxDuration = 15;
|
20 | var WeChat = (function (_super) {
|
21 | __extends(WeChat, _super);
|
22 | function WeChat(bot) {
|
23 | return _super.call(this, { bot: bot, log: new logger_1.WeChatLogger(bot) }) || this;
|
24 | }
|
25 | WeChat.prototype.execute = function (body, parameters, handler) {
|
26 | var _this = this;
|
27 | return new Promise(function (resolve) {
|
28 | _this.context.log.incoming(body);
|
29 | if (body.Event && (body.Event === 'unsubscribe' || body.Event.search(/LOCATION/ig) === 0))
|
30 | return resolve();
|
31 | if (body.Event && (body.Event === 'event' || body.Event.search(/VIEW/ig) === 0))
|
32 | return resolve();
|
33 | handler(_this.context.bot, body, 'wechat', function (messages) {
|
34 | var response = (messages || []).map(function (message) { return function () {
|
35 | return _this.sendMessage(body.FromUserName, message);
|
36 | }; });
|
37 | return response
|
38 | .reduce(function (a, b) {
|
39 | return a.then(function (messages) {
|
40 | return b().then(function (message) {
|
41 | return;
|
42 | });
|
43 | });
|
44 | }, Promise.resolve()).then(function () {
|
45 | resolve();
|
46 | });
|
47 | }, function (err) {
|
48 | resolve(err.message);
|
49 | });
|
50 | });
|
51 | };
|
52 | WeChat.prototype.sendMessage = function (toUser, message) {
|
53 | var _this = this;
|
54 | return new Promise(function (resolve, reject) {
|
55 | var reqData = {
|
56 | touser: toUser,
|
57 | msgtype: message.type,
|
58 | };
|
59 | switch (message.type) {
|
60 | case "text": {
|
61 | reqData.text = {
|
62 | content: message.content
|
63 | };
|
64 | break;
|
65 | }
|
66 | case "audio":
|
67 | case "image":
|
68 | case "video": {
|
69 | message.type = message.type === "audio" ? "voice" : message.type;
|
70 | var media = function () {
|
71 | if (message.media_id) {
|
72 | return Promise.resolve({
|
73 | media_id: message.media_id
|
74 | });
|
75 | }
|
76 | return _this.uploadMedia(message.url, message.type).then(function (data) {
|
77 | reqData.msgtype = data.type;
|
78 | if (data.media_id) {
|
79 | return {
|
80 | media_id: data.media_id
|
81 | };
|
82 | }
|
83 | });
|
84 | };
|
85 | return media().then(function (file) {
|
86 | reqData[message.type] = file;
|
87 | return _this.postMessage(reqData).then(function () {
|
88 | return resolve();
|
89 | });
|
90 | });
|
91 | }
|
92 | case "news": {
|
93 | reqData.news = {
|
94 | "articles": ((message.news || []).articles || []).map(function (article) {
|
95 | return {
|
96 | title: article.title,
|
97 | picurl: article.picurl,
|
98 | url: article.url,
|
99 | description: article.digest
|
100 |
|
101 | };
|
102 | })
|
103 | };
|
104 | break;
|
105 | }
|
106 | case "typing": {
|
107 | return _this.sendTypingMessage(toUser).then(function () {
|
108 | var duration = parseInt(message.duration);
|
109 | setTimeout(function () {
|
110 | return resolve();
|
111 | }, (duration === MaxDuration ? duration - 1 : duration) * 1000);
|
112 | });
|
113 | }
|
114 | default: {
|
115 | reqData.msgtype = "text";
|
116 | reqData.text = {
|
117 | content: message.text || "Message type not supported."
|
118 | };
|
119 | }
|
120 | }
|
121 | ;
|
122 | return _this.postMessage(reqData).then(function () {
|
123 | return resolve();
|
124 | });
|
125 | });
|
126 | };
|
127 | WeChat.prototype.postMessage = function (data) {
|
128 | var _this = this;
|
129 | return new Promise(function (resolve, reject) {
|
130 | _this.accessToken().then(function (accessToken) {
|
131 | var reqData = {
|
132 | url: "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + accessToken,
|
133 | json: true,
|
134 | method: "POST",
|
135 | body: data
|
136 | };
|
137 | request(reqData, function (err, response, body) {
|
138 | if (err) {
|
139 | console.error(err);
|
140 | return resolve();
|
141 | }
|
142 | if (response.statusCode < 200 && response.statusCode > 299) {
|
143 | console.error(body);
|
144 | return resolve();
|
145 | }
|
146 | if (body.errcode) {
|
147 | console.error(body);
|
148 | return resolve();
|
149 | }
|
150 | _this.context.log.outgoing(reqData, body);
|
151 | resolve();
|
152 | });
|
153 | })
|
154 | .catch(function (err) {
|
155 | console.error(err);
|
156 | resolve();
|
157 | });
|
158 | });
|
159 | };
|
160 | WeChat.prototype.sendTypingMessage = function (toUser) {
|
161 | var _this = this;
|
162 | return new Promise(function (resolve) {
|
163 | _this.accessToken().then(function (accessToken) {
|
164 | var options = {
|
165 | uri: "https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=" + accessToken,
|
166 | method: 'POST',
|
167 | json: {
|
168 | command: 'Typing',
|
169 | touser: toUser
|
170 | }
|
171 | };
|
172 | request(options, function (err, response, body) {
|
173 | resolve();
|
174 | });
|
175 | }).catch(function (err) {
|
176 | console.error(err);
|
177 | resolve();
|
178 | });
|
179 | });
|
180 | };
|
181 | WeChat.prototype.accessToken = function () {
|
182 | var _this = this;
|
183 | var db = recime_keyvalue_store_1.default.init(this.context.bot.id);
|
184 | return new Promise(function (resolve, reject) {
|
185 | db.get(_this.context.bot.config.RECIME_WECHAT_APP_ID).then(function (value) {
|
186 | if (value && value.expires > new Date().getTime())
|
187 | resolve(value.token);
|
188 | else {
|
189 | request.get("https://proxy.recime.io/wechat/token?grant_type=client_credential&appid=" + (process.env.RECIME_WECHAT_APP_ID || _this.context.bot.config.RECIME_WECHAT_APP_ID) + "&secret=" + (process.env.RECIME_WECHAT_APP_SECRET || _this.context.bot.config.RECIME_WECHAT_APP_SECRET), {
|
190 | "Accept": "application/json"
|
191 | }, function (err, response, body) {
|
192 | var result = JSON.parse(body);
|
193 | if (response.statusCode < 200 && response.statusCode > 299)
|
194 | return reject(new Error("[Error] Invalid access token"));
|
195 | if (result.errcode)
|
196 | return reject(new Error(result.errmsg));
|
197 | var accessToken = JSON.parse(response.body).access_token;
|
198 | db.set(_this.context.bot.config.RECIME_WECHAT_APP_ID, {
|
199 | expires: Date.now() + 7200,
|
200 | token: accessToken
|
201 | }).then(function () { return resolve(accessToken); });
|
202 | resolve(accessToken);
|
203 | });
|
204 | }
|
205 | });
|
206 | });
|
207 | };
|
208 | WeChat.prototype.uploadMedia = function (mediaUrl, type) {
|
209 | var _this = this;
|
210 | var parsed = require("url").parse(mediaUrl).path;
|
211 | return new Promise(function (resolve) {
|
212 | _this.accessToken().then(function (accessToken) {
|
213 | var readStream = request(mediaUrl);
|
214 | request({
|
215 | url: "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + accessToken + "&type=" + type,
|
216 | json: true,
|
217 | method: "POST",
|
218 | formData: {
|
219 | media: readStream,
|
220 | filename: path.basename(parsed)
|
221 | }
|
222 | }, function (err, response, body) {
|
223 | if (response.statusCode < 200 && response.statusCode > 299) {
|
224 | return resolve({
|
225 | msgtype: "text",
|
226 | content: "[Error] Invalid media url."
|
227 | });
|
228 | }
|
229 | if (body.errcode) {
|
230 | return resolve({
|
231 | msgtype: "text",
|
232 | content: body.errmsg
|
233 | });
|
234 | }
|
235 | return resolve(body);
|
236 | });
|
237 | }, function (err) {
|
238 | return resolve({
|
239 | msgtype: "text",
|
240 | content: err.message
|
241 | });
|
242 | });
|
243 | });
|
244 | };
|
245 | WeChat.prototype.verify = function (query) {
|
246 | var accessToken = process.env.RECIME_WECHAT_ACCESS_TOKEN || this.context.bot.config.RECIME_WECHAT_ACCESS_TOKEN;
|
247 | var token = [query['timestamp'], query['nonce'], accessToken].sort().join("");
|
248 | var hash = crypto.createHash('sha1').update(token).digest('hex');
|
249 | return hash === query['signature'];
|
250 | };
|
251 | return WeChat;
|
252 | }(channel_1.Channel));
|
253 | exports.WeChat = WeChat;
|