<commit_before>/*
	Moderation Feature
*/

exports.id = 'moderation';
exports.desc = 'Automated moderation for chat rooms';

var MOD_CONSTS = {
	FLOOD_MESSAGE_NUM: 5,
	FLOOD_PER_MSG_MIN: 500, // this is the minimum time between messages for legitimate spam. It's used to determine what "flooding" is caused by lag
	FLOOD_MESSAGE_TIME: 6 * 1000,
	MIN_CAPS_LENGTH: 18,
	MIN_CAPS_PROPORTION: 0.8,
	MAX_STRETCH: 7,
	MAX_REPEAT: 4
};

function getConst (c) {
	if (Config.moderation && Config.moderation.MOD_CONSTS && typeof Config.moderation.MOD_CONSTS[c] !== 'undefined') return Config.moderation.MOD_CONSTS[c];
	return MOD_CONSTS[c];
}

var DEFAULT_MOD_VALUES = {
	'spam-p': 3,
	'spam': 4,
	'spam-link': 4,
	'flood-hard': 3,
	'flood': 2,
	'caps': 1,
	'stretch': 1,
	'banwords': 2,
	'inapwords': 2,
	'servers': 2,
	'youtube': 2,
	'spoiler': 2
};

function getValue (key) {
	if (Config.moderation && Config.moderation.values && typeof Config.moderation.values[key] === 'number') return parseInt(Config.moderation.values[key]);
	return DEFAULT_MOD_VALUES[key] || 1;
}

function getModException (room) {
	if (Settings.settings['modexception'] && typeof Settings.settings['modexception'][room] !== 'undefined') return Settings.settings['modexception'][room];
	if (Config.moderation && typeof Config.moderation.modException !== 'undefined') return Config.moderation.modException;
	return Tools.getGroup('driver');
}

function trad (data, room) {
	var lang = Config.language || 'english';
	if (Settings.settings['language'] && Settings.settings['language'][room]) lang = Settings.settings['language'][room];
	return Tools.translateGlobal('moderation', data, lang);
}

var chatData = exports.chatData = {};
var chatLog = exports.chatLog = {};
var zeroTol = exports.zeroTol = {};
var cleanDataTimer = null;

var cleanData = exports.cleanData = function () {
	for (var room in chatData) {
		for (var user in chatData[room]) {
			var now = Date.now();
			if (!chatData[room][user] || !chatData[room][user].times.length) {
				delete chatData[room][user];
				continue;
			}
			if (now - chatData[room][user].times[chatData[room][user].times.length - 1] > 24 * 60 * 60 * 1000) {
				delete chatData[room][user];
				continue;
			}
			var newTimes = [];
			for (var j = 0; j < chatData[room][user].times.length; j++) {
				if (now - chatData[room][user].times[j] < 60 * 60 * 1000) newTimes.push(chatData[room][user].times[j]);
			}
			delete chatData[room][user].times;
			chatData[room][user].times = newTimes;
			if (chatData[room][user].points) chatData[room][user].points--;
		}
	}
};

function isBotRanked (room, rank) {
	if (!Bot.rooms[room]) return false;
	var ident = Bot.rooms[room].users[toId(Bot.status.nickName)];
	if (ident) return Tools.equalOrHigherRank(ident, rank);
	return false;
}

function isBanned (room, user, noregexp) {
	user = toId(user);
	if (Settings.settings['autoban'] && Settings.settings['autoban'][room] && Settings.settings['autoban'][room][user]) return true;
	if (!noregexp && Settings.settings['regexautoban'] && Settings.settings['regexautoban'][room]) {
		for (var i in Settings.settings['regexautoban'][room]) {
			try {
				var regexObj = new RegExp(i.substr(1, i.length - 3), 'i');
				if (regexObj.test(user)) return '#range';
			} catch (e) {}
		}
	}
	return false;
}

function isGlobalBanned (room, user, noregexp) {
	user = toId(user);
	if (Settings.settings['globalautoban'] && Settings.settings['globalautoban'][room] && Settings.settings['globalautoban'][room][user]) return true;
	if (!noregexp && Settings.settings['regexglobalautoban'] && Settings.settings['regexglobalautoban'][room]) {
		for (var i in Settings.settings['regexglobalautoban'][room]) {
			try {
				var regexObj = new RegExp(i.substr(1, i.length - 3), 'i');<commit_msg>Separate autoban and global autoban<commit_after>/*
	Moderation Feature
*/

exports.id = 'moderation';
exports.desc = 'Automated moderation for chat rooms';

var MOD_CONSTS = {
	FLOOD_MESSAGE_NUM: 5,
	FLOOD_PER_MSG_MIN: 500, // this is the minimum time between messages for legitimate spam. It's used to determine what "flooding" is caused by lag
	FLOOD_MESSAGE_TIME: 6 * 1000,
	MIN_CAPS_LENGTH: 18,
	MIN_CAPS_PROPORTION: 0.8,
	MAX_STRETCH: 7,
	MAX_REPEAT: 4
};

function getConst (c) {
	if (Config.moderation && Config.moderation.MOD_CONSTS && typeof Config.moderation.MOD_CONSTS[c] !== 'undefined') return Config.moderation.MOD_CONSTS[c];
	return MOD_CONSTS[c];
}

var DEFAULT_MOD_VALUES = {
	'spam-p': 3,
	'spam': 4,
	'spam-link': 4,
	'flood-hard': 3,
	'flood': 2,
	'caps': 1,
	'stretch': 1,
	'banwords': 2,
	'inapwords': 2,
	'servers': 2,
	'youtube': 2,
	'spoiler': 2
};

function getValue (key) {
	if (Config.moderation && Config.moderation.values && typeof Config.moderation.values[key] === 'number') return parseInt(Config.moderation.values[key]);
	return DEFAULT_MOD_VALUES[key] || 1;
}

function getModException (room) {
	if (Settings.settings['modexception'] && typeof Settings.settings['modexception'][room] !== 'undefined') return Settings.settings['modexception'][room];
	if (Config.moderation && typeof Config.moderation.modException !== 'undefined') return Config.moderation.modException;
	return Tools.getGroup('driver');
}

function trad (data, room) {
	var lang = Config.language || 'english';
	if (Settings.settings['language'] && Settings.settings['language'][room]) lang = Settings.settings['language'][room];
	return Tools.translateGlobal('moderation', data, lang);
}

var chatData = exports.chatData = {};
var chatLog = exports.chatLog = {};
var zeroTol = exports.zeroTol = {};
var cleanDataTimer = null;

var cleanData = exports.cleanData = function () {
	for (var room in chatData) {
		for (var user in chatData[room]) {
			var now = Date.now();
			if (!chatData[room][user] || !chatData[room][user].times.length) {
				delete chatData[room][user];
				continue;
			}
			if (now - chatData[room][user].times[chatData[room][user].times.length - 1] > 24 * 60 * 60 * 1000) {
				delete chatData[room][user];
				continue;
			}
			var newTimes = [];
			for (var j = 0; j < chatData[room][user].times.length; j++) {
				if (now - chatData[room][user].times[j] < 60 * 60 * 1000) newTimes.push(chatData[room][user].times[j]);
			}
			delete chatData[room][user].times;
			chatData[room][user].times = newTimes;
			if (chatData[room][user].points) chatData[room][user].points--;
		}
	}
};

function isBotRanked (room, rank) {
	if (!Bot.rooms[room]) return false;
	var ident = Bot.rooms[room].users[toId(Bot.status.nickName)];
	if (ident) return Tools.equalOrHigherRank(ident, rank);
	return false;
}

function isBanned (room, user, noregexp) {
	user = toId(user);
	if (Settings.settings['autoban'] && Settings.settings['autoban'][room] && Settings.settings['autoban'][room][user]) return true;
	if (!noregexp && Settings.settings['regexautoban'] && Settings.settings['regexautoban'][room]) {
		for (var i in Settings.settings['regexautoban'][room]) {
			try {
				var regexObj = new RegExp(i.substr(1, i.length - 3), 'i');
				if (regexObj.test(user)) return '#range';
			} catch (e) {}
		}
	}
	return false;
}

function isGlobalBanned (room, user, noregexp) {
	user = toId(user);
	if (Settings.settings['gautoban'] && Settings.settings['gautoban'][room] && Settings.settings['gautoban'][room][user]) return true;
	if (!noregexp && Settings.settings['regexgautoban'] && Settings.settings['regexgautoban'][room]) {
		for (var i in Settings.settings['regexgautoban'][room]) {
			try {
				var regexObj = new RegExp(i.substr(1, i.length - 3), 'i');
{'fraction_non_alphanumeric': 0.169676048858205, 'fraction_numerical': 0.013807753584705257, 'mean_word_length': 5.002390438247012, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, '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': '3923085', 'n_tokens_mistral': 2855, 'n_tokens_neox': 2621, 'n_words': 587}