Commit fdb2abd41e9dafca6fb0a059b8f4c1ff34e762f1
1 parent
b450476d61
Exists in
master
registerResendDelay
Showing 4 changed files with 313 additions and 3 deletions Side-by-side Diff
... | ... | @@ -0,0 +1,301 @@ |
1 | +var redis = require('redis'); | |
2 | +var moment = require('moment'); | |
3 | +var LRU = require('lru-cache'); | |
4 | + | |
5 | +var config; | |
6 | +var logger; | |
7 | +var partner; | |
8 | +var imConfig; | |
9 | +var redisClient; | |
10 | + | |
11 | +var resendHandlers = LRU({max: 2000, maxAge: 1000 * 3600 * 36}); | |
12 | + | |
13 | +function init(options) { | |
14 | + if (options && options.config) { | |
15 | + config = options.config; | |
16 | + } | |
17 | + | |
18 | + if (options && options.logger) { | |
19 | + logger = options.logger; | |
20 | + } | |
21 | + | |
22 | + createRedisClient(config.globals.redis_host, config.globals.redis_port); | |
23 | + readImConfig(); | |
24 | +} | |
25 | + | |
26 | +function createRedisClient(host, port) { | |
27 | + try { | |
28 | + redisClient = redis.createClient(port, host); | |
29 | + } catch(err) { | |
30 | + logger.warn("Error creating redis client to " + host + ':' + port); | |
31 | + } | |
32 | +} | |
33 | + | |
34 | +function readImConfig(filename) { | |
35 | + | |
36 | + if (!filename) { | |
37 | + filename = process.cwd() + '/config.im.json'; | |
38 | + } | |
39 | + | |
40 | + try { | |
41 | + imConfig = require(filename); | |
42 | + } | |
43 | + catch(e) { | |
44 | + imConfig = {}; | |
45 | + } | |
46 | + | |
47 | + logger.verbose('IM Config', {imConfig: imConfig}); | |
48 | + return imConfig; | |
49 | +} | |
50 | + | |
51 | +function getPatternFromMessage(message, pattern, patternMatchIndex) { | |
52 | + var re = new RegExp(pattern); | |
53 | + var matches = message.match(re); | |
54 | + | |
55 | + if (!matches) { | |
56 | + return null; | |
57 | + } | |
58 | + | |
59 | + if (patternMatchIndex < matches.length) { | |
60 | + return matches[patternMatchIndex]; | |
61 | + } else { | |
62 | + return null; | |
63 | + } | |
64 | +} | |
65 | + | |
66 | +function getPatternsFromMessage(message, patterns) { | |
67 | + var patternCount = patterns.length; | |
68 | + for (var i = 0; i < patternCount; i++) { | |
69 | + | |
70 | + var pattern = patterns[i]; | |
71 | + | |
72 | + var result = getPatternFromMessage(message, pattern.pattern, pattern.matchIndex); | |
73 | + if (result) { | |
74 | + return result; | |
75 | + } | |
76 | + } | |
77 | +} | |
78 | + | |
79 | +function getTaskKey(task, chipInfo, today) { | |
80 | + if (!chipInfo && config && config.globals && config.globals.gateway_name) { | |
81 | + chipInfo = config.globals.gateway_name; | |
82 | + } | |
83 | + | |
84 | + if (task.timestamp && !today) { | |
85 | + today = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYYMMDD'); | |
86 | + } | |
87 | + | |
88 | + return chipInfo + '.trx.date:' + today + '.rProduct:' + task.remoteProduct.toUpperCase() + '.dest:' + task.destination ; | |
89 | +} | |
90 | + | |
91 | +function saveTask(task, cb) { | |
92 | + var key = getTaskKey(task, config.globals.gateway_name); | |
93 | + logger.verbose('Saving task', {key: key, task: task}); | |
94 | + | |
95 | + redisClient.set(key, JSON.stringify(task), function() { | |
96 | + redisClient.expire(key, 3600*24); | |
97 | + if (cb) { | |
98 | + cb(); | |
99 | + } | |
100 | + }); | |
101 | +} | |
102 | + | |
103 | +function getTask(remoteProduct, destination, cb) { | |
104 | + var dummyTask = { | |
105 | + remoteProduct: remoteProduct, | |
106 | + destination: destination, | |
107 | + } | |
108 | + | |
109 | + var key = getTaskKey(dummyTask, config.globals.gateway_name, moment().format('YYYYMMDD')); | |
110 | + redisClient.get(key, function(err, result) { | |
111 | + if (err) { | |
112 | + logger.verbose('getTask: task not found', {key: key, params: dummyTask}); | |
113 | + | |
114 | + cb(err, null); | |
115 | + return; | |
116 | + } | |
117 | + | |
118 | + var task = {}; | |
119 | + | |
120 | + try { | |
121 | + task = JSON.parse(result); | |
122 | + } | |
123 | + catch(e) { | |
124 | + logger.warn('getTask: Can not parse result', {key: key, params: dummyTask, data: result}); | |
125 | + err = "Can not parse result" | |
126 | + } | |
127 | + cb(err, task); | |
128 | + }); | |
129 | +} | |
130 | + | |
131 | +function deleteTask(remoteProduct, destination) { | |
132 | + var dummyTask = { | |
133 | + remoteProduct: remoteProduct, | |
134 | + destination: destination, | |
135 | + } | |
136 | + | |
137 | + var key = getTaskKey(dummyTask, config.globals.gateway_name, moment().format('YYYYMMDD')); | |
138 | + | |
139 | + try { | |
140 | + redisClient.del(key); | |
141 | + } | |
142 | + catch(e) {}; | |
143 | +} | |
144 | + | |
145 | +function createMessage(pattern, keywords) { | |
146 | + var msg = pattern; | |
147 | + | |
148 | + for (var key in keywords) { | |
149 | + msg = msg.replace('[' + key + ']', keywords[key]); | |
150 | + } | |
151 | + return msg; | |
152 | +} | |
153 | + | |
154 | +function getRemoteProductFromMessage(msg) { | |
155 | + return getPatternsFromMessage(msg, imConfig.product_patterns); | |
156 | +} | |
157 | + | |
158 | +function getDestinationFromMessage(msg) { | |
159 | + return getPatternsFromMessage(msg, imConfig.destination_patterns); | |
160 | +} | |
161 | + | |
162 | +function getSnFromMessage(msg) { | |
163 | + return getPatternsFromMessage(msg, imConfig.sn_patterns); | |
164 | +} | |
165 | + | |
166 | +function getRcFromMessage(msg) { | |
167 | + var rcs = imConfig.response_codes; | |
168 | + var rcsCount = rcs.length; | |
169 | + | |
170 | + for (var i = 0; i < rcsCount; i++) { | |
171 | + | |
172 | + var item = rcs[i]; | |
173 | + var re = new RegExp(item.pattern); | |
174 | + if (msg.search(re) != -1) { | |
175 | + return item.rc; | |
176 | + } | |
177 | + | |
178 | + } | |
179 | + return '68'; | |
180 | +} | |
181 | + | |
182 | +function isAllowedFrom(sender) { | |
183 | + if (!config || !config.h2h_out || !config.h2h_out.allowed_response_from) { | |
184 | + return true; | |
185 | + } | |
186 | + | |
187 | + whitelist = config.h2h_out.allowed_response_from.split(','); | |
188 | + whitelistCount = whitelist.length; | |
189 | + | |
190 | + for(var i=0; i<whitelistCount; i++) { | |
191 | + if (sender == whitelist[i]) { | |
192 | + return true; | |
193 | + } | |
194 | + } | |
195 | + | |
196 | + return false; | |
197 | +} | |
198 | + | |
199 | +function checkForSameDayDuplicate(task, cbNoDupe, cbDupe, cbDupeWithSameReqId) { | |
200 | + getTask(task.remoteProduct, task.destination, function(err, archivedTask) { | |
201 | + if (err) { | |
202 | + logger.warn('Error on checking same day duplicate', {task: task}); | |
203 | + cbNoDupe(task); | |
204 | + return; | |
205 | + } | |
206 | + | |
207 | + if (archivedTask && archivedTask.requestId) { | |
208 | + if (cbDupeWithSameReqId && task.requestId == archivedTask.requestId) { | |
209 | + logger.verbose('Duplicate trx on same day with same requestId', {task: task}); | |
210 | + cbDupeWithSameReqId(task, archivedTask); | |
211 | + return; | |
212 | + } | |
213 | + | |
214 | + logger.verbose('Duplicate trx on same day', {task: task, archivedTask: archivedTask}); | |
215 | + cbDupe(task, archivedTask); | |
216 | + return; | |
217 | + } | |
218 | + | |
219 | + cbNoDupe(task); | |
220 | + }); | |
221 | +} | |
222 | + | |
223 | +function registerResendDelay(task) { | |
224 | + if (!task.requestId) { | |
225 | + logger.warn('Invalid task on resendDelay') | |
226 | + return; | |
227 | + } | |
228 | + | |
229 | + if (!config || !config.globals || !config.globals.auto_resend_on_delay_secs) { | |
230 | + return; | |
231 | + } | |
232 | + | |
233 | + if (!partner || !partner.topupRequest) { | |
234 | + logger.warn('Skip request resend delay because partner.topupRequest is not exists'); | |
235 | + return; | |
236 | + } | |
237 | + | |
238 | + var retry = 10; | |
239 | + var oldHandler = resendHandlers.get(task.requestId); | |
240 | + if (oldHandler) { | |
241 | + retry = oldHandler.retry - 1; | |
242 | + } | |
243 | + | |
244 | + if (retry <= 0) { | |
245 | + logger.verbose('Resend delay retry exceeded', {task: task}); | |
246 | + cancelResendDelay(task); | |
247 | + return; | |
248 | + } | |
249 | + | |
250 | + logger.info('Resending task request', {task}); | |
251 | + var handlerData = { | |
252 | + handler: setTimeout(partner.topupRequest, config.globals.auto_resend_on_delay_secs, task), | |
253 | + task: task, | |
254 | + retry: retry | |
255 | + } | |
256 | + | |
257 | + resendHandlers.set(task.requestId, handlerData); | |
258 | +} | |
259 | + | |
260 | +function cancelResendDelay(task) { | |
261 | + if (!task || !task.requestId) { | |
262 | + logger.warn('Invalid task on cancelResendDelay'); | |
263 | + return; | |
264 | + } | |
265 | + | |
266 | + var oldHandler = resendHandlers.get(task.requestId); | |
267 | + if (!oldHandler) { | |
268 | + return; | |
269 | + } | |
270 | + | |
271 | + logger.verbose('Canceling resend delay', {task: task}); | |
272 | + | |
273 | + try { | |
274 | + if (oldHandler.handler) { | |
275 | + clearTimeout(oldHandler.handler); | |
276 | + } | |
277 | + } | |
278 | + catch(e) {}; | |
279 | + | |
280 | + try { | |
281 | + resendHandlers.del(task.requestId); | |
282 | + } | |
283 | + catch(e) {}; | |
284 | +} | |
285 | + | |
286 | +exports.init = init; | |
287 | +exports.start = init; | |
288 | +exports.getPatternFromMessage = getPatternFromMessage; | |
289 | +exports.getPatternsFromMessage = getPatternsFromMessage; | |
290 | +exports.saveTask = saveTask; | |
291 | +exports.getTask = getTask; | |
292 | +exports.readImConfig = readImConfig; | |
293 | +exports.createMessage = createMessage; | |
294 | +exports.getRemoteProductFromMessage = getRemoteProductFromMessage; | |
295 | +exports.getDestinationFromMessage = getDestinationFromMessage; | |
296 | +exports.getRcFromMessage = getRcFromMessage; | |
297 | +exports.getSnFromMessage = getSnFromMessage; | |
298 | +exports.isAllowedFrom = isAllowedFrom; | |
299 | +exports.checkForSameDayDuplicate = checkForSameDayDuplicate; | |
300 | +exports.deleteTask = deleteTask; | |
301 | +exports.registerResendDelay = registerResendDelay; |
index.js
package.json
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", |
23 | 23 | "license": "ISC", |
24 | 24 | "dependencies": { |
25 | + "lru-cache": "^4.0.1", | |
25 | 26 | "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", |
26 | 27 | "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", |
27 | 28 | "yahoomessenger": "^0.1.3-Beta" |
partner-ym.js
1 | -var im = require('sate24/im.js') | |
1 | +var im = require('./im.js') | |
2 | 2 | var YM = require('yahoomessenger'); |
3 | 3 | var imAdaptor = require('./adaptor-ym'); |
4 | 4 | |
... | ... | @@ -52,7 +52,11 @@ function onPM(from, msg) { |
52 | 52 | im.deleteTask(remoteProduct, destination); |
53 | 53 | } |
54 | 54 | |
55 | - callbackReport(task.requestId, rc, msg); | |
55 | + if (rc != '68') { | |
56 | + im.cancelResendDelay(task); | |
57 | + } | |
58 | + | |
59 | + callbackReportWrapper(task.requestId, rc, msg); | |
56 | 60 | }); |
57 | 61 | } |
58 | 62 | |
... | ... | @@ -114,6 +118,8 @@ function _topupRequest(task) { |
114 | 118 | } |
115 | 119 | |
116 | 120 | im.saveTask(task, function() { |
121 | + im.registerResendDelay(task); | |
122 | + | |
117 | 123 | var msg = im.createMessage(pattern, keywords); |
118 | 124 | imAdaptor.sendMessage(config.h2h_out.partner, msg); |
119 | 125 | }); |
... | ... | @@ -124,9 +130,10 @@ function topupRequest(task) { |
124 | 130 | if (!aaa.isTodayTrx(task)) { |
125 | 131 | logger.warn('Maaf, transaksi beda hari tidak dapat dilakukan'); |
126 | 132 | callbackReport(task.requestId, '68', 'Maaf, transaksi beda hari tidak dapat dilakukan'); |
133 | + im.cancelResendDelay(task); | |
127 | 134 | return; |
128 | 135 | } |
129 | - | |
136 | + | |
130 | 137 | im.checkForSameDayDuplicate(task, _topupRequest, onSameDayDupe, _topupRequest); |
131 | 138 | } |
132 | 139 |