<commit_before>var fs = require("fs");
var steam = require("steam");
var winston = require("winston");
var _ = require("underscore");
var SteamTrade = require('steam-trade');
var http = require("http");
var Express = require('express');
var expressWinston = require('express-winston');

var TriggerFactory = require("./triggerFactory.js").TriggerFactory;

var serversFile = "servers";

// Load latest servers from file
if (fs.existsSync(serversFile)) {
	try {
		winston.silly("Attempting to read servers file");
		steam.servers = JSON.parse(fs.readFileSync(serversFile));
	} catch(err) {
		winston.error("Error reading serversFile:");
		winston.error(err.stack);
	}
} else {
	winston.warn("No servers file found, using defaults");
}


// Bot should be usually created without options, it is a parameter mainly for testing
var ChatBot = function(username, password, options) {
	this.winston = new winston.Logger;
	this.options = options || {};

	this.steamClient = options.client || new steam.SteamClient();
	this.steamTrade = new SteamTrade();
	this.name = options.name || username;
	this.username = username;
	this.password = password;
	if(options.guardCode) {
		this.guardCode = options.guardCode;
	} else {
		this.guardCode = false;
	}
	this.sentryFile = undefined;
	this.games = [];
	this.logFile = undefined;
	this.cookie = undefined;
	this.maingroup = options.maingroup || undefined;

	this.autojoinFile = options.autojoinFile || "bot." + this.name+".autojoin"; this.autojoinRooms = undefined;

	this.winston.add(winston.transports.Console,{
		handleExceptions: false,
		colorize: options.consoleColors||true,
		timestamp: options.consoleTime||true,
		level: options.consoleLogLevel||"info",
		json: false
	});

	var that = this;
	if(!options.logFile || options.logFile !== false || options.logFile === null || options.logFile === undefined)  {
		if(options.logFile === true) { that.logFile = "bot." + that.name + ".log"; }
		else { that.logFile = options.logFile; }
		that.winston.info("Chatbot "+that.name+" logging output to: " + that.logFile);
		that.winston.add(
			winston.transports.File, {
				level: options.logLevel || "info",
				colorize:false,
				timestamp:true,
				filename:that.logFile,
				json:false
			}
		);
	}

	if(options.sentryFile) {
		this.sentryFile = options.sentryFile;
		if(fs.existsSync(options.sentryFile)) {
			this.winston.info("Chatbot "+that.name+" using sentryfile as defined by options: "+options.sentryfile);
		} else {
			this.winston.warn("Chatbot "+that.name+": Sentry file defined in config does not exist. "
				+ options.sentryFile + " will be created on successful login");
		}
	} else {
		this.winston.info("Chatbot "+that.name+": Config file does not define a sentryfile. Attempting to autodetect sentry...");
		var sentryFile = _.find([
			"sentry."+this.name+".hash",
			"sentry."+this.name,
			"sentryfile."+this.name,
			"sentryfile."+this.name+".hash",
			"bot."+this.name+".hash",
			"bot."+this.name+".sentry",
			this.name+".hash",
			this.name+".sentry"
		], function(val) {
			return fs.existsSync(val);
		});
		if ("undefined" !== typeof sentryFile) {
			this.sentryFile = sentryFile;
			this.winston.info("Chatbot "+that.name+": Sentry file "+this.sentryFile+" detected.");
		} else if(fs.existsSync("sentry")) {
			this.sentryFile = "sentry";
			this.winston.warn("Chatbot "+that.name+": Sentry file sentry detected, but it may be incorrect as it does not claim a username. If correct, please rename to bot."
				+ this.name + ".sentry or another supported sentry filename, or rename and define file name in config.");
		} else {
			this.sentryFile = "bot."+this.name+".sentry";
			this.winston.warn("Chatbot "+that.name+": Could not detect a sentryfile, and no guardCode defined. Using default sentryFile "
				+ this.sentryFile + ". I hope you have a guardCode defined or steamguard disabled. ");
		}
	}

	this.connected = false; // Bot is connected to the steam network
	this.muted = false; // Should not send any messages to a chat room when muted

	if(!options.disableWebserver) {
		this._startWebServer(options.webServerPort||8080)
	}

	this.triggers = {};
	if (options.triggerFactory) {
		this.triggerFactory = options.triggerFactory;
	} else {
		this.triggerFactory = new TriggerFactory();
	}

	this.unmutedState = steam.EPersonaState.Online;
	this.mutedState = steam.EPersonaState.Snooze;

	if (options.autoConnect) {
		this.connect();
	}

	// Connect relevant events
	this.steamClient.on("error",           function(error)  { that._onError(error); });
	this.steamClient.on("loggedOn",        function()       { that._onLoggedOn(); });
	this.steamClient.on("loggedOff",       function()       { that._onDisconnected(); });
	this.steamClient.on("chatInvite",      function(roomId, roomName, inviterId)      { that._onChatInvite(roomId, roomName, inviterId); });
	this.steamClient.on("friend",          function(userId, relationship)             { that._onRelationship(userId, relationship); });
	this.steamClient.on("friendMsg",       function(userId, message, type)            { that._onFriendMsg(userId, message, type); });
	this.steamClient.on("chatMsg",         function(roomId, message, type, chatterId) { that._onChatMsg(roomId, message, type, chatterId); });
	this.steamClient.on("chatStateChange", function(stateChange, chatterActedOn, steamChatId, actedOnBy) { that._onChatStateChange(stateChange, chatterActedOn, steamChatId, actedOnBy); });
	this.steamClient.on("sentry",          function(sentry) { that._onSentry(sentry); });
	this.steamClient.on('webSessionID',    function(sessionID) { that._onWebSessionID(sessionID); });
	this.steamClient.on('tradeOffers',     function(number) { that._onTradeOffers(number); });
	this.steamClient.on('tradeProposed',   function(tradeID, steamID) { that._onTradeProposed(tradeID, steamID, that.acceptTrade); });
	this.steamClient.on('tradeResult',     function(tradeID, result, steamID) { that._onTradeResult(tradeID, result, steamID) });
	this.steamClient.on('sessionStart',    function(steamID) { that._onSessionStart(steamID) });
	this.steamClient.on('announcement',    function(groupID, headline) { that._onAnnouncement(that.maingroup, headline); });
/*
// These events exist in node-steam, but are only half-implemented in this file...
	this.steamClient.on("user", function(obscureData)              {that._onUser(obscureData); });
	this.steamClient.on("group", function(group, clanRelationship) {that._onGroup(group, clanRelationship); });
	this.steamClient.on("richPresence", function(steamId,status,obscureData) {that._onRichPresence(steamId,status,obscureData); });
*/
	
	// Store latest servers
	this.steamClient.on("servers", function(servers) {
		try{
			that.winston.silly("Chatbot "+that.name+": Attempting to write servers file");
			fs.writeFileSync(serversFile, JSON.stringify(servers));
		} catch(err) {
			that.winston.error("Chatbot "+that.name+": Error writing servers file:");
			that.winston.error(err.stack);
		}
	});
};


// Public interface
ChatBot.prototype.connect = function() {
	// Continuously try to reconnect if started but not connected 
	// If someone logs in as the bot it will be disconnected, so this allows the bot to recover automatically when it can
	if (this.options.autoReconnect && !this.babysitInterval) {
		var babysitTimer = this.options.babysitTimer || 5*60*1000;
		var that = this;<commit_msg>Add onLogon that can be defined in config<commit_after>var fs = require("fs");
var steam = require("steam");
var winston = require("winston");
var _ = require("underscore");
var SteamTrade = require('steam-trade');
var http = require("http");
var Express = require('express');
var expressWinston = require('express-winston');

var TriggerFactory = require("./triggerFactory.js").TriggerFactory;

var serversFile = "servers";

// Load latest servers from file
if (fs.existsSync(serversFile)) {
	try {
		winston.silly("Attempting to read servers file");
		steam.servers = JSON.parse(fs.readFileSync(serversFile));
	} catch(err) {
		winston.error("Error reading serversFile:");
		winston.error(err.stack);
	}
} else {
	winston.warn("No servers file found, using defaults");
}


// Bot should be usually created without options, it is a parameter mainly for testing
var ChatBot = function(username, password, options) {
	this.winston = new winston.Logger;
	this.options = options || {};

	this.steamClient = options.client || new steam.SteamClient();
	this.steamTrade = new SteamTrade();
	this.name = options.name || username;
	this.username = username;
	this.password = password;
	if(options.guardCode) {
		this.guardCode = options.guardCode;
	} else {
		this.guardCode = false;
	}
	this.sentryFile = undefined;
	this.games = [];
	this.logFile = undefined;
	this.cookie = undefined;
	this.maingroup = options.maingroup || undefined;

	this.autojoinFile = options.autojoinFile || "bot." + this.name+".autojoin"; this.autojoinRooms = undefined;

	this.winston.add(winston.transports.Console,{
		handleExceptions: false,
		colorize: options.consoleColors||true,
		timestamp: options.consoleTime||true,
		level: options.consoleLogLevel||"info",
		json: false
	});

	var that = this;
	if(!options.logFile || options.logFile !== false || options.logFile === null || options.logFile === undefined)  {
		if(options.logFile === true) { that.logFile = "bot." + that.name + ".log"; }
		else { that.logFile = options.logFile; }
		that.winston.info("Chatbot "+that.name+" logging output to: " + that.logFile);
		that.winston.add(
			winston.transports.File, {
				level: options.logLevel || "info",
				colorize:false,
				timestamp:true,
				filename:that.logFile,
				json:false
			}
		);
	}

	if(options.sentryFile) {
		this.sentryFile = options.sentryFile;
		if(fs.existsSync(options.sentryFile)) {
			this.winston.info("Chatbot "+that.name+" using sentryfile as defined by options: "+options.sentryfile);
		} else {
			this.winston.warn("Chatbot "+that.name+": Sentry file defined in config does not exist. "
				+ options.sentryFile + " will be created on successful login");
		}
	} else {
		this.winston.info("Chatbot "+that.name+": Config file does not define a sentryfile. Attempting to autodetect sentry...");
		var sentryFile = _.find([
			"sentry."+this.name+".hash",
			"sentry."+this.name,
			"sentryfile."+this.name,
			"sentryfile."+this.name+".hash",
			"bot."+this.name+".hash",
			"bot."+this.name+".sentry",
			this.name+".hash",
			this.name+".sentry"
		], function(val) {
			return fs.existsSync(val);
		});
		if ("undefined" !== typeof sentryFile) {
			this.sentryFile = sentryFile;
			this.winston.info("Chatbot "+that.name+": Sentry file "+this.sentryFile+" detected.");
		} else if(fs.existsSync("sentry")) {
			this.sentryFile = "sentry";
			this.winston.warn("Chatbot "+that.name+": Sentry file sentry detected, but it may be incorrect as it does not claim a username. If correct, please rename to bot."
				+ this.name + ".sentry or another supported sentry filename, or rename and define file name in config.");
		} else {
			this.sentryFile = "bot."+this.name+".sentry";
			this.winston.warn("Chatbot "+that.name+": Could not detect a sentryfile, and no guardCode defined. Using default sentryFile "
				+ this.sentryFile + ". I hope you have a guardCode defined or steamguard disabled. ");
		}
	}

	this.connected = false; // Bot is connected to the steam network
	this.muted = false; // Should not send any messages to a chat room when muted

	if(!options.disableWebserver) {
		this._startWebServer(options.webServerPort||8080)
	}

	this.triggers = {};
	if (options.triggerFactory) {
		this.triggerFactory = options.triggerFactory;
	} else {
		this.triggerFactory = new TriggerFactory();
	}

	this.unmutedState = steam.EPersonaState.Online;
	this.mutedState = steam.EPersonaState.Snooze;

	if(options.onLogon instanceof Function) {
		this.onLogon = options.onLogon);
	}

	if (options.autoConnect) {
		this.connect();
	}

	// Connect relevant events
	this.steamClient.on("error",           function(error)  { that._onError(error); });
	this.steamClient.on("loggedOn",        function()       { that._onLoggedOn(); that.onLogon(that); });
	this.steamClient.on("loggedOff",       function()       { that._onDisconnected(); });
	this.steamClient.on("chatInvite",      function(roomId, roomName, inviterId)      { that._onChatInvite(roomId, roomName, inviterId); });
	this.steamClient.on("friend",          function(userId, relationship)             { that._onRelationship(userId, relationship); });
	this.steamClient.on("friendMsg",       function(userId, message, type)            { that._onFriendMsg(userId, message, type); });
	this.steamClient.on("chatMsg",         function(roomId, message, type, chatterId) { that._onChatMsg(roomId, message, type, chatterId); });
	this.steamClient.on("chatStateChange", function(stateChange, chatterActedOn, steamChatId, actedOnBy) { that._onChatStateChange(stateChange, chatterActedOn, steamChatId, actedOnBy); });
	this.steamClient.on("sentry",          function(sentry) { that._onSentry(sentry); });
	this.steamClient.on('webSessionID',    function(sessionID) { that._onWebSessionID(sessionID); });
	this.steamClient.on('tradeOffers',     function(number) { that._onTradeOffers(number); });
	this.steamClient.on('tradeProposed',   function(tradeID, steamID) { that._onTradeProposed(tradeID, steamID, that.acceptTrade); });
	this.steamClient.on('tradeResult',     function(tradeID, result, steamID) { that._onTradeResult(tradeID, result, steamID) });
	this.steamClient.on('sessionStart',    function(steamID) { that._onSessionStart(steamID) });
	this.steamClient.on('announcement',    function(groupID, headline) { that._onAnnouncement(that.maingroup, headline); });
/*
// These events exist in node-steam, but are only half-implemented in this file...
	this.steamClient.on("user", function(obscureData)              {that._onUser(obscureData); });
	this.steamClient.on("group", function(group, clanRelationship) {that._onGroup(group, clanRelationship); });
	this.steamClient.on("richPresence", function(steamId,status,obscureData) {that._onRichPresence(steamId,status,obscureData); });
*/
	
	// Store latest servers
	this.steamClient.on("servers", function(servers) {
		try{
			that.winston.silly("Chatbot "+that.name+": Attempting to write servers file");
			fs.writeFileSync(serversFile, JSON.stringify(servers));
		} catch(err) {
			that.winston.error("Chatbot "+that.name+": Error writing servers file:");
			that.winston.error(err.stack);
		}
	});
};


// Public interface
ChatBot.prototype.onLogon = function(bot) {
	return;
}

ChatBot.prototype.connect = function() {
	// Continuously try to reconnect if started but not connected 
	// If someone logs in as the bot it will be disconnected, so this allows the bot to recover automatically when it can
	if (this.options.autoReconnect && !this.babysitInterval) {
		var babysitTimer = this.options.babysitTimer || 5*60*1000;
		var that = this;
{'fraction_non_alphanumeric': 0.14772117962466488, 'fraction_numerical': 0.0014745308310991957, 'mean_word_length': 4.449598246895544, 'pattern_counts': {'":': 14, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5781266', 'n_tokens_mistral': 5130, 'n_tokens_neox': 4790, 'n_words': 1041}