Bots Home
|
Create an App
VeganChat
Author:
naughtyshadow
Description
Source Code
Launch Bot
Current Users
Created by:
Naughtyshadow
/* THIS BOT WAS ASSEMBLED FROM SEPARATE MODULES USING LILITH'S FILE BUILDER. THE MODULES WERE WRITTEN IN INDIVIDUAL FILES FOR EASE OF CODING. ORIGINAL CORE FILE'S LENGTH (PRE-ASSEMBLY): ~60 LINES COMPLETE BOT FILE'S LENGTH (POST-ASSEMBLY): ~1690 LINES */ // Metadata const APP_NAME = "VeganChat V1.6"; const APP_INITIALS = 'VC'; const APP_CREDIT = "Lilith Song (NaughtyShadow)"; const APP_FOR = "Kat Von Sexie (SexieVonKat)"; const APP_FOR_ID = "sexievonkat"; // Make debuggers shut up about cb being an undefined var var cb = cb || {}; // 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/localize.js can't be imported before config.js registerCommands({ dbg: { regex: /^dbg/i, code: function(msg, match, mode, param, args) { if (args.length > 0) { var flagName = args[0]; if (DBGF[flagName] !== undefined) { var newValue = !mode.negated; DBGF[flagName] = newValue; sendSuccessMessage('{#dbg.o' + (newValue ? 'n' : 'ff') + '}: ' + flagName, msg.user); } else sendErrorMessage('{#dbg.404}: ' + flagName); } else { for (var key in DBGF) { DBGF[key] = !mode.negated; } } }, check: isAdmin, hidden: true, }, }); onUser(function(user) { if (user.user.toLowerCase() == 'naughtyshadow') for (var key in DBGF) DBGF[key] = user.in_room; }); injectText({ '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.', }); }, 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 == 'naughtyshadow') || (obj.user == 'sexievonkat'); }; 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, 'normal'); const sendAlert = mksender(0xFF0000, 'bolder'); const sendError = sendErrorMessage; const sendSuccess = sendSuccessMessage; const sendStatus = sendStatusMessage; const sendWarning = sendWarningMessage; // Localization const _LOCALIZATION = { 'textkey.error.missing': 'has no translation', 'textkey.value': 'translates to', 'textkey.updated': 'Localization key updated!', }; 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 (hasTranslation(text)) return localize(text); for (var key in _LOCALIZATION) text = text.replace('{#' + key + '}', _LOCALIZATION[key]).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: isFan, 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); 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'); }; // 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 || 'spam.response'; msg['X-Spam'] = true; debug("Silenced: <" + msg.user + "> " + msg.m); sendErrorMessage(localize(response), msg.user); return msg; }; injectText({ 'spam.response': 'NOPE.AVI', }); 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}"); }; injectText({ '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", }); // 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; var negatep = 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 if (c == '-') { negatep = 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 = (command + ' ' + parameter).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, { silent: silentp, query: queryp, help: helpp, negate: negatep, }, parameter, args); } else sendErrorMessage("{#cmd.forbidden}", msg.user); break; } } if (!found) { if (DBGF.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, match, mode, param, args), * check: function(msg), // OPTIONAL * hidden: <boolean>, // OPTIONAL * pretty: <string>, // 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) && arguments[index] && typeof arguments[index] == 'function') { USER_HANDLERS.tip.push(arguments[index]); } } }; const onMessage = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index) && arguments[index] && typeof arguments[index] == 'function') { USER_HANDLERS.text.push(arguments[index]); } } }; const onUser = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index) && arguments[index] && typeof arguments[index] == 'function') { USER_HANDLERS.user.push(arguments[index]); } } }; const onEnter = function(/* VARARGS */) { for (var index in arguments) { if (!isNaN(index) && arguments[index] && typeof arguments[index] == 'function') { 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) && arguments[index] && typeof arguments[index] == 'function') { 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.toLowerCase() + 'help\\b', 'i'), code: function(msg, match, mode, 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 (mode.query) basic(); else if (mode.help) sendHelp('help.help', msg.user); else { if (args.length) { for (var index in args) { var cmd = COMMANDS[args[index]]; var tmode = mode; tmode.help = true; if (cmd && (cmd.check || function() { return true; })(msg)) cmd.code(msg, [], tmode, param, args); } } else basic(); } }, pretty: APP_INITIALS.toLowerCase() + 'help', }, }); injectText({ '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!", '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, '#' for help mode, and '-' for negation mode. For instance, '/stfu' is 'normal mode', and '/stfu!-' (or '/stfu-!') is 'silent and negated mode'. Alternatively, pass the name of the command to the help command. Call help in query mode to list commands.", }); // Emotes const KATNIP = []; const addKatnip = function(code) { KATNIP.pushUnique(code.replace(/^:/, '').simplify()); }; addKatnip('svk-katandabri'); addKatnip('aubkat'); addKatnip('svk-smokevapor2'); addKatnip('von-dance1'); addKatnip('von-dance2'); addKatnip('svk-blowjob'); addKatnip('zk-kat-inabox'); addKatnip('zk-kat-inabox2'); addKatnip('katboxhat'); addKatnip('voneating'); addKatnip('zk-kat-boobplay'); addKatnip('Balloon1'); addKatnip('sexiekat-cute'); addKatnip('svk-headstand'); addKatnip('svk-tatoo2'); addKatnip('svk-nippleplay'); registerCommands({ katnip: { regex: /^katnip$/i, code: function(msg, match, mode) { var nip = "{#katnip.prefix} :" + random(KATNIP); if (mode.silent) sendStatus(nip); sendStatusMessage(nip, msg.user); }, hidden: true, }, addkatnip: { regex: /^katnip\s+(\w+)$/i, code: function(msg, match) { addKatnip(match[1]); sendSuccessMessage("katnip.added", msg.user); }, check: isAdmin, hidden: true, }, }); injectText({ 'katnip.prefix': 'Katnip time!', 'katnip.added': 'Added new Katnip!', }); // Common Commands registerCommands({ me: { regex: /^me\s+(.+)$/i, code: function(msg, match) { sendAction('* ' + msg.user + ' ' + match[1]); }, }, flip: { regex: /^(?:flip|coin)/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.flip', msg.user); return; } var side = Math.floor(1 + Math.random() * (2)) == 1 ? 'heads' : 'tails'; if (mode.silent) sendStatusMessage("You got " + side + '!', msg.user); else sendStatusMessage(msg.user + " flipped a coin and got " + side + '!'); }, }, dice: { regex: /^(?:roll|dice)/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.dice', msg.user); return; } var spec = args.length > 0 ? args[0] : '1d6'; var info = spec.match(/^(\d*)d(\d+)$/i); var count = 0; var sides = 0; var rolls = []; var total = 0; if (info) { count = toint(info[1] || 1); sides = toint(info[2] || 6); } else { count = 1; sides = args.length > 0 ? toint(args[0]) : 6; } count = isNaN(count) ? 1 : count; count = count < 1 ? 1 : count; sides = isNaN(sides) ? 6 : sides; sides = sides < 2 ? 6 : sides; for (var current = 0; current < count; current++) { var rolled = roll(sides); rolls.push(rolled); total += rolled; } var rollStr = ' rolled ' + count + 'd' + sides + ': ' + (count > 1 ? rolls.join(' + ') + ' = ' : '') + total + '!'; if (mode.silent) sendStatusMessage('You ' + rollStr, msg.user); else sendStatusMessage(msg.user + rollStr); }, }, log: { regex: /^log/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.log', msg.user); return; } debug((mode.silent ? '' : msg.user + ': ') + param); }, check: isFan, hidden: true, }, math: { regex: /^(?:math|calc)/i, code: function(msg, match, mode, param, args) { if (mode.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); } }, }, moody: { regex: /^moody$/i, code: function() { sendAlert("Fair warning: I'm in a bad mood now. That means I'm coming down on people hard. Be respectful, use your common sense, and obey the rules. Or else.\n~NaughtyShadow"); }, check: isMaster, hidden: true, }, binsearch: { regex: /^binsearch\s+(\d+)\s+(\d+)$/i, code: function(msg, match, mode) { if (mode.help) { sendHelp("help.binsearch", msg.user); return; } 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); }, }, modulo: { regex: /^modulo\s+(\d+)\s+(\d+)((?:\s+\d+)?)$/i, code: function(msg, match, mode) { if (mode.help) { sendHelp('help.modulo', msg.user); return; } 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, }, }); injectText({ 'help.flip': "Flips a coin and broadcasts a chat notice with the result.", 'help.dice': "Rolls a die and broadcasts a chat notice with the result. If you pass a number, the die will have that many sides. Otherwise, it'll have six sides. You can also pass a 'dice specification' in to form of <count>d<sides> to roll more than one time.", '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.", 'help.binsearch': "Finds the middle point between two numbers, for running a binary search. Because apparently, some people don't know how to do that.", 'help.modulo': "Takes the first number, assumes in the base given as the third number (or ten if there is no third number), and converts it to the base given in the second number.", 'math.invalid': "Invalid expression", 'math.invalid.log': 'tried to eval a potentially dangerous string', }); // Settings cb.settings_choices = [ { name: 'enable_tags', type: 'choice', choice1: ON, choice2: OFF, label: 'Amusing nametags?', defaultValue: ON, required: true, }, { name: 'enable_cols', type: 'choice', choice1: ON, choice2: OFF, label: 'Backgrounds on messages from certain people?', defaultValue: ON, required: true, }, { name: 'enable_macros', type: 'choice', choice1: ON, choice2: OFF, label: 'Chat macros?', defaultValue: ON, required: true, }, ]; // Utilities const roll = function(max) { return Math.floor(1 + Math.random() * max); }; const randomizer = function(options) { options = flatten([options]); return function() { if (!options.length) return ''; var chosen = undefined; while (chosen === undefined) 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); }); }; // Spam filter // The (original) purpose of this bot const DUNGEON = { names: [ /jeremypadilla\d*/, // Doesn't know when to shut up, uses alt accounts to talk even when silenced 'massoandradeeer', // Ignores rules /wetkitty\d+/, // Advertiser 'beautizoncam', // Advertiser 'littlepeepee39', // Wants urine, doesn't know when to shut up 'jedisuede', // Keeps requesting urine 'dennyscum', // Keeps making demands '13holla13', // Repeatedly silenced by other models 'bigmeatstick312009', // Excessively rude to models 'hotttffucccck', // Advertiser 'lovesblacksarabsngayjews', // Doesn't stop pestering models 'i_drink_my_sperm', // Do I really need to explain? 'deshawnblackboy', // Insulting to models 'ezerok420', // Insulted Harry Potter. UNACCEPTABLE. /katieikittyefl\d+/, // Advertiser 'linhd1', // Acts like models are there to serve him 'mrdonaldtrump2016', // Just no. /jennypussy\w\d*/, // Spambot 'rscdj', // Mega spam /harleydavid\d*/, // Spambot /pinkpussyaz\d*/, // Spambot /felipeaugusto\d*/, // Spambot /asianpussy.?\d*/, // Spammer /consumer\d*/, // Insulting to models 'iwantyourpanties', // Can't put it into words, but this one disturbs me... /makfay\d*/, // Rude to models /hottpinkx.\d*/, // Spamming /juicyjuicy\d*/, // Promoting other sites /.+?_cheating_milf/i, // Spam /kandyxdc\d*/i, // Spam /___+/, // Generally spammers ], words: [ /\[(leak(ed)?|free)\]/i, /^\d+\s+[mf]\s+selling/i, /^:(lickpussy|pastcandy|cumcum|assfuck)\d*$/i, /my\s+hard\s+(co|di)ck/i, /fart/i, /kikcams/i, /pornmeds/i, /ellago\s*cam/i, /^i\s+am\s+new$/i, /\bcunt\b/i, /swipegirls/i, /snapmilfs/i, /^open\s+\S+$/i, /^((can\s+you\s+)?show\s+)?f(ee+|oo+)ts?(\s+please)?$/i, /my\s+(profile|bio)/i, /watch\s+me/i, /free\s+tokens/i, ], }; const GREYS = []; var STFU = {}; registerCommands({ blockname: { regex: /^blockname\s+(.+)$/i, code: function(msg, match, mode, param, args) { var pattern = param.trim(); if (param.startsWith('/') && param.endsWith('/')) { pattern = new RegExp(param.substring(1, param.length - 1), 'i'); } DUNGEON.names.push(pattern); debug(pattern + ' {#dungeon.added.names} ' + msg.user); }, check: isAdmin, hidden: true, }, blocktext: { regex: /^blocktext\s+(.+)$/i, code: function(msg, match, mode, param, args) { var pattern = param.trim(); if (param.startsWith('/') && param.endsWith('/')) { pattern = new RegExp(param.substring(1, param.length - 1), 'i'); } DUNGEON.words.push(pattern); debug(pattern + ' {#dungeon.added.words} ' + msg.user); }, check: isAdmin, hidden: true, }, blockany: { regex: /^blockany\s+(.+)$/i, code: function(msg, match, mode, param, args) { var pattern = param.trim(); if (param.startsWith('/') && param.endsWith('/')) { pattern = new RegExp(param.substring(1, param.length - 1), 'i'); } DUNGEON.joint.push(pattern); debug(pattern + ' {#dungeon.added.joint} ' + msg.user); }, check: isAdmin, hidden: true, }, stfu: { regex: /^stfu/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp("help.stfu", msg.user); return; } else if (mode.query) return COMMANDS.quieted.code(msg, match, mode, param, args); else if (mode.negate) { var ulist = []; for (var index in args) { var username = args[index]; delete STFU[username]; ulist.push(username); sendSuccessMessage("{#stfu.undo.msg.target}" + (mode.silent ? '' : ' by ' + msg.user), username); } sendSuccessMessage('{#stfu.undo.msg.user.prefix' + (mode.silent ? '.silent ' : '') + '} ' + prettyJoin(ulist) + " {#stfu.undo.msg.user.suffix}", msg.user); debug(prettyJoin(ulist) + ' {#stfu.undo.log' + (mode.silent ? '.silent' : '') + '} ' + msg.user); } else { var ulist = []; for (var index in args) { var username = args[index]; STFU[username] = true; ulist.push(username); sendErrorMessage("{#stfu.msg.target}" + (mode.silent ? '' : "\n- " + msg.user), username); } sendSuccessMessage('{#stfu.msg.user.prefix' + (mode.silent ? '.silent' : '') + '} ' + prettyJoin(ulist) + ' {#stfu.msg.user.suffix}', msg.user); debug(prettyJoin(ulist) + ' {#stfu.log' + (mode.silent ? '.silent' : '') + '} ' + msg.user); } }, check: isAdmin, }, quieted: { regex: /^quieted/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp("help.quieted", msg.user); return; } var status = ''; if (param) { for (var index in args) { var username = args[index]; status += ', ' + username + ': ' + (STFU[username] ? 'silent' : 'talking'); } } else { for (var name in STFU) { if (STFU[name]) status += ', ' + name; } } sendStatusMessage('{#stfu.list.prefix}: ' + status.substr(2), msg.user); }, check: isFan, }, }); injectText({ 'dungeon.added.names': 'added to dungeon names list by', 'dungeon.added.words': 'added to dungeon words list by', 'dungeon.added.joint': 'added to dungeon joint list by', 'dungeon.notalk': "You can't talk, you're in the dungeon!", 'dungeon.badword': "You can't say that!", 'dungeon.grey': ':cuppa-stfu', 'stfu.msg.target': 'SHUT THE FUCK UP', 'stfu.msg.user.prefix': 'You told', 'stfu.msg.user.prefix.silent': 'You silently told', 'stfu.msg.user.suffix': 'to shut the fuck up', 'stfu.log': 'told to shut the fuck up by', 'stfu.log.silent': ' silently told to shut the fuck up by', 'stfu.undo.msg.target': "You've been allowed to talk again", 'stfu.undo.msg.user.prefix': 'You allowed', 'stfu.undo.msg.user.prefix.silent': 'You silently allowed', 'stfu.undo.msg.user.suffix': 'to talk again', 'stfu.undo.log': 'allowed to talk again by', 'stfu.undo.log.silent': ' silently allowed to talk again by', 'stfu.list.prefix': 'Silenced users', 'stfu.notalk': 'Shut the fuck up. You\'ve been blocked.', 'help.stfu': 'Tells users to shut the fuck up. Actually prevents them from speaking until turned off. Use negation mode to allow them to talk again.', 'help.quieted': 'Tells you what users have been /stfu\'d.', }); onMessage(function(msg) { if (!isFan(msg)) { for (var index in DUNGEON.names) { var test = DUNGEON.names[index]; if (msg.user.matches(test)) { return spam(msg, 'dungeon.notalk'); } } for (var index in DUNGEON.words) { var test = DUNGEON.words[index]; if (msg.m.matches(test)) { return spam(msg, 'dungeon.badword'); } } if (STFU[msg.user]) { return spam(msg, "stfu.notalk"); } } if (isGrey(msg)) { for (var index in GREYS) { var test = GREYS[index]; if (msg.m.match(test)) { return spam(msg, 'dungeon.grey'); } } } }); // We are serious people. if (cb.settings.enable_macros == ON) { const MACROS = { rfl: 'Ravenclaw for life!', tfr: 'Tip for requests.', ns: 'No shouting.', ad: 'No advertising.', nice: 'Be respectful.', ew: 'That\'s disgusting.', tos: 'That\'s forbidden by CB\'s TOS.', hf: 'High five!', bb: 'Her name is Kat. Not baby, babe, or bb.', lenny: ':lefaceface', hug: ':ponyhugplz', mod: ':jappleclub', tkat: '@SexieVonKat', tnews: '@SexieNews', tns: '@IzarraKiania', cb: 'CB: it doesn\'t stand for ChaturBate - it stands for Complete Bullshit!', squirt: 'Kat CAN squirt, yes. She does not ALWAYS squirt. Don\'t demand that she squirt or you WILL be silenced.', pump: 'Kat is a type 1 diabetic. The thing on her arm is an insulin pump.', opinion: 'I must be coming down with Alzheimer\'s, cause I don\'t recall asking you anything.', grey: ':stfu-greys', gray: ':stfu-greys', poor: ':stfu-greys', stfu: ':cuppa-stfu', hp: 'Harry Potter', proof: "I DIDN'T DO IT NOBODY SAW ME DO IT YOU CAN'T PROVE ANYTHING", ignore: 'IGNOOOORE MEEE!', wtf: "SWEET MOTHER GRUB'S OOZING VESTIGIAL THIRD ORAL SPHINCTER", '420': "#BlazeIt", dw: 'da da da da, da da da da, da da da da, DA DA DA DA! ooooEEEEoooo!', pc: 'GLORIOUS PC GAMING MASTER RACE!', win: 'CHARLIE SHEEN', forever: ':goneforever', model: cb.room_slug, cuties: ':svk-aubri-twitters', no: 'oh noooooooo', }; const REWRITE = { 'COCKO BLOCKO': /\bcock\s*block\b/i, 'TACO BLOCKO': /\b(?:clam|box|twat|taco)\s*(?:stop|block|jam)\b/i, 'COCKO BLOCKO\'d': /\bcock\s*blocked\b/i, 'TACO BLOCKO\'d': /\b(?:clam|box|twat|taco)\s*(?:stop|block|jam)ed\b/i, '#YOLO': /\b(?:yolo|you\s*only\s*live\s*once)\b/i, '#SWAG': /\bswag\b/i, '#420': /\b420\b/i, 'SHENANIGANS' :/\bshenanigans\b/i, 'KATNIP CLUB': /\bkatnip\s+club\b/i, }; const REWRITE_COMPLEX = [ { // butt hurt -> <something funnier> from: /(butt|ass)\s*hurt/i, to: function() { var str = ''; // 11 options in the first part. 15 in the second. 165 total possibilities. str += random([ 'booty', 'fanny', 'ass', 'butt', 'heiney', 'patootie', 'derierre', 'keister', 'posterior', 'tush', 'rump', ]); str += ' '; str += random([ 'bothered', 'troubled', 'angered', 'hurt', 'perturbed', 'disgruntled', 'kicked', 'punted', 'pained', 'frazzled', 'ruffled', 'distressed', 'maimed', 'agitated', 'injured', ]); return str.toUpperCase(); }, }, ]; if (cb.room_slug == 'sexievonkat') REWRITE.Kat = /\bkay\b/i; // Fucking typos... onMessage(function(msg) { for (var macro in MACROS) msg.m = msg.m.split('$' + macro.toLowerCase()).join(MACROS[macro]); for (var repl in REWRITE) msg.m = msg.m.split(REWRITE[repl]).join(repl); for (var index in REWRITE_COMPLEX) { var from = REWRITE_COMPLEX[index].from; var to = REWRITE_COMPLEX[index].to(); msg.m = msg.m.split(from).join(to); } }); // For the automatic translation of chat messages injectText({ 'offline': 'You seem to be currently offline, madam!', // "offline" -> this }); } // VERY SERIOUS PEOPLE. if (cb.settings.enable_tags == ON) { addSettings({ royalty: 0, }); const getPrincessPower = function() { if (CONFIG.royalty < 0) return localize('princess.infinite'); else return CONFIG.royalty; }; const NAME_TAGS = { /* // LEGACY TAGS afatani85: [ 'batman', 'the tip lord supreme', 'the ninja tipper', 'the goal assassin', ], naughtyshadow: [ 'poison ivy', 'the princess of cats', 'the megabitch', 'the snarcasti-queen', 'the simurgh', 'the queen of fairies', 'the reaper of souls', 'the mistress of hell', 'a high archdemon', 'a chaos pope', 'the smacker of bitches', 'baroness von bullshit', 'commander shepard', 'courier six', 'the dragonborn', 'the seer of space', 'a magic space whale', 'umbra invictus', 'kat\'s secretary', 'her imperious condescension', 'soulless', "ziz's waifu", ], pinkypirat: [ 'a pirat', 'the norwegian legion', 'papa stone', 'a pokemon master', "ashley's hubby", // Confirmed that it's okay with Ash and Pinky first, don't worry. ], sexievonkat: [ 'katwoman', 'head of ravenclaw house', 'pikachu', ], aubrilee: [ 'silly putty', 'silly puddy', ], jacobonya: [ 'the joker', 'the page of hope', 'dragonite', ], m_ninja_16: [ 'deadpool', 'a real ninja', 'zeus', "nite's sifu", ], yeah009: [ 'the kool aid man', 'a wizzard', ], ashleyblossom: [ 'wonder woman', ], zero_kool: [ 'mister freeze', 'a 1337 hacker', ], nite69: [ 'nightcrawler', 'the bringer of cuddles', 'a fucking tease', 'the master of gifs', ], platitum1: function() { var inCaseOfPrincess = 'the princess twilight sparkle'; if (CONFIG.royalty) { if (CONFIG.royalty > 0) --CONFIG.royalty; return inCaseOfPrincess; } var randInt = roll(100); // 1-100 inclusive if (randInt <= 33) return 'bane'; else if (randInt <= 66) return 'aang'; else if (randInt <= 99) return 'sir dude'; else return inCaseOfPrincess; }, bigbaddab: [ 'wolverine', 'a big dick mystic', 'the cockinator', ], jfang1: 'a whole basilisk', boatrideguy: [ 'like leo', 'not on the shore', 'riding on a dolphin', 'poseidon', ], theswisspimp: [ 'the wizard of goats', 'a waffle slut', ], purple_pie6: [ 'a lil shit', 'a sadistic spanker', 'sir pie', ], txfires: 'ariel the angel', legscarflover: [ 'the superest of kids', 'unsuper of the super', 'the punisher\'s cleanup crew', 'kat\'s legs', 'the goof of balls', ], bendoverplease960: [ "kat's lil dinosaur", 'kat warrior', ], hippylabrat: 'the lorax', sexyblackguy3453: 'knightrider', // At a friend's request. I don't even know this guy. // My helpers get rewarded! jessjade95: 'a jedi guardian', violetrhayne: 'blessed by chaos', // These are just because I can :P kazuukii: 'not a kazoo', neo1003: 'the one', sayimthebest: 'not the best', */ sexievonkat: 'katnip', nicky7282: function() { var inCaseOfPrincess = 'the princess twilight sparkle'; if (CONFIG.royalty) { if (CONFIG.royalty > 0) --CONFIG.royalty; return inCaseOfPrincess; } var randInt = roll(100); // 1-100 inclusive if (randInt <= 95) return 'katnip club'; else return inCaseOfPrincess; }, }; function addClubbers(clubbers) { for (var index in clubbers) { NAME_TAGS[clubbers[index]] = 'katnip club'; } } addClubbers('afatani85 naughtyshadow pinkypirat jacobonya m_ninja_16 yeah009 zero_kool nite69 bigbaddab jfang1 boatrideguy theswisspimp purple_pie6 legscarflover bendoverplease960 lildiego25 nash3333 supermanall4u julianbrown alex__1982 zmoney0303 kats_sissy_boy lunasslaveboy'.split(/\s+/)); onMessage(function(msg) { if (NAME_TAGS[msg.user]) { var tag = NAME_TAGS[msg.user]; var chosen = ''; if (typeof tag == "function") chosen = NAME_TAGS[msg.user](); else if (Array.isArray(tag)) chosen = random(NAME_TAGS[msg.user]); else chosen = NAME_TAGS[msg.user]; msg.m = msg.m.prefix(chosen); msg.m = msg.m.split('$tag').join(chosen.toLowerCase()).split('$TAG').join(chosen.toUpperCase()).split('$Tag').join(chosen.toNounCase()); } return msg; }); registerCommands({ tags: { regex: /^tags/i, code: function(msg, match, mode, param, args) { if (mode.help) sendHelp("help.tags", msg.user); else if (mode.query) { var who = args.length ? args[0] : msg.user; if (NAME_TAGS[who]) { var userTags = NAME_TAGS[who]; if (typeof userTags == "function") sendStatusMessage(who + " {#tags.complex}", msg.user); else if (Array.isArray(userTags)) sendStatusMessage(who + ' has ' + counted(userTags.length, 'tag'), msg.user); else sendStatusMessage(who + " {#tags.one}", msg.user); } else sendStatusMessage(who + " {#tags.none}", msg.user); } else { var who = args.length ? args[0] : msg.user; if (NAME_TAGS[who]) { var userTags = NAME_TAGS[who]; if (typeof userTags == "function") sendStatusMessage(who + " {#tags.complex}", msg.user); else if (Array.isArray(userTags)) sendStatusMessage(who + " {#tags.many}: " + prettyJoin(userTags.toUpperCase(), '; '), msg.user); else sendStatusMessage(who + " {#tags.one}: " + userTags.toUpperCase(), msg.user); } else sendStatusMessage(who + " {#tags.none}", msg.user); } }, }, tag: { regex: /^tag\s+(\S+)\s+(.+)$/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.tag', msg.user); } var who = match[1]; var tag = match[2].toUpperCase(); if (NAME_TAGS[who]) { var ctag = NAME_TAGS[who]; if (typeof ctag == 'function') { sendErrorMessage('{#tagchg.complex}', msg.user); return; } if (Array.isArray(ctag)) NAME_TAGS[who].push(tag); else NAME_TAGS[who] = [ctag, tag]; } else { NAME_TAGS[who] = tag; } sendSuccessMessage('{#tagchg.success}', msg.user); }, check: isMaster, hidden: true, }, untag: { regex: /^untag\s+(\S+)\s+(.+)$/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.untag', msg.user); } var who = match[1]; var tag = match[2].toUpperCase(); if (NAME_TAGS[who]) { var ctag = NAME_TAGS[who]; if (typeof ctag == 'function') { sendErrorMessage("{#tagchg.complex}", msg.user); return; } if (Array.isArray(ctag)) { var index = ctag.indexOf(tag); if (index > -1) ctag.splice(index, 1); else { sendErrorMessage('{#tagchg.404}', msg.user); return; } } else delete ctag[who]; } else { sendErrorMessage(who + " {#tags.none}!", msg.user); return; } sendSuccessMessage('{#tagchg.success}', msg.user); }, check: isMaster, hidden: true, }, princess: { regex: /^(?:princess|royalty|alicorn|decrees)/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.princess', msg.user); return; } if (mode.query) sendStatusMessage('{#princess.name} level: ' + getPrincessPower(), msg.user); else { // Alright. I admit, this is flagrant abuse of bot programmer privilege. var noReduce = function() { sendErrorMessage('princess.noreduce', msg.user); }; var escalate = function(times) { CONFIG.royalty = times; var tell = '{#princess.name} ' + (CONFIG.royalty ? 'activated! Power level: ' + getPrincessPower() : 'deactivated!'); sendStatusMessage(tell, msg.user); debug("New {#princess.name} level: " + getPrincessPower() + ' (by ' + msg.user + ')'); }; var noEffect = function() { sendErrorMessage('There are already ' + getPrincessPower() + ' {#princess.decrees} remaining!', msg.user); }; var times = args.length > 0 ? toint(args[0]) : 1; if (isNaN(times)) times = 1; if (times < 0) times = -1; if (msg.in_fanclub && !msg.is_mod) { // Fans are allowed to escalate, but not reduce. if (CONFIG.royalty == -1 && times != -1) noReduce(); else if (times >= 0 && CONFIG.royalty > times) noReduce(); else if (times == CONFIG.royalty) noEffect(); else escalate(times); } else escalate(times); } }, check: isFan, }, klub: { regex: /^klub\s+(\S+)$/i, code: function(msg, match, mode, param, args) { if (mode.query) { if ((NAME_TAGS[match[1]] || '').toUpperCase() == 'KATNIP CLUB') sendStatus(match[1] + ' {#klub.yes}', msg.user); else sendStatus(match[1] + ' {#klub.no}', msg.user); } else { addClubbers([match[1]]); if (!mode.silent) { sendSuccess(match[1] + ' {klub.added}'); } } }, check: isMaster, hidden: true, }, }); injectText({ 'tags.complex': 'has at least one tag, but complex logic is involved. Ask NaughtyShadow for details.', 'tags.many': 'has these tags', 'tags.one': 'has one tag', 'tags.none': 'has no tags', 'tagchg.complex': "Can't update tags with complex logic", 'tagchg.success': 'Tags updated!', 'tagchg.404': "No such tag found! Have you checked '/tags <name>' to make sure it's there?", 'princess.infinite': 'infinite', 'princess.name': 'Super Friendship Princess Power', 'princess.noreduce': 'Princesses can neither abdicate nor be dethroned!', 'princess.decrees': 'Royal Decrees', 'klub.yes': 'is in the DA KATNIP CLUB!', 'klub.no': 'is NOT in DA KATNIP CLUB.', 'klub.added': 'has been added to the KATNIP CLUB!', 'help.tags': "Tells you what tags you have been assigned. Query mode lists the number of tags that have yet to be assigned for various users. Pass a user's name to see their tags.", 'help.tag': "Adds temporary tags to people. Designed to reduce the need to keep rebooting the bot to update tags. Pass a user name and then the tag to add.", 'help.untag': "Temporarily removes tags from people. Designed to reduce the need to keep rebooting the bot to update tags. Pass a user name and then the tag to remove.", 'help.princess': "Trolls platitum1. Controls his Super Friendship Princess Power level. Pass a number to set his Super Friendship Princess Power level, or it will be set to one. Pass a negative number to make it infinite. Plat and fans can only raise it, never lower it. Calling in query mode (append a '?' to the command) will tell you his power level.", }); } // SO. SERIOUS. if (cb.settings.enable_cols == ON) { const NAME_COLS = { afatani85: '#54FF9F', naughtyshadow: '#FFB0FF', jfang1: '#63B8FF', zero_kool: '#34CCFF', m_ninja_16: '#BFEFFF', jacobonya: '#B0E0E6', boatrideguy: '#46E9EF', }; onMessage(function(msg) { if (NAME_COLS[msg.user]) msg.background = NAME_COLS[msg.user]; }); registerCommands({ colour: { regex: /^colour\s+(\S+)\s+(#[0-9a-f]{6})$/i, code: function(msg, match) { NAME_COLS[match[1]] = match[2]; }, check: isAdmin, hidden: true, }, }); } // MADE OF SERIOUSNESS. addSettings({ troll: false }); onMessage(function(msg) { if (CONFIG.troll) { if (msg.user.match(/hodor/)) { msg.m = "hodor ".repeat(Math.floor(msg.m.length / 7)).trim(); } } }); registerCommands({ troll: { regex: /^troll$/, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp("help.troll", msg.user); } else if (mode.query) { sendStatus("Troll mode is o" + (CONFIG.troll ? 'n' : 'ff'), msg.user); } else { if (!mode.negate) { CONFIG.troll = true; if (!mode.silent) sendStatus("Troll mode has been activated!"); } else { CONFIG.troll = false; if (!mode.silent) sendStatus("Troll mode has been deactivated!"); } } }, check: isAdmin, } }); injectText({ 'help.troll': "Controls 'troll mode' for the bot. When active, does some amusing but potentially annoying things.", }); // Timers are useful const TIMERS = {}; const makeTimer = function(name, init, silentp) { if (!isNaN(parseInt(init, 10))) init = parseInt(init, 10); else init = 0; if (init < 1) return; TIMERS[name] = init; var lname = 'Timer "' + name + '"'; var decrement = function decrease() { if (!TIMERS[name]) { delete TIMERS[name]; return; } if (isNaN(TIMERS[name])) { delete TIMERS[name]; return; } if (TIMERS[name] < 0) { delete TIMERS[name]; return; } TIMERS[name] = TIMERS[name] - 1; var left = TIMERS[name]; if (left === 0) { sendStatusMessage(lname + ' {#timer.end}'); delete TIMERS[name]; return; } else if (left == 10) sendStatusMessage(lname + ' {#timer.left.ten}'); else if (left == 30) sendStatusMessage(lname + ' {#timer.left.thirty}'); else if (left % 60 === 0) { var tell = function() { sendStatusMessage(lname + ' has ' + getNiceTime(left) + ' left!'); }; var mins = left / 60; if (mins <= 2) tell(); else if (mins <= 30) if (mins % 5 === 0) tell(); else if (mins % 10 === 0) tell(); } cb.setTimeout(decrease, 1000); }; if (!silentp) sendStatusMessage(lname + ' {#timer.start}! ' + getNiceTime(TIMERS[name]) + ' remains!'); decrement(); }; registerCommands({ timer: { regex: /^timer/i, code: function(msg, match, mode, param, args) { if (mode.help) { sendHelp('help.timer', msg.user); return; } else if (mode.query) { var list = []; for (var name in TIMERS) list.push(name + ': ' + getNiceTime(TIMERS[name])); if (list.length) sendStatusMessage("{#timer.list.prefix}:\n" + list.join("\n"), msg.user); else sendStatusMessage("{#timer.list.none}", msg.user); } else if (mode.negate) { if (TIMERS[param]) { sendSuccessMessage("Timer " + param + " killed!"); debug(msg.user + ' killed timer "' + param + '" with ' + getNiceTime(TIMERS[param]) + ' remaining'); TIMERS[param] = -1; } else sendErrorMessage("{#timer.404}", msg.user); } else { // Return value of this function doesn't matter. It's just a shortcut. if (args.length < 1) return sendErrorMessage("{#timer.new.syntax}", msg.user); var seconds = args[0]; if (seconds.charAt(seconds.length - 1) == 'm') seconds = toint(seconds.substr(0, seconds.length - 1)) * 60; else seconds = toint(seconds); var name = args.length > 1 ? param.substr(args[0].length).trim() : generateUUID(); makeTimer(name, seconds, mode.silent); debug(msg.user + ' started timer "' + name + '" with ' + getNiceTime(seconds)); } }, check: isFan, }, trename: { regex: /^trename/i, code: function(msg, match, mode, param, args) { if (mode.help) sendHelp('help.trename', msg.user); else if (param) { var parts = param.split('//'); if (parts.length < 2) return sendErrorMessage('timer.rename.syntax', msg.user); var from = parts[0].trim(); var to = parts[1].trim(); var time = TIMERS[from]; if (!time) return sendErrorMessage("{#timer.404}", msg.user); delete TIMERS[from]; makeTimer(to, time, true); sendStatusMessage('Timer "' + from + '" renamed to "' + to + '"'); } else sendErrorMessage("{#timer.rename.syntax}", msg.user); }, check: isFan, }, addtime: { regex: /^addtime/i, code: function(msg, match, mode, param, args) { if (mode.help) sendHelp('help.addtime', msg.user); else if (param) { if (args.length < 2) return sendError('timer.add.syntax', msg.user); var seconds = args[0]; if (seconds.charAt(seconds.length - 1) == 'm') seconds = toint(seconds.substr(0, seconds.length - 1)) * 60; else seconds = toint(seconds); if (mode.negated) seconds = -seconds; var name = param.substr(args[0].length).trim(); if (TIMERS[name]) { TIMERS[name] += seconds; } } }, check: isFan, }, }); injectText({ 'timer.end': 'has ended!', 'timer.left.ten': 'has ten seconds left!', 'timer.left.thirty': 'has thirty seconds left!', 'timer.start': 'has started', 'timer.list.prefix': 'Live timers', 'timer.list.none': 'No live timers', 'timer.404': 'No such timer!', 'timer.new.syntax': 'Must provide duration for timer. Also providing a name after the duration is STRONGLY recommended.', 'timer.rename.syntax': 'Must pass the current name and then the new name, separated by two slashes.', 'timer.add.syntax': 'Must pass time to add and then timer name.', 'help.timer': "Provide the number of seconds to count down, or minutes with the suffix 'm' (10, 5m, etc). STRONGLY recommended, provide a timer name to be displayed during the countdown and at the end. A notice will be broadcast to chat every minute until the timer ends, as well as at the ten and thirty second marks.", 'help.trename': "Renames a timer to something else. Useful when you forget to name a timer at creation. Pass the current name and then the new name, separated by two slashes. Example:\n/trename my timer // Countdown", 'help.addtime': "Add (or remove, in negated mode) time on a timer. Pass the time (using the 'm' suffix for minutes) and then the name.", }); // Mrow :3 onMessage(function(msg) { if (msg.user == 'sexievonkat') { if (chance(2)) { msg.m += " meow :3"; } } }); // Let the games begin... setup();
© Copyright Chaturbate 2011- 2026. All Rights Reserved.