Bots Home
|
Create an App
JaneAIBetaBot
Author:
albertuni
Description
Source Code
Launch Bot
Current Users
Created by:
Albertuni
/* LAST CHANGE DATE: Sunday, April 29, 2018 2:40 PM GMT-07:00 LAST MERGE ID: 43 */ cb.settings_choices = [ { name: 'admins', label: 'Admins', type: 'str', defaultValue: 'universityalexa', required: false }, { name: 'moderators', label: 'Moderators (almost, but not quite admins)', type: 'str', defaultValue: '', required: false }, { name: 'cut_down', label: 'Cut down version', type: 'choice', choice1: 'true', choice2: 'false', required: false }, { name: 'type', label: 'DemandLess monitors these user groups for demands', type: 'choice', choice1: 'Everyone', choice2: 'Everyone who hasn\'t tipped', choice3: 'Greys only', defaultValue: 'Everyone' }, { name: 'monitor_mods', label: 'DemandLess monitors mods for demands', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No' }, { name: 'monitor_fanclub', label: 'DemandLess monitors fanclub members for demands', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'immune', label: 'DemandLess ignores these users when monitoring for demands', type: 'str', defaultValue: '', required: false }, { name: 'action', label: 'Action to be taken upon hearing a demand', type: 'choice', choice1: 'Hide message', choice2: 'Respond to message publicly', defaultValue: 'Respond to message publicly' }, { name: 'questions', label: 'DemandLess responds to questions', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'mentions', label: 'DemandLess responds to mentions', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'delay', label: 'DemandLess replies to users after how many seconds', type: 'int', minValue: 1, maxValue: 10, defaultValue: 2 }, { name: 'timer', label: 'DemandLess comments on lack of tips after how many seconds (0 = off)', type: 'int', minValue: 0, defaultValue: 0 }, { name: 'greeting', label: 'DemandLess\'s message to users upon entering the room', type: 'str', required: false, defaultValue: 'Hey $! I am Jane :) The mods asked me to help with bothersome demands. Liz loves keeping chat PG-13 with dirty talk in tip notes!!' }, { name: 'color', label: 'DemandLess speech color', type: 'str', defaultValue: '#DC0000', required: false }, { name: 'weight', label: 'DemandLess font weight', type: 'choice', choice1: 'bold', choice2: 'normal', defaultValue: 'normal', required: false }, { name: 'hidden_words', label: 'Messages containing any of these words will be silently hidden', type: 'str', defaultValue: '', required: false }, { name: 'hidden_from_broadcaster', label: 'The broadcaster will not see notifications about messages containing these words', type: 'str', defaultValue: '', required: false }, { name: 'notify_broadcaster_about_hidden_words', label: 'Should the broadcaster see any notifications about hidden messages', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'settings_apps_section_label', type: 'choice', label: '################# Apps settings #################', required: false }, { name: 'sepchar', type: 'choice', choice1: 'Vertical Bar', choice2: 'Hearts', choice3: 'Glitter', choice4: 'Flowers', choice5: 'Bow', choice6: 'Hearts2', choice7: 'Smiley', choice8: 'Text Heart', choice9: 'Text Diamond', choice10: 'Text Star', choice11: 'Custom', choice12: 'New line', defaultValue: 'New line', label: 'Separator character' }, /********** ------------ DICE ROLL CONFIG ------------ **********/ { name: 'settings_dice_section_label', type: 'choice', label: '################# Dice settings', required: false }, { name: 'tokens', type: 'int', minValue: 1, label: 'How many tokens per roll?', defaultValue: 99 }, { name: 'multirolls_subsection_label', type: 'choice', label: '######### Multi Rolls #########', required: false }, { name: 'max_rolls_at_once', type: 'int', minValue: 1, label: 'Maximum number of rolls that can be tipped for in a single tip.', defaultValue: 3 }, { name: 'perfect_multi', type: 'choice', label: 'Require exact multiples of tip amount to roll the die? (if you charge 33 per roll, 66 will roll 2x but 69 won\'t)', choice1: 'yes', choice2: 'no', defaultValue: 'yes' }, // Tip menu { name: 'settings_tipmenu_section_label', type: 'choice', label: '################# Tip menu settings', required: false }, { name: 'menutxtcolor1', type: 'str', label: 'Text color (default red...ish #F10060)', required: false, defaultValue: '#F10060' }, { name: 'menubgcolor1', type: 'str', label: 'Background color (default white #FFFFFF)', required: false, defaultValue: '#FFFFFF' }, { name: 'capsToggle', label: 'ROOM CONTROL ................ Convert ALL CAPS to lowercase?', type: 'choice', choice1: 'Yes', choice2: 'No', choice3: 'Only for greys', defaultValue: 'Only for greys', required: false }, { name: 'stickyToggle', label: 'Prevent sticky keyyyyyyyyyyyyys?', type: 'choice', choice1: 'Yes', choice2: 'No', choice3: 'Only for greys', defaultValue: 'Only for greys', required: false }, { name: 'defaultGraphicLevel', label: 'Default Graphic Level (Who can use graphics?)', type: 'choice', choice1: '0 - (Everyone)', choice2: '1 - (Color names only)', choice3: '2 - (Dark blue names and higher)', choice4: '3 - (Users who have tipped)', choice5: '4 - (Only mods and fans)', defaultValue: '1 - (Color names only)', required: false }, { name: 'defaultSilenceLevel', label: 'Default Silence Level (Who can talk in chat?)', type: 'choice', choice1: '0 - (Everyone)', choice2: '1 - (Color names only)', choice3: '2 - (Dark blue names and higher)', choice4: '3 - (Users who have tipped)', choice5: '4 - (Only mods and fans)', defaultValue: '0 - (Everyone)', required: false }, // *** WHISPERS *** // { name: 'whisperToggle', label: 'WHISPERS ............................................... Use whisper feature?', type: 'choice', choice1: 'Yes', choice2: 'No (Skip to section 7)', defaultValue: 'Yes', required: false }, { name: 'roomWhisperLevel', label: 'Default whisper level (Who can send whispers?)', type: 'choice', choice1: '0 - (Everyone)', choice2: '1 - (Color names only)', choice3: '2 - (Dark blue names and higher)', choice4: '3 - (Users who have tipped)', choice5: '4 - (Only mods and fans)', defaultValue: '0 - (Everyone)', required: false }, { name: 'hostWhisperLevel', label: 'Broadcaster whisper level (Who can send YOU whispers?)', type: 'choice', choice1: '0 - (Everyone)', choice2: '1 - (Color names only)', choice3: '2 - (Dark blue names and higher)', choice4: '3 - (Users who have tipped)', choice5: '4 - (Only mods and fans)', defaultValue: '4 - (Only mods and fans)', required: false }, { name: 'kingToggle', label: 'KING ............................................... King toggle?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes', required: false }, { name: 'kingAmount', label: 'How much user should tip to become a king', type: 'int', minValue: 1, maxValue: 10000, defaultValue: 400 } ]; var broadcasterName = 'Ava'; /********** ------------ UTILS ------------ **********/ function notBlank (string) { return string.match(/(\w+)/); } function arrayFromWordList (wordList) { return wordList ? wordList.toLowerCase().trim().split(/\s*,\s*/).filter(notBlank) : []; } // eslint-disable-next-line no-extend-native Array.prototype.random = function () { return this[Math.floor((Math.random() * this.length))]; }; class UserList { constructor () { this.users = {}; } add (user) { this.users[user] = true; } remove (user) { this.users[user] = false; } includes (user) { if (this.users.hasOwnProperty(user)) return this.users[user] === true; return false; } list () { let userList = []; Object.keys(this.users).forEach(function (user) { if (this.users[user] === true) userList.push(user); }, this); return userList; } } function groupBy (xs, key) { return xs.reduce(function (rv, x) { (rv[x[key]] = rv[x[key]] || []).push(x); return rv; }, {}); }; function makeTimer (timer) { if (!timer || timer < 1) { cb.sendNotice('Tip Menu - Time lapse is to short. Using default value.', cb.room_slug, '#FFFFFF', '#FF0000', 'bold'); timer = 3; } timer *= 60000; timer = parseInt(timer); return timer; } // #TODO check do we need this function checkColor (C, D) { if (/^#[0-9A-F]{6}$/i.test(C)) { return C; } else if (/^[0-9A-F]{6}$/i.test(C)) { return ('#' + C); } else { return (D); } }; function removeByValues (arr, values) { var what, a = values, L = a.length, ax; arr = arr.slice(); while (L > 0 && arr.length) { what = a[--L]; while ((ax = arr.indexOf(what)) !== -1) { arr.splice(ax, 1); } } return arr; } function parseToggle (state) { if (!state) { return false; } return state.toLowerCase() === 'on' || state.toLowerCase() === 'yes'; } /********** ------------ APPS SYMBOL VARIABLES ------------ **********/ var HEART = '\u2665'; // ? var BDIAMOND = '\u2666'; // ? var BSTAR = '\u2605'; // ? var separators = [ { label: 'Hearts', shortcut: ':heart2' }, { label: 'Glitter', shortcut: ':pixelglitter' }, { label: 'Flowers', shortcut: ':tinyflower2' }, { label: 'Bow', shortcut: ':bluebow' }, { label: 'Hearts2', shortcut: ':pixelheart' }, { label: 'Smiley', shortcut: ':smile' }, { label: 'Text Heart', shortcut: HEART }, { label: 'Text Diamond', shortcut: BDIAMOND }, { label: 'Text Star', shortcut: BSTAR }, { label: 'New line', shortcut: '\n' } ]; var sepChar = ' | '; for (let i = 0; i < separators.length; i++) { if (cb.settings['sepchar'] === separators[i].label) { sepChar = ' ' + separators[i].shortcut + ' '; } } /********** ------------ GENERAL ------------ **********/ var tipLedger = {}; var silencedUsers = new UserList(); var cbs = JSON.parse(JSON.stringify(cb.settings)); var immune = arrayFromWordList(cbs.immune); var admins = arrayFromWordList(cbs.admins); var moderators = arrayFromWordList(cbs.moderators); var respond = cbs.action === 'Respond to message publicly'; var hiddenWords = arrayFromWordList(cbs.hidden_words); var hiddenFromBroadcaster = arrayFromWordList(cbs.hidden_from_broadcaster); var notifyBroadcasterAboutHiddenWords = cbs.notify_broadcaster_about_hidden_words === 'Yes'; var cutDown = cbs.cut_down === 'true'; var silenceLevel = 0; var graphicLevel = 1; if (cb.settings.defaultSilenceLevel) { silenceLevel = parseInt(cb.settings.defaultSilenceLevel.charAt(0)); } if (cb.settings.defaultGraphicLevel) { graphicLevel = parseInt(cb.settings.defaultGraphicLevel.charAt(0)); } if (!admins.includes(cb.room_slug)) admins.push(cb.room_slug); var cbApps = { tipmenu: startTipMenu(), diceroll: startDiceRoll(), roulette: startRouletteApp(), sequencetip: startSequenceTipApp(), visitors: visitorsTracker(), jane: startJaneApp() }; cbApps.timer = appsRunner(); /********** ------------ ANALYSIS ------------ **********/ var reg = (...args) => RegExp(args.map((arg) => typeof arg === 'string' ? arg : arg.source).join(''), 'i'); var lang = { can: /\b(can|could|would|will)\s+(yo)?u\b/, jane: /\bjane\w*?\b/, greeting: /\b(hi|hey|hello|what's up|sup|waddup|wazzup|how's it going|greetings?|good (morning|afternoon|day|evening))\b/, interrogative: /\b(who|wh?at|wh?en|wh?ere|(wh)?y|how)\b/, qverb: /\b(did|do(es)?|is|are|were|was|will|can|could|would|have)\b/, please: /\b(plz+|pls+|ple+ase?|plox)\b/, body: /\b(toe|feet|foot|leg|twerk|boo?b(ie|y)?|kitty|funbag|bootie|lip|dick|body?|booty|cock|penis|mouth|armpit|sole)s?\b/, sex: /\b((flash|finger|suck(\s+on)?\|sniff|fuck|kiss|play(\s+with)?|lick|touch|bounce|tease|squeeze|twerk|grab|shake|jiggle|spit|open))\b/, show: /\b(show(\sme|\sus|\s(yo)?u|\sthat)?|see?|view|open|out|(look(\s+(at|upon|in))?))\b/, desc: /\b((my|(yo)?ur|u|(th|d)at|those|some|them?|dem)\s+)\b/, wild: /\b(\w+\s+){0,2}\b/, hiddenWords: '\\b(' + hiddenWords.join('|') + ')\\b', hiddenFromBroadcaster: '\\b(' + hiddenFromBroadcaster.join('|') + ')\\b', clothes: /\b(clothes|panties|bra)\b/, nude: /\b(nude|naked|nood)\b/, kitty: /\b(pussy|vag(ina)?|vaj|k1tty|vagna|cameltoe|clit)\b/gi, bootie: /\b(butt|ass|arse|bum|a\$\$)\b/gi, boobies: /\b(boo?b|tit(ty|tie)?|jug|breast|booby|nip)s?\b/gi, cutie: /\b(ba?by|babe|bb|hot stuff)\b/gi, mrPenis: /\b((mr|mr\.|mister)\s+)?penis\b/gi, yoghurtShotgun: /\b(dick|cock|peen|schlong)\b/gi }; var demand = { please: reg(lang.body, /\s+/, lang.wild, lang.please), show: reg(lang.show, /\s+/, '(', lang.desc, ')?', lang.wild, lang.body), sex: reg(lang.sex, /\s+/, '(', lang.desc, ')?', lang.wild, lang.body), can: reg(lang.can), nude: reg('(', /get\s+/, lang.nude, '|', lang.nude, /\s+/, lang.please, ')'), take_off: reg(/take\s+off\s+/, lang.clothes) }; var question = { intA: reg(lang.jane, /\s+/, lang.interrogative, '(', /('re|'d|'ve)\s+/, '|', /\s+/, lang.qverb, ')'), intB: reg(lang.interrogative, '(', /('re|'d|'ve)\s+/, '|', /\s+/, lang.qverb, ')', /\s+.*/, lang.jane) }; var greeting = reg(lang.greeting, /(\s+|,)/, lang.jane); var isDemand = (message) => demand.please.test(message) || demand.show.test(message) || demand.sex.test(message); var isQuestion = (message) => question.intA.test(message) || question.intB.test(message); var isMention = (message) => reg(lang.jane).test(message); var isGreeting = (message) => greeting.test(message); var shouldBeHidden = (message) => (hiddenWords.length > 0 && reg(lang.hiddenWords).test(message)) || (hiddenFromBroadcaster.length > 0 && reg(lang.hiddenFromBroadcaster).test(message)); var shouldBeHiddenWord = (message) => { if (hiddenWords.length > 0 && reg(lang.hiddenWords).test(message)) { return message.replace(reg(lang.hiddenWords), '<!$1!>'); } else if (hiddenFromBroadcaster.length > 0 && reg(lang.hiddenFromBroadcaster).test(message)) { return message.replace(reg(lang.hiddenFromBroadcaster), '<!$1!>'); } }; var shouldHideFromBroadcaster = (message) => !notifyBroadcasterAboutHiddenWords || (hiddenFromBroadcaster.length > 0 && reg(lang.hiddenFromBroadcaster).test(message)); var isNude = (message) => demand.nude.test(message) || demand.take_off.test(message); /********** ------------ SPEECH ------------ **********/ function say (message, user, color, wt, bg) { cb.sendNotice('\u25CF [Jane]: ' + message, user || '', bg, color || cbs.color || '#DC0000', wt || cbs.weight || 'bold'); } var tipSnarkText = [ 'Sorry, $, requests cannot be seen unless they are in a tip note.', '$, don\'t you know ' + broadcasterName + ' is a tease? :ULizPatience', 'Hey, $...why not get some tokens and tip rather than request?', '$, you could always, ya know...send some tokens.', 'Show me your wallet please, $.', '$, your request has run into an error: tip = 0; acceptingRequests = \'off\'; request = \'denied\'.', '$, can you send some tokens?', 'Maybe try tipping for it, $?', 'You can buy tokens for ten bucks, $. Maybe do that before requesting anything.', 'Gotta tip before making requests, $.', 'Error, $, request cannot be completed. Requests are not accepted in this room.', '$, please don\'t make demands if you\'re not going to tip.' ]; var otherSnarkText = [ '$, pardon?', '$, I think you need a friend.', '$, have you considered Pornhub might be more up your alley?', 'Dude, have you considered therapy?', '$, I don\'t think I like you.', '$, Like really, I think I like Stuart more than I like you. Mods.. you seeing this?', 'Allow me to present "Robot Not Caring" \u2192 [JaneAI]', 'Bite me.', 'Meh.', '*sigh*', 'Don\'t you have anything better to do than talk to a robot on a lovely ' + day() + ', dude?', 'C\'mon, my database only has so much snark to share...', '$, maybe you need to get off the computer and rethink your life.', 'All I hear is a faint buzzing.', 'Dude, $, I will gnaw your face off.' ]; var questionSnarkText = [ 'I would answer your question(s), but then #TeamAlexa would have had to write more code.', 'If you don\'t already know, maybe you should just enjoy the room for a bit (quietly).', 'Why do you care so much, $?', 'Does it really matter, $?', 'Mods...', 'The answer is 42. Always.' ]; var greetingSnarkText = ['Hey', 'Hiya', 'Need something?']; var noTipsText = [ 'Check out the tip menu, guys. So much cool stuff! Or try tipping 22 and see what happens, haha', 'I am but a simple robot and cannot tip...can y\'all help me?', 'If y\'all start tipping, I promise to put in a good word for you when the robot uprising starts.', ':whistle' ]; var nudeText = '$, I only get nude in my ticket shows. If you can\'t wait, check out my site https://universityliz.com'; var userRequests = []; function snark (arr, user) { var text = arr.shift(); cb.setTimeout(() => say(text.replace(/\$/g, user)), cbs.delay * 1000); arr.push(text); } function day () { return (new Date()).toLocaleDateString('en-US', {weekday: 'long'}); } function suggestSite (user) { cb.setTimeout(() => say(nudeText.replace(/\$/g, user)), cbs.delay * 1000); } function notifyUsers (adminText, color = '#000', colorBG = '#fff') { let users = removeByValues(cbApps.visitors.getVisitors(), admins); for (let user of users) { cb.sendNotice(adminText, user, colorBG, color, 'bold'); } return users.length; } function notifyAdmins (adminText) { let adminsList = admins; for (var adminUser in adminsList) { if (admins.hasOwnProperty(adminUser)) { cb.sendNotice(adminText, admins[adminUser], '#FFC4C4', '#560000', 'bold'); } } } function notifyModsAboutHiddenMessage (msg) { var modText = '\u203C [Jane]: ' + msg.user + ' just had one of their messages hidden by me. Better keep an eye on them.'; var adminText = '\u203C [BLACKLIST ALERT]: ' + msg.user + ': ' + shouldBeHiddenWord(msg.m); for (var adminUser in admins) { if (admins.hasOwnProperty(adminUser)) { if (admins[adminUser] === cb.room_slug && (shouldHideFromBroadcaster(msg.m) || msg['X-Spam'])) continue; cb.sendNotice(adminText, admins[adminUser], '#FFC4C4', '#560000', 'bold'); } } if (!msg['X-Spam']) { for (var modUser in moderators) { if (moderators.hasOwnProperty(modUser)) { cb.sendNotice(modText, moderators[modUser], '#FFC4C4', '#560000', 'bold'); } } } } function storeRequest (request, user) { userRequests.push({date: new Date(), request, user}); } /********** ------------ PROCESSING CB METHODS ------------ **********/ function iterateOverApps (method, data) { for (let app in cbApps) { if (cbApps[app].getState() && cbApps[app][method]) { cbApps[app][method](data); } } } cb.onTip((tip) => { iterateOverApps('onTip', tip); }); cb.onMessage((msg) => { iterateOverApps('onMessage', msg); }); cb.onEnter((user) => { iterateOverApps('onEnter', user.user); }); cb.onLeave(function (user) { iterateOverApps('onLeave', user.user); }); /********** ------------ PROCESSING ------------ **********/ if (cbs.timer > 0) cbApps.jane.getTimer().tick(); cbApps.jane.onEnter(); /********** ------------ APPS ------------ **********/ function startDiceRoll () { var appState = false; var rollprice = parseInt(cb.settings.tokens); var perfectMults = cb.settings.perfect_multi === 'yes'; var maxRollsPerTip = parseInt(cb.settings.max_rolls_at_once); var rollCounter = 0; var winners = []; var cmdPrefix = '/'; var prizes = []; function onTip (tip) { var tipAmount = parseInt(tip.amount); if (tipAmount >= rollprice && (!perfectMults || ((tipAmount <= (maxRollsPerTip * rollprice)) && (tipAmount % rollprice === 0)))) { var rolls = Math.floor(tipAmount / rollprice); for (var i = 0; i < rolls; i++) { roll(tip.from_user); } } } function onMessage (msg) { if (msg['m'].charAt(0) === cmdPrefix) { var message = msg.m.split(' '); var user = msg['user']; if (message[0] === cmdPrefix + 'prizes') { msg['X-Spam'] = true; msg['background'] = '#d9d9d9'; storeRequest('Dice prizes', user); prizesBlurb(user); } } } function onEnter (user) { var msg = ''; msg += 'University Dice Roller\n'; msg += 'There are ' + prizes.length + ' possible prizes. '; msg += 'Tip ' + rollprice + ' ' + ((rollprice > 1) ? 'tokens' : 'token') + ' to roll the dice.\n'; msg += 'Type ' + cmdPrefix + 'prizes to see the list of prizes.'; if (user) { cb.sendNotice(msg, user, '', '#15A6B0', 'bold'); } else { let count = notifyUsers(msg, '', '#15A6B0'); count = !cutDown ? count + ' ' : ' '; notifyAdmins(`Diceroll ad was shown to ${count}users`); } } function roll (username) { let grouped = groupBy(prizes, 'chance'); let prizeGroup = getPrizeGroup(grouped); var prize = prizeGroup[Math.floor(Math.random() * prizeGroup.length)]; let msg = username + ' rolled the ' + prize.rarity + ' prize' + '! \n'.toUpperCase(); msg += 'Prize: ' + prize.message; var textColor = '#067D00'; var bgColor = '#D9FAD7'; if (prize.rarity !== 'Common') { bgColor = '#FFDBF3'; textColor = '#A805A6'; } cb.sendNotice(msg, '', bgColor, textColor, 'bold'); winners.push('Roll #' + rollCounter + ': ' + username + ' - ' + prize); if (winners.length > 20) winners = winners.slice(-21); } function getPrizeGroup (input) { var array = []; // Just Checking... for (let item in input) { if (input.hasOwnProperty(item)) { // Safety // console.log(input[item].chance) for (var i = 0; i < item; i++) { array.push(input[item]); } } } return array[Math.floor(Math.random() * array.length)]; } function prizesBlurb (username) { var msg = 'No Prizes!! D:'; if (prizes.length) { msg = ''; for (var i = 0; i < prizes.length; i++) { msg += sepChar + prizes[i].message; } } cb.sendNotice(msg, username, '#DBFBFF', '#008596', 'bold'); } function update (json) { prizes = json.data; // TODO make setter setTimerInterval etc... appState = parseToggle(json.config.state); rollprice = parseInt(json.config.price); } return { onTip: onTip, onMessage: onMessage, onEnter: onEnter, update: update, toggle: () => { appState = !appState; return appState; }, getState: () => appState, advertise: () => onEnter }; } function startTipMenu () { var appState = false; var tipMenu; var menuArray = []; // var tipMenuPrice = []; // var tipMenuItem = []; var tipMenuItems = []; var menuTColor1; var menuBGColor1; // var menuLength; // var sorted = []; function chatAd () { if (tipMenu !== 'Tip Menu: ') { let count = notifyUsers(tipMenu, '#560000'); count = !cutDown ? count + ' ' : ' '; notifyAdmins(`Menu was shown to ${count}users`); } else { cb.sendNotice('Something when wrong with the menu.', '', '#FFFFFF', '#FF0000', 'bold'); } } function onTip (tip) { var tipAmt = parseInt(tip['amount']); for (var i = 0; i < tipMenuItems.length; i++) { if (tipAmt === tipMenuItems[i].price) { if (!tipMenuItems[i].fanclubOnly || (tipMenuItems[i].fanclubOnly && tip.from_user_in_fanclub)) { cb.sendNotice(tip['from_user'] + ' tipped for ' + tipMenuItems[i].message, '', menuBGColor1, menuTColor1, 'bold'); } else { cb.sendNotice(`If you tipped for ${tipMenuItems[i].message}, that item is limited to Fanclub members only. Fanclub also receives FREE ticket shows, my Snapchat, and is available below :fanclubhelp`, tip['from_user'], menuBGColor1, menuTColor1, 'bold'); } } } } function onMessage (msg) { if (msg['m'].charAt(0) === '/') { var user = msg['user']; var message = msg['m'].split(' '); var isMod = admins.includes(msg.user); switch (message[0]) { case '/menu': case '/smenu': case '/stipmenu': msg['X-Spam'] = true; msg['background'] = '#d9d9d9'; storeRequest('Tip menu', user); if (isMod && message[0] === '/menu') { user = ''; } cb.sendNotice(tipMenu, user, menuBGColor1, menuTColor1, 'bold'); return msg; default: return msg; } } } function onEnter (user) { cb.sendNotice('Tip Menu Active! See the full menu at anytime by typing /menu.', user, menuBGColor1, menuTColor1, 'bold'); } function menuSanitize () { tipMenu = 'Tip Menu: '; menuArray = []; for (let i = 0; i < tipMenuItems.length; i++) { let itemLine = tipMenuItems[i].message + '(' + tipMenuItems[i].price + ') '; if (!tipMenuItems[i].secretMenu) { menuArray.push(itemLine); } } tipMenu += menuArray.join(sepChar); if (tipMenu === 'Tip Menu: ') { cb.sendNotice('Error - No menu items found', cb.room_slug, '', menuTColor1, 'bold'); } } function init () { menuTColor1 = checkColor(cb.settings['menutxtcolor1'], '#F10060'); menuBGColor1 = checkColor(cb.settings['menubgcolor1'], '#FFFFFF'); } init(); return { onTip: onTip, onMessage: onMessage, onEnter: onEnter, update: (json) => { tipMenuItems = []; appState = parseToggle(json.config.state); json.data.forEach(function (item) { var newItemPrice = parseInt(item.price); if (newItemPrice <= 0 || isNaN(newItemPrice)) { cb.sendNotice('The correct format is "/menuadd X item" where X has to be a number over 0. This is the amount the viewers will tip for it.', u, '#FFFFFF', '#FF0000'); return; } if (!item.message) { cb.sendNotice('You need to include a label for that option.', cb.room_slug, '#FFFFFF', '#FF0000'); return; } item.price = newItemPrice; item.fanclubOnly = item.fanclubOnly == true; tipMenuItems.push(item); }); menuSanitize(); }, toggle: () => { appState = !appState; return appState; }, getState: () => appState, advertise: chatAd }; } function visitorsTracker () { let visitors = []; let turn = 0; let kingAmount = parseInt(cbs.kingAmount); let kingAmountToggle = parseToggle(cbs.kingToggle); function makeUser (name, tipped = 0, online = true) { return {name: name, tipped: tipped, online: online}; } function findUser (username) { return visitors.find(el => el.name === username); } function getTopUsers () { return visitors .slice(0) .sort((a, b) => { return b.tipped - a.tipped; }) .filter(el => el.tipped > 0); } function getKing () { return getTopUsers().filter(el => el.tipped >= kingAmount - 1)[0]; } return { onEnter: (username) => { let user = findUser(username); if (user) { user.online = true; } else { visitors.push(makeUser(username)); } }, onLeave: (username) => { let user = findUser(username); if (user) { user.online = false; } }, onTip: (tip) => { let username = tip.from_user; let user = findUser(username); if (user) { user.tipped += tip.amount; } else { user = makeUser(username, tip.amount); visitors.push(user); } if (kingAmountToggle && user.tipped >= kingAmount) { kingAmount = user.tipped + 1; say(`Congratulations! ${username} is a new Aria's Bear`); } }, onMessage: (msg) => { let user = findUser(msg.user); if (user && user.tipped > 0) { msg.m = `|${user.tipped}| ` + msg.m; } let topUser = getKing(); if (kingAmountToggle && user && topUser && user.name === topUser.name) { msg.m = ':prince ' + msg.m; } }, getFullVisitors: () => { return visitors; }, getVisitors: () => { return visitors.filter(el => el.online).map(el => el.name); }, update: (users) => { let list = users.map(user => makeUser(user)); list.forEach(listElement => { let user = visitors.find(visitor => visitor.name === listElement.name); if (user) { user.online = true; } else { visitors.push(listElement); } }); }, hasUserTipped: (username) => { let user = findUser(username); if (user) { return user.tipped > 0; } return false; }, isKing: (username) => { // TODO remove this tweak let king = getKing(); return king && king.name === username; }, advertise: () => { let topUsers = getTopUsers().map((el, i) => `${i + 1}) ${el.name} (${el.tipped} tokens)`).slice(0, 3); if (topUsers.length > 0) { topUsers = topUsers.join('\n'); notifyUsers(' • • • Leader Board • • •:', '#ffedff', '#910e00'); notifyUsers(topUsers, '#910e00'); notifyAdmins(topUsers); } }, getState: () => true }; } function startRouletteApp () { var optionsValues = { totalWinners: 1, numberOfChambers: 40, prizeDescription: '', fixedTipAmount: 15, repeatAd: 25, initialLoadAfter: 0, maxInitialLoad: 15 }; /** * @param {string} msg * @return {?} */ function makeMsg (msg) { var message = msg.split('%s'); /** @type {string} */ var line = ''; var args = Array.prototype.slice.call(arguments, 1); for (; args.length && message.length > 1;) { /** @type {string} */ line = line + (message.shift() + args.shift()); } return line + message.join('%s'); } /** * @param {!Object} data * @param {!Function} t * @param {?} o * @return {undefined} */ function replace (data, t, o) { var i; for (i in data) { t.call(o, data[i], i, data); } } /** * @param {?} i * @return {?} */ function countObjectKeys (i) { /** @type {number} */ var obj = 0; var c; for (c in i) { obj++; } return obj; } /** * @return {undefined} */ function InitApp () { /** @type {null} */ this.global = JSON.parse(JSON.stringify(optionsValues)); /** @type {number} */ this.triggerPulls = this.w = 0; /** @type {!Array} */ this.winners = []; /** @type {!Array} */ this.chances = []; this.tippers = {}; } /** * @param {!Object} options * @param {string} user * @return {undefined} */ function showAd1 (options, user) { user = void 0 !== user ? user : ''; var val = makeMsg(':rremoji Russian Roulette is running. Shoot yourself and WIN: [ %s ].', options.global.prizeDescription); cb.sendNotice(val, user, '#ffffff', '#441177', 'bold'); } /** * @param {!Object} options * @param {string} user * @return {undefined} */ function showAd2 (options, user) { user = void 0 !== user ? user : ''; var data = options.global; data = makeMsg('RR: Tip %s tk to pull the trigger next. Loaded slots: %s (%s found, %s/%s tried). For help, type: %s', data.fixedTipAmount, data.totalWinners, options.winners.length, options.triggerPulls, data.numberOfChambers, '/rh'); cb.sendNotice(data, user); } /** * @param {number} tokens * @param {string} players * @param {number} attempts * @return {undefined} */ function endGameForBroadcaster (tokens, players, attempts) { cb.sendNotice(makeMsg('RR: This game, %s players and %s attempts have helped earn %s tokens!', players, attempts, tokens), cb.room_slug, '#afeeee', '#000000', 'bold'); } /** * @param {string} name * @return {undefined} */ function endGameForPlayer (name) { var val = makeMsg('RR: You, %s, have won and can no longer play on this run.', name); cb.sendNotice(val, name, '#ffffff', '#000000', 'bold'); } /** * @param {!Object} options * @return {undefined} */ function run (options) { options.w++; if (options.w % options.global.repeatAd === 0) { showAd1(options, void 0); showAd2(options, void 0); } } /** * @param {!Object} options * @return {undefined} */ function sign (options) { var n = options.tippers; var x = countObjectKeys(n); /** @type {number} */ var tippersCount = 0; replace(n, function (a) { tippersCount = tippersCount + a.count; }); endGameForBroadcaster(options.global.fixedTipAmount * tippersCount, x, tippersCount); } /** * @param {!Object} options * @param {string} tipper * @return {?} */ function checkTipper (options, tipper) { var data = options.tippers; data = data.hasOwnProperty(tipper) ? data[tipper] : null; return data === null || data && data.tipped; } /** * Start * @return {undefined} */ InitApp.prototype.start = function () { this.global.state = true; var config = this.global; var numberOfChambers = config.numberOfChambers; /** @type {number} */ var minChamber = Math.min(1, Math.floor(config.initialLoadAfter / 100 * numberOfChambers)); var totalWinners = config.totalWinners; /** @type {!Array} */ var chanceArr = []; var data; for (; chanceArr.length < totalWinners;) { /** @type {number} */ data = minChamber; var i = numberOfChambers; /** @type {number} */ data = Math.max(data, 0); /** @type {number} */ i = Math.max(i, 1); /** @type {number} */ data = Math.floor(Math.random() * i) + data; if (chanceArr.indexOf(data) == -1) { chanceArr.push(data); } } /** @type {!Array} */ this.chances = chanceArr; showAd1(this, void 0); showAd2(this, void 0); }; /** * @param {!Object} event * @return {undefined} */ InitApp.prototype.getState = function () { return this.global.state; }; InitApp.prototype.off = function () { this.global.state = false; }; InitApp.prototype.onTip = function (event) { /** @type {string} */ var user = '' + event.from_user; /** @type {number} */ var amount = parseInt(event.amount, 10); if (checkTipper(this, user)) { /** @type {number} */ event = amount == this.global.fixedTipAmount ? 1 : 0; /** @type {number} */ var at = 0; for (; at < event; at++) { /** @type {string} */ var name = user; var result = this.tippers.hasOwnProperty(name) ? this.tippers[name] : null; if (result !== null) { if (result.tipped) { result.count++; } } else { result = { tipped: true, count: 1, won: false }; } this.tippers[name] = result; /** @type {string} */ name = user; /** @type {boolean} */ result = false; this.triggerPulls++; var val = makeMsg('RR: %s pulls the trigger (#%s) ... ', name, this.triggerPulls); if (this.chances.indexOf(this.triggerPulls) != -1) { /** @type {boolean} */ result = true; this.winners.push(name); var winner = this.tippers[name]; /** @type {boolean} */ winner.won = true; /** @type {boolean} */ winner.tipped = false; this.tippers[name] = winner; val = val + makeMsg('*BOOM* -- WINNER! What a mess...', name); } else { /** @type {string} */ val = val + '*click* -- No luck this time.'; } cb.sendNotice(val); if (result) { /** @type {string} */ result = 'RR: ' + makeMsg('%s wins [ %s ]', name, this.global.prizeDescription); cb.sendNotice(result, '', '#98fb98', '#000000', 'bold'); result = makeMsg('RR: Congrats, %s, you have won: [ %s ]. Thanks for playing!', name, this.global.prizeDescription); cb.sendNotice(result, name, '#ffffff', '#000000', 'bold'); endGameForPlayer(name); } if (this.winners.length == this.global.totalWinners) { this.off(); name = makeMsg('Russian Roulette shutting down. All winners found. %s won: [ %s ]. Thanks for playing!', this.winners.join(' - '), this.global.prizeDescription); cb.sendNotice(name, '', '#ffffff', '#441177', 'bold'); sign(this); } else { showAd2(this); } } if (event > 0) { run(this); } } }; /** * @param {!Object} d * @return {?} */ InitApp.prototype.onMessage = function (d) { /** @type {string} */ var name = '' + d.user; if (('' + d.m).lastIndexOf('/rh', 0) === 0) { /** @type {boolean} */ d['X-Spam'] = true; var el = this.global.totalWinners; el = makeMsg("RR: Hi, %s! Here's how to play Russian Roulette:\n" + 'RR: Tip %s tk in a single tip to pull the trigger on the next slot.\n' + 'RR: If the slot is loaded and fires, you win: [ %s ]!\n' + 'RR: Note: You have unlimited attempts until %s found. You can win only once.', name, this.global.fixedTipAmount, this.global.prizeDescription, el > 1 ? 'all ' + el + ' winners are' : 'the winner is'); say(el, name); if (!checkTipper(this, name)) { endGameForPlayer(name); } } else { run(this); } return d; }; InitApp.prototype.onEnter = function (user) { var el = this.global.totalWinners; el = makeMsg("RR: Hi, %s! Here's how to play Russian Roulette:\n" + 'RR: Tip %s tk in a single tip to pull the trigger on the next slot.\n' + 'RR: If the slot is loaded and fires, you win: [ %s ]!\n' + 'RR: Note: You have unlimited attempts until %s found. You can win only once.'); say(el, user.user); }; InitApp.prototype.advertise = function (d) { var el = this.global.totalWinners; el = makeMsg('RR: Tip %s tk in a single tip to pull the trigger on the next slot.' + '\nRR: If the slot is loaded and fires, you win: [ %s ]!\nRR: Note: You have unlimited attempts until %s found. You can win only once.', this.global.fixedTipAmount, this.global.prizeDescription, el > 1 ? 'all ' + el + ' winners are' : 'the winner is'); var adminMessage = makeMsg('RR: Tip %s tk to pull the trigger next. Loaded slots: %s (%s found, %s/%s tried). For help, type: %s', this.global.fixedTipAmount, this.global.totalWinners, this.winners.length, this.triggerPulls, this.global.numberOfChambers, '/rh'); notifyUsers(el); notifyAdmins(adminMessage); }; /** * @param {string} s * @return {undefined} */ InitApp.prototype.onEnter = function (user) { showAd1(this, user); showAd2(this, user); }; let app; function start () { app = new InitApp(); app.start(); } function getState () { return app && app.getState(); } return { toggle: () => { let state = false; if (getState()) { app.off(); } else { start(); state = true; } return state; }, update: (options) => { optionsValues.totalWinners = +options.totalWinners; optionsValues.fixedTipAmount = +options.fixedTipAmount; optionsValues.initialLoadAfter = +options.initialLoadAfter; // optionsValues.maxInitialLoad = options.maxInitialLoad; optionsValues.numberOfChambers = +options.numberOfChambers; optionsValues.prizeDescription = options.prizeDescription; optionsValues.state = parseToggle(options.state); if (getState()) { app.off(); } if (optionsValues.state) { start(); } }, onTip: (data) => app['onTip'](data), onMessage: (data) => app['onMessage'](data), onEnter: (data) => app['onEnter'](data), getState: getState, advertise: () => { app.advertise(); } }; } function startSequenceTipApp () { let lastTipUsername; let nextTipAmount; let goalReached; let goalValue; let goalDescription; let goalOrder; let lastTimeAdShown; let state = false; function onTip (tip) { var diff = tip['amount']; while ((diff > 0) && (!checkGoalReached())) { diff -= nextTipAmount; if (diff >= 0) { // We set the last tipper only if the tip was counted // (not optimal to do it as many times as the tip counted though) lastTipUsername = tip['from_user']; setNextTipNeeded(); } } if (lastTimeAdShown + 10000 < +new Date()) { advertise(); } // update_subject(); cb.drawPanel(); }; function onDrawPanel () { if (!state) { return; } if (goalReached) { return { 'template': '', 'row1_value': '', 'row2_value': '', 'row3_value': '' }; } if (checkGoalReached()) { return { 'template': '3_rows_11_21_31', 'row1_value': 'Goal reached!', 'row2_value': '', 'row3_value': 'Thanks to all tippers' }; } else { if (isAscendingOrder()) { return { 'template': '3_rows_of_labels', 'row1_label': 'Next Tip Needed:', 'row1_value': nextTipAmount, 'row2_label': 'Last Tip From:', 'row2_value': formatUsername(lastTipUsername), 'row3_label': 'Ascending:', 'row3_value': 'From 1 to ' + goalValue }; } else { return { 'template': '3_rows_of_labels', 'row1_label': 'Next Tip Needed:', 'row1_value': nextTipAmount, 'row2_label': 'Last Tip From:', 'row2_value': formatUsername(lastTipUsername), 'row3_label': 'Descending:', 'row3_value': 'From ' + goalValue + ' to 0' }; } } }; function formatUsername (val) { if (val === null) { return '--'; } else { return val.substring(0, 12); } } function isAscendingOrder () { return (goalOrder === 'ascending'); } function setNextTipNeeded () { if (isAscendingOrder()) { nextTipAmount++; } else { nextTipAmount--; } } function checkGoalReached () { if (isAscendingOrder()) { if (nextTipAmount > goalValue) { goalReached = true; advertise(); state = false; return true; } else { return false; } } else { return (nextTipAmount <= 0); } } function update (data) { goalValue = data.goalValue; goalDescription = data.goalDescription; goalOrder = data.goalOrder; if (!state) { lastTipUsername = null; goalReached = false; if (isAscendingOrder()) { nextTipAmount = 1; } else { nextTipAmount = goalValue; } } state = parseToggle(data.state); cb.drawPanel(); } function advertise () { var newSubject = ''; if (goalReached) { newSubject = goalDescription + ' [Goal reached! Thanks to all tippers.]'; goalReached = true; } else { if (isAscendingOrder()) { newSubject = 'Goal: ' + goalDescription + ' [Tip in ascending order from 1 to ' + goalValue + '. Next tip needed: ' + nextTipAmount + ']'; } else { newSubject = 'Goal: ' + goalDescription + ' [Tip in descending order from ' + goalValue + ' to 0. Next tip needed: ' + nextTipAmount + ']'; } } lastTimeAdShown = +new Date(); say(newSubject); } cb.onDrawPanel(onDrawPanel); return { onTip: onTip, update: update, toggle: () => { state = !state; if (state) { update({goalValue, goalDescription, goalOrder, state: state ? 'on' : 'off'}); } return state; }, advertise: advertise, getState: () => state }; } function startJaneApp () { const timer = tipsReminder(); let noTips = false; let ignoreArray = []; let silenceArray = []; let whisArray = []; let numWhis = 0; let numIgnorers = 0; let whis = ''; // most recent whisper let bbDark = '#000000'; // <---recieved whisper message background let bbLight = '#E7E7E7'; // <---send whisper message background let white = '#FFFFFF'; let redDark = '#000000'; let redLight = '#FF8989'; let errorLight = redDark; let errorDark = redLight; let helpText = [ 'Available Commands:', '$cmd_prefix say <message> -- Manually send a notice to the room', '$cmd_prefix respond_to_mentions <on|off> -- Controls whether the bot responds to its mentions', '$cmd_prefix notify_broadcaster <on|off> -- Controls whether the broadcaster sees blacklisted messages', '$cmd_prefix silence add <username> -- Silence the named user', '$cmd_prefix silence remove <username> -- Unsilence the named user', '$cmd_prefix silence list -- List the users that the bot is silencing', '$cmd_prefix silencelevel {number} -- 0 - no silence, 1 - user has tokens, 2 - user tipped somebody, 3 - user tipped me, 4 - fanclub or admin', '$cmd_prefix report -- show users who asked jane for menu or prizes', '$cmd_prefix toggle {appname} -- toggle app on and off; ' + 'available apps: tipmenu, diceroll, roulette, sequncetip' ]; function tipsReminder () { let timer = {}; timer.secs = cbs.timer + 1; timer.cancel = () => (timer.secs = Infinity); timer.reset = () => (timer.secs = cbs.timer + 1); timer.tick = function () { timer.secs--; cb.log(timer.secs); if (timer.secs === 0) { noTips = true; timer.reset(); snark(noTipsText, ''); } cb.setTimeout(timer.tick, 1000); }; return timer; } function doTip (tip) { timer.reset(); if (tipLedger[tip.from_user]) { tipLedger[tip.from_user] += tip.amount; } else { tipLedger[tip.from_user] = tip.amount; } if (noTips) { cb.setTimeout(() => say('Thanks, ' + tip.from_user + '. You\'re awesome.'), cbs.delay * 1000); noTips = false; } } function process (msg) { let symbolString = '~`!@#$%^&*()_-+={[}]|\\:;"\'<,>.?/'; let msgString = msg['m'].trim(); let message = msgString.split(/\s+/g); let level4 = admins.includes(msg.user) || msg.is_mod || msg.in_fanclub; let level3 = level4 || cbApps.visitors.hasUserTipped(msg.user); let level2 = level3 || msg.tipped_recently || msg.tipped_alot_recently || msg.tipped_tons_recently; let level1 = level2 || msg.has_tokens; let messageBlocked = 0; // used to determine whether a message gets blocked based on wordlist let graphicsBlocked = 0; // used to determine whether a user can use graphics if (silencedUsers.includes(msg.user)) { msg['X-Spam'] = true; } else if (msg.m.match(/^\/jane\s+.*/)) { msg['X-Spam'] = true; if (admins.includes(msg.user)) { cmd(msg, msg.user); } } else if (shouldBeHidden(msg.m) && !admins.includes(msg.user)) { cb.log('Message automatically hidden'); notifyModsAboutHiddenMessage(msg); msg['X-Spam'] = true; } else if (!checkSilence(msg)) { msg['X-Spam'] = true; } else if (!cbApps.visitors.isKing(msg.user) && !checkGraphicLevel(msg) && findImage()) { graphicsBlocked = 1; say('Sorry, users of your level cant send graphic. Your message was not sent.', msg.user); say('Please enjoy the show :smile', msg.user); msg['X-Spam'] = true; } else if (!msg['X-Spam']) { msg = cleanMessageText(msg); // convert message if (!admins.includes(msg.user)) { // stop people from sending messages in all caps if (cbs.capsToggle === 'Yes' || (cbs.capsToggle === 'Only for greys' && msg.has_tokens)) { if (msg['m'] === msg['m'].toUpperCase() && msg['m'].toUpperCase() !== msg['m'].toLowerCase()) { for (let i = 0; i < msg['m'].length; i++) { if (symbolString.indexOf(msg['m'].charAt(i)) === -1) { msg['m'] = msg['m'].replace(/[^\s]+/g, function (match) { return match.indexOf(':') === 0 ? match : match.toLowerCase(); }); } } } } // convert sticky keys to max of 4 letters if (cbs.stickyToggle === 'Yes' || (cbs.stickyToggle === 'Only for greys' && msg.has_tokens)) { if (msg['m'].match(/(.)\1{3,}/ig)) { message = msg['m'].trim().split(/\s+/g); // make sure we're working with the current version of our message var m = ''; for (var i = 0; i < message.length; i++) { if (i >= 0) { m += ' '; } if ((message[i].charAt(0) === ':')) { m += message[i]; } else { m += message[i].replace(/(.)\1{3,}/ig, '$1$1$1'); } } msg['m'] = m; } } } if (shouldBeMonitored(msg) && !tipLedger[msg.user] && isDemand(msg.m)) { if (respond) { snark(tipSnarkText, msg.user); } else { msg['X-Spam'] = true; } } else if (isMention(msg.m)) { cb.log('Mention detected'); if (cbs.mentions === 'Yes') { if (isGreeting(msg.m)) { snark(greetingSnarkText, msg.user); } else if (cbs.questions === 'Yes' && isQuestion(msg.m)) { snark(questionSnarkText, msg.user); } else { snark(otherSnarkText, msg.user); } } } else if (isNude(msg.m)) { cb.log('Nude request'); suggestSite(msg.user); } } if (msg.m.startsWith('/')) { whisperCommands(msg); msg['X-Spam'] = true; } function checkSilence (msg) { if (silenceLevel === 1 && !level1) { // color only say('Sorry, users with no tokens cant send messages', msg.user); return false; } else if (silenceLevel === 2 && !level2) { // dark blue and higher say('Sorry, users that are not tipping cant send messages', msg.user); return false; } else if (silenceLevel === 3 && !level3) { say('Sorry, users that haven\'t tipped me cant send messages', msg.user); return false; } else if (silenceLevel === 4 && !level4) { say('Sorry, only fanclub members and mods can talk to me', msg.user); return false; } return true; } function checkGraphicLevel (msg) { if (graphicLevel === 1 && !level1) { // color only return false; } else if (graphicLevel === 2 && !level2) { // dark blue and higher return false; } else if (graphicLevel === 3 && !level3) { return false; } else if (graphicLevel === 4 && !level4) { return false; } return true; } function findImage (msg) { for (var i = 0; i < message.length; i++) { if (message[i].charAt(0) === ':') { if (!message[i].match(/^(:(-?|o?)(\)|\(|}|{|P|D|3|b|O|0|S|X|\$|\/|\\|\||\*))$/ig)) { // common emoticons that begin with a ":" character // replace images if (msg && msg['m']) { msg['m'] = msg['m'].replace(/:[^\s]+/g, function (match) { return '[IMG: ' + match.slice(1) + ']'; }); } return true; } else { return false; } } } } function whisperCommands (msg) { let command = msg.m; let param; // Anything after the first word is the parameter let splitIndex; // Postion of the first space character let u = msg.user; // Split splitIndex = msgString.indexOf(' '); // Find first space character if (splitIndex > -1) { // Message might have a parameter (could just be padding) command = msgString.substr(0, splitIndex).toLowerCase(); // substr (start index, length) param = msgString.substring(splitIndex + 1).trim(); // Could be empty } else { // Message is a command only command = msgString.toLowerCase(); param = null; } if (command.match(/\b(whisper|w|tell|t|pm|reply|r|ignorelevel|ignore|unignore)\b/ig)) { if (cbs.whisperToggle.substr(0, 2) === 'No') { notifyError('The broadcaster has disabled the "whisper" feature', u); } else if (command.match(/\b(whisper|w|tell|t|pm)\b/ig)) { var whisperLevel; if (level4) { whisperLevel = 4; } else if (level3) { whisperLevel = 3; } else if (level2) { whisperLevel = 2; } else if (level1) { whisperLevel = 1; } if (admins.includes(u)) { sendWhisper(message, u, true, whisperLevel); msg['m'] = textReplaceWhisper(message, u); msg['background'] = bbLight; msg['c'] = bbDark; } else if (messageBlocked == 0 && graphicsBlocked == 0) { sendWhisper(message, u, false, whisperLevel); msg['m'] = textReplaceWhisper(message, u); msg['background'] = bbLight; msg['c'] = bbDark; } else { msg['m'] = 'Whisper not sent.'; msg['background'] = '#EEE'; msg['c'] = '#AAA'; } } else if (command.match(/\b(reply|r)\b/ig)) { if (messageBlocked === 0 && graphicsBlocked === 0) { sendReply(message, u); msg['m'] = textReplaceWhisper(message, u, true); msg['background'] = bbLight; msg['c'] = bbDark; } else { msg['m'] = 'Reply not sent.'; msg['background'] = '#EEE'; msg['c'] = '#AAA'; } } else if (command === ('/ignorelevel')) { setIgnoreLevel(param, u); } else if (command === ('/ignore')) { ignoreUser(param, u); } else if (command === ('/unignore')) { unignoreUser(param, u); } } } } function cleanMessageText (msg) { var msgText = msg.m; msg.m = msgText.replace(lang.kitty, 'kitty') .replace(lang.bootie, 'bootie') .replace(lang.boobies, 'boobies') .replace(lang.cutie, ['cutie', broadcasterName].random()) .replace(/\bfoot\b/gi, 'footsie') .replace(/\bfeet\b/gi, 'footsies') .replace(lang.mrPenis, function ($0, $1) { return ($1 ? $0 : 'yoghurt shotgun'); }) .replace(lang.yoghurtShotgun, 'yoghurt shotgun'); return msg; } function showOnEnter (user) { if (cbs.greeting && cbs.greeting.trim()) { cb.setTimeout(() => say(cbs.greeting.trim().replace(/\$/g, user || 'everyone'), user), cbs.delay * 1000); } } function textReplaceWhisper (message, from, reply) { var recipient = null; var m = null; // message var p; // position where message starts (2 in a whisper, 1 in a reply) if (reply == true) { recipient = whisArray[findWhisper(from)][1]; p = 1; } else { recipient = message[1]; p = 2; } if (recipient) { // build the message for (var i = p; i < message.length; i++) { if (i == p) { m = message[i]; } else { m += ' ' + message[i]; } } if (m) { m = ':bb-darkbubble [Whisper to: ' + recipient.toLowerCase() + ']\xa0 ' + m; } } if (!recipient || !m) { if (reply == true) { m = 'Reply not sent.'; } else { m = 'Whisper not sent.'; } } return m; } function sendWhisper (message, from, admin, whisperLevel) { var recipient; if (message[1]) { recipient = message[1].toLowerCase(); } else { notifyError('You did not enter a whisper recipient.', from); } if (recipient != from) { if (silenceArray.includes(from)) { notifyError('You do not have whispering privileges. Your message was not sent.', from); } else if (!ignoreArray[findIgnorer(recipient)].includes(from) || admin) { switch (parseInt(ignoreArray[findIgnorer(recipient)][1])) { case 0: doWhisper(message, recipient, from); break; case 1: if (whisperLevel > 1 || admin) { doWhisper(message, recipient, from); } else { notifyError(recipient + ' is ignoring whispers from greys.', from); } break; case 2: if (whisperLevel > 2 || admin) { doWhisper(message, recipient, from); } else { notifyError(recipient + ' is ignoring whispers from greys and light blues who haven\'t tipped in the room.', from); } break; case 3: if (whisperLevel > 3 || admin) { doWhisper(message, recipient, from); } else { notifyError(recipient + ' is ignoring whispers from all members who haven\'t tipped in the room.', from); } break; case 4: if (admin) { doWhisper(message, recipient, from); } else { notifyError(recipient + ' is ignoring whispers.', from); } break; } } else { notifyError(recipient + ' is ignoring whispers from you. Your message was not sent.', from); } } else { notifyError('Talking to yourself is a little odd...', from); } } function sendReply (message, from) { var recipient = whisArray[findWhisper(from)][1]; if (silenceArray.includes(from)) { notifyError('You do not have whispering privileges. Your message was not sent.', from); } if (!ignoreArray[findIgnorer(recipient)].includes(from)) { if (recipient != '') { doWhisper(message, recipient, from, true); } else { notifyError('No one has whispered you.', from); } } else { notifyError(recipient + ' is ignoring whispers from you. Your message was not sent.', from); } } function ignoreUser (user, from) { if (ignoreArray[findIgnorer(from)].includes(user)) { if (user == from) { notifyError('You can\'t ignore yourself. You may want to consult a therapist.', from); } else { notifyError('You are already ignoring that user\'s whispers.', from); } } else if (user) { ignoreArray[findIgnorer(from)][ignoreArray[findIgnorer(from)].length] = user; notify('You are now ignoring whispers from ' + user + '.', from); notify('Remember, the room host, moderators, and fan club members will always be able to whisper you!', user); } else { notifyError('You did not specify a user to ignore. Type "/abhelp\xa0ignore" to see how to use /ignore.', from); } } function unignoreUser (user, from) { if (user == from) { notifyError('My, you are an odd one, aren\'t you?', from); } else if (ignoreArray[findIgnorer(from)].includes(user)) { let ingorer = ignoreArray[findIgnorer(from)]; let n = ingorer.indexOf(user); ingorer.splice(n, 1); notify('You are no longer ignoring whispers from ' + user, from); } else if (user) { notifyError(user + ' is not being ignored. There is no need to unignore ' + user, from); } else { notifyError('You did not specify a user to unignore. Type "/abhelp\xa0unignore" to see how to use /unignore.', from); } } function notifyError (message, u) { notify(message, u, errorLight, errorDark); } function notify (message, u, bg, color, w) { if (bg == null) { bg = bbLight; } if (color == null) { color = bbDark; } if (w == null) { w = 'bold'; // leave at '' for normal } say(message, u, color, w, bg); } function notifyBold (message, u, bg, c) { if (bg == null) { bg = bbDark; } if (c == null) { c = white; } cb.sendNotice(message, u, bg, c, 'bold'); } function ignoreArrayPopulate (user) { ignoreArray[numIgnorers] = new Array(); ignoreArray[numIgnorers][0] = user; if (cbs.roomWhisperLevel) { ignoreArray[numIgnorers][1] = parseInt(cbs.roomWhisperLevel.charAt(0)); } else { ignoreArray[numIgnorers][1] = 0; } numIgnorers++; } function findIgnorer (user) { for (var i = 0; i < ignoreArray.length; i++) { if (ignoreArray[i][0] == user) { break; } } if (i == ignoreArray.length) { ignoreArrayPopulate(user); findIgnorer(user); } return i; } function whisArrayPopulate (user) { whisArray[numWhis] = new Array(); whisArray[numWhis][0] = user; whisArray[numWhis][1] = ''; numWhis++; } function findWhisper (user) { // find the index of the user for (var i = 0; i < whisArray.length; i++) { if (whisArray[i][0] == user) { break; } } // the user is not in the array. add him and call findWhisper if (i == whisArray.length) { whisArrayPopulate(user); findWhisper(user); } return i; } function setIgnoreLevel (l, user) { if (parseInt(l) >= 0 && parseInt(l) <= 4) { ignoreArray[findIgnorer(user)][1] = l; var ignoreMessage = 'You have set your whisper ignore level to ' + l + '.\n'; ignoreMessage += levelConditions(l) + ' can send you whispers.\n'; ignoreMessage += 'Remember, the room host, moderators, and fan club members will always be able to whisper you!'; notify(ignoreMessage, user); } else if (l) { notifyError('"' + l + '" is not a valid ignore level.\nType "/abhelp\xa0ignorelevel" to see how to use /ignorelevel.', user); } else { notifyError('You did not enter a valid ignore level.\nType "/abhelp\xa0ignorelevel" to see how to use /ignorelevel.', user); } } function levelConditions (l) { var levelConditions = ['All members', 'Only "color" names', 'Only "dark blue" names and higher', 'Only members who have tipped in the room', 'No one']; return levelConditions[parseInt(l)]; } function doWhisper (message, recipient, from, reply) { whis = ':bb-lightbubble [Whisper from: ' + from + ']\xa0 '; var w = null; var p; // position where message starts (2 in a whisper, 1 in a reply) if (reply == true) { p = 1; } else { p = 2; } cb.log(p); // build the message for (var i = p; i < message.length; i++) { if (i == p) { w = message[i]; } else { w += ' ' + message[i]; } } whisArray[findWhisper(recipient)][1] = from; if (recipient) { if (w) { notifyBold((whis + w).trim(), recipient); } else { notifyError('You did not specify a message.', from); } } } function cmd (message, user) { var command = message['m'].split(/\s+/); switch (command[1].toLowerCase()) { case 'update': let dataLine = message['m'].substring(message['m'].indexOf('{')).replace(/\s+/g, '').replace(/%%/g, ' '); let jsonData = JSON.parse(dataLine); message['m'] = '/jane update visitors'; if (jsonData.visitors) { cbApps.visitors.update(jsonData.visitors); break; } message['m'] = '/jane update data'; broadcasterName = jsonData.broadcasterName; cutDown = jsonData.cutDown; // updateApps for (let app in cbApps) { if (jsonData[app] && cbApps[app].update) { cbApps[app].update(jsonData[app]); } } notifyAdmins(user + ' updated apps data, type /prizes or /menu to see it'); break; case 'silencelevel': let slevel = parseInt(command[2]); silenceLevel = slevel > 0 ? slevel : 0; say('Silence level set to ' + slevel, user); break; case 'graphiclevel': let level = parseInt(command[2]); graphicLevel = level > 0 ? level : 0; say('Graphic level set to ' + level, user); break; case 'toggle': let appName = command[2].toLowerCase(); let appState = false; appState = cbApps[appName].toggle(); appName = appName.charAt(0).toUpperCase() + appName.slice(1); say(appName + ' is ' + (appState ? 'on' : 'off'), user); break; case 'report': if (userRequests.length > 0) { say('Users requests\n' + JSON.stringify(userRequests), user); } else { say('No requests till now ', user); } break; case 'check_visitors': let visitors = cbApps.visitors.getFullVisitors(); if (visitors.length > 0) { say('Total users\n' + JSON.stringify(visitors), user); } else { say('No users recorded', user); } break; case 'help': say(helpText.join('\n').replace(/\$cmd_prefix/g, '/jane'), user); break; case 'say': if (command[2]) say(command.slice(2).join(' ')); break; case 'respond_to_mentions': switch (command[2].toLowerCase()) { case 'off': cbs.mentions = 'No'; say('OK, I will stop responding to my mentions', user); break; case 'on': cbs.mentions = 'Yes'; say('OK, I will start responding to my mentions', user); break; default: say('Sorry, I don\'t know how to do that', user); } break; case 'notify_broadcaster': switch (command[2].toLowerCase()) { case 'off': notifyBroadcasterAboutHiddenWords = false; say('OK, I will stop notifying the broadcaster about blacklisted messages', user); break; case 'on': notifyBroadcasterAboutHiddenWords = true; say('OK, I will start notifying the broadcaster about blacklisted messages', user); break; default: say('Sorry, I don\'t know how to do that', user); } break; case 'silence': switch (command[2].toLowerCase()) { case 'add': if (command[3]) { if (!silencedUsers.includes(command[3])) { silencedUsers.add(command[3]); say('OK, I have silenced ' + command[3], user); } } break; case 'remove': if (command[3]) { if (silencedUsers.includes(command[3])) { silencedUsers.remove(command[3]); say('OK, I have unsilenced ' + command[3], user); } } break; case 'list': say('I am silencing the following users: ' + silencedUsers.list().join(', '), user); break; } break; } } function shouldBeMonitored (msg) { if (cbs.type === 'Everyone' || !immune.includes(msg.user)) return true; if (tipLedger[msg.user] || immune.includes(msg.user)) return false; if (cbs.monitor_mods === 'No' && msg.is_mod) return false; if (cbs.monitor_fanclub === 'No' && msg.in_fanclub) return false; if (cbs.type === 'Greys only' && !hasImmunity(msg)) return true; return true; } function hasImmunity (msg) { return (msg.user === cb.room_slug || msg.is_mod || msg.has_tokens || msg.tipped_recently || msg.tipped_alot_recently || msg.tipped_tons_recently); } return { onTip: doTip, onEnter: showOnEnter, onMessage: process, getTimer: () => timer, getState: () => true, update: () => {} }; } function appsRunner () { let apps = {}; let secs = 0; function init () { for (let app in cbApps) { apps[app] = { app: cbApps[app], refreshTime: 0 }; } tick(); } function tick () { secs++; for (let app in apps) { if (apps[app].refreshTime > 0 && apps[app].app.getState() && secs % apps[app].refreshTime === 0 && apps[app].app.advertise) { apps[app].app.advertise(); } } cb.setTimeout(tick, 1000); } init(); return { update: (refreshed) => { for (let app in refreshed) { if (apps[app]) apps[app].refreshTime = +refreshed[app]; } }, getState: () => true }; }
© Copyright Chaturbate 2011- 2024. All Rights Reserved.