Source: lib/bot.js

var parent_dir = process.getAbsolutePath(__dirname);
var check = require("check-types");
var fs = require('fs');
var Lock = require(parent_dir + '/lib/lock.js');
var _ = require("underscore");
/**
	Represent a bot in a cluster.
	@constructor
	@author Tilak Patidar <tilakpatidar@gmail.com>
	@param {Message} message_obj

*/
var Bot = function(message_obj) {
    var message = message_obj;
    var config = message.get('config');
    var proto = require(parent_dir + "/lib/proto.js");
    var JSONX = proto.JSONX;
    /**
    	Controls the pinging of master
		@type boolean
		@private
    */
    var lock_master_ping = new Lock();

    var cluster = message.get('cluster');
    var pool = message.get('pool');
    var that = this;

    var log = message.get('log');

    this._id = config.getConfig('bot_name');
    this.registerTime = new Date();
    this.createdBuckets = 0;
    this.processedBuckets = 0;
    this.crawledPages = 0;
    this.failedPages = 0;


    /**
		Resets the stats whenever stats are flushed into db.
		@private
    */
    function resetBotStats() {
        that.createdBuckets = 0;
        that.processedBuckets = 0;
        that.crawledPages = 0;
        that.failedPages = 0;
    }


    /**
		Updates the stats in the db
		@private
		@param {boolean} updateConfig - to update config or not
		@param {Function} fn - callback

    */
    function botInfoUpdater(updateConfig, fn) {
        var dic = that.getBotData(); //data is pulled and reseted
        var n_dic = {};
        //console.log(dic);
        _.each(dic, function(elem, i) {

            if (check.number(dic[i])) {
                if (!n_dic['$inc']) {
                    n_dic['$inc'] = {};
                }
                n_dic['$inc'][i] = dic[i];
            }

        });

        //console.log(n_dic, "n_dic");

        if (updateConfig) {
            var jsonx_obj = new JSONX(config.getConfig());
            n_dic['config'] = JSON.parse(jsonx_obj.stringify());
        }
        //#debug#console.log(n_dic);
        pool.bot.updateBotInfo(n_dic, function updateBotInfo(err, results) {
            //#debug#console.log(results);
            //#debug#console.log(err);
            if (err) {
                msg("Error ocurred while updating bot info ", "error");
                if (check.assigned(fn)) {
                    return fn(false);
                }
            } else {

                msg("Bot info updated", "success");
                if (check.assigned(fn)) {
                    return fn(true);
                }
            }
        });
    }

    /**
		Tests if master is active by pinging from cluster network.
		If master fails it clears semaphore collection to start re-elect.
		@private

    */

    function pingMaster() {

        if (!lock_master_ping.enter()) {
            return;
        }

        //#debug#console.log("pingMaster")
        //pings master if master is dead ,clears the semaphore collection so that bots can compete again
        try {
            cluster.getMaster(function(master) {

                cluster.send(master, {
                    "active": 1
                }, function(s, res) {
                    // console.log(s,res)
                    if (s) {
                        if (check.assigned(res)) {
                            if (JSON.parse(res)["active"] === true) {
                                //	//#debug#console.log("still active");
                                //master is still active
                                msg("Master is active", "success");
                                lock_master_ping.release();
                            } else {
                                pool.semaphore_collection.remove({}, function() {
                                    msg("Master is dead", "error");
                                    lock_master_ping.release();
                                });
                            }
                        } else {
                            //#debug#console.log("dead");
                            pool.semaphore_collection.remove({}, function() {
                                msg("Master is dead", "error");
                                lock_master_ping.release();
                            });
                        }
                    } else {
                        //#debug#console.log("dead");
                        pool.semaphore_collection.remove({}, function() {
                            msg("Master is dead", "error");
                            lock_master_ping.release();
                        });
                    }

                });
            });
        } catch (err) {
            console.log(err);
            lock_master_ping.release();
        }

    };

    /**
        	Checks if an active bot with same name is present return true if you can start the bot
        	return false if an instance of the same bot name is running.
        	@private
        	@param {Function} fn
        */
    function checkIfActiveBot(fn) {


        if (message.get('webappOnly')) {
            fn(true); //bypass starting the bot if webapp mode is on
            return;
        }
        var t = new Date().getTime();

        pool.bot.startBotGetBotActiveStatus(function startBotGetBotActiveStatus(err, result) {

            if (result) {
                msg("A bot with same is name is still active in cluster", "error");

                return fn(false);

            } else {
                pool.bot.startBotAddNewBot(t, function startBotAddNewBot(err, result) {

                    if (!err) {
                        msg("Inserted new bot info into cluster", "success");
                        pool.bot.requestToBecomeMaster(config.getConfig("bot_name"), function(status) {
                            if (status) {
                                message.set('cluster_master', true);
                            } else {
                                message.set('cluster_master', false);
                            }
                            return fn(true);
                        });

                    } else {
                        msg("Unable to insert new bot into cluster", "error");
                        return fn(false);
                    }

                });


            }

        });



    }


    /**
		Updates a stat value by given value.
		@public
		@param {String} key
		@param {String} value
    */
    this.updateStats = function updateStats(key, value) {
        if (check.number(value)) {
            //will be incremented by the value given
            that[key] = that[key] + value;
        } else {
            that[key] = value;
        }
    };


    /**
		Fetches and then resets the bot data.
		@public
    */
    this.getBotData = function getBotData() {
        //data is reseted when it is pulled

        var stats = {};
        stats._id = that._id;
        stats.registerTime = that.registerTime;
        stats.createdBuckets = that.createdBuckets;
        stats.processedBuckets = that.processedBuckets;
        stats.crawledPages = that.crawledPages;
        stats.failedPages = that.failedPages;
        resetBotStats();
        return stats;
    };


    /**
		Starts the bot
		@public
		@param {boolean} force_mode - Whether to start bot with force mode or not.

    */
    this.startBot = function startBot(force_mode, fn) {
        pool.resetBuckets(function() {
            checkIfActiveBot(function checkIfActiveBot(success) {
                //#debug#console.log("HEY")
                //start the bot and verify if other bot with same name exists
                if (!success) {
                    if (force_mode) {
                        msg("Force mode enabled", 'info');
                        //try to kill other bot with same name
                        cluster.closeBot(config.getConfig('bot_name'), function(status) {

                            if (status) {
                                checkIfActiveBot(function(status1) {
                                    if (status1) {
                                        return fn(true);
                                    } else {
                                        return fn(false);
                                    }
                                });

                            } else {
                                return fn(false);
                            }
                        });
                    } else {
                        return fn(false);
                    }
                } else {
                    //#debug#console.log("DDD")
                    return fn(true);
                }

            });
        });
    }



    /**
		Stops the bot and resets queues, buckets and cache status.
		@public
		@param {Function} fn - callback

    */
    this.stopBot = function stopBot(fn) {
        var files = fs.readdirSync(parent_dir + '/pdf-store/');
        for (var i = 0; i < files.length; i++) {
            if (files[i].indexOf(".") === 0) {
                //do not take hidden files
                continue;
            }
            var domain = files[i].replace(/##/g, "/");
            var data = fs.unlinkSync(parent_dir + '/pdf-store/' + files[i]);
        };
        pool.resetBuckets(function() {
            pool.bot.BotMarkInactive(function BotMarkInactive(err, results) {
                if (err) {
                    msg("Bot was not found something fishy ", "error");
                    return fn(false);
                } else {
                    botInfoUpdater(false, function botInfoUpdater(updated) {
                        if (updated) {
                            msg("Bot cleaned up ", "success");
                            return fn(true);
                        } else {
                            msg("Bot cleaned up ", "error");
                            fn(false);
                            return;
                        }
                    });

                }

            });


        });
    }

    //intervals

    var b = setInterval(function() {
        if (message.get('begin_intervals')) {
            botInfoUpdater(false);
        }
    }, 10000);
    message.get('my_timers').push(b);
    var c = setInterval(function() {
        if (message.get('begin_intervals')) {
            pool.bot.checkIfStillMaster(function(status) {
                /*					
					This function checks if the bot is still a master and also creates one if do not exist
            	*/
                if (status) {
                    message.set('cluster_master', true);
                } else {
                    message.set('cluster_master', false);
                }
            });
        }

    }, 10000);
    message.get('my_timers').push(c);
    var d = setInterval(function() {
        if (message.get('begin_intervals')) {
            pingMaster();
        }

    }, 10000);
    message.get('my_timers').push(d);

    /**
		Used to call Logger object with the caller function name.
		@private
	*/
    function msg() {
        log.put(arguments[0], arguments[1], __filename.split('/').pop(), arguments.callee.caller.name.toString());
    }

};





module.exports = Bot;