Bots Home
|
Create an App
TCKY Ultra Bot Test
Author:
atthem
Description
Source Code
Launch Bot
Current Users
Created by:
Atthem
//Summary /* Combination of britney_and_justin's Ultra Bot and features from NaughtyShadow's House Elf Bot, plus a few extras for tcky31 */ //Description /* Modifed by atthem for TCKY31. Credit goes to britney_and_justin & NaughtyShadow for their original bots. Tip king (UB) Leaderboard (UB) coin flip (HEB) dice roll (HEB) custom tip emoticons (HEB) custom entry messages (HEB) custom tip messages (HEB) dungeon list (HEB) name tags (HEB) chat macros (HEB) named timers (HEB) Tip Menu (atthem) Alternate Tip Menu (atthem) prohibit promoting of other cams (atthem) Room Rules (atthem) Spam Block Counter (atthem) Tip God (highest tip in a single show) (atthem) This bot is modified from the original specifically for tcky31. If you like this bot, you should use britney_and_justin's Ultra Bot and NaughtyShadow's House Elf bot. Their bots are better, have more features, and will be updated more often. I highly recommend their bots, and discourage anyone else from using this one. - atthem */ /* Update Log ---------------------------------------------------------------- 2016-05-16 Added Tip Menu. 2016-05-18 Added prohibition of promoting other girl/couple cams. 2016-05-18 Added ALTERNATE tip menu. 2016-06-15 Updated list of things prohibited from chat. Added some nametags 2016-06-25 Added a few more phrases to the prohibited list. 2016-06-29 Added highest single tip tracker and prize. 2016-06-30 Corrected bug where highest single tip would trigger before meeting minimum set tip. Removed a lot of On/Off options from settings for things that TCKY31 always has on, to clean up the settings window. Added 5 more items to standard tip menu, for a total of 20 possible items. (alt menu still has 15). Corrected bug where model was also prevented from saying things that other females can't say in chat. Added snapmilfs to banned words. 2016-07-02 Corrected bug causing app to crash when any female typed in chat. 2016-07-13 Modified the banned words to be more forgiving and added some additional words and phrases. 2016-08-24 Modified the banned words list to account for more sneaky ways spammers are getting through. 2016-09-01 Added Room Rules (sorry, hard-coded - but easy to edit). Modified banned words list further. Added spam block counter for shits and grins. 2016-09-08 Tip God added, with 5-minute notifier and entry message 2016-09-10 Corrected issue with Tip God where once the tip god tks were surpassed, additional tokens weren't counted. Added code to block non-ANSI characters (spammers use Unicode to bypass spam filters) 2016-09-14 Added /rules command to broadcast rules to room. Added reasons for banned words so the model knows why something was flagged as spam. 2016-09-16 Added notification for Fan Club Members and Mods when they enter the room to notify them of the Appreciation Show schedule. 2016-10-07 Added Top Master, just like Tip God, but manually reset every month. Added variables for all the different colors used. 2016-10-13 Added a few more anti-spam banned words/phrases. 2016-12-09 Corrected errant ; causing tip menu to always display, even if the setting was off. 2017-01-19 RoomMaster and model are now allowed to use non-ansi characters in chat. 2017-03-22 Added more words banned from chat 2017-03-24 Banned grays from using emoticons at the start of message (spam) 2017-04-04 Banned grays from using emoticons at all, unless on the white list. Spammers can go fuck themselves. 2017-07-28 Grays can't chat for 10 minutes. Gad damn spammers, stop damaging my calm. ------------------------------------------------------------------------------*/ //variables { var tipperArray = new Array; var numTippers = 0; var currentKing = ''; var kingTip = 0; var kingTimer = parseInt(cb.settings.kingTimer); var leaderArray = [['',0],['',0],['',0]]; var leaderTimer = parseInt(cb.settings.leaderTimer); var notifierMessage = cb.settings.spamMessage; var purple = "#C287C2"; if (cb.settings.tipMenuSet == 'STANDARD'){ var tipItemArray = [cb.settings.menuItem1,cb.settings.menuItem2,cb.settings.menuItem3,cb.settings.menuItem4,cb.settings.menuItem5,cb.settings.menuItem6,cb.settings.menuItem7,cb.settings.menuItem8,cb.settings.menuItem9,cb.settings.menuItem10,cb.settings.menuItem11,cb.settings.menuItem12,cb.settings.menuItem13,cb.settings.menuItem14,cb.settings.menuItem15,cb.settings.menuItem16,cb.settings.menuItem17,cb.settings.menuItem18,cb.settings.menuItem19,cb.settings.menuItem20]; var tipCostArray = [cb.settings.tipMenu1,cb.settings.tipMenu2,cb.settings.tipMenu3,cb.settings.tipMenu4,cb.settings.tipMenu5,cb.settings.tipMenu6,cb.settings.tipMenu7,cb.settings.tipMenu8,cb.settings.tipMenu9,cb.settings.tipMenu10,cb.settings.tipMenu11,cb.settings.tipMenu12,cb.settings.tipMenu13,cb.settings.tipMenu14,cb.settings.tipMenu15,cb.settings.tipMenu16,cb.settings.tipMenu17,cb.settings.tipMenu18,cb.settings.tipMenu19,cb.settings.tipMenu20]; } else { var tipItemArray = [cb.settings.menuAltItem1,cb.settings.menuAltItem2,cb.settings.menuAltItem3,cb.settings.menuAltItem4,cb.settings.menuAltItem5,cb.settings.menuAltItem6,cb.settings.menuAltItem7,cb.settings.menuAltItem8,cb.settings.menuAltItem9,cb.settings.menuAltItem10,cb.settings.menuAltItem11,cb.settings.menuAltItem12,cb.settings.menuAltItem13,cb.settings.menuAltItem14,cb.settings.menuAltItem15]; var tipCostArray = [cb.settings.tipMenuAlt1,cb.settings.tipMenuAlt2,cb.settings.tipMenuAlt3,cb.settings.tipMenuAlt4,cb.settings.tipMenuAlt5,cb.settings.tipMenuAlt6,cb.settings.tipMenuAlt7,cb.settings.tipMenuAlt8,cb.settings.tipMenuAlt9,cb.settings.tipMenuAlt10,cb.settings.tipMenuAlt11,cb.settings.tipMenuAlt12,cb.settings.tipMenuAlt13,cb.settings.tipMenuAlt14,cb.settings.tipMenuAlt15]; } var roomMaster = "atthem"; var highestTip = 0; var highestTipper = 0; } var spamCount = 0; // room rules var roomRules = '----------------------Room Rules---------------------'; roomRules += '\n' + 'IF YOU BREAK THESE RULES YOU COULD BE SILENCED OR BANNED!'; roomRules += '\n' + '#1...No demands, requests, or dirty talk without tipping!'; roomRules += '\n' + '#2...Please read my bio.'; roomRules += '\n' + '#3...Don\'t ask questions that are answered in the bio.'; roomRules += '\n' + '#4...Be respectful to me and others in my room.'; roomRules += '\n' + '#5...No CAPS LOCK!'; roomRules += '\n' + '#6...Do not promote your cam or site.'; roomRules += '\n' + '#7...No family role play!'; roomRules += '\n' + '#8...My name is Paige, not \'bb\' or \'mom\' or \'MILF\'!'; roomRules += '\n' + '#9...Grays can\'t chat for 10 minutes. Sit back and enjoy the show!'; roomRules += '\n' + '--------------------------------------------------'; var tipGod = '' + cb.settings.tipGod; tipGod = tipGod.toLowerCase(); var tipGodtks = cb.settings.tgTks; var tipMaster = '' + cb.settings.tipMaster; tipMaster = tipMaster.toLowerCase(); var tipMastertks = cb.settings.tmTks; /* if there is no tip master, decrement the token setting, so that if 1000 is the minimum, they don't have to tip 1001 to become the first tip master */ if (tipMaster.length < 2) { tipMastertks -= 1; } // Colors var tipGodBackColor = '#99ff66'; // light green var tipGodTextColor = '#000000'; // black var tipMasterBackColor = '#b3ccff'; // pastel blue var tipMasterTextColor = '#000000'; // black var tipMenuBackColor = '#ffffff'; // white var tipMenuTextColor = '#ff1a1a'; // red var tipMenuActiveBackColor = '#ff0000'; // red var tipMenuActiveTextColor = '#000000'; // black var highestSingleTipBackColor = '#ffffff'; // white var highestSingleTipTextColor = '#1a66ff'; // blue var rulesBackColor = '#00ffff'; // light blue var rulesTextColor = '#663300'; // brown var apprecShowBackColor = '#dd99ff'; // light purple var apprecShowTextColor = '#220033'; // dark purple var spamNoticeBackColor = '#ff6600'; // orange var spamNoticeTextColor = '#ffffff'; // white // Metadata const APP_NAME = "TCKY31's Ultra Bot Plus"; const APP_INITIALS = 'T_UBP'; const APP_CREDIT = "britney_and_justin [Ultra Bot] & Lilith Song (NaughtyShadow) [House Elf Bot], modifed 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 || {}; // grays in room var arrGraysIn = []; // grays can chat var arrGraysChat = []; // gray "white list" var arrWhiteList = [ "tombstone93", ]; // 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 == roomMaster) 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 == roomMaster || 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)); }; const isFemale = function(obj) { return (obj['gender'] == 'f' || obj['gender'] == 'c'); }; // 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!', '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', 'camgirls.notalk': 'You\'re a known model, and we\'ve had issues with models self-promoting, so your message was blocked. Sorry.', 'camgirls.badword': 'Maybe it was innocent, but it looked like you might have been trying to promote your own room, so your message was blocked. Sorry.', 'spam.response': ':nopesign', '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", '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?", '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.', '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.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.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.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.", }; 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'); kingSpam(); leaderSpam(); notifierSpam(); tipGodSpam(); tipMasterSpam(); if(cb.settings.color != 'C287C2') { colorChecker(); } //set notify timer if (cb.settings.tipMenu == ON) { cb.setTimeout(tipMenuAnnounce, 1000 * 60 * cb.settings.timMenuTimer); } //set highest tip notify cb.setTimeout(highestTipAnnounce, 1000 * 60 * cb.settings.highestTipTimer); }; 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', }, }); const tipMenuAnnounce = function() { var note = '-------------------- Tip Menu --------------------'; for (i = 0; i < tipItemArray.length; i++) { if (tipItemArray[i] != '') { note += "\n" + tipCostArray[i] + ' tks for ' + tipItemArray[i]; } } note += "\n" + '------------------------------------------------------------'; cb.sendNotice(note,'',tipMenuBackColor,tipMenuTextColor); cb.setTimeout(tipMenuAnnounce, 1000 * 60 * cb.settings.timMenuTimer); }; const highestTipAnnounce = function() { var note = ':tip'; if (highestTip >= cb.settings.highestTipMin) { note += "\n" + highestTipper + ' currently has the highest single tip of ' + highestTip + ' tokens.'; note += "\n" + 'Tip at least ' + (highestTip+1) + ' tokens in a single tip to take the lead!'; } else { note += "\n" + 'We haven\'t seen the minimum tip yet.'; note += "\n" + 'Tip at least ' + cb.settings.highestTipMin + ' tokens in a single tip to take the lead!'; } if (cb.settings.highestTipWinner == ON) { note += "\n" + 'The winner tonight will get ' + cb.settings.highestTipPrize + "!!!"; } note += "\n" + ':tip'; cb.sendNotice(note,'','',''); cb.setTimeout(highestTipAnnounce, 1000 * 60 * cb.settings.highestTipTimer); }; // 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, silent, query, help, param, args) { if (help) { sendHelp('help.flip', msg.user); return; } var side = Math.floor(1 + Math.random() * (2)) == 1 ? 'heads' : 'tails'; if (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, silent, query, help, param, args) { if (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 (silent) sendStatusMessage('You ' + rollStr, msg.user); else sendStatusMessage(msg.user + rollStr); }, }, 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 NORMAL = 'normal'; const BOLD = 'bold'; const BOLDER = 'bolder'; const CONFIG_CONSTANTS = { on: ON, yes: ON, off: OFF, no: OFF, }; // Settings cb.settings_choices = [ { name: 'color', type: 'str', minLength: 6, maxLength: 6, label: 'Enter the six digit hex color code you would like to use for the background highlight.', required: true, defaultValue: 'C287C2' }, { name: 'highestTipWinner', type: 'choice', choice1: ON, choice2: OFF, label: 'Do you want to award a prize to the highest single tip?', required: true, defaultValue: ON }, {name: 'highestTipMin', type: 'int', minValue: 1, maxValue: 1000000, label: 'Enter the minimum tip to become the highest tip leader:', required: true, defaultValue: 100 }, {name: 'highestTipPrize', type: 'str', minLength: 1, maxLength: 1000, label: 'Enter the prize for this highest single tip.', required: true, defaultValue: 'ManyVids video of your choice' }, {name: 'highestTipTimer', type: 'int', minValue: 1, maxValue: 60, label: 'Number of minutes between announcing the highest single tip:', required: true, defaultValue: 4 }, {name: 'kingMin', type: 'int', minValue: 1, maxValue: 1000000, label: 'Enter the minimum tip level to become Tip King:', required: true, defaultValue: 100 }, {name: 'kingTimer', type: 'int', minValue: 1, maxValue: 60, label: 'Number of minutes between announcing the Tip King:', required: true, defaultValue: 5 }, {name: 'leaderTimer', type: 'int', minValue: 1, maxValue: 60, label: 'Number of minutes between announcing the LeaderBoard:', required: true, defaultValue: 5 }, {name: 'enterMessage', type: 'str', minLength: 1, maxLength: 1000, label: 'Message to send when someone enters your room:', required: true, defaultValue: 'Welcome to my room!' }, {name: 'spamMessage', type: 'str', minLength: 1, maxLength: 1000, label: 'Notice to send to room periodically', required: true, defaultValue: 'Be nice!' }, {name: 'spamTimer', type: 'int', minValue: 1, maxValue: 60, label: 'Number of minutes between sending the above Notice:', required: true, defaultValue: 5 }, {name: 'tipMessage', type: 'str', minLength: 1, maxLength: 1000, label: 'Message to display on good tips:', required: true, defaultValue: 'Thank you!' }, {name: 'tipMessageMin', type: 'int', minValue: 1, maxValue: 1000000, label: 'Minimum tip amount to trigger the message', required: true, defaultValue: 10 }, {name: 'tipGod', type: 'str', minLength: 1, maxLength: 1000, label: 'Who is the current Tip God?', required: true, defaultValue: '' }, {name: 'tgTks', type: 'int', minValue: 1, maxValue: 1000000, label: 'How much did they tip in one show?', required: true, defaultValue: 10 }, {name: 'tipMaster', type: 'str', minLength: 1, maxLength: 1000, label: 'Who is the current monthly Tip Master?', required: false, defaultValue: '' }, {name: 'tmTks', type: 'int', minValue: 1, maxValue: 1000000, label: 'How much did they tip in one show? (If reset for new month, enter minimum amount to become Tip Master)', required: true, defaultValue: 1000 }, {name: 'showTime', type: 'str', minLength: 1, maxLength: 1000, label: 'When is the Appreciation Show?', required: true, defaultValue: 'Saturday 9/17 at 11:00 PM Central' }, {name: 'showPass', type: 'str', minLength: 1, maxLength: 1000, label: 'What is the password?', required: true, defaultValue: 'I love lamp' }, {name: 'tipMenu', type: 'choice', choice1: ON, choice2: OFF, label: 'Would you like to use a tip menu?', required: true, defaultValue: ON }, {name: 'tipMenuSet', type: 'choice', choice1: 'STANDARD', choice2: 'ALTERNATE', label: 'Which Tip Menu? STANDARD or ALTERNATE', required: true, defaultValue: ON }, {name: 'timMenuTimer', type: 'int', minValue: 1, maxValue: 60, label: 'How often to notify room (in minutes)?', required: true, defaultValue: 5 }, {name: 'tipMenu1', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 1 (each menu item should have a unique tip amount)', required: true, defaultValue: '' }, {name: 'menuItem1', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 1', required: false, defaultValue: '' }, {name: 'tipMenu2', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 2', required: true, defaultValue: '' }, {name: 'menuItem2', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 2', required: false, defaultValue: '' }, {name: 'tipMenu3', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 3', required: true, defaultValue: '' }, {name: 'menuItem3', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 3', required: false, defaultValue: '' }, {name: 'tipMenu4', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 4', required: true, defaultValue: '' }, {name: 'menuItem4', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 4', required: false, defaultValue: '' }, {name: 'tipMenu5', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 5', required: true, defaultValue: '' }, {name: 'menuItem5', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 5', required: false, defaultValue: '' }, {name: 'tipMenu6', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 6', required: true, defaultValue: '' }, {name: 'menuItem6', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 6', required: false, defaultValue: '' }, {name: 'tipMenu7', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 7', required: true, defaultValue: '' }, {name: 'menuItem7', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 7', required: false, defaultValue: '' }, {name: 'tipMenu8', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 8', required: true, defaultValue: '' }, {name: 'menuItem8', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 8', required: false, defaultValue: '' }, {name: 'tipMenu9', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 9', required: true, defaultValue: '' }, {name: 'menuItem9', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 9', required: false, defaultValue: '' }, {name: 'tipMenu10', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 10', required: true, defaultValue: '' }, {name: 'menuItem10', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 10', required: false, defaultValue: '' }, {name: 'tipMenu11', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 11', required: true, defaultValue: '' }, {name: 'menuItem11', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 11', required: false, defaultValue: '' }, {name: 'tipMenu12', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 12', required: true, defaultValue: '' }, {name: 'menuItem12', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 12', required: false, defaultValue: '' }, {name: 'tipMenu13', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 13', required: true, defaultValue: '' }, {name: 'menuItem13', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 13', required: false, defaultValue: '' }, {name: 'tipMenu14', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 14', required: true, defaultValue: '' }, {name: 'menuItem14', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 14', required: false, defaultValue: '' }, {name: 'tipMenu15', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 15', required: true, defaultValue: '' }, {name: 'menuItem15', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 15', required: false, defaultValue: '' }, {name: 'tipMenu16', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 16', required: true, defaultValue: '' }, {name: 'menuItem16', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 16', required: false, defaultValue: '' }, {name: 'tipMenu17', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 17', required: true, defaultValue: '' }, {name: 'menuItem17', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 17', required: false, defaultValue: '' }, {name: 'tipMenu18', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 18', required: true, defaultValue: '' }, {name: 'menuItem18', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 18', required: false, defaultValue: '' }, {name: 'tipMenu19', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 19', required: true, defaultValue: '' }, {name: 'menuItem19', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 19', required: false, defaultValue: '' }, {name: 'tipMenu20', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for STANDARD menu item 20', required: true, defaultValue: '' }, {name: 'menuItem20', type: 'str', minLength: 1, maxLength: 50, label: 'Enter STANDARD menu item 20', required: false, defaultValue: '' }, {name: 'tipMenuAlt1', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 1 (each menu item should have a unique tip amount)', required: true, defaultValue: '' }, {name: 'menuAltItem1', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 1', required: false, defaultValue: '' }, {name: 'tipMenuAlt2', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 2', required: true, defaultValue: '' }, {name: 'menuAltItem2', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 2', required: false, defaultValue: '' }, {name: 'tipMenuAlt3', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 3', required: true, defaultValue: '' }, {name: 'menuAltItem3', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 3', required: false, defaultValue: '' }, {name: 'tipMenuAlt4', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 4', required: true, defaultValue: '' }, {name: 'menuAltItem4', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 4', required: false, defaultValue: '' }, {name: 'tipMenuAlt5', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 5', required: true, defaultValue: '' }, {name: 'menuAltItem5', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 5', required: false, defaultValue: '' }, {name: 'tipMenuAlt6', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 6', required: true, defaultValue: '' }, {name: 'menuAltItem6', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 6', required: false, defaultValue: '' }, {name: 'tipMenuAlt7', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 7', required: true, defaultValue: '' }, {name: 'menuAltItem7', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 7', required: false, defaultValue: '' }, {name: 'tipMenuAlt8', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 8', required: true, defaultValue: '' }, {name: 'menuAltItem8', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 8', required: false, defaultValue: '' }, {name: 'tipMenuAlt9', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 9', required: true, defaultValue: '' }, {name: 'menuAltItem9', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 9', required: false, defaultValue: '' }, {name: 'tipMenuAlt10', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 10', required: true, defaultValue: '' }, {name: 'menuAltItem10', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 10', required: false, defaultValue: '' }, {name: 'tipMenuAlt11', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 11', required: true, defaultValue: '' }, {name: 'menuAltItem11', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 11', required: false, defaultValue: '' }, {name: 'tipMenuAlt12', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 12', required: true, defaultValue: '' }, {name: 'menuAltItem12', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 12', required: false, defaultValue: '' }, {name: 'tipMenuAlt13', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 13', required: true, defaultValue: '' }, {name: 'menuAltItem13', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 13', required: false, defaultValue: '' }, {name: 'tipMenuAlt14', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 14', required: true, defaultValue: '' }, {name: 'menuAltItem14', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 14', required: false, defaultValue: '' }, {name: 'tipMenuAlt15', type: 'int', minValue: 1, maxValue: 100000, label: 'Enter the tip amount for ALTERNATE menu item 15', required: true, defaultValue: '' }, {name: 'menuAltItem15', type: 'str', minLength: 1, maxLength: 50, label: 'Enter ALTERNATE menu item 15', required: false, defaultValue: '' }, ]; // 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); }); }; // UB functions const tipperArrayPopulate = function(user) { tipperArray[numTippers] = new Array; tipperArray[numTippers][0] = user; tipperArray[numTippers][1] = 0; numTippers++; }; const findTipper = function(user) { //find the index of the user for(var i = 0; i < tipperArray.length; i++) { if(tipperArray[i][0] == user) { break; } } //the user is not in the array. add him and call findTipper if(i == tipperArray.length) { tipperArrayPopulate(user); findTipper(user); } return i; } const setTipTitles = function(user, message) { if(user == currentKing) { var mes = ':smallCrown |' + tipperArray[findTipper(user)][1] + '| ' + message; } else { var mes = '|' + tipperArray[findTipper(user)][1] + '| ' + message; } return mes; } const kingSpam = function() { cb.setTimeout(kingSpamTimer,cb.settings.kingTimer*60000); } const kingSpamTimer = function() { if(kingTip < parseInt(cb.settings.kingMin)) { var supplant = cb.settings.kingMin; } else { var supplant = kingTip + 1; } cb.sendNotice('Tip ' + supplant + ' to become the new King!','',purple); kingSpam(); } const leaderSpam = function() { cb.setTimeout(leaderSpamTimer,cb.settings.leaderTimer*60000); } const leaderSpamTimer = function() { cb.sendNotice('Leaderboard!','',purple); cb.sendNotice ( leaderArray[0][0] + ' : ' + leaderArray[0][1] + '\n' + leaderArray[1][0] + ' : ' + leaderArray[1][1] + '\n' + leaderArray[2][0] + ' : ' + leaderArray[2][1] ); cb.sendNotice ( '' ,'',purple); leaderSpam(); } const showLeaderBoard = function(from) { cb.sendNotice ( 'Leaderboard!' ,from,purple ); cb.sendNotice ( leaderArray[0][0] + ' : ' + leaderArray[0][1] + '\n' + leaderArray[1][0] + ' : ' + leaderArray[1][1] + '\n' + leaderArray[2][0] + ' : ' + leaderArray[2][1] ,from); cb.sendNotice ( '' ,from,purple); } const tipGodSpam = function() { cb.setTimeout(tipGodSpamTimer,5*60000); } const tipGodSpamTimer =function() { cb.sendNotice(tipGod + ' (' + tipGodtks + ') is the TIP GOD!\nTip ' + (tipGodtks + 1) + ' tks in one show to become the new TIP GOD.','',tipGodBackColor,tipGodTextColor); tipGodSpam(); } const tipMasterSpam = function() { cb.setTimeout(tipMasterSpamTimer,5*60000); } const tipMasterSpamTimer =function() { if (tipMaster.length > 1) { cb.sendNotice(tipMaster + ' (' + tipMastertks + ') is the monthly TIP MASTER!\nTip ' + (tipMastertks + 1) + ' tks in one show to become the new TIP MASTER.','',tipMasterBackColor,tipMasterTextColor); } else { cb.sendNotice('No one is the monthly TIP MASTER yet!\nTip ' + (tipMastertks + 1) + ' tks in one show to become the new TIP MASTER.','',tipMasterBackColor,tipMasterTextColor); } tipMasterSpam(); } const notifierSpam = function() { cb.setTimeout(notifierSpamTimer,cb.settings.spamTimer*60000); } const notifierSpamTimer =function() { cb.sendNotice(notifierMessage,'',purple); notifierSpam(); } const colorChecker = function() { var colorString = '0123456789abcdefABCDEF'; var valid = true; var color = "C287C2"; if(cb.settings.color != null) { color = cb.settings.color; } for(var i = 0; i < 6; i++) { if(colorString.indexOf(color.charAt(i)) == -1) { valid = false; } if(valid == false) { purple = '#C287C2'; cb.sendNotice(color + ' is not a valid option for the highlight color. Hex color codes use numbers and letters only.\nUse www.color-hex.com to find the code for the color you want.\nDo not include the #.\nReverting to default color.',cb.room_slug,purple); break; } } if(valid) { purple = '#' + cb.settings.color; } } // Because we're serious people. // Honest. const EMOTICES = { exact: { 100: ':tckyTitShake1', 500: ':tckyTitBounce1', 505: ':tckyButtPlug1', 1111: ':tckyToyShow1', }, tiers: { /* 100: ':zk-kat-inabox', 250: [':svk-hoop', ':svk-hoop2'], 500: ':zk-kat-inabox2', 750: ':zk-kat-boobplay', 1000: ':sexievonkatomganya', 1500: [':svk-tomride', ':zk-kat-dildoride', ':zk-kat-dildoride2', ':zk-kat-cum'], */ } }; // VERY. SERIOUS. PEOPLE. const ENTRY_MSGS = { atthem: 'When atthem joins the room, panties drop! :tckyPantiesOff1', slipperywenwhet: ':slippery', innout99: ':tckyBlowJob1', nickhischar1234: ':nickDeadpool', }; // Ordering matters, okay? onTip(function(tip) { tipperArray[findTipper(tip.from_user)][1] += parseInt(tip.amount); // Tip God if(tip.from_user != tipGod && parseInt(tipperArray[findTipper(tip.from_user)][1]) > tipGodtks) { tipGod = tip.from_user; tipGodtks = parseInt(tipperArray[findTipper(tip.from_user)][1]) var tgNote = '----------------------------------------------------------------------'; tgNote += '\n' + '-------------------------NEW TIP GOD!!!-------------------------'; tgNote += '\n' + tipGod + ' (' + tipGodtks + ') is the new TIP GOD! All hail ' + tipGod + '!!!'; tgNote += '\n' + '----------------------------------------------------------------------'; cb.sendNotice(tgNote,'',tipGodBackColor,tipGodTextColor); } else if (tip.from_user == tipGod && parseInt(tipperArray[findTipper(tip.from_user)][1]) > tipGodtks) { tipGodtks = parseInt(tipperArray[findTipper(tip.from_user)][1]); } // Tip Master if(tip.from_user != tipMaster && parseInt(tipperArray[findTipper(tip.from_user)][1]) > tipMastertks) { tipMaster = tip.from_user; tipMastertks = parseInt(tipperArray[findTipper(tip.from_user)][1]) var tgNote = '-------------------------------------------------------------------------'; tgNote += '\n' + '-------------------------NEW TIP MASTER!!!-----------------------'; tgNote += '\n' + tipMaster + ' (' + tipMastertks + ') is the new TIP MASTER! All bow to ' + tipMaster + '!!!'; tgNote += '\n' + '-------------------------------------------------------------------------'; cb.sendNotice(tgNote,'',tipMasterBackColor,tipMasterTextColor); } else if (tip.from_user == tipMaster && parseInt(tipperArray[findTipper(tip.from_user)][1]) > tipMastertks) { tipMastertks = parseInt(tipperArray[findTipper(tip.from_user)][1]); } if(tip.from_user != currentKing && parseInt(tipperArray[findTipper(tip.from_user)][1]) > kingTip && parseInt(tipperArray[findTipper(tip.from_user)][1]) >= parseInt(cb.settings.kingMin)) { if(currentKing != '') { cb.sendNotice('You have been dethroned by ' + tip.from_user + ', but revenge is sweet...', currentKing, purple); } cb.sendNotice('We have a new King!\nAll hail ' + tip.from_user + '!','',purple); currentKing = tip.from_user; kingTip = parseInt(tipperArray[findTipper(tip.from_user)][1]); } else if(tip.from_user == currentKing) { kingTip = parseInt(tipperArray[findTipper(tip.from_user)][1]); } //create an array of the names var nameArray = new Array; for(var i = 0; i < leaderArray.length; i++) { nameArray[i] = leaderArray[i][0]; } //the user is not currently in the top 3 if(!cbjs.arrayContains(nameArray,tip.from_user)) { if(tipperArray[findTipper(tip.from_user)][1] > leaderArray[0][1]) { leaderArray[2][0] = leaderArray[1][0]; leaderArray[2][1] = leaderArray[1][1]; leaderArray[1][0] = leaderArray[0][0]; leaderArray[1][1] = leaderArray[0][1]; leaderArray[0][0] = tip.from_user; leaderArray[0][1] = tipperArray[findTipper(tip.from_user)][1]; } else if(tipperArray[findTipper(tip.from_user)][1] < leaderArray[0][1] && tipperArray[findTipper(tip.from_user)][1] > leaderArray[1][1] || tipperArray[findTipper(tip.from_user)][1] == leaderArray[0][1]) { leaderArray[2][0] = leaderArray[1][0]; leaderArray[2][1] = leaderArray[1][1]; leaderArray[1][0] = tip.from_user; leaderArray[1][1] = tipperArray[findTipper(tip.from_user)][1]; } else if(tipperArray[findTipper(tip.from_user)][1] < leaderArray[1][1] && tipperArray[findTipper(tip.from_user)][1] > leaderArray[2][1] || tipperArray[findTipper(tip.from_user)][1] == leaderArray[1][1]) { leaderArray[2][0] = tip.from_user; leaderArray[2][1] = tipperArray[findTipper(tip.from_user)][1]; } } //the user is currently in the top 3 else { //the user is already #1 if(leaderArray[0][0] == tip.from_user) { leaderArray[0][1] = tipperArray[findTipper(tip.from_user)][1]; } //the user is #2 and is moving to #1 if(leaderArray[1][0] == tip.from_user && tipperArray[findTipper(tip.from_user)][1] > parseInt(leaderArray[0][1])) { leaderArray[1][0] = leaderArray[0][0]; leaderArray[1][1] = leaderArray[0][1]; leaderArray[0][0] = tip.from_user; leaderArray[0][1] = parseInt(tipperArray[findTipper(tip.from_user)][1]); } //the user is #2 and is not moving to #1 else if(leaderArray[1][0] == tip.from_user && tipperArray[findTipper(tip.from_user)][1] <= parseInt(leaderArray[0][1])) { leaderArray[1][1] = parseInt(tipperArray[findTipper(tip.from_user)][1]); } //the user is #3 and is moving to #2 else if(leaderArray[2][0] == tip.from_user && tipperArray[findTipper(tip.from_user)][1] > parseInt(leaderArray[1][1])) { leaderArray[2][0] = leaderArray[1][0]; leaderArray[2][1] = leaderArray[1][1]; leaderArray[1][0] = tip.from_user; leaderArray[1][1] = parseInt(tipperArray[findTipper(tip.from_user)][1]); } //the user is #3 and is moving to #1 else if(leaderArray[2][0] == tip.from_user && tipperArray[findTipper(tip.from_user)][1] > parseInt(leaderArray[0][1])) { leaderArray[2][0] = leaderArray[1][0]; leaderArray[2][1] = leaderArray[1][1]; leaderArray[1][0] = leaderArray[0][0]; leaderArray[1][1] = leaderArray[0][1]; leaderArray[0][0] = tip.from_user; leaderArray[0][1] = parseInt(tipperArray[findTipper(tip.from_user)][1]); } //the user is #3 and is not moving else if(leaderArray[2][0] == tip.from_user && tipperArray[findTipper(tip.from_user)][1] <= parseInt(leaderArray[1][1])) { leaderArray[2][1] = tipperArray[findTipper(tip.from_user)][1]; } if(leaderArray[2][0] == leaderArray[1][0] || leaderArray[2][0] == leaderArray[0][0]) { leaderArray[2][0] = ''; leaderArray[2][1] = 0; } if(leaderArray[1][0] == leaderArray[0][0]) { leaderArray[1][0] = ''; leaderArray[1][1] = 0; } } if(parseInt(tip.amount) >= cb.settings.tipMessageMin) { cb.sendNotice(cb.settings.tipMessage,'',purple); } if(cb.settings.tipMenu == ON) { for (i = 0; i < tipItemArray.length; i++) { if ((tip.amount == tipCostArray[i]) && (tipItemArray[i] != '')) { var note = tip.from_user + ' tipped for ' + tipItemArray[i]; cb.sendNotice(note, '',tipMenuActiveBackColor,tipMenuActiveTextColor); } } } var tokens = tip.amount; var source = tip.from_user; // Future use /* if (source == 'afatani85') { cb.setTimeout(function() { var tani = ''; if (tokens >= 500) tani = "All hail Tip Lord Tani!"; if (tokens >= 1000) tani = "Tip Lord Tani reigns supreme!"; if (tokens >= 1500) tani = "Bow before the Eternal Tip Lord!"; if (tokens >= 2000) tani = "GOD TIER UNLOCKED: AFATANI85"; if (tani) sendTaniMessage("-".repeat(tani.length) + "\n" + tani + "\n" + "-".repeat(tani.length) ); }, 2000); } else if (source == 'yeah009') { cb.setTimeout(function() { var yeah = ''; if (tokens >= 500) yeah = "All hail Tip Lord Yeah!"; if (tokens >= 1000) yeah = "Tip Lord Yeah reigns supreme!"; if (tokens >= 1500) yeah = "Bow before the Eternal Tip Lord!"; if (tokens >= 2000) yeah = "GOD TIER UNLOCKED: YEAH009"; if (yeah) sendTaniMessage("-".repeat(yeah.length) + "\n" + yeah + "\n" + "-".repeat(yeah.length) ); }, 2000); } */ if (EMOTICES.exact[tokens]) { var emotice = EMOTICES.exact[tokens]; if (Array.isArray(emotice)) sendEmotice(random(emotice)); else sendEmotice(emotice); } else { var emotice = ''; for (var minimum in EMOTICES.tiers) { if (tokens >= minimum) emotice = EMOTICES.tiers[minimum]; } if (emotice) { if (Array.isArray(emotice)) sendEmotice(random(emotice)); else sendEmotice(emotice); } } // highest single tip if (tokens > highestTip) { highestTip = tokens; highestTipper = source; if (tokens >= cb.settings.highestTipMin) cb.sendNotice(':nicetip9 ' + highestTipper + ' tipped ' + highestTip + ', the biggest tip so far tonight!!!','',highestSingleTipBackColor,highestSingleTipTextColor); } }); onEnter(function(user) { cb.setTimeout(function() { if (ENTRY_MSGS[user.user]) { var value = ENTRY_MSGS[user.user]; if (typeof value == 'string') { cb.setTimeout(function() { sendTrollMessage(value); }, 2000); } else if (typeof value == 'object') { if (Array.isArray(value)) { cb.setTimeout(function() { sendTrollMessage(random(value)); }, 2000); } else { var check = (value.cond || function() { return true; })(user); if (check) { var delay = value.time || 2000; var codes = value.code; var func = value.func || sendTrollMessage; if (typeof codes == 'function') codes = codes(); codes = flatten([codes]); cb.setTimeout(function() { sendTrollMessage(random(codes)); }, delay); } } } } cb.sendNotice(cb.settings.enterMessage,user.user,purple); }, 1000); // room rules cb.setTimeout(function() { cb.sendNotice(roomRules,user.user,rulesBackColor,rulesTextColor,'bold'); }, 1500); // Tip God if (user.user == tipGod) { cb.setTimeout(function() { cb.sendNotice('TIP GOD ' + tipGod + ' (' + tipGodtks + ') is in the house! All hail ' + tipGod + '!!!','',tipGodBackColor,tipGodTextColor,'bold'); }, 2000); } // Tip Master if (user.user == tipMaster) { cb.setTimeout(function() { cb.sendNotice('TIP MASTER ' + tipMaster + ' (' + tipMastertks + ') has entered the room! All bow to ' + tipMaster + '!!!','',tipMasterBackColor,tipMasterTextColor,'bold'); }, 2000); } // Appreciation Show if (user.is_mod || user.in_fanclub) { cb.setTimeout(function() { var showNote = '----------------------------------------------------------------------'; showNote += '\n' + '-----------------------Appreciation Show!!!-----------------------'; showNote += '\n' + 'This month, the show is scheduled for ' + cb.settings.showTime + ' - Don\'t miss it!!!'; showNote += '\n' + 'The password is: ' + cb.settings.showPass; showNote += '\n' + '----------------------------------------------------------------------'; cb.sendNotice(showNote,user.user,apprecShowBackColor,apprecShowTextColor,'bold'); }, 2500); } // greys can't chat for 10 minutes if (isGrey(user)) { // add user to gray list arrGraysIn.push(user.user); // wait 10 minutes to add user to array cb.setTimeout(function() { // add user to array if still in room var grayIndex = arrGraysIn.indexOf(user.user); if (grayIndex > -1) { arrGraysChat.push(user.user); cb.sendNotice("You may chat now! Please respect my rules to make sure you can keep chatting.",user.user,tipMenuActiveBackColor,tipMenuActiveTextColor,'bold'); cb.sendNotice(roomRules,user.user,rulesBackColor,rulesTextColor,'bold'); } }, 60000); } }); onLeave(function(user) { // remove from array if there var grayIndex = arrGraysIn.indexOf(user.user); if (grayIndex > -1) { arrGraysIn.splice(grayIndex,1); } }); // Spam filter const DUNGEON = { names: [ /jeremypadilla\d*/, // Doesn't know when to shut up, uses alt accounts to talk even when silenced /sexxymom\d*/, // known spammer '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 'goodblkcock', // sent PM saying model was being cheated on (she wasn't) ], words: [ /^\d+\s+[mf]\s+selling/i, // '1 f selling' '3 m selling' etc. /^:(pastcandy|assfuck)\d*$/i, // gifs starting with those words /\bf.?a.?r.?t/i, // fart /\bk.?i.?k.?c.?a.?m.?s\s*\.\s*com/i, // kikcams.com /\bp.?o.?r.?n.?m.?e.?d.?s/i, // pornmeds /\be.?l.?l.?a.?g.?o\s*cam/i, // ellago cam /\bi\s*.?m\s*(n.?e.?w|o.?n)\b/i, // "i m (on/new)" "i am (on/new)" "i'm (on/new)" /\bbb\b/i, // bb /\b(pee|poo)\b/i, // pee or poo /\bf\s?a\s?t\b/i, //fat /\bu.?g.?l.?y/i, //ugly /\bl.?e.?a.?k.?e.?d/i, //leaked /\bs.?k.?y.?p.?e/i, //skype /\b(c.?h.?e.?c.?k|j.?o.?i.?n|w.?a.?t.?c.?h|s.?e.?e|o.?p.?e.?n|t.?a.?p|g.?o.? .?t.?o|l.?o.?o.?k.? .?a.?t|c.?l.?i.?c.?k|v.?i.?e.?w)\b(\s|\w)?(o.?n)?(\s|\w)?\b(m.?e|m.?y)\b/i, // (check/join/watch/see/open/tap/go to/look at/click/view) [on] (me/my) /\bs.?n.?a.?p.?m.?i.?l.?f.?s/i, // snapmilfs /\bp.?r.?o.?f.?i.?l.?e/i, // profile /\bK.?I.?K.?/i, // K.I.K /\bf.?r.?e.?e t.?o.?k.?e.?n.?s/i, // free tokens /([^A-Z]|[^a-z]|\b)dot([^A-Z]|[^a-z]|\b)/i, // dot (for advertizing websites) /\be.?r.?o.?t.?i.?m.?o/i, // Erotimo (spammed site) /\bw.?e.?b.?c.?a.?m/i, // webcam (common spammer word) /\bp.?a.?t.?c.?h.?e.?d/i, //patched /\.\s?com/i, // ". com" /\bm.?y.? .?(b.?i.?o|p.?a.?g.?e)/i, //my (bio/page) /\.\s?c0m/i, // ". c0m" /\.\s?net/i, // ". net" /bigger in Texas/i, // "bigger in texas" /(small|tiny|little|short|baby|mini|puny) (cock|dick|penis|pecker|prick)/i, // dick insults /\br.?e.?c.?o.?r.?d.?(e.?d|i.?n.?g)/i, //record (ed/ing) /\bc.?2.?c/i, //c2c /\bc.?a.?m.?2.?c.?a.?m/i, //cam2cam /\bc.?a.?m.*o.?p.?e.?n/i, //cam [anything] open /\bv.?i.?s.?i.?t.*m.?e/i, // visit [anything] me /\bo.?p.?e.?n.*(m.?e|m.?y)/i, // open [anything] me/my /\bt.?c.?k.?y.?3.?1/i, // tcky31 /\bm.?y.? c.?a.?m/i, // my cam /^(B.?o.?|G.?u.?)y.?s.?(!|\?|.)?/i, // Boys, Guys /\b.?m.?a.?y.?b.?e.*:/i, // Maybe : /\bt.?u.?r.?d/i, //turd ], cause: [ '1 f selling', 'pastcandy or assfuck gifs', 'fart', 'kikcams.com', 'pornmeds', 'ellago cam', 'i am new or i am on', 'bb', 'pee or poo', 'fat', 'ugly', 'leaked', 'skype', 'check/join/watch/see/open/tap/go to/look at/click/view) [on] (me/my)', 'snapmilfs', 'profile', 'K.I.K', 'free tokens', 'dot', 'erotimo', 'webcam', 'patched', '.com', 'my bio/page', '.c0m', '.net', 'bigger in texas', '(small|tiny|little|short|baby|mini|puny) (cock|dick|penis|pecker|prick)', 'recorded or recording', 'c2c', 'cam2cam', 'cam open', 'visit me', 'open [anything] me/my', 'tcky31', 'my cam', 'Boys!, Boys?, Guys!, Guys?', 'Maybe :', 'Turd', ], }; const CAMGIRLS = { names: [ ], words: [ /\b(my|i|me)\b(\s|\w)*(cam|room|show|model|broadcast)\b/i // (my/i/me) (cam/room/show/model/broadcast) ], }; const GREYS = []; registerCommands({ blockname: { regex: /^\/blockname\s+(.+)$/i, code: function(msg, match, silent, query, help, param, args) { var pattern = param; if (param.startsWith('/') && param.endsWith('/')) { pattern = new RegExp(param.substr(0, param.length - 1), 'i'); } DUNGEON.names.push(pattern); debug(pattern + ' {#dungeon.added.names} ' + msg.user); }, check: isMaster, hidden: true, }, blocktext: { regex: /^\/blocktext\s+(.+)$/i, code: function(msg, match, silent, query, help, param, args) { var pattern = param; if (param.startsWith('/') && param.endsWith('/')) { pattern = new RegExp(param.substr(0, param.length - 1), 'i'); } DUNGEON.words.push(pattern); debug(pattern + ' {#dungeon.added.words} ' + msg.user); }, check: isMaster, hidden: true, }, blockany: { regex: /^\/blockany\s+(.+)$/i, code: function(msg, match, silent, query, help, param, args) { var pattern = param; if (param.startsWith('/') && param.endsWith('/')) { pattern = new RegExp(param.substr(0, param.length - 1), 'i'); } DUNGEON.joint.push(pattern); debug(pattern + ' {#dungeon.added.joint} ' + msg.user); }, check: isMaster, hidden: true, }, }); onMessage(function(msg) { // check for non-ansi characters (spammers use unicode to bypass spam filters) var ansiMsg = msg.m.replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\'\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, '') if (ansiMsg.length != msg.m.length && !isMaster(msg)) { spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' used non-ansi characters. Their msg is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } if (!isAdmin(msg)) { for (var index in DUNGEON.names) { var test = DUNGEON.names[index]; if (msg.user.matches(test)) { sendErrorMessage("{#dungeon.notalk}", msg.user); spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' is on the banned list. Their msg is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } } for (var index in DUNGEON.words) { var test = DUNGEON.words[index]; if (msg.m.matches(test)) { sendErrorMessage("{#dungeon.badword}", msg.user); spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' said a banned word or phrase (' + DUNGEON.cause[index] + '). Their msg is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } } } if (isGrey(msg)) { for (var index in GREYS) { var test = GREYS[index]; if (msg.m.match(test)) { sendErrorMessage("{#dungeon.grey}", msg.user); return spam(msg); } } // no chat for 10 minutes if gray (if not on gray list), unless on white list if ((arrGraysChat.indexOf(msg.user) < 0) && (arrWhiteList.indexOf(msg.user) < 0 )) { sendErrorMessage("Sorry, grays cannot chat until they have been in the room for at least 10 minutes.", msg.user); spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' is gray (not on White List) and tried to chat too soon. Their message is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } // no emoticons for grays (spam block) unless on white list if (msg.m.search(":") > -1 ) { // if not on list, block as spam if (arrWhiteList.indexOf(msg.user) < 0) { sendErrorMessage("{#dungeon.badword}", msg.user); spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' is gray (not on White List) and tried to use emoticons. Their message is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } } } if (isFemale(msg) && !isModel(msg)) { for (var index in CAMGIRLS.names) { var test = CAMGIRLS.names[index]; if (msg.user.matches(test)) { sendErrorMessage("{#camgirls.notalk}", msg.user); spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' is a known model. Her msg is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } } for (var index in CAMGIRLS.words) { var test = CAMGIRLS.words[index]; if (msg.m.matches(test)) { sendErrorMessage("{#camgirls.badword}", msg.user); spamCount += 1; //notify model var modelNote = 'Spam Count: ' + spamCount + '. ' + msg.user + ' might have been promoting her room. Her msg is below:'; modelNote += "\n" + msg.m; cb.sendNotice(modelNote,cb.room_slug,spamNoticeBackColor,spamNoticeTextColor); //notify master cb.sendNotice(modelNote,roomMaster,spamNoticeBackColor,spamNoticeTextColor); return spam(msg); } } } }); // WE ARE SO SERIOUS YOU DON'T EVEN KNOW. const NAME_TAGS = { atthem: [ 'awesome bf', 'code monkey', ], tcky31: [ 'amazing gf', 'sugar sack', ], trny71: [ 'sexy mod', 'super mod', 'super sexy mod', ], masterbetaa: [ 'sexy mod', 'super mod', 'super sexy mod', 'big dick daddy from cincinnati', //mod ], aramis827: [ 'PAIGE LOVER', // fan club ], radium1: [ 'Sexiest photographer', // fan club ], /* wol5fang: [ 'Cookie the kind barbarian', //fan club ], */ durdlestein: [ 'Real Deal Sex Appeal', // mod 'Macho King', // fan club ], /* devi0nious: [ 'DEVO', // fan club ], */ triviium: [ 'Hugh Jackman', // fan club ], nickhischar1235: [ 'Deadpool', // fan club ], }; 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()); } // if they are a mod and don't have personal name tags else if (isMod(msg)) { var tag = ['sexy mod','super mod','super sexy mod']; var chosen = ''; chosen = random(tag); 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()); } if (parseInt(tipperArray[findTipper(msg.user)][1]) > 0) { msg.m = setTipTitles(msg.user,msg.m); } return msg; }); registerCommands({ tags: { regex: /^\/tags/i, code: function(msg, match, silent, query, help, param, args) { if (help) sendHelp("help.tags", msg.user); else if (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, silent, query, help, param, args) { if (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, silent, query, help, param, args) { if (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, }, }); // SERIOUS. PEOPLE. const NAME_COLS = { }; onMessage(function(msg) { if (NAME_COLS[msg.user]) msg.background = NAME_COLS[msg.user]; }); // SO. SERIOUS. 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!', lenny: ':lefaceface', hug: ':ponyhugplz', mod: ':jappleclub', squirt: 'Paige CANNOT squirt.', milk: 'Paige doesn\'t have milk. :nobaby', 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!', forever: ':goneforever', model: cb.room_slug, no: 'oh noooooooo', }; onMessage(function(msg) { for (var macro in MACROS) msg.m = msg.m.split('$' + macro.toLowerCase()).join(MACROS[macro]); }); // Named timers const TIMERS = {}; const makeTimer = function(name, init, silentp) { if (!isNaN(parseInt(init, 10))) init = parseInt(init, 10); 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 <= 5) 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, silent, query, help, param, args) { if (help) { sendHelp('help.timer', msg.user); return; } else if (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 (silent) { 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); debug(msg.user + ' started timer "' + name + '" with ' + getNiceTime(seconds)); } }, check: isAdmin, }, trename: { regex: /^\/trename/i, code: function(msg, match, silent, query, help, param, args) { if (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: isAdmin, }, }); // Commands registerCommands({ 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: isAdmin, hidden: true, }, leaderboard: { regex: /^\/leaderboard/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.leaderboard', msg.user); return; } showLeaderBoard(msg.user); }, check: isAdmin, hidden: true, }, notifiermessage: { regex: /^\/notifiermessage/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.notifiermessage', msg.user); return; } if(message[1] == '' || message[1] == null) { cb.sendNotice('You must enter a new message for the notifier feature. If you want to disable the notifications, enter /notifierspam off.',msg.user,purple) } else { notifierMessage = msg.m.substring(16).trim(); cb.sendNotice('You have set the notifier spam message to: ' + notifierMessage,msg.user,purple); } }, check: isAdmin, hidden: true, }, tipmenu: { regex: /^\/tipmenu/i, code: function(msg, match, silent, query, help, param, args) { if (help) { sendHelp('help.tipmenu', msg.user); return; } tipMenuAnnounce(); }, check: isAdmin, hidden: true, }, rules: { regex: /^\/rules/i, code: function(msg, match, silent, query, help, param, args) { cb.sendNotice(roomRules,'',rulesBackColor,rulesTextColor,'bold'); }, check: isAdmin, hidden: true, }, }); // Init cb.setTimeout(function() { setup(); }, 1000);
© Copyright Chaturbate 2011- 2024. All Rights Reserved.