im.js 8.47 KB
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 dumpStats() {
    if (config.globals.auto_resend_delay_secs) {
        logger.verbose('DUMP STATS', {
            'resendHandlers:length': resendHandlers.length,
            'resendHandlers:itemCount': resendHandlers.itemCount,
        });
    }
}

function dumpStatsPeriodic() {
    var dumpStatsInterval = 1000 * 120;
    if (config.globals.dump_stats_interval_secs) {
        dumpStatsInterval = config.globals.dump_stats_interval_secs * 1000;
    }
    logger.verbose('Dump stats every ' + dumpStatsInterval / 1000 + ' secs (override it with config.globals.dump_stats_interval_secs)');
    setInterval(dumpStats, dumpStatsInterval);
}

function init(options) {
    if (options && options.config) {
        config = options.config;
    }

    if (options && options.logger) {
        logger = options.logger;
    }

    if (options && options.partner) {
        partner = options.partner;
    }

    createRedisClient(config.globals.redis_host, config.globals.redis_port);
    readImConfig();

    dumpStatsPeriodic();
}

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_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;

        try {
            cancelResendDelay(task);
        }
        catch(e) {}
    }

    if (retry <= 0) {
        logger.verbose('Resend delay retry exceeded', {task: task});
        cancelResendDelay(task);
        return;
    }

    logger.verbose('Registering resend delay task request', {task: task, delay: config.globals.auto_resend_delay_secs});
    var handlerData = {
        handler: setTimeout(partner.topupRequest, config.globals.auto_resend_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;
exports.cancelResendDelay = cancelResendDelay;