Commit fdb2abd41e9dafca6fb0a059b8f4c1ff34e762f1

Authored by Adhidarma Hadiwinoto
1 parent b450476d61
Exists in master

registerResendDelay

Showing 4 changed files with 313 additions and 3 deletions Inline Diff

File was created 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;
302
1 var fs = require('fs'); 1 var fs = require('fs');
2 var ini = require('ini'); 2 var ini = require('ini');
3 var expresso = require('sate24-expresso'); 3 var expresso = require('sate24-expresso');
4 var partner = require('./partner-ym'); 4 var partner = require('./partner-ym');
5 var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8')); 5 var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8'));
6 6
7 process.chdir(__dirname); 7 process.chdir(__dirname);
8 8
9 var logger = require('sate24/logger.js').start(); 9 var logger = require('sate24/logger.js').start();
10 var HttpServer = require('sate24/httpserver.js'); 10 var HttpServer = require('sate24/httpserver.js');
11 var aaa = require('sate24/aaa.js'); 11 var aaa = require('sate24/aaa.js');
12 var partner = require('./partner-ym.js'); 12 var partner = require('./partner-ym.js');
13 13
14 var matrix = aaa.prepareMatrix(); 14 var matrix = aaa.prepareMatrix();
15 15
16 var options = { 16 var options = {
17 'aaa': aaa, 17 'aaa': aaa,
18 'logger': logger, 18 'logger': logger,
19 'config': config, 19 'config': config,
20 'matrix': matrix, 20 'matrix': matrix,
21 'partner': partner,
21 } 22 }
22 23
23 var httpServer = HttpServer.start(config, options); 24 var httpServer = HttpServer.start(config, options);
24 25
25 aaa.pause(); 26 aaa.pause();
26 27
27 partner.start(options); 28 partner.start(options);
28 aaa.start(config, partner, options); 29 aaa.start(config, partner, options);
29 expresso.start(options); 30 expresso.start(options);
30 31
1 { 1 {
2 "name": "sate24-to-ym", 2 "name": "sate24-to-ym",
3 "version": "1.0.0", 3 "version": "1.0.0",
4 "description": "ST24 to YM H2H Out", 4 "description": "ST24 to YM H2H Out",
5 "main": "index.js", 5 "main": "index.js",
6 "scripts": { 6 "scripts": {
7 "test": "mocha" 7 "test": "mocha"
8 }, 8 },
9 "repository": { 9 "repository": {
10 "type": "git", 10 "type": "git",
11 "url": "git@gitlab.kodesumber.com:reload97/sate24-to-ym.git" 11 "url": "git@gitlab.kodesumber.com:reload97/sate24-to-ym.git"
12 }, 12 },
13 "keywords": [ 13 "keywords": [
14 "st24", 14 "st24",
15 "reload97", 15 "reload97",
16 "r97", 16 "r97",
17 "yahoo", 17 "yahoo",
18 "ym", 18 "ym",
19 "yahoomessenger", 19 "yahoomessenger",
20 "ppob" 20 "ppob"
21 ], 21 ],
22 "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", 22 "author": "Adhidarma Hadiwinoto <me@adhisimon.org>",
23 "license": "ISC", 23 "license": "ISC",
24 "dependencies": { 24 "dependencies": {
25 "lru-cache": "^4.0.1",
25 "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", 26 "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git",
26 "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", 27 "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git",
27 "yahoomessenger": "^0.1.3-Beta" 28 "yahoomessenger": "^0.1.3-Beta"
28 } 29 }
29 } 30 }
30 31
1 var im = require('sate24/im.js') 1 var im = require('./im.js')
2 var YM = require('yahoomessenger'); 2 var YM = require('yahoomessenger');
3 var imAdaptor = require('./adaptor-ym'); 3 var imAdaptor = require('./adaptor-ym');
4 4
5 var config; 5 var config;
6 var aaa; 6 var aaa;
7 var logger; 7 var logger;
8 var callbackReport; 8 var callbackReport;
9 9
10 function onLoginSuccessful() { 10 function onLoginSuccessful() {
11 logger.info('Login successful, resuming aaa communication'); 11 logger.info('Login successful, resuming aaa communication');
12 aaa.resume(); 12 aaa.resume();
13 } 13 }
14 14
15 function onPM(from, msg) { 15 function onPM(from, msg) {
16 16
17 if (!im.isAllowedFrom(from)) { 17 if (!im.isAllowedFrom(from)) {
18 logger.info('Ignoring message from unknown sender', {from: from, msg: msg}); 18 logger.info('Ignoring message from unknown sender', {from: from, msg: msg});
19 return; 19 return;
20 } 20 }
21 21
22 var remoteProduct = im.getRemoteProductFromMessage(msg); 22 var remoteProduct = im.getRemoteProductFromMessage(msg);
23 var destination = im.getDestinationFromMessage(msg); 23 var destination = im.getDestinationFromMessage(msg);
24 24
25 if (!remoteProduct && !destination) { 25 if (!remoteProduct && !destination) {
26 logger.warn('Missing remote product or destination', {remoteProduct: remoteProduct, destination: destination, msg: msg}); 26 logger.warn('Missing remote product or destination', {remoteProduct: remoteProduct, destination: destination, msg: msg});
27 return; 27 return;
28 } 28 }
29 29
30 logger.info('Got report from partner', {remoteProduct: remoteProduct, destination: destination, msg: msg}); 30 logger.info('Got report from partner', {remoteProduct: remoteProduct, destination: destination, msg: msg});
31 im.getTask(remoteProduct, destination, function(err, task) { 31 im.getTask(remoteProduct, destination, function(err, task) {
32 if (err) { 32 if (err) {
33 logger.warn('Error getting relevant task'); 33 logger.warn('Error getting relevant task');
34 return; 34 return;
35 } 35 }
36 36
37 if (!task) { 37 if (!task) {
38 logger.warn('Something wrong, undefined task without error') 38 logger.warn('Something wrong, undefined task without error')
39 return; 39 return;
40 } 40 }
41 41
42 logger.verbose('Got relevant task', {task: task, msg: msg}); 42 logger.verbose('Got relevant task', {task: task, msg: msg});
43 var rc = im.getRcFromMessage(msg); 43 var rc = im.getRcFromMessage(msg);
44 if (rc == '00') { 44 if (rc == '00') {
45 var sn = im.getSnFromMessage(msg); 45 var sn = im.getSnFromMessage(msg);
46 if (sn) { 46 if (sn) {
47 msg = 'SN=' + sn + ';' + msg; 47 msg = 'SN=' + sn + ';' + msg;
48 } 48 }
49 } 49 }
50 50
51 if (['00', '55', '68'].indexOf(rc) == -1) { 51 if (['00', '55', '68'].indexOf(rc) == -1) {
52 im.deleteTask(remoteProduct, destination); 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
59 function start(options) { 63 function start(options) {
60 if (options && options.config) { 64 if (options && options.config) {
61 config = options.config; 65 config = options.config;
62 } else { 66 } else {
63 console.log('Unknown options.config'); 67 console.log('Unknown options.config');
64 process.exit('1'); 68 process.exit('1');
65 } 69 }
66 70
67 if (options && options.aaa) { 71 if (options && options.aaa) {
68 aaa = options.aaa; 72 aaa = options.aaa;
69 } 73 }
70 74
71 if (options && options.aaa && options.aaa.callbackReport) { 75 if (options && options.aaa && options.aaa.callbackReport) {
72 callbackReport = options.aaa.callbackReport; 76 callbackReport = options.aaa.callbackReport;
73 } else { 77 } else {
74 console.log('Unknown options.aaa.callbackReport') 78 console.log('Unknown options.aaa.callbackReport')
75 process.exit(2); 79 process.exit(2);
76 } 80 }
77 81
78 if (options && options.logger) { 82 if (options && options.logger) {
79 logger = options.logger; 83 logger = options.logger;
80 } else { 84 } else {
81 logger = new winston.Logger({ 85 logger = new winston.Logger({
82 transports: [ 86 transports: [
83 new (winston.transports.Console)() 87 new (winston.transports.Console)()
84 ] 88 ]
85 }); 89 });
86 } 90 }
87 91
88 var callbacks = { 92 var callbacks = {
89 onLoginSuccessful: onLoginSuccessful, 93 onLoginSuccessful: onLoginSuccessful,
90 onPM: onPM, 94 onPM: onPM,
91 } 95 }
92 96
93 im.init(options); 97 im.init(options);
94 imAdaptor.init(config.h2h_out.ym_id, config.h2h_out.ym_password, logger, callbacks); 98 imAdaptor.init(config.h2h_out.ym_id, config.h2h_out.ym_password, logger, callbacks);
95 } 99 }
96 100
97 function onSameDayDupe(task, archivedTask) { 101 function onSameDayDupe(task, archivedTask) {
98 if (task.requestId == archivedTask.requestId) { 102 if (task.requestId == archivedTask.requestId) {
99 logger.info('Mengulang trx untuk advice', {task: task}); 103 logger.info('Mengulang trx untuk advice', {task: task});
100 _topupRequest(task); 104 _topupRequest(task);
101 } else { 105 } else {
102 logger.info('Terdeteksi trx sama dalam satu hari yang sama', {task: task}); 106 logger.info('Terdeteksi trx sama dalam satu hari yang sama', {task: task});
103 callbackReport(task.requestId, '55', 'Terdeteksi trx sama dalam satu hari yang sama'); 107 callbackReport(task.requestId, '55', 'Terdeteksi trx sama dalam satu hari yang sama');
104 } 108 }
105 } 109 }
106 110
107 function _topupRequest(task) { 111 function _topupRequest(task) {
108 var pattern = config.h2h_out.request_pattern; 112 var pattern = config.h2h_out.request_pattern;
109 113
110 var keywords = { 114 var keywords = {
111 remoteProduct: task.remoteProduct, 115 remoteProduct: task.remoteProduct,
112 destination: task.destination, 116 destination: task.destination,
113 pin: config.h2h_out.pin 117 pin: config.h2h_out.pin
114 } 118 }
115 119
116 im.saveTask(task, function() { 120 im.saveTask(task, function() {
121 im.registerResendDelay(task);
122
117 var msg = im.createMessage(pattern, keywords); 123 var msg = im.createMessage(pattern, keywords);
118 imAdaptor.sendMessage(config.h2h_out.partner, msg); 124 imAdaptor.sendMessage(config.h2h_out.partner, msg);
119 }); 125 });
120 126
121 } 127 }
122 128
123 function topupRequest(task) { 129 function topupRequest(task) {
124 if (!aaa.isTodayTrx(task)) { 130 if (!aaa.isTodayTrx(task)) {
125 logger.warn('Maaf, transaksi beda hari tidak dapat dilakukan'); 131 logger.warn('Maaf, transaksi beda hari tidak dapat dilakukan');
126 callbackReport(task.requestId, '68', 'Maaf, transaksi beda hari tidak dapat dilakukan'); 132 callbackReport(task.requestId, '68', 'Maaf, transaksi beda hari tidak dapat dilakukan');
133 im.cancelResendDelay(task);
127 return; 134 return;
128 } 135 }
129 136
130 im.checkForSameDayDuplicate(task, _topupRequest, onSameDayDupe, _topupRequest); 137 im.checkForSameDayDuplicate(task, _topupRequest, onSameDayDupe, _topupRequest);
131 } 138 }
132 139
133 exports.start = start; 140 exports.start = start;
134 exports.topupRequest = topupRequest; 141 exports.topupRequest = topupRequest;
135 142