From fdb2abd41e9dafca6fb0a059b8f4c1ff34e762f1 Mon Sep 17 00:00:00 2001
From: Adhidarma Hadiwinoto <me@adhisimon.org>
Date: Wed, 27 Jul 2016 09:50:00 +0700
Subject: [PATCH] registerResendDelay

---
 im.js         | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 index.js      |   1 +
 package.json  |   1 +
 partner-ym.js |  13 ++-
 4 files changed, 313 insertions(+), 3 deletions(-)
 create mode 100644 im.js

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);
 }
 
-- 
1.9.0