diff --git a/im.js b/im.js new file mode 100644 index 0000000..4cfeda9 --- /dev/null +++ b/im.js @@ -0,0 +1,301 @@ +var redis = require('redis'); +var moment = require('moment'); +var LRU = require('lru-cache'); + +var config; +var logger; +var partner; +var imConfig; +var redisClient; + +var resendHandlers = LRU({max: 2000, maxAge: 1000 * 3600 * 36}); + +function init(options) { + if (options && options.config) { + config = options.config; + } + + if (options && options.logger) { + logger = options.logger; + } + + createRedisClient(config.globals.redis_host, config.globals.redis_port); + readImConfig(); +} + +function createRedisClient(host, port) { + try { + redisClient = redis.createClient(port, host); + } catch(err) { + logger.warn("Error creating redis client to " + host + ':' + port); + } +} + +function readImConfig(filename) { + + if (!filename) { + filename = process.cwd() + '/config.im.json'; + } + + try { + imConfig = require(filename); + } + catch(e) { + imConfig = {}; + } + + logger.verbose('IM Config', {imConfig: imConfig}); + return imConfig; +} + +function getPatternFromMessage(message, pattern, patternMatchIndex) { + var re = new RegExp(pattern); + var matches = message.match(re); + + if (!matches) { + return null; + } + + if (patternMatchIndex < matches.length) { + return matches[patternMatchIndex]; + } else { + return null; + } +} + +function getPatternsFromMessage(message, patterns) { + var patternCount = patterns.length; + for (var i = 0; i < patternCount; i++) { + + var pattern = patterns[i]; + + var result = getPatternFromMessage(message, pattern.pattern, pattern.matchIndex); + if (result) { + return result; + } + } +} + +function getTaskKey(task, chipInfo, today) { + if (!chipInfo && config && config.globals && config.globals.gateway_name) { + chipInfo = config.globals.gateway_name; + } + + if (task.timestamp && !today) { + today = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYYMMDD'); + } + + return chipInfo + '.trx.date:' + today + '.rProduct:' + task.remoteProduct.toUpperCase() + '.dest:' + task.destination ; +} + +function saveTask(task, cb) { + var key = getTaskKey(task, config.globals.gateway_name); + logger.verbose('Saving task', {key: key, task: task}); + + redisClient.set(key, JSON.stringify(task), function() { + redisClient.expire(key, 3600*24); + if (cb) { + cb(); + } + }); +} + +function getTask(remoteProduct, destination, cb) { + var dummyTask = { + remoteProduct: remoteProduct, + destination: destination, + } + + var key = getTaskKey(dummyTask, config.globals.gateway_name, moment().format('YYYYMMDD')); + redisClient.get(key, function(err, result) { + if (err) { + logger.verbose('getTask: task not found', {key: key, params: dummyTask}); + + cb(err, null); + return; + } + + var task = {}; + + try { + task = JSON.parse(result); + } + catch(e) { + logger.warn('getTask: Can not parse result', {key: key, params: dummyTask, data: result}); + err = "Can not parse result" + } + cb(err, task); + }); +} + +function deleteTask(remoteProduct, destination) { + var dummyTask = { + remoteProduct: remoteProduct, + destination: destination, + } + + var key = getTaskKey(dummyTask, config.globals.gateway_name, moment().format('YYYYMMDD')); + + try { + redisClient.del(key); + } + catch(e) {}; +} + +function createMessage(pattern, keywords) { + var msg = pattern; + + for (var key in keywords) { + msg = msg.replace('[' + key + ']', keywords[key]); + } + return msg; +} + +function getRemoteProductFromMessage(msg) { + return getPatternsFromMessage(msg, imConfig.product_patterns); +} + +function getDestinationFromMessage(msg) { + return getPatternsFromMessage(msg, imConfig.destination_patterns); +} + +function getSnFromMessage(msg) { + return getPatternsFromMessage(msg, imConfig.sn_patterns); +} + +function getRcFromMessage(msg) { + var rcs = imConfig.response_codes; + var rcsCount = rcs.length; + + for (var i = 0; i < rcsCount; i++) { + + var item = rcs[i]; + var re = new RegExp(item.pattern); + if (msg.search(re) != -1) { + return item.rc; + } + + } + return '68'; +} + +function isAllowedFrom(sender) { + if (!config || !config.h2h_out || !config.h2h_out.allowed_response_from) { + return true; + } + + whitelist = config.h2h_out.allowed_response_from.split(','); + whitelistCount = whitelist.length; + + for(var i=0; i<whitelistCount; i++) { + if (sender == whitelist[i]) { + return true; + } + } + + return false; +} + +function checkForSameDayDuplicate(task, cbNoDupe, cbDupe, cbDupeWithSameReqId) { + getTask(task.remoteProduct, task.destination, function(err, archivedTask) { + if (err) { + logger.warn('Error on checking same day duplicate', {task: task}); + cbNoDupe(task); + return; + } + + if (archivedTask && archivedTask.requestId) { + if (cbDupeWithSameReqId && task.requestId == archivedTask.requestId) { + logger.verbose('Duplicate trx on same day with same requestId', {task: task}); + cbDupeWithSameReqId(task, archivedTask); + return; + } + + logger.verbose('Duplicate trx on same day', {task: task, archivedTask: archivedTask}); + cbDupe(task, archivedTask); + return; + } + + cbNoDupe(task); + }); +} + +function registerResendDelay(task) { + if (!task.requestId) { + logger.warn('Invalid task on resendDelay') + return; + } + + if (!config || !config.globals || !config.globals.auto_resend_on_delay_secs) { + return; + } + + if (!partner || !partner.topupRequest) { + logger.warn('Skip request resend delay because partner.topupRequest is not exists'); + return; + } + + var retry = 10; + var oldHandler = resendHandlers.get(task.requestId); + if (oldHandler) { + retry = oldHandler.retry - 1; + } + + if (retry <= 0) { + logger.verbose('Resend delay retry exceeded', {task: task}); + cancelResendDelay(task); + return; + } + + logger.info('Resending task request', {task}); + var handlerData = { + handler: setTimeout(partner.topupRequest, config.globals.auto_resend_on_delay_secs, task), + task: task, + retry: retry + } + + resendHandlers.set(task.requestId, handlerData); +} + +function cancelResendDelay(task) { + if (!task || !task.requestId) { + logger.warn('Invalid task on cancelResendDelay'); + return; + } + + var oldHandler = resendHandlers.get(task.requestId); + if (!oldHandler) { + return; + } + + logger.verbose('Canceling resend delay', {task: task}); + + try { + if (oldHandler.handler) { + clearTimeout(oldHandler.handler); + } + } + catch(e) {}; + + try { + resendHandlers.del(task.requestId); + } + catch(e) {}; +} + +exports.init = init; +exports.start = init; +exports.getPatternFromMessage = getPatternFromMessage; +exports.getPatternsFromMessage = getPatternsFromMessage; +exports.saveTask = saveTask; +exports.getTask = getTask; +exports.readImConfig = readImConfig; +exports.createMessage = createMessage; +exports.getRemoteProductFromMessage = getRemoteProductFromMessage; +exports.getDestinationFromMessage = getDestinationFromMessage; +exports.getRcFromMessage = getRcFromMessage; +exports.getSnFromMessage = getSnFromMessage; +exports.isAllowedFrom = isAllowedFrom; +exports.checkForSameDayDuplicate = checkForSameDayDuplicate; +exports.deleteTask = deleteTask; +exports.registerResendDelay = registerResendDelay; diff --git a/index.js b/index.js index 5263ee5..06ed839 100644 --- a/index.js +++ b/index.js @@ -18,6 +18,7 @@ var options = { 'logger': logger, 'config': config, 'matrix': matrix, + 'partner': partner, } var httpServer = HttpServer.start(config, options); diff --git a/package.json b/package.json index 3500f3d..b6abb83 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", "license": "ISC", "dependencies": { + "lru-cache": "^4.0.1", "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", "yahoomessenger": "^0.1.3-Beta" diff --git a/partner-ym.js b/partner-ym.js index fc20000..9c1b7b9 100644 --- a/partner-ym.js +++ b/partner-ym.js @@ -1,4 +1,4 @@ -var im = require('sate24/im.js') +var im = require('./im.js') var YM = require('yahoomessenger'); var imAdaptor = require('./adaptor-ym'); @@ -52,7 +52,11 @@ function onPM(from, msg) { im.deleteTask(remoteProduct, destination); } - callbackReport(task.requestId, rc, msg); + if (rc != '68') { + im.cancelResendDelay(task); + } + + callbackReportWrapper(task.requestId, rc, msg); }); } @@ -114,6 +118,8 @@ function _topupRequest(task) { } im.saveTask(task, function() { + im.registerResendDelay(task); + var msg = im.createMessage(pattern, keywords); imAdaptor.sendMessage(config.h2h_out.partner, msg); }); @@ -124,9 +130,10 @@ function topupRequest(task) { if (!aaa.isTodayTrx(task)) { logger.warn('Maaf, transaksi beda hari tidak dapat dilakukan'); callbackReport(task.requestId, '68', 'Maaf, transaksi beda hari tidak dapat dilakukan'); + im.cancelResendDelay(task); return; } - + im.checkForSameDayDuplicate(task, _topupRequest, onSameDayDupe, _topupRequest); }