Bots Home
|
Create an App
TCKY House Bot Test
Author:
atthem
Description
Source Code
Launch Bot
Current Users
Created by:
Atthem
//Summary /* Modification of NaughtyShadow's House Elf Bot for tcky31 */ //Description /* Modifed by atthem for TCKY31. Oringinal bot credit goes to NaughtyShadow. Modified from version 3.3 - kept the houses, removed the extra features, customized for tcky31. Sorts users into Hogwarts Houses and allows tips from sorted users to accumulate points for their house. This bot is modified from the original specifically for tcky31. If you like this bot, you should use NaughtyShadow's original version, which is better, has more features, and will be updated more often. I highly recommend her bot, and discourage anyone else from using this one. -atthem */ /* Original credit for this bot goes to Lilith Song (NaughtyShadow). I have modified it for TCKY31, customizing it for her needs. Basically, it has all the house stuff, and none of the extra features. */ /* Update Log ---------------------------------------------------------------- 2016-05-09 Corrected error when users tip for bribe without house name in tip note. 2016-05-10 Added House Entry Instructions to points announcement (regular or manual) 2016-05-10 Added model to permission level "isMaster" 2016-05-15 Corrected issue with Randomizer function where it could not choose array[0] (was causing Ravenclaw to never be randomly selected when sorting into houses) 2016-05-15 Added house colors to message text 2016-05-23 Modified house colors to be a light background with black text - much better look 2016-07-13 Added notification about monthly ticket show for winning house. 2016-09-01 Added ability to see what house members are currently in the room. 2016-09-14 Corrected issue where loading a previously saved state would add all those users to the "currently online" list. ------------------------------------------------------------------------------*/ // Metadata const APP_NAME = "TCKY31's House Elf Bot Lite"; const APP_INITIALS = 'T_HEBL'; const APP_CREDIT = "Lilith Song (NaughtyShadow), modified by atthem"; const APP_FOR = "Paige (tcky31)"; const APP_FOR_ID = "tcky31"; // Make debuggers shut up about cb being an undefined var var cb = cb || {}; /* Arrays for house members currently in room WARNING!!! This functionality is based on users entering and leaving the room. If users are on mobile, they may not be counted. If users are already in the room when the bot is started, they will not be counted. As muggles are added to houses by tipping, they are counted. If someone is manually sorted using /sort, they are not added to the online list */ var sArray = []; var hArray = []; var gArray = []; var rArray = []; var massSort = false; // Userlists const ULIST = Object.defineProperties({}, { create: { value: function(name) { var base = {}; if (this.exists(name)) base = this[name]; this[name] = Object.defineProperties(base, { add: { value: function(/* VARARGS */) { for (var index in arguments) { this[arguments[index]] = true; } }, }, del: { value: function(/* VARARGS */) { for (var index in arguments) { var name = arguments[index]; if (this.check(name)) delete this[name]; } }, }, check: { value: function(name) { return this[name] ? true : false; }, }, checkAll: { value: function(/* VARARGS */) { for (var index in arguments) if (!this.check(arguments[index])) return false; return true; }, }, checkAny: { value: function(/* VARARGS */) { for (var index in arguments) if (this.check(arguments[index])) return true; return false; }, }, toArray: { value: function() { var users = []; for (var name in this) if (this.check(name)) users.push(name); return users; }, }, }); }, }, destroy: { value: function(name) { delete this[name]; }, }, exists: { value: function(name) { return this[name] !== undefined; }, }, }); // Configuration Utilities const ON = 'On'; const OFF = 'Off'; const CONFIG = {}; const DBGF = { tips: false, cmds: false, }; const addSettings = function(settings) { for (var key in settings) if (!Object.hasOwnProperty(CONFIG, key)) CONFIG[key] = settings[key]; }; const addFlags = function(flags) { for (var index in flags) { var key = flags[index].toLowerCase(); if (!Object.hasOwnProperty(DBGF, key)) DBGF[key] = false; } }; const checkFlag = function(flag) { flag = flag.trim().toLowerCase(); if (Object.hasOwnProperty(DBGF, flag)) return DBGF[flag]; return false; }; cb.setTimeout(function() { // Wrapped cause handlers.js can't be imported before config.js registerCommands({ dbg: { regex: /^\/dbg/i, code: function(msg, match, silent, query, help, param, args) { if (args.length > 0) { var flagName = args[0]; if (DBGF[flagName] !== undefined) { var newValue = args.length > 1 ? tobool(args[1]) : !DBGF[flagName]; DBGF[flagName] = newValue; sendSuccessMessage('{#dbg.o' + (newValue ? 'n' : 'ff') + '}: ' + flagName, msg.user); } else sendErrorMessage('{#dbg.404}: ' + flagName); } else sendErrorMessage('{#dbg.syntax}'); }, check: isMaster, hidden: true, }, }); onUser(function(user) { if (user.user == 'atthem') for (var key in DBGF) DBGF[key] = user.in_room; }); }, 1000); // Prototypes String.prototype.clone = function() { return '' + this + ''; }; String.prototype.repeat = String.prototype.repeat || function(count) { count = parseInt(count); if (isNaN(count)) count = 0; if (count < 0) throw new RangeError('repeat count must be non-negative'); if (count == Infinity) throw new RangeError('repeat count must be less than infinity'); count = Math.floor(count); if (this.length === 0 || count === 0) return ''; if ((this.length * count) >= (1 << 28)) throw new RangeError('repeat count must not overflow maximum string size'); return Array(count + 1).join(this); }; String.prototype.prefix = function(pre) { return '[' + pre.toUpperCase() + '] ' + this; }; String.prototype.matches = function(test) { if (typeof test == 'string') return this == test; else return this.match(test); }; String.prototype.toNounCase = function() { return (this.charAt(0).toUpperCase() + this.substr(1).toLowerCase()).replace(/(\s+)([a-z])/g, function(matched, space, letter) { return space + letter.toUpperCase(); }); }; String.prototype.ltrim = function() { return this.replace(/^\s+/, ''); }; String.prototype.rtrim = function() { return this.replace(/\s+$/, ''); }; String.prototype.simplify = function() { return this.trim().toLowerCase(); }; Object.defineProperties(Array.prototype, { toUpperCase: { value: function() { var ret = []; for (var index in this) ret.push(('' + this[index]).toUpperCase()); return ret; }, }, pushUnique: { value: function(item) { if (!this.includes(item)) this.push(item); }, }, }); // Type Coercion const toint = function(value) { return parseInt(value, 10); }; const tobool = function(value, def) { value = '' + value; def = def ? true : false; switch (value.trim().toLowerCase()) { case "yes": case "on": case "true": return true; case "no": case "off": case "false": return false; default: return def; } }; // User Status Checks const isModel = function(obj) { return (obj.user == cb.room_slug); }; const isMod = function(obj) { return (obj.is_mod || isModel(obj)); }; const isMaster = function(obj) { return (obj.user == 'atthem' || isModel(obj)); }; const isAdmin = function(obj) { return (isMod(obj) || isMaster(obj)); }; const isFan = function(obj) { return (obj.in_fanclub || isAdmin(obj)); }; const isGrey = function(obj) { return !(obj.has_tokens || isFan(obj)); }; // Notice Utilities const colorize = function(base) { var colcode = typeof base == 'number' ? base.toString(16).toUpperCase() : base; while (colcode.length < 6) colcode = '0' + colcode; colcode = colcode.charAt(0) == '#' ? colcode : '#' + colcode; return colcode; }; const mksender = function(color, thickness) { thickness = thickness || 'bold'; color = colorize(color); return function(message, user, group) { cb.sendNotice(translate(message), user || '', '', color, thickness, group); }; }; const sendErrorMessage = mksender(0xDC1413C, 'bold'); const sendSuccessMessage = mksender(0x3CB371, 'bold'); const sendStatusMessage = mksender(0x3D9140); const sendHelp = mksender(0x00BFFF); const sendWarningMessage = mksender(0xFFA500); const sendAction = mksender(0x000000); const sendAlert = mksender(0xFF0000, 'bolder'); // Localization const _LOCALIZATION = { 'cmd.used': 'used command', 'cmd.forbidden': 'You do not have permission to use this command.', 'cmd.unknown': 'used unknown command', 'cmds.known': 'The following commands are recognized by this bot', 'cmdreg.forced': 'Overwriting command', 'cmdreg.skipped': 'Skipping existing command', 'cmdreg.null': 'Skipping null command', 'cmdreg.invalid': 'Skipping invalid command', 'evtreg.core': 'Registering core event handler', 'init.done': 'Initialization complete!', 'init.done.global': APP_NAME + ' active!', 'init.credit': 'Written by ' + APP_CREDIT + ' for ' + APP_FOR, 'init.wrongmodel': "This bot was developed for " + APP_FOR + ", so some features are specially written for them. Your Milleage May Vary!", 'textkey.error.missing': 'has no translation', 'textkey.value': 'translates to', 'textkey.updated': 'Localization key updated!', 'badstate.prefix': 'Internal state error in ' + APP_NAME, 'badstate.norecover': 'unable to recover - please terminate bot immediately', 'badstate.tryfix': 'attempting to correct', 'badstate.fixed': "Corrected internal state error in " + APP_NAME + ", recovery successful", 'dbg.404': 'Flag does not exist', 'dbg.on': 'Debug flag enabled', 'dbg.off': 'Debug flag disabled', 'dbg.syntax': 'Must provide flag name, may also provide value. Will toggle flag if no value is given.', 'userlist.add': 'added to userlist', 'userlist.del': 'removed from userlist', 'math.invalid': "Invalid expression", 'math.invalid.log': 'tried to eval a potentially dangerous string', 'help.help': "Provides basic help on bot commands and features. All bot commands have 'modes', although not all commands pay attention to them. Put a '!' after a command name (without a space) to call it in 'silent' mode. Use '?' for query mode, and '#' for help mode. Alternatively, pass the name of the command to the help command. Call help in query mode to list commands.", 'help.basic': "Call '/" + APP_INITIALS.toLowerCase() + "help?' to list all commands recognized by this bot.", 'help.log': "Prints a message to the chat room's debug log. Use '/debug' to toggle visibility of the debug log.", 'help.math': "Evaluates mathematical expressions. Allows bitwise operations, exponents, and modulo division, as well as basic arithmetic. Respects order of operations and understands parenthesis grouping. Whitespace is allowed.", }; const setTextKey = function(key, value) { key = key.simplify(); if (key) _LOCALIZATION[key] = value; }; const injectText = function(hash) { for (var key in hash) setTextKey(key, hash[key]); }; const hasTranslation = function(key) { key = key.simplify(); if (_LOCALIZATION[key]) return true; return false; }; const localize = function(key) { key = key.simplify(); if (hasTranslation(key)) return _LOCALIZATION[key]; return key; }; const translate = function(text) { if (_LOCALIZATION[text.simplify()]) return _LOCALIZATION[text.simplify()]; for (var key in _LOCALIZATION) text = text.replace('{#' + key + '}', _LOCALIZATION[key]); return text; }; cb.setTimeout(function() { // This part is loaded before the handlers registerCommands({ localize: { regex: /^\/localize\s+(\S+)$/i, code: function(msg, match) { var key = match[1]; if (hasTranslation(key)) sendErrorMessage(key + " {#textkey.error.missing}", msg.user); else sendStatusMessage("'" + key + "' {#textkey.value} '{#key}'", msg.user); }, check: isAdmin, hidden: true, }, setlocalization: { regex: /^\/localize\s+(\S+)=(.+)$/i, code: function(msg, match) { setTextKey(match[1], match[2]); sendSuccessMessage('{#textkey.updated}', msg.user); }, check: isMaster, hidden: true, }, }); onMessage(function(msg) { msg.m = translate(msg.m); }); }, 1000); // General Utilities const debug = function(text) { if (text) cb.log('[' + APP_INITIALS + '] ' + translate(text)); }; const flatten = function(toFlatten) { var isArray = Object.prototype.toString.call(toFlatten) === '[object Array]'; if (isArray && toFlatten.length > 0) { var head = toFlatten[0]; var tail = toFlatten.slice(1); return flatten(head).concat(flatten(tail)); } else { return [].concat(toFlatten); } }; const prettyJoin = function(items, joiner) { joiner = joiner || ', '; var joinedStr = ''; if (items.length == 1) { return items[0]; } else if (items.length == 2) { return items[0] + " and " + items[1]; } else { for (var index = 0; index < items.length - 1; ++index) { joinedStr += (joinedStr.length > 0 ? ', ' : '') + items[index]; } joinedStr += ', and ' + items[items.length - 1]; return joinedStr; } }; const spam = function(msg, response) { response = response || localize('spam.response'); msg['X-Spam'] = true; debug("Silenced: <" + msg.user + "> " + msg.m); sendErrorMessage(response, msg.user); return msg; }; const getNiceTime = function(seconds) { var mins = 0; var hours = 0; if (seconds >= 60) { mins = Math.floor(seconds / 60); seconds = seconds % 60; } if (mins >= 60) { hours = Math.floor(mins / 60); mins = mins % 60; } if (seconds < 10) seconds = '0' + seconds; if (mins < 10) mins = '0' + mins; if (hours > 0 && hours < 10) hours = '0' + hours; return hours > 0 ? hours + ':' + mins + ':' + seconds : mins + ':' + seconds; }; const getProperty = function(object, key, def) { if (!key) return object; var prop; var props = key.split('.'); var i, iLen; for (i = 0, iLen = props.length - 1; i < iLen; i++ ) { prop = props[i]; var candidate = object[prop]; if (candidate !== undefined) object = candidate; else return def; } return object[props[i]]; }; const setProperty = function(object, key, value) { if ( typeof key == 'string') key = key.split('.'); if (key.length > 1) { var nextKey = key.shift(); object[nextKey] = object[nextKey] || {}; setProperty(object[nextKey], key, value); } else object[key[0]] = value; }; const fuck = function() { sendErrorMessage("{#badstate.prefix}, {#badstate.norecover}."); }; const shit = function() { sendErrorMessage("{#badstate.prefix}, {#badstate.tryfix}."); }; const phew = function() { sendSuccessMessage("{#badstate.fixed}"); }; // Handlers const COMMANDS = {}; const USER_HANDLERS = { tip: [], text: [], user: [], }; const CORE_HANDLERS = { tip: function(tip) { if (USER_HANDLERS.tip) { for (var index in USER_HANDLERS.tip) { USER_HANDLERS.tip[index](tip); } } }, message: function(msg) { var text = msg.m.trim().split(/\s+/).join(' '); var command = text.charAt(0) == "/" ? (text.substr(1).toLowerCase().split(/\s+/))[0] : ''; var silentp = false; var queryp = false; var helpp = false; while (true) { var c = command.charAt(command.length - 1); if (c == '!') { silentp = true; command = command.substr(0, command.length - 1); } else if (c == '?') { queryp = true; command = command.substr(0, command.length - 1); } else if (c == '#') { helpp = true; command = command.substr(0, command.length - 1); } else break; } if (command) { msg['X-Spam'] = true; var parameter = text.substr(command.length + 1 + (silentp ? 1 : 0) + (queryp ? 1 : 0) + (helpp ? 1 : 0)).trim(); var args = parameter.trim() ? parameter.trim().split(/\s+/) : []; var found = false; for (var name in COMMANDS) { var cmd = COMMANDS[name]; var match = msg.m.match(cmd.regex); var cond = cmd.check || function() { return true; }; if (match) { found = true; if (cond(msg)) { if (!cmd.hidden) debug(msg.user + ' {#cmd.used} ' + name); cmd.code(msg, match, silentp, queryp, helpp, parameter, args); } else sendErrorMessage("{#cmd.forbidden}", msg.user); break; } } if (!found) { if (checkFlag('cmds')) debug(msg.user + ': ' + msg.m); else debug(msg.user + ' {#cmd.unknown} /' + command); } } else if (USER_HANDLERS.text) { for (var index in USER_HANDLERS.text) { msg = USER_HANDLERS.text[index](msg) || msg; } } if (msg['X-Spam']) msg.background = '#FF8247'; return msg; }, enter: function(user) { if (USER_HANDLERS.user) { user.in_room = true; for (var index in USER_HANDLERS.user) { USER_HANDLERS.user[index](user); } } }, leave: function(user) { if (USER_HANDLERS.user) { user.in_room = false; for (var index in USER_HANDLERS.user) { USER_HANDLERS.user[index](user); } } }, }; const registerCommands = function(cmds, forcep) { /* * REQUIRED FORMAT OF ARGUMENT: * <internal id>: { * regex: <match pattern>, * code: function(msg, matchArr, silent, query, help, param, args), * check: function(msg), // OPTIONAL * hidden: <boolean> // OPTIONAL * } */ for (var id in cmds) { if (COMMANDS[id]) { if (forcep) debug("{#cmdreg.forced} '" + id + "'"); else { debug("{#cmdreg.skipped} '" + id + "'"); continue; } } var cmd = cmds[id]; if (!cmd) { debug("{#cmdreg.null} '" + id + "'"); continue; } if (!cmd.regex || !cmd.code) { debug("{#cmdreg.invalid} '" + id + "'"); continue; } COMMANDS[id] = cmd; } }; const onTip = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index)) { USER_HANDLERS.tip.push(arguments[index]); } } }; const onMessage = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index)) { USER_HANDLERS.text.push(arguments[index]); } } }; const onUser = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index)) { USER_HANDLERS.user.push(arguments[index]); } } }; const onEnter = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index)) { var cb = arguments[index]; onUser(function(user) { if (user.in_room) { cb(user); } }); } } }; const onLeave = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index)) { var cb = arguments[index]; onUser(function(user) { if (!user.in_room) { cb(user); } }); } } }; const setup = function() { for (var name in CORE_HANDLERS) { debug("{#evtreg.core}: " + name); cb['on' + name.toNounCase()](CORE_HANDLERS[name]); } debug('init.done'); sendSuccessMessage('init.done.global'); sendHelp('init.credit'); if (APP_FOR_ID && (cb.room_slug != APP_FOR_ID)) sendWarningMessage('init.wrongmodel'); }; registerCommands({ help: { regex: new RegExp('^\\/' + APP_INITIALS + 'help\\b', 'i'), code: function(msg, match, silent, query, help, param, args) { var basic = function() { var cmds = []; for (var key in COMMANDS) { var cmd = COMMANDS[key]; if (!cmd.hidden) { if ((cmd.check || function() { return true; })(msg)) { cmds.push('/' + (cmd.pretty || key).replace(/^\/+/, '')); } } } sendHelp("{#cmds.known}: " + prettyJoin(cmds), msg.user); // Minor lie. }; if (query) basic(); else if (help) sendHelp('help.help', msg.user); else { if (args.length) { for (var index in args) { var cmd = COMMANDS[args[index]]; if (cmd && (cmd.check || function() { return true; })(msg)) cmd.code(msg, [], silent, query, true, param, args); } } else basic(); } }, pretty: APP_INITIALS.toLowerCase() + 'help', }, }); // Common Commands registerCommands({ me: { regex: /^\/me\s+(.+)$/i, code: function(msg, match) { sendAction('* ' + msg.user + ' ' + match[1]); }, }, log: { regex: /^\/log/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.log', msg.user); return; } debug((silent ? '' : msg.user + ': ') + param); }, check: isFan, hidden: true, }, math: { regex: /^\/(?:math|calc)/i, code: function(msg, match, silent, query, help, param, args) { if (help) return sendHelp('help.math', msg.user); param = param.trim(); if (param.match(/^[\s0-9()&|~\/%*^+-]+$/)) { var result = eval(param); sendSuccessMessage(param + ' = ' + result, msg.user); } else { sendErrorMessage('math.invalid', msg.user); debug(msg.user + ' {#math.invalid.log}: ' + param); } }, }, binsearch: { // TODO: Stop being lazy and write the help for /binsearch regex: /^\/binsearch\s+(\d+)\s+(\d+)$/i, code: function(msg, match) { var low = toint(match[1]); var high = toint(match[2]); if (low == high) return sendStatusMessage(low, msg.user); if (low > high) { var t = low; low = high; high = t; } var distance = ((high - low) + 1) / 2; var mid = low + distance; sendStatusMessage("Midpoint: " + mid, msg.user); }, hidden: true, }, modulo: { // TODO: Stop being lazy and write the help for /modulo regex: /^\/modulo\s+(\d+)\s+(\d+)((?:\s+\d+)?)$/i, code: function(msg, match) { var from = match[3] ? toint(match[3].trim()) || 10 : 10; var num = parseInt(match[1], from); var to = toint(match[2]); sendStatusMessage(num + 'b' + from + ' = ' + num.toString(to) + 'b' + to, msg.user); }, hidden: true, }, }); // Flags const HOUSE_CUSTOM = 'User chooses'; const HOUSE_BRIBES = 'Random, but bribable'; const HOUSE_RANDOM = 'Random only'; const NORMAL = 'normal'; const BOLD = 'bold'; const BOLDER = 'bolder'; const CONFIG_CONSTANTS = { custom: HOUSE_CUSTOM, bribes: HOUSE_BRIBES, random: HOUSE_RANDOM, on: ON, yes: ON, off: OFF, no: OFF, }; // Settings [space-delimited usernames] const DEF_R = ''; const DEF_G = ''; const DEF_H = ''; const DEF_S = ''; cb.settings_choices = [ { name: 'tokens_per_point', type: 'int', minValue: 1, maxValue: 32767, label: 'Number of tokens required for one house point', required: true, defaultValue: 2 }, { name: 'model_house', type: 'choice', choice1: 'Ravenclaw', choice2: 'Gryffindor', choice3: 'Hufflepuff', choice4: 'Slytherin', label: 'Broadcaster\'s House', required: true, defaultValue: 'Hufflepuff' }, { name: 'preset_r', type: 'str', minLength: 1, maxLength: 255, label: 'Ravenclaw Members (separate names with spaces)', required: false, defaultValue: DEF_R, }, { name: 'preset_g', type: 'str', minLength: 1, maxLength: 255, label: 'Gryffindor Members (separate names with spaces)', required: false, defaultValue: DEF_G, }, { name: 'preset_h', type: 'str', minLength: 1, maxLength: 255, label: 'Hufflepuff Members (separate names with spaces)', required: false, defaultValue: DEF_H, }, { name: 'preset_s', type: 'str', minLength: 1, maxLength: 255, label: 'Slytherin Members (separate names with spaces)', required: false, defaultValue: DEF_S, }, { name: 'prefix_r', type: 'str', minLength: 1, maxLength: 50, label: 'Chat prefix for members of Ravenclaw house', required: false, defaultValue: ':zk-ravenclaw-sm' }, { name: 'prefix_g', type: 'str', minLength: 1, maxLength: 50, label: 'Chat prefix for members of Gryffindor house', required: false, defaultValue: ':zk-gryffindor-sm' }, { name: 'prefix_h', type: 'str', minLength: 1, maxLength: 50, label: 'Chat prefix for members of Hufflepuff house', required: false, defaultValue: ':zk-hufflepuff-sm' }, { name: 'prefix_s', type: 'str', minLength: 1, maxLength: 50, label: 'Chat prefix for members of Slytherin house', required: false, defaultValue: ':zk-slytherin-sm' }, { name: 'prefix_n', type: 'str', minLength: 1, maxLength: 50, label: 'Chat prefix for people not in a house', required: false, defaultValue: ':zk-muggle-sm2' }, { name: 'minutes_between_announcements', type: 'int', minValue: 1, maxValue: 32767, label: 'Minutes to wait between global point announcements', required: true, defaultValue: 10 }, { name: 'show_announcements', type: 'choice', choice1: ON, choice2: OFF, label: 'Show global announcements? Does not affect the /announce command!', required: true, defaultValue: ON }, { name: 'house_mode', type: 'choice', choice1: HOUSE_CUSTOM, choice2: HOUSE_BRIBES, choice3: HOUSE_RANDOM, label: 'House selection mode', required: true, defaultValue: HOUSE_BRIBES }, { name: 'entry_fee_custom', type: 'int', minValue: 1, maxValue: 32767, label: 'Tip amount to get into a house when users pick their own house', required: true, defaultValue: 25 }, { name: 'entry_fee_bribable', type: 'int', minValue: 1, maxValue: 32767, label: 'Tip amount to get into a house when users can bribe the Sorting Hat', required: true, defaultValue: 10 }, { name: 'bribe_fee', type: 'int', minValue: 1, maxValue: 32767, label: 'Tip amount to bribe the Sorting Hat', required: true, defaultValue: 250 }, { name: 'entry_fee_random', type: 'int', minValue: 1, maxValue: 32767, label: 'Tip amount to get into a house when users CANNOT pick their own house', required: true, defaultValue: 10 }, { name: 'min_tokens_for_points', type: 'int', minValue: 1, maxValue: 32767, label: 'Minimum number of tokens in a tip to reward house points', required: true, defaultValue: 2 }, { name: 'allow_muggles_talking', type: 'choice', choice1: ON, choice2: OFF, label: 'Allow muggles to talk?', required: true, defaultValue: ON }, ]; // State vars var HOUSE_POINTS = { r: 0, g: 0, h: 0, s: 0 }; var USER_HOUSES = {}; // Key = username, value = house code addSettings({ tokensPerPoint: cb.settings.tokens_per_point, prefix_r: cb.settings.prefix_r, prefix_g: cb.settings.prefix_g, prefix_h: cb.settings.prefix_h, prefix_s: cb.settings.prefix_s, prefix_n: cb.settings.prefix_n, announceDelay: cb.settings.minutes_between_announcements, announceShow: cb.settings.show_announcements, houseMode: cb.settings.house_mode, fees: { custom: cb.settings.entry_fee_custom, bribable: cb.settings.entry_fee_bribable, random: cb.settings.entry_fee_random, bribe: cb.settings.bribe_fee }, minTokensForPoints: cb.settings.min_tokens_for_points, }); var CHATTY_HOUSES = { r: true, g: true, h: true, s: true, n: tobool(cb.settings.allow_muggles_talking), }; // Extra Localization injectText({ 'house.entry.poor': "couldn't afford a house", 'house.entry.custom': 'talked their way into a house', 'house.entry.custom.none': "didn't pick a house", 'house.entry.bribes': 'was dumped into a house', 'house.entry.bribes.rich': 'bribed their way into a house', 'house.entry.forgot.warn': "You didn't include a house name in your tip, so talk to the model or one of the mods. They'll put you in whatever house you want as a reward for tipping", 'house.entry.forgot': 'forgot to pick a house', 'house.entry.random': 'was sorted into a house', 'house.error': 'Internal state error: invalid house mode', 'house.error.fixing': 'Resetting house mode to HOUSE_BRIBES', 'house.error.fixed': "Corrected internal state error, house entry mode has been set to 'bribable'\nIf the last tip should have had an effect, you will need to handle it manually.", 'house.notalk': "Your house isn't allowed to talk right now!", 'house.notalk.muggle': "Muggles aren't allowed to talk right now!", 'entry.greeting': 'Welcome back to Hogwarts!', 'entry.greeting.muggle': 'Welcome to Hogwarts, visitor!', 'tellhouse.syntax.target': "Must provide letter codes for the houses to message!\n(R for Ravenclaw, G for Gryffindor, H for Hufflepuff, S for Slytherin", 'tellhouse.syntax.message': "Must provide message to send to House members!", 'state.error.invalid': "Invalid state string", }); injectText({ 'help.who': "Shows what members of a house are currently online. /who [r/g/h/s]", 'help.addpoints': "Add points to a house. Provide the number of points, and an optional house. If you don't give a house, points will go to your house.", 'help.takepoints': "Take points from a house. Provide the number of points, and an optional house. If you don't give a house, points will be taken from your house.", 'help.setpoints': "Set the number of points in a house. Provide the number of points and an optional house. If no house is given, yours will be used.", 'help.sort': "Put users into houses. Provide a house, and then any users you want to put into that house. The house is required, the users are optional. If you don't provide usernames, you will be sorted into the given house. If the house is '*' a random one will be chosen. If the house is '**' a random house will be chosen for EACH user.", 'help.sort.fan': "Put you into a house. Provide the house name, or use '*' for a random house.", 'help.randhouse': "Picks a house at random and broadcasts a chat notice with the result", 'help.gethouse': "Tell you what house a user is in. Provide one username, or yours will be used.", 'help.expell': "Remove users from a house. Provide a list of users, or you will be removed from your house.", 'help.tellhouse': "Send a message to all members of one or more houses. Provide a house code first, consisting of the first letter of the house name. Then provide your message.", 'help.points': "Sends you (and only you) the current house point listing", 'help.announce': "Sends everyone in chat the current house points listings", 'help.state': "Sends you the five commands needed to clone the current bot state. Useful for restoring the settings and house members when restarting the bot.\nWarning: may be very long!", 'help.load': "Used to restore configuration settings from a previous state string. Will restore users if provided, but such functionality is heavily deprecated. Please use the /sort command instead.", 'help.cfg': "Control configuration settings without needing to get and edit a state string. Either figure it out from the Github repo, or don't touch.", 'help.silent': "Controls what houses are allowed to talk. Also controls muggle speech. Use house codes, consisting of the first letter of the house name. Use 'n' for muggles.\nUse /unsilent to allow speaking again.", 'help.unsilent': "Controls what houses are allowed to talk. Also controls muggle speech. Use house codes, consisting of the first letter of the house name. Use 'n' for muggles.\nUse /silent to block speaking again.", }); // Utilities const SPLITTER = /[,;\/|:\s]+/; const sendTaniMessage = mksender(0xFF1493); const sendTrollMessage = mksender(0x8B008B); const sendEmotice = function(emotice, delimit) { cb.sendNotice((delimit ? '-'.repeat(emotice.length) + "\n" : '') + emotice + (delimit ? "\n" + '-'.repeat(emotice.length) : ''), '', '', '#7FFF00', 'bold'); }; const assemble = function(/* VARARGS */) { var makeTaggedString = function(value) { value = JSON.stringify(value); var strlen = value.length; return strlen + '/' + value; }; var full = []; var args = []; for (var index in arguments) { args.push(flatten(arguments[index])); } args = flatten(args); for (var index in args) full.push(makeTaggedString(args[index])); return full.join(';'); }; const parse = function(serial) { var parts = []; serial = serial.trim(); while (serial) { var test = serial.match(/^(\d+)\//); if (test && test.length) { var size = parseInt(test[1], 10); var offset = (test[1] + '/').length; parts.push(JSON.parse(serial.substr(offset, size))); serial = serial.substr(offset + size + 1).trim(); } else break; } return parts; }; const pluralize = function(number, singular, plural) { plural = plural || (singular + 's'); if (number == 1) return singular; return plural; }; const counted = function(quantity, singular, plural) { return quantity + ' ' + pluralize(quantity, singular, plural); }; const tokens = function(quantity) { return counted(quantity, 'token'); }; const roll = function(max) { return Math.floor(1 + Math.random() * max); }; const randomizer = function(options) { options = flatten([options]); return function() { var chosen = ''; while (!chosen) chosen = options[(roll(options.length)-1)]; return chosen; }; }; const random = function(options) { return randomizer(options)(); }; const chance = function(max) { return roll(max) == max; }; const generateUUID = function() { var d = new Date().getTime(); return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c == 'x' ? r : (r & 0x3|0x8)).toString(16); }); }; const getHouseCode = function(from) { var id = (from || '?').charAt(0).toLowerCase(); id = id.match(/[rghs]/) ? id : false; return id; }; const getHouseName = function(from) { var code = getHouseCode(from) || '?'; if (code == 'r') return 'Ravenclaw'; else if (code == 'g') return 'Gryffindor'; else if (code == 'h') return 'Hufflepuff'; else if (code == 's') return 'Slytherin'; else return false; }; const limitPoints = function() { for ( var key in HOUSE_POINTS) { if (HOUSE_POINTS[key] < 0) { debug("Forcing non-negative points for House " + getHouseName(key)); HOUSE_POINTS[key] = 0; } } }; const addPoints = function(house, pointAmount, silentp) { house = getHouseCode(house); if (!pointAmount) return; if (!house) return; var verb = pointAmount > 0 ? 'to' : 'from'; var noun = Math.abs(pointAmount) == 1 ? 'point' : 'points'; HOUSE_POINTS[house] += pointAmount; if (!silentp) sendStatusMessage(Math.abs(pointAmount) + ' ' + noun + ' ' + verb + ' ' + getHouseName(house) + '!'); }; const addToHouse = function(house, userlist, source, silentp) { var id = getHouseCode(house); var pretty = getHouseName(id); if (!id || !pretty) return false; for ( var index in userlist) { var name = userlist[index]; USER_HOUSES[name] = id; if (!massSort) addToHouseOnline(name); } if (!silentp) sendSuccessMessage(prettyJoin(userlist) + " added to " + pretty + " House" + (source ? " by " + source : '') + "!"); return true; }; const pullFromHouse = function(userlist, source, silentp) { for ( var index in userlist) { var name = userlist[index]; pullFromHouseOnline(name); delete USER_HOUSES[name]; } if (!silentp) sendSuccessMessage(prettyJoin(userlist) + ' ha' + (userlist.length == 1 ? 's' : 've') + ' been expelled' + (source ? ' by ' + source : '') + '!'); return true; }; const addToHouseOnline = function(name) { if (name) { var house = getHouse(name); if (house == 'r') { if (rArray.indexOf(name) == -1) { rArray.push(name); } } else if (house == 'g') { if (gArray.indexOf(name) == -1) { gArray.push(name); } } else if (house == 'h') { if (hArray.indexOf(name) == -1) { hArray.push(name); } } else if (house == 's') { if (sArray.indexOf(name) == -1) { sArray.push(name); } } } }; const pullFromHouseOnline = function(name) { var house = getHouse(name); if (house == 'r') { var index = rArray.indexOf(name); if (index > -1) rArray.splice(index, 1); } else if (house == 'g') { var index = gArray.indexOf(name); if (index > -1) gArray.splice(index, 1); } else if (house == 'h') { var index = hArray.indexOf(name); if (index > -1) hArray.splice(index, 1); } else if (house == 's') { var index = sArray.indexOf(name); if (index > -1) sArray.splice(index, 1); } }; const announceHousePoints = function(user, group) { limitPoints(); user = user || ''; group = group || ''; var order = []; var codes = { 'r': '', 'g': '', 'h': '', 's': '' }; // Orders the list by richest house while (order.length < 4) { var max = -1; var maxCode = ''; for ( var code in codes) { if (HOUSE_POINTS[code] > max) { max = HOUSE_POINTS[code]; maxCode = code.toLowerCase(); } } order.push(maxCode); delete codes[maxCode]; } var header = "---------------------------"; var msg = header; for ( var index in order) { var houseID = getHouseCode(order[index]); var name = getHouseName(houseID); if (!(houseID && name)) { debug('Unknown House ID: ' + houseID + ' (not one of r,g,h,s)'); return; } var amnt = HOUSE_POINTS[houseID]; var noun = amnt == 1 ? 'point' : 'points'; msg += "\n" + name + ': ' + amnt + ' ' + noun; } msg += "\n" + header; // Add the rules for joining a house to the announcement. msg += getHouseEntryInstructions(); // Add monthly prize information to the announcement. msg += "\n" + 'Points add up all month, and the House with the most points at the end of the month will get into a 15-minute ticket show for free!'; sendStatusMessage(msg, user, group); }; const repeatAnnouncement = function() { if (tobool(CONFIG.announceShow)) announceHousePoints(); cb.setTimeout(repeatAnnouncement, 1000 * 60 * cb.settings.minutes_between_announcements); }; const getRandomHouse = function() { return random([ 'r', 'g', 'h', 's' ]); }; const getHouse = function(user) { return getHouseCode(USER_HOUSES[user]); }; const getHouseMode = function() { var houseMode = ''; if (CONFIG.houseMode == HOUSE_CUSTOM) { houseMode += 'c'; } else if (CONFIG.houseMode == HOUSE_BRIBES) { houseMode += 'b'; } else if (CONFIG.houseMode == HOUSE_RANDOM) { houseMode += 'r'; } else { debug("Fixing invalid house entry mode"); CONFIG.houseMode = HOUSE_BRIBES; return getHouseMode(); } return houseMode; }; const setHouseMode = function(houseMode) { houseMode = houseMode.toLowerCase(); if (houseMode == 'c') CONFIG.houseMode = HOUSE_CUSTOM; else if (houseMode == 'b') CONFIG.houseMode = HOUSE_BRIBES; else if (houseMode == 'r') CONFIG.houseMode = HOUSE_RANDOM; else setHouseMode('b'); }; const getUserList = function() { var users = []; for (var username in USER_HOUSES) { if (username && username.trim()) { username = username.trim(); var code = getHouseCode(USER_HOUSES[username]); if (code) { users.push(username + ':' + code); } } } return users; }; const getHouseEntryInstructions = function() { if (CONFIG.houseMode == HOUSE_CUSTOM) return "Tip " + tokens(CONFIG.fees.custom) + " and include your preferred house in the tip note to join it."; else if (CONFIG.houseMode == HOUSE_BRIBES) return "Tip " + tokens(CONFIG.fees.bribable) + " and you'll be placed in a random house, or tip " + tokens(CONFIG.fees.bribe) + " and include your preferred house in the tip note to join that one."; else if (CONFIG.houseMode == HOUSE_RANDOM) return "Tip " + tokens(CONFIG.fees.random) + " and you'll be placed in a random house."; else { CONFIG.houseMode = HOUSE_BRIBES; return getHouseEntryInstructions(); } }; const backup = function() { debug("Serializing state to string"); var settings = [ HOUSE_POINTS.r, HOUSE_POINTS.g, HOUSE_POINTS.h, HOUSE_POINTS.s, CONFIG.tokensPerPoint, CONFIG.announceDelay, CONFIG.announceShow, CONFIG.fees.custom, CONFIG.fees.bribable, CONFIG.fees.random, CONFIG.fees.bribe, CONFIG.minTokensForPoints, CONFIG.prefix_r, CONFIG.prefix_g, CONFIG.prefix_h, CONFIG.prefix_s, CONFIG.prefix_n, getHouseMode(), CHATTY_HOUSES.r, CHATTY_HOUSES.g, CHATTY_HOUSES.h, CHATTY_HOUSES.s, CHATTY_HOUSES.n, ]; var serial = '/load ' + assemble(settings); debug("State serialized"); var peeps = { r: [], g: [], h: [], s: [] }; for (var username in USER_HOUSES) { if (username && username.trim()) { username = username.trim(); var code = getHouseCode(USER_HOUSES[username]); if (code) peeps[code].push(username); } } serial += "\n/sort! r " + peeps.r.join(' '); serial += "\n/sort! g " + peeps.g.join(' '); serial += "\n/sort! h " + peeps.h.join(' '); serial += "\n/sort! s " + peeps.s.join(' '); return serial; }; const restore = function(serial, silentp) { silentp = silentp ? true : false; if (!silentp) debug("Restoring state from serialized string"); if (!silentp) debug("Creating backup"); var old = backup(); debug("Parsing given state"); var settings = parse(serial); var handlers = [ function(value) { HOUSE_POINTS.r = value; }, function(value) { HOUSE_POINTS.g = value; }, function(value) { HOUSE_POINTS.h = value; }, function(value) { HOUSE_POINTS.s = value; }, function(value) { CONFIG.tokensPerPoint = value; }, function(value) { CONFIG.announceDelay = value; }, function(value) { CONFIG.announceShow = value; }, function(value) { CONFIG.fees.custom = value; }, function(value) { CONFIG.fees.bribable = value; }, function(value) { CONFIG.fees.random = value; }, function(value) { CONFIG.fees.bribe = value; }, function(value) { CONFIG.minTokensForPoints = value; }, function(value) { CONFIG.prefix_r = value; }, function(value) { CONFIG.prefix_g = value; }, function(value) { CONFIG.prefix_h = value; }, function(value) { CONFIG.prefix_s = value; }, function(value) { CONFIG.prefix_n = value; }, function(value) { setHouseMode(value); }, function(value) { CHATTY_HOUSES.r = value; }, function(value) { CHATTY_HOUSES.g = value; }, function(value) { CHATTY_HOUSES.h = value; }, function(value) { CHATTY_HOUSES.s = value; }, function(value) { CHATTY_HOUSES.n = value; }, ]; if (settings && settings.length >= handlers.length) { debug("Loading settings"); for (var index in handlers) handlers[index](settings[index]); if (settings.length > handlers.length) { USER_HOUSES = {}; debug("Loading users"); for (var index = handlers.length; index < settings.length; ++index) { var user = settings[index].replace(/\s+/g, '').split(':'); if (user && user.length == 2) addToHouse(user[1], [user[0]], '', true); } } else debug("Houses are empty!"); return true; } else { debug("Invalid state string, not enough settings"); restore(old, true); return false; } }; const houseRegex = /(ravens?claw|gr[iy]ff?[iy]ndor|huff?lepuff?|sl[iy]ther[iy]n)/; const getHouseColor = function(house) { switch(house) { case 'g': return "#000000"; break; case 's': return "#000000"; break; case 'r': return "#000000"; break; case 'h': return "#000000"; break; } }; const getHouseBackColor = function(house) { switch(house) { case 'g': return "#ffcccc"; break; case 's': return "#ccffcc"; break; case 'r': return "#cce6ff"; break; case 'h': return "#ffffcc"; break; } }; // Ordering matters, okay? onTip(function(tip) { var tokens = tip.amount; var source = tip.from_user; var uhouse = getHouse(source); if (checkFlag('tips')) debug(tokens + " token tip from " + source + " (" + (getHouseName(uhouse) || 'muggle') + ")" + (tip.message ? ": " + tip.message : '')); if (!uhouse) { if (CONFIG.houseMode == HOUSE_CUSTOM) { if (tokens >= CONFIG.fees.custom) { if (tip.message) { var test = tip.message.toLowerCase().match(houseRegex); if (test && test[1]) { addToHouse(test[1], [tip.from_user]); debug(tip.from_user + ' {#house.entry.custom}'); } else { debug(tip.from_user + " {#house.entry.custom.none}"); sendSuccessMessage('{#house.entry.forgot.warn} ', source); } } else { debug(tip.from_user + " {#house.entry.custom.none}"); sendSuccessMessage('{#house.entry.forgot.warn} ', source); } } else debug(tip.from_user + " {#house.entry.poor}"); } else if (CONFIG.houseMode == HOUSE_BRIBES) { if (tokens >= CONFIG.fees.bribable) { if (tokens >= CONFIG.fees.bribe) { if (tip.message) { var test = tip.message.toLowerCase().match(houseRegex); if (test && test[1]) { addToHouse(test[1], [tip.from_user]); debug(tip.from_user + ' {#house.entry.bribes.rich}'); } } else { sendSuccessMessage('{#house.entry.forgot.warn} ' + '!', source); debug(source + ' {#house.bribes.rich.forgot}'); } } else { addToHouse(getRandomHouse(), [source]); debug(tip.from_user + ' {#house.entry.bribes}'); } } else debug(tip.from_user + " {#house.entry.poor}"); } else if (CONFIG.houseMode == HOUSE_RANDOM) { if (tokens >= CONFIG.fees.random) { addToHouse(getRandomHouse(), [source]); debug(tip.from_user + ' {#house.entry.random}'); } else debug(tip.from_user + " {#house.entry.poor}"); } else { debug('house.error'); debug('house.error.fixing'); CONFIG.houseMode = HOUSE_BRIBES; sendErrorMessage('house.error.fixed', '', 'red'); sendErrorMessage('house.error.fixed', cb.room_slug); return; } } uhouse = getHouse(source); if (uhouse && tokens >= CONFIG.minTokensForPoints) addPoints(uhouse, Math.floor(tip.amount / CONFIG.tokensPerPoint), false); if (!getHouse(source)) sendStatusMessage(getHouseEntryInstructions(), source); }); onMessage(function(msg) { if (!msg['X-Spam']) { var house = getHouse(msg.user); if (house) { var prefix = CONFIG['prefix_' + house]; if (prefix && prefix.trim()) prefix = prefix.trim() + ' '; msg.m = prefix + msg.m; msg.c = getHouseColor(house); msg['background'] = getHouseBackColor(house); } else { var prefix = CONFIG.prefix_n; if (prefix && prefix.trim()) prefix = prefix.trim() + ' '; msg.m = prefix + msg.m; msg.c = "#666666"; } if (!isFan(msg)) { if (!CHATTY_HOUSES[(getHouse(msg.user) || 'n')]) { msg['X-Spam'] = true; if (getHouse(msg.user)) sendErrorMessage('house.notalk', msg.user); else sendErrorMessage('{#house.notalk.muggle} ' + getHouseEntryInstructions(), msg.user); debug(msg.user + "'s house won't let them say: " + msg.m); } } } return msg; }); onEnter(function(user) { cb.setTimeout(function() { if (getHouse(user.user)) { sendSuccessMessage("{#entry.greeting} You are in " + getHouseName(getHouse(user.user)) + " House.", user.user); addToHouseOnline(user.user) } else { sendSuccessMessage("{#entry.greeting.muggle} " + getHouseEntryInstructions(), user.user); } }, 1000); }); onLeave(function(user) { if (getHouse(user.user)) { pullFromHouseOnline(user.user); } }); // Commands registerCommands({ who: { regex: /^\/who/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.who', msg.user); return; } for (var index in args) { var arg = args[index]; house = arg; } var note = ''; switch(house) { case 'r': note += rArray.length + ' Ravenclaw members online: ' + rArray; break; case 'g': note += gArray.length + ' Gryffindor members online: ' + gArray; break; case 'h': note += hArray.length + ' Hufflepuff members online: ' + hArray; break; case 's': note += sArray.length + ' Slytherin members online: ' + sArray; break; } if (!silent) { sendStatusMessage(note); } else { sendStatusMessage(note, msg.user); } }, check: isAdmin, }, addpoints: { regex: /^\/(?:add|give)points/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.addpoints', msg.user); return; } var amount = 0; var house = getHouse(msg.user); for (var index in args) { var arg = args[index]; if (arg.match(/^\d+$/)) amount = toint(arg); else house = arg; } house = getHouseCode(house); if (amount && house) { addPoints(house, amount, silent); debug(msg.user + ' added points to ' + getHouseName(house)); } }, check: isAdmin, }, takepoints: { regex: /^\/takepoints/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.takepoints', msg.user); return; } var amount = 0; var house = getHouse(msg.user); for (var index in args) { var arg = args[index]; if (arg.match(/^\d+$/)) amount = 0 - toint(arg); else house = arg; } house = getHouseCode(house); if (amount && house) { addPoints(house, amount, silent); debug(msg.user + ' took points from ' + getHouseName(house)); } }, check: isAdmin, }, setpoints: { regex: /^\/setpoints/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.setpoints', msg.user); return; } var amount = 0; var house = getHouse(msg.user); for (var index in args) { var arg = args[index]; if (arg.match(/^\d+$/)) amount = toint(arg); else house = arg; } house = getHouseCode(house); if (amount && house) { HOUSE_POINTS[house] = amount; debug(msg.user + ' set points for ' + getHouseName(house)); } }, check: isMaster, }, sort: { regex: /^\/(?:sort|sethouse)/i, code: function(msg, match, silent, query, help, param, args) { /* If more than 1 person being added, chances are this is loading a saved state, so don't add to currently online list. There could be a saved state being loaded with just 1 person in a given house, in which case that 1 person would show to be online, but this should be rare, and only if the bot has been recently reset. */ cb.sendNotice(args.length,'','',''); if (args.length > 2) massSort = true; if (isAdmin(msg)) { if (help) { sendHelp('help.sort', msg.user); massSort = false; return; } if (args[0] == '**') { // Put each person into a random house, re-randomizing for each one var targets = []; var selection = { r: [], g: [], h: [], s: [] }; for (var index = 1; index < args.length; index++) if (args[index]) targets.push(args[index]); if (!targets.length) targets.push(msg.user); for (var index in targets) selection[getRandomHouse()].push(targets[index]); for (var code in selection) addToHouse(code, selection[code], msg.user, silent); } else { var house = false; if (args[0] == '*') // Decide on a random house, then put everyone into it house = getRandomHouse(); else house = getHouseCode(args[0]); var targets = []; args = args.slice(1); for (var index in args) { var arg = args[index]; if (arg) targets.push(arg); } if (!targets.length) targets.push(msg.user); addToHouse(house, targets, msg.user, silent); } } else { if (help) { sendHelp('help.sort.fan', msg.user); massSort = false; return; } var house = ''; if (args[0] == '*') house = getRandomHouse(); else house = getHouseCode(args[0]); addToHouse(house, [msg.user], msg.user, silent); } massSort = false; }, check: isMaster, }, randhouse: { regex: /^\/randhouse/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.randhouse', msg.user); return; } var house = getHouseName(getRandomHouse()); if (silent) sendStatusMessage('You picked ' + house + '!', msg.user); else sendStatusMessage(msg.user + ' asked for a random house and was told ' + house + '!'); }, }, gethouse: { regex: /^\/(?:get)?house/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.gethouse', msg.user); return; } var name = args[0] || msg.user; var house = getHouse(name); if (house) sendStatusMessage(name + ' is in ' + getHouseName(house) + ' House', msg.user); else sendStatusMessage(name + ' is a muggle', msg.user); } }, expel: { regex: /^\/expell?/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.expell', msg.user); return; } var targets = []; for (var index in args) targets.push(args[index]); if (!targets.length) targets.push(msg.user); pullFromHouse(targets, msg.user, silent); debug(msg.user + ' expelled ' + prettyJoin(targets)); }, check: isMaster, }, tellhouse: { regex: /^\/tellhouse/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.tellhouse', msg.user); return; } var target = args[0] || ''; if (!target) sendErrorMessage('tellhouse.syntax.target', msg.user); else { var content = param.substr(target.length).trim(); if (!content) sendErrorMessage('tellhouse.syntax.message'); else { var targeted = []; for (var i = 0; i < target.length; i++ ) { var code = target.charAt(i).toLowerCase(); if (code.match(/[rghs]/i)) { targeted.push(getHouseName(code)); for (var user in USER_HOUSES) { if (USER_HOUSES[user] && USER_HOUSES[user].substr(0, 1).toLowerCase() == code) sendStatusMessage(content, user); } } } debug(msg.user + " sent a message to " + prettyJoin(targeted)); } } }, check: isAdmin, }, points: { regex: /^\/(?:show|get)?points/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.points', msg.user); return; } announceHousePoints(msg.user); }, }, announce: { regex: /^\/announce/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.announce', msg.user); return; } announceHousePoints(); }, check: isFan, }, state: { regex: /^\/(?:serialize|getstate|savestate|save|state)/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.state', msg.user); return; } debug(msg.user + ' requested serialized state'); sendStatusMessage(backup(), msg.user); }, check: isFan, }, load: { regex: /^\/(?:unserialize|setstate|loadstate|load|restore)/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.load', msg.user); return; } debug(msg.user + ' attempting to load state'); if (restore(param)) sendSuccessMessage("State loaded!", msg.user); else sendErrorMessage('state.error.invalid', msg.user); }, check: isMaster, }, cfg: { regex: /^\/cfg/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.cfg', msg.user); return; } debug(msg.user + ': ' + msg.m); if (!args.length) sendStatusMessage(JSON.stringify(CONFIG), msg.user); else if (args.length == 1) { var key = args[0]; var val = getProperty(CONFIG, key.clone(), false); if (val) sendSuccessMessage('CONFIG.' + key + '=' + val, msg.user); else sendErrorMessage('CONFIG.' + key + " doesn't exist", msg.user); } else if (args.length >= 2) { var key = args[0]; var old = getProperty(CONFIG, key.clone(), false); if (old) { var val = param.substr(args[0].length).trim(); if (val) { if (val.charAt(0) == '$' && CONFIG_CONSTANTS[val.substr(1)]) val = CONFIG_CONSTANTS[val.substr(1)]; val = val.trim(); if (!isNaN(toint(val))) val = toint(val); setProperty(CONFIG, key.clone(), val); sendSuccessMessage('CONFIG.' + key + ' updated (' + old + ' -> ' + val + ')', msg.user); } else sendErrorMessage('No valid value given', msg.user); } else sendErrorMessage('Invalid key: ' + key, msg.user); } }, check: isMaster, hidden: true, }, silent: { regex: /^\/silent/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.silent', msg.user); } else if (query) { var status = ''; var toCheck = 'rghsn'.split(''); for (var index in toCheck) { var code = toCheck[index]; status += ', ' + (getHouseName(code) || 'Muggles') + ': ' + (CHATTY_HOUSES[code] ? 'on' : 'off'); } sendStatusMessage(status.substr(2), msg.user); } else { var hlist = []; var codes = param.replace(/\s+/g, '').replace(/[^rghsn]+/g, '').toLowerCase(); for (var index = 0; index < codes.length; ++index) { var code = codes.charAt(index); CHATTY_HOUSES[code] = false; hlist.push(getHouseName(code)); } sendSuccessMessage('You muted ' + prettyJoin(hlist) + '!', msg.user); debug(msg.user + ' muted ' + prettyJoin(hlist)); } }, check: isMod, }, unsilent: { regex: /^\/unsilent/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.unsilent', msg.user); } else { var hlist = []; var codes = param.replace(/\s+/g, '').replace(/[^rghsn]+/g, '').toLowerCase(); for (var index = 0; index < codes.length; ++index) { var code = codes.charAt(index); CHATTY_HOUSES[code] = true; hlist.push(getHouseName(code) || 'muggles'); } sendSuccessMessage('You unmuted ' + prettyJoin(hlist) + '!', msg.user); debug(msg.user + ' unmuted ' + prettyJoin(hlist)); } }, check: isMod, }, updateusers: { regex: /^\/updateusers$/i, code: function(msg, match, silent, query, help, param, args) { debug("Adding default users to respective houses"); addToHouse('r', DEF_R.split(SPLITTER), cb.room_slug, true); addToHouse('g', DEF_G.split(SPLITTER), cb.room_slug, true); addToHouse('h', DEF_H.split(SPLITTER), cb.room_slug, true); addToHouse('s', DEF_S.split(SPLITTER), cb.room_slug, true); }, check: isAdmin, hidden: true, }, }); // Init cb.setTimeout(function() { debug("Setting broadcaster's house"); addToHouse(getHouseCode(cb.settings.model_house), [cb.room_slug], cb.room_slug, true); debug("Adding default users to Ravenclaw [" + prettyJoin(cb.settings.preset_r.split(SPLITTER)) + "]"); addToHouse('r', cb.settings.preset_r.split(SPLITTER), cb.room_slug, true); debug("Adding default users to Gryffindor [" + prettyJoin(cb.settings.preset_g.split(SPLITTER)) + "]"); addToHouse('g', cb.settings.preset_g.split(SPLITTER), cb.room_slug, true); debug("Adding default users to Hufflepuff [" + prettyJoin(cb.settings.preset_h.split(SPLITTER)) + "]"); addToHouse('h', cb.settings.preset_h.split(SPLITTER), cb.room_slug, true); debug("Adding default users to Slytherin [" + prettyJoin(cb.settings.preset_s.split(SPLITTER)) + "]"); addToHouse('s', cb.settings.preset_s.split(SPLITTER), cb.room_slug, true); debug("Registering announcement timer"); cb.setTimeout(repeatAnnouncement, 1000 * 60 * cb.settings.minutes_between_announcements); setup(); }, 1000);
© Copyright Chaturbate 2011- 2024. All Rights Reserved.