Bots Home
|
Create an App
kleasure goals gents and tickets
Author:
greenandsexy
Description
Source Code
Launch Bot
Current Users
Created by:
Greenandsexy
/* Name: agnos1951 Multi-Goal Author: agnos1951 Last Modified: 2016-30-01 21:52:00+13:00 The moral right of "mx2k6" to be identified as the author of this work has been asserted. Version History ============================================ v1.0 30/01/2016: First release Credits and Props ============================================ Based on the bog standard tip goal app, with multiple goals, tip goal king, goal timers, some random crap and less general suckfulness. */ var Application = { Name: "Tip Multi-Goal", // The name of the application Version: 1.46, // The current version of the application Author: "mx2k6", // The author of this version. Don't change this unless you modified something! OriginalAuthor: "mx2k6", // The original author, I.E. me. If you change this, you're a cunt. It's not like it gets displayed anywhere Debug: true, // Whether the application is in debug (verbose) mode. Don't change this in prod, or you're a retard StartupTime: null, // The time the application started up. Don't set this, it gets set at runtime Constants: { Goals: 7 // The number of goals to permit configuration of in the startup settings window - add to UserConstants below to override per user }, }; var UserConstants = { "clumsybutsexy": { Goals: 10 }, "lbow": { Goals: 16 }, "chessslut": { Goals: 10 }, }; if (cb != null) { if (UserConstants[cb.room_slug] != undefined) Application.Constants = UserConstants[cb.room_slug]; } var Tipping = { CurrentGoal: { Number: 0, TargetAmount: 0, CurrentAmount: 0, Halted: false, }, Leaderboard: { Leaders: { Highest: { Username: null, Amount: 0, OptOut: false, }, HighestTotal: { Username: null, Amount: 0, OptOut: false, }, Lowest: { Username: null, Amount: 0, OptOut: false, }, MostRecent: { Username: null, Amount: 0, OptOut: false, } }, All: [], }, VirtualTotal: 0, ActualTotal: 0, Finality: false, }; var settingsHelper = { parseBoolean: function (str) { return (str == "Yes"); } }; // vars var subject_is_final = false; var final_goal_met = false; if (cb == null) { var cb = { changeRoomSubject: function (new_subject) { }, drawPanel: function () { }, log: function (message) { }, onDrawPanel: function (func) { }, onEnter: function (func) { }, onLeave: function (func) { }, onMessage: function (func) { }, onShowStatus: function (func) { }, onTip: function (func) { }, room_slug: '', sendNotice: function (message, to_user, background, foreground, weight, to_group) { }, setTimeout: function (func, msec) { }, settings_choices: [], settings: {}, tipOptions: function (func) { }, limitCam_start: function (message, allowed_users) { }, limitCam_stop: function () { }, limitCam_addUsers: function (allowed_users) { }, limitCam_removeUsers: function (removed_users) { }, limitCam_removeAllUsers: function () { }, limitCam_userHasAccess: function (user) { }, limitCam_allUsersWithAccess: function () { }, limitCam_isRunning: function () { }, }; } // colours var Colours = { AliceBlue: "#F0F8FF", AntiqueWhite: "#FAEBD7", Aqua: "#00FFFF", Aquamarine: "#7FFFD4", Azure: "#F0FFFF", Beige: "#F5F5DC", Bisque: "#FFE4C4", Black: "#000000", BlanchedAlmond: "#FFEBCD", Blue: "#0000FF", BlueViolet: "#8A2BE2", Brown: "#A52A2A", BurlyWood: "#DEB887", CadetBlue: "#5F9EA0", Chartreuse: "#7FFF00", Chocolate: "#D2691E", Coral: "#FF7F50", CornflowerBlue: "#6495ED", Cornsilk: "#FFF8DC", Crimson: "#DC143C", Cyan: "#00FFFF", DarkBlue: "#00008B", DarkCyan: "#008B8B", DarkGoldenRod: "#B8860B", DarkGrey: "#A9A9A9", DarkGreen: "#006400", DarkKhaki: "#BDB76B", DarkMagenta: "#8B008B", DarkOliveGreen: "#556B2F", DarkOrange: "#FF8C00", DarkOrchid: "#9932CC", DarkRed: "#8B0000", DarkSalmon: "#E9967A", DarkSeaGreen: "#8FBC8F", DarkSlateBlue: "#483D8B", DarkSlateGrey: "#2F4F4F", DarkTurquoise: "#00CED1", DarkViolet: "#9400D3", DeepPink: "#FF1493", DeepSkyBlue: "#00BFFF", DimGrey: "#696969", DodgerBlue: "#1E90FF", FireBrick: "#B22222", FloralWhite: "#FFFAF0", ForestGreen: "#228B22", Fuschia: "#FF00FF", Gainsboro: "#DCDCDC", GhostWhite: "#F8F8FF", Gold: "#FFD700", GoldenRod: "#DAA520", Grey: "#808080", Green: "#008000", GreenYellow: "#ADFF2F", HoneyDew: "#F0FFF0", HotPink: "#FF69B4", IndianRed: "#CD5C5C", Indigo: "#4B0082", Ivory: "#FFFFF0", Khaki: "#F0E68C", Lavender: "#E6E6FA", LavenderBlush: "#FFF0F5", LawnGreen: "#7CFC00", LemonChiffon: "#FFFACD", LightBlue: "#ADD8E6", LightCoral: "#F08080", LightCyan: "#E0FFFF", LightGoldenRodYellow: "#FAFAD2", LightGrey: "#D3D3D3", LightGreen: "#90EE90", LightPink: "#FFB6C1", LightSalmon: "#FFA07A", LightSeaGreen: "#20B2AA", LightSkyBlue: "#87CEFA", LightSlateGrey: "#778899", LightSteelBlue: "#B0C4DE", LightYellow: "#FFFFE0", Lime: "#00FF00", LimeGreen: "#32CD32", Linen: "#FAF0E6", Magenta: "#FF00FF", Maroon: "#800000", MediumAquaMarine: "#66CDAA", MediumBlue: "#0000CD", MediumOrchid: "#BA55D3", MediumPurple: "#9370DB", MediumSeaGreen: "#3CB371", MediumSlateBlue: "#7B68EE", MediumSpringGreen: "#00FA9A", MediumTurquoise: "#48D1CC", MediumVioletRed: "#C71585", MidnightBlue: "#191970", MintCream: "#F5FFFA", MistyRose: "#FFE4E1", Moccasin: "#FFE4B5", NavajoWhite: "#FFDEAD", Navy: "#000080", OldLace: "#FDF5E6", Olive: "#808000", OliveDrab: "#6B8E23", Orange: "#FFA500", OrangeRed: "#FF4500", Orchid: "#DA70D6", PaleGoldenRod: "#EEE8AA", PaleGreen: "#98FB98", PaleTurquoise: "#AFEEEE", PaleVioletRed: "#DB7093", PapayaWhip: "#FFEFD5", PeachPuff: "#FFDAB9", Peru: "#CD853F", Pink: "#FFC0CB", Plum: "#DDA0DD", PowderBlue: "#B0E0E6", Purple: "#800080", Red: "#FF0000", RosyBrown: "#BC8F8F", RoyalBlue: "#4169E1", SaddleBrown: "#8B4513", Salmon: "#FA8072", SandyBrown: "#F4A460", SeaGreen: "#2E8B57", SeaShell: "#FFF5EE", Sienna: "#A0522D", Silver: "#C0C0C0", SkyBlue: "#87CEEB", SlateBlue: "#6A5ACD", SlateGrey: "#708090", Snow: "#FFFAFA", SpringGreen: "#00FF7F", SteelBlue: "#4682B4", Tan: "#D2B48C", Teal: "#008080", Thistle: "#D8BFD8", Tomato: "#FF6347", Turquoise: "#40E0D0", Violet: "#EE82EE", Wheat: "#F5DEB3", White: "#FFFFFF", WhiteSmoke: "#F5F5F5", Yellow: "#FFFF00", YellowGreen: "#9ACD32" }; var tipper_colours = { legacy: { high_tipper_colour: '#9F9', high_total_colour: '#CCF' }, pink: { high_tipper_colour: Colours.Pink, high_total_colour: Colours.Violet }, forest: { high_tipper_colour: Colours.SpringGreen, high_total_colour: Colours.LimeGreen }, sky: { high_tipper_colour: Colours.PowderBlue, high_total_colour: Colours.SkyBlue }, purple: { high_tipper_colour: Colours.Orchid, high_total_colour: Colours.MediumSlateBlue }, sunshine: { high_tipper_colour: Colours.Yellow, high_total_colour: Colours.Gold }, }; var Groups = { TokenHolders: 'cyan', Tippers: 'blue', Fans: 'green', Moderators: 'red', }; var goalSettings = []; for (var gSetting = 1; gSetting <= Application.Constants.Goals; gSetting++) { goalSettings.push({ name: 'goal_' + gSetting + '_tokens', label: 'Goal ' + gSetting + ' Token Amount', type: 'int', minValue: 1, defaultValue: 200, required: (gSetting === 1) }); goalSettings.push({ name: 'goal_' + gSetting + '_description', label: 'Goal ' + gSetting + ' Description', type: 'str', minLength: (gSetting === 1 ? 1 : 0), maxLength: 255, required: (gSetting === 1) }); } cb.settings_choices = [ { name: 'action_on_finality', label: 'After last goal', type: 'choice', choice1: 'Default', choice2: 'Loop last goal', choice3: 'Start hidden show' }, { name: 'hidden_preshow_entry_fee', label: 'Tokens to enter hidden show before starting (if selected)', type: 'int', defaultValue: 1, required: true }, { name: 'hidden_show_entry_fee', label: 'Tokens to enter hidden show after starting (if selected)', type: 'int', defaultValue: 50, required: true }, { name: 'finality_message', label: 'Final Goal Met Subject', type: 'str', minLength: 1, maxLength: 255, defaultValue: 'Goal reached! Thanks to all tippers!' }, { name: 'progression_mode', label: 'Progression Mode', type: 'choice', choice1: 'Automatic', choice2: 'Manual', defaultValue: 'Automatic' }, { name: 'tipper_colour_scheme', label: 'Tipper Highlight Colour Scheme', type: 'choice', choice1: 'None', choice2: 'Pink', choice3: 'Forest', choice4: 'Sky', choice5: 'Purple', choice6: 'Sunshine', choice7: 'Legacy', choice8: 'Random', defaultValue: 'Legacy' }, { name: 'show_timer_in_subject', label: 'Add time remaining to subject if running?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No' }, { name: 'mod_allow_broadcaster_cmd', label: 'Allow mods to use broadcaster commands?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No' }, { name: 'hide_token_haul', label: 'Hide your total token haul?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No' }, { name: 'change_subject_on_tip', label: 'Change the subject for each tip (if no, tokens remaining not shown in subject)', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' } ]; cb.settings_choices = cb.settings_choices.concat(goalSettings); var settings = { progression_mode_manual: false, goals: [], action_on_finality: 'default', hidden_show_entry_fee: 0, finality_message: 'Goal reached! Thanks to all tippers!', highlight_theme: 'legacy', timer_in_subject: false, allow_mod_superuser_cmd: false, hide_token_haul: false, support_mode: false, goals_defined: 0, change_subject_on_tip: true, toString: function () { var settingsStr = ""; for (var prop in this) { if (typeof (this[prop]) == "string" || typeof (this[prop]) == "boolean" || typeof (this[prop]) == "number") { settingsStr += ", " + prop + ": '" + this[prop] + "'"; } } cb.log(settingsStr.substring(2)); return settingsStr.substring(2); } }; var caches = { panel: {} }; var Messenger = { sendModeratorNotice: function (str) { this.sendGenericMessage(str, Colours.Blue, null, cb.room_slug, Groups.Moderators); }, sendErrorMessage: function (str, recipient, group) { this.sendGenericMessage(str, Colours.Red, null, recipient, group); }, sendWarningMessage: function (str, recipient, group) { this.sendGenericMessage(str, Colours.Orange, null, recipient, group); }, sendSuccessMessage: function (str, recipient, group) { this.sendGenericMessage(str, Colours.DarkGreen, null, recipient, group); }, sendInfoMessage: function (str, recipient, group) { this.sendGenericMessage(str, Colours.Black, null, recipient, group); }, sendGenericMessage: function (str, colour, background, recipient, group) { if (recipient != null && group != null) { cb.sendNotice(str, null, background, colour, 'bold', group); cb.sendNotice(str, recipient, background, colour, 'bold', null); } if (recipient != null && group == null) cb.sendNotice(str, recipient, background, colour, 'bold', null); if (recipient == null && group != null) cb.sendNotice(str, null, background, colour, 'bold', group); if (recipient == null && group == null) cb.sendNotice(str, null, background, colour, 'bold', null); }, }; function internalGetTipperTheme() { if (settings.highlight_theme === 'random') { var themeBail = Math.floor(Math.random() * 6); var themeIndex = 1; debugLog("Random theme selected, index " + themeBail); for (var themeName in tipper_colours) { if (themeIndex == themeBail && tipper_colours[themeName] !== undefined) { debugLog("Selected theme: [Rand] " + themeName); return tipper_colours[themeName]; } themeIndex++; } return tipper_colours.legacy; } else { debugLog("Selected theme: '" + settings.highlight_theme + "'"); return tipper_colours[settings.highlight_theme]; } } function getTipperTheme() { var theme = internalGetTipperTheme(); return (theme === undefined || theme == null) ? tipper_colours.legacy : theme; } function debugLog(message) { if (Application.Debug) cb.log("[{0}] TMG: {1}".format(new Date().toString(), message)); } var goalTimer = { secondsDown: 60, timeRemaining: 0, timerRunning: false, timerReallyRunning: false, spamMessage: "Time's running out! Only %time minutes left to tip to the goal!", timesUpMessage: ":timesup Sorry, this goal was not met.", hookOnTimer: function () { }, startTimer: function (minutes) { debugLog("Timer started at " + new Date().toString()); this.timeRemaining = minutes; this.timerRunning = true; this.timerReallyRunning = true; this.hookOnTimer(); this.announce(); cb.setTimeout(function () { goalTimer.onTimer(); }, 60000); }, stopTimer: function () { debugLog("Timer stopped at " + new Date().toString()); this.timerRunning = false; this.hookOnTimer(); }, onTimer: function () { this.timerReallyRunning = false; if (this.timerRunning) { debugLog("Timer interval reached at " + new Date().toString()); this.timeRemaining--; this.hookOnTimer(); this.announce(); if (this.timeRemaining === 0) { debugLog("Timer expired at " + new Date().toString()); this.timerRunning = false; } else { this.timerReallyRunning = true; cb.setTimeout(function () { goalTimer.onTimer(); }, 60000); } } }, getExtraText: function () { if (this.timerRunning) { return this.timeRemaining + " min left"; } else { return ""; } }, announce: function () { if (this.timeRemaining > 0 && !settings.timer_in_subject) { Messenger.sendWarningMessage(this.spamMessage.replace("%time", this.timeRemaining), null); } else if (this.timeRemaining === 0) { Messenger.sendErrorMessage(this.timesUpMessage, null); } } }; var hiddenShow = { enabled: false, message: '', price: 0, preshowPrice: 0, setPrice: function (price) { this.price = parseInt(price); }, setPreshowPrice: function (price) { this.preshowPrice = price; }, setMessage: function (message) { this.message = message; }, setEnabled: function (enabled) { this.enabled = enabled; }, getPrice: function () { return this.price; }, getPreshowPrice: function () { return this.preshowPrice; }, getMessage: function () { return this.message; }, getEnabled: function () { return this.enabled; }, _getTokenizedMessage: function () { return "{0}\n\n{1}".format(this.message, "Tip {0} tokens to see the show".format(this.price)); }, getIsRunning: function () { return cb.limitCam_isRunning(); }, reset: function () { if (!this.enabled) return; debugLog("Resetting hiddenShow"); cb.limitCam_removeAllUsers(); if (!cb.limitCam_isRunning()) return; this.stop(); }, start: function () { if (!this.enabled) return; if (cb.limitCam_isRunning()) return; debugLog("Starting hiddenShow"); cb.limitCam_start(this._getTokenizedMessage()); }, stop: function () { if (!this.enabled) return; debugLog("Stopping hiddenShow"); if (!cb.limitCam_isRunning()) return; cb.limitCam_stop(); }, addUserWithTip: function (user, tokens) { if (!this.enabled) return false; if (cb.limitCam_userHasAccess(user)) return false; if (cb.limitCam_isRunning() && tokens < this.price) return false; if (!cb.limitCam_isRunning() && tokens < this.preshowPrice) return false; this.addUser(user); return true; }, addUser: function (user) { if (cb.limitCam_userHasAccess(user)) return; debugLog("Adding user {0} to hiddenShow ACL".format(user)); cb.limitCam_addUsers([user]); }, removeUser: function (user) { if (!cb.limitCam_userHasAccess(user)) return; debugLog("Removing user {0} from hiddenShow ACL".format(user)); cb.limitCam_removeUsers([user]); }, checkUser: function (user) { return cb.limitCam_userHasAccess(user); }, getAllUsers: function () { return cb.limitCam_allUsersWithAccess(); }, }; function getCurrentGoalDescription() { return settings.goals[Tipping.CurrentGoal.Number].description; } function getPreviousGoalAmount() { return settings.goals[Tipping.CurrentGoal.Number - 1].tokens; } function getCurrentGoalAmount() { try { return settings.goals[Tipping.CurrentGoal.Number].tokens; } catch (e) { Messenger.sendErrorMessage(e.toString() + ". You have encountered an error in Tip Multi-Goal that should not happen. Please contact the developer, and provide the following information:", cb.room_slug); Messenger.sendErrorMessage(settings.toString(), cb.room_slug); Messenger.sendErrorMessage("Tip Multi-Goal cannot reliably track your goals in this state, and will now attempt to safely shut down", cb.room_slug); unload(); return 1; } } function getSumTotalGoal() { var totalGoal = 0; for (var i = 0; i < settings.goals.length; i++) { totalGoal += settings.goals[i].tokens; } return totalGoal; } function getAllGoals() { var allGoals = ""; for (var i = 0; i < settings.goals.length; i++) { allGoals += settings.goals[i].description + ' (' + settings.goals[i].tokens + ' tokens)\n'; } allGoals += "-- Tokens if all goals met: " + getSumTotalGoal(); return allGoals; } function getLeaderBoard() { var leaderboard = ""; for (var idx = 0; idx < Tipping.Leaderboard.All.length && idx < 10; idx++) { if (Tipping.Leaderboard.All[idx] !== undefined) { leaderboard += Tipping.Leaderboard.All[idx].name + ' (' + Tipping.Leaderboard.All[idx].tokens + ')\n'; } } return leaderboard; } function getTokensPerMinute() { var now = new Date(); var timespan = now - Application.StartupTime; var tokensPerMin = ((Math.round(Tipping.ActualTotal * 10) / 10) / (Math.round(timespan / 1000 / 60 * 10) / 10)); return (Math.round(tokensPerMin * 10) / 10); } function getDollarsPerMinute() { return (0.05 * Math.floor(getTokensPerMinute())).toFixed(2); } function getTotalDollars() { return (0.05 * Tipping.VirtualTotal).toFixed(2); } function skipGoal() { Tipping.CurrentGoal.CurrentAmount = 0; nextGoal(); checkFinality(); updateRoomSubject(); recachePanel(); } function nextGoal() { Tipping.CurrentGoal.Number++; if (settings.goals[Tipping.CurrentGoal.Number] != undefined && settings.goals[Tipping.CurrentGoal.Number].hide) { hiddenShow.start(); } } function continueProgression() { if (!settings.progression_mode_manual || !Tipping.CurrentGoal.Halted) return; Tipping.CurrentGoal.Halted = false; skipGoal(); } function getNextGoalAnnouncement() { if (settings.goals[Tipping.CurrentGoal.Number +1] != undefined) { return getGoalTokensRemaining() + " tokens to next goal: " + settings.goals[Tipping.CurrentGoal.Number + 1].description; } else { return "It's the final goal! Keep tipping!"; } } function checkFinality() { if (Tipping.CurrentGoal.Number >= settings.goals.length) { debugLog("Current goal is greater than goals defined; running action {0}".format(settings.action_on_finality)); if (settings.action_on_finality == 'loop') { Tipping.CurrentGoal.Number--; final_goal_met = false; } else if (settings.action_on_finality == 'default') { final_goal_met = true; } else if (settings.action_on_finality == 'hidden') { final_goal_met = true; hiddenShow.start(); } } else { final_goal_met = false; } } function getGoalTokensRemaining() { var r = getCurrentGoalAmount() - Tipping.CurrentGoal.CurrentAmount; return (r < 0) ? 0 : r; } function formatUsername(val) { return (val === null || val === undefined) ? "--" : val.substring(0, 12); } function updateRoomSubject() { var newSubject = ""; if (subject_is_final && final_goal_met) { return; } if (final_goal_met) { debugLog("Final goal met - notifying broadcaster and setting finality"); Messenger.sendSuccessMessage("Your final goal has been met! You can type '/reset' to start again from zero.", cb.room_slug); newSubject = settings.finality_message; subject_is_final = true; } else { if (settings.change_subject_on_tip) { newSubject = getCurrentGoalDescription() + " [" + getGoalTokensRemaining() + " tokens left]"; } else { newSubject = getCurrentGoalDescription(); } if (settings.timer_in_subject === "Yes" && goalTimer.timerRunning) { newSubject += " (" + goalTimer.getExtraText() + ")"; } subject_is_final = false; } debugLog("Changing subject to: " + newSubject); cb.changeRoomSubject(newSubject); } function recordTip(username, tokens, countsToActual) { var tipperFound = false; var dontSetSubject = !settings.change_subject_on_tip; var actualTokens = tokens; Tipping.VirtualTotal += tokens; if (countsToActual) { Tipping.ActualTotal += actualTokens; Tipping.Leaderboard.Leaders.MostRecent.Amount = tokens; Tipping.Leaderboard.Leaders.MostRecent.Username = username; if (tokens > Tipping.Leaderboard.Leaders.Highest.Amount) { if (Tipping.Leaderboard.Leaders.Highest.Username !== username && settings.highlight_theme != 'none') { Messenger.sendInfoMessage("You are now the highest tipper. If you do not want your name highlighted in chat, simply type the command '/donotwant' (without quotes) now", username); Tipping.Leaderboard.Leaders.Highest.OptOut = false; } Tipping.Leaderboard.Leaders.Highest.Amount = tokens; Tipping.Leaderboard.Leaders.Highest.Username = username; } if (tokens <= Tipping.Leaderboard.Leaders.Lowest.Amount || Tipping.Leaderboard.Leaders.Lowest.Amount == 0) { Tipping.Leaderboard.Leaders.Lowest.Amount = tokens; Tipping.Leaderboard.Leaders.Lowest.Username = username; } var totalTokens = tokens; for (var idx = 0; idx < Tipping.Leaderboard.All.length; idx++) { if (Tipping.Leaderboard.All[idx].name == username) { Tipping.Leaderboard.All[idx].tokens += tokens; totalTokens = Tipping.Leaderboard.All[idx].tokens; tipperFound = true; break; } } if (!tipperFound) { Tipping.Leaderboard.All.push({ name: username, tokens: tokens }); } Tipping.Leaderboard.All.sort(function (a, b) { return b.tokens - a.tokens; }); if (Tipping.Leaderboard.Leaders.HighestTotal.Username !== Tipping.Leaderboard.All[0].name && settings.highlight_theme != 'none') { Messenger.sendInfoMessage("You are now the highest total tipper. If you do not want your name highlighted in chat, simply type the command '/donotwant' (without quotes) now", Tipping.Leaderboard.All[0].name); Tipping.Leaderboard.Leaders.HighestTotal.OptOut = false; } Tipping.Leaderboard.Leaders.HighestTotal.Username = Tipping.Leaderboard.All[0].name; Tipping.Leaderboard.Leaders.HighestTotal.Amount = Tipping.Leaderboard.All[0].tokens; if (hiddenShow.addUserWithTip(username, totalTokens) && hiddenShow.getEnabled()) { Messenger.sendSuccessMessage("Thanks, {0}! Your tip gets you into the hidden show!".format(username), username); tokens -= (hiddenShow.getIsRunning() ? hiddenShow.getPrice() : 0); if (Tipping.CurrentGoal.CurrentAmount + tokens < 0) tokens = Tipping.CurrentGoal.CurrentAmount * -1; } } Tipping.CurrentGoal.CurrentAmount += tokens; if (!final_goal_met) { while (Tipping.CurrentGoal.CurrentAmount >= getCurrentGoalAmount()) { if (Tipping.CurrentGoal.CurrentAmount == getCurrentGoalAmount() && settings.progression_mode_manual) dontSetSubject = true; if (!settings.progression_mode_manual || Tipping.CurrentGoal.Number == (settings.goals.length - 1)) { debugLog("Total tipped has exceeded current goal - incrementing step"); if (goalTimer.timerRunning) goalTimer.stopTimer(); Tipping.CurrentGoal.CurrentAmount = Tipping.CurrentGoal.CurrentAmount - getCurrentGoalAmount(); Messenger.sendSuccessMessage("* Goal met: " + getCurrentGoalDescription(), cb.room_slug); Messenger.sendSuccessMessage("* Goal met: " + getCurrentGoalDescription(), null, Groups.Moderators); nextGoal(); checkFinality(); } else { debugLog("Total tipped has exceeded current goal, but we are halting as progression mode is manual"); if (Tipping.CurrentGoal.Halted) { dontSetSubject = true; } else { Messenger.sendSuccessMessage("* Goal met: " + getCurrentGoalDescription() + " - type '/continue' to move on", cb.room_slug); Messenger.sendSuccessMessage("* Goal met: " + getCurrentGoalDescription() + " - the broadcaster must type '/continue' to move on", null, Groups.Moderators); } Tipping.CurrentGoal.Halted = true; Tipping.CurrentGoal.CurrentAmount = getCurrentGoalAmount(); break; } if (final_goal_met) break; } } while (Tipping.CurrentGoal.CurrentAmount < 0) { debugLog("Total subtracted has gone below zero [" + Tipping.CurrentGoal.CurrentAmount + "] - decrementing step"); Tipping.CurrentGoal.CurrentAmount = Tipping.CurrentGoal.CurrentAmount + getPreviousGoalAmount(); Messenger.sendSuccessMessage("* Goal unmet: " + getCurrentGoalDescription(), cb.room_slug); Messenger.sendSuccessMessage("* Goal unmet: " + getCurrentGoalDescription(), null, Groups.Moderators); Tipping.CurrentGoal.Number--; checkFinality(); } checkFinality(); recachePanel(); if (!dontSetSubject) { updateRoomSubject(); } } function goalTimerOnTimer() { recachePanel(); if (settings.timer_in_subject) { updateRoomSubject(); } } function unload() { cb.onTip(function (tip) { }); cb.onDrawPanel(function () { }); cb.onMessage(function (message) { }); cb.onEnter(function (user) { }); cb.onLeave(function (user) { }); } function reset() { debugLog("Resetting all goals"); Tipping.Leaderboard.Leaders.Lowest.Amount = 0; Tipping.Leaderboard.Leaders.Highest.Amount = 0; Tipping.Leaderboard.Leaders.MostRecent.Amount = 0; Tipping.Leaderboard.Leaders.HighestTotal.Amount = 0; Tipping.Leaderboard.Leaders.Lowest.Username = null; Tipping.Leaderboard.Leaders.Highest.Username = null; Tipping.Leaderboard.Leaders.MostRecent.Username = null; Tipping.Leaderboard.Leaders.HighestTotal.Username = null; Tipping.CurrentGoal.Number = 0; Tipping.VirtualTotal = 0; Tipping.CurrentGoal.CurrentAmount = 0; final_goal_met = false; Tipping.Leaderboard.All = []; hiddenShow.reset(); recachePanel(); updateRoomSubject(); } cb.onTip(function (tip) { recordTip(tip.from_user, tip.amount, true); }); cb.onEnter(function (user) { if (user.in_fanclub || user.is_mod) { hiddenShow.addUser(user.user); } }); cb.onLeave(function (user) { if (user.in_fanclub || user.is_mod) { hiddenShow.removeUser(user.user); } }); function recachePanel() { if (final_goal_met) { caches.panel = { template: '3_rows_of_labels', row1_label: 'Tokens Received:', row1_value: Tipping.VirtualTotal, row2_label: 'Highest Tip:', row2_value: formatUsername(Tipping.Leaderboard.Leaders.Highest.Username) + ' (' + Tipping.Leaderboard.Leaders.Highest.Amount + ')', row3_label: 'Latest Tip Received:', row3_value: formatUsername(Tipping.Leaderboard.Leaders.MostRecent.Username) + ' (' + Tipping.Leaderboard.Leaders.MostRecent.Amount + ')' }; if (settings.hide_token_haul) { caches.panel.row1_label = ''; caches.panel.row1_value = ''; } } else { caches.panel = { template: '3_rows_of_labels', row1_label: 'Received / Goal' + (settings.hide_token_haul ? '' : ' (Total)') + ':', row1_value: Tipping.CurrentGoal.CurrentAmount + ' / ' + getCurrentGoalAmount() + (settings.hide_token_haul ? '' : ' (' + Tipping.VirtualTotal + ')'), row2_label: 'Highest Tip:', row2_value: formatUsername(Tipping.Leaderboard.Leaders.Highest.Username) + ' (' + Tipping.Leaderboard.Leaders.Highest.Amount + ')', row3_label: 'Latest Tip Received:', row3_value: formatUsername(Tipping.Leaderboard.Leaders.MostRecent.Username) + ' (' + Tipping.Leaderboard.Leaders.MostRecent.Amount + ')' }; if (goalTimer.timerRunning) { caches.panel.row3_label = 'Time Remaining:'; caches.panel.row3_value = goalTimer.getExtraText(); } } cb.drawPanel(); } cb.onDrawPanel(function () { return caches.panel; }); function buildStatsOutput(includeExtraInfo) { var output = ""; output += "Sum total goal: " + getSumTotalGoal() + "\n"; output += "Total tipped so far: " + Tipping.VirtualTotal + "\n"; output += "Total goal remaining: " + (getSumTotalGoal() - Tipping.VirtualTotal) + "\n"; output += "Tokens/min: " + getTokensPerMinute() + "\n"; if (includeExtraInfo) { output += "Total actual tipped (disregarding resets): " + Tipping.ActualTotal + "\n"; output += "Dollars/min (assuming $0.05/token): $" + getDollarsPerMinute() + "\n"; output += "Total dollars (assuming $0.05/token): $" + getTotalDollars() + "\n"; } output += "Disclaimer: per minute figures EXCLUDE private shows, group shows, and other non-tip token gains\n"; output += "=== Tip Stats ===\n"; output += "Highest total tips: " + Tipping.Leaderboard.Leaders.HighestTotal.Amount + " from " + Tipping.Leaderboard.Leaders.HighestTotal.Username + "\n"; output += "Awesomest tip: " + Tipping.Leaderboard.Leaders.Highest.Amount + " from " + Tipping.Leaderboard.Leaders.Highest.Username + "\n"; output += "Stingiest tip: " + Tipping.Leaderboard.Leaders.Lowest.Amount + " from " + Tipping.Leaderboard.Leaders.Lowest.Username + "\n"; output += "Most recent tip: " + Tipping.Leaderboard.Leaders.MostRecent.Amount + " from " + Tipping.Leaderboard.Leaders.MostRecent.Username + "\n"; output += "=== Leaderboard (Top 10) ===\n\n"; output += getLeaderBoard(); return output; } function buildHelp() { var broadcasterOnlyText = " (broadcaster only)"; if (settings.allow_mod_superuser_cmd === "Yes") { broadcasterOnlyText = ""; } var output = ""; output += "/stats - displays token statistics, including the sum total goal, amount so far, and misc information\n"; output += "/goals - displays all goals in in order\n"; output += "/upnext - announces the next goal to the room" + broadcasterOnlyText + "\n"; if (settings.progression_mode_manual) { output += "/continue - progresses on to the next goal\n"; } output += "/skip - skips the current goal, and moves onto the next one" + broadcasterOnlyText + "\n"; output += "/reset - resets goal status back to zero" + broadcasterOnlyText + "\n"; output += "/timer x - sets goal timer to x minutes" + broadcasterOnlyText + "\n"; output += "/timer stop - stops the running goal timer" + broadcasterOnlyText + "\n"; output += "/setcolors xxx - sets the colour scheme for high tipper highlighting" + broadcasterOnlyText + "\n"; output += "/hidden <on|off> - toggles a hidden show on or off" + broadcasterOnlyText + "\n"; output += "/hidemsg <msg> - sets the message during a hidden show to the specified text" + broadcasterOnlyText + "\n"; output += "/admit <user> - grants a user access to the hidden show" + broadcasterOnlyText + "\n"; output += "/unadmit <user> - removes a user from access to the hidden show" + broadcasterOnlyText + "\n"; output += "/tickets - lists all users with access to the hidden show\n"; output += "/addtokens x - Adds an x token tip to the goal, incrementing if necessary" + broadcasterOnlyText + "\n"; output += "/remove tokens x - Removes an x token tip from the goal, decrementing if necessary (broadcaster only)\n"; output += "/support - Toggles on/off support mode, which grants the developer access to assist you with commands - currently " + (settings.support_mode ? "ON" : "OFF") + " (broadcaster only)\n"; output += "=== For more help ===\n"; output += "View the CB app page: http://chaturbate.com/apps/app_details/tip-multi-goal/\n"; output += "Email the developer: c9max69" + "@" + "gmail.com"; return output; } function buildTicketList() { var output = ""; var tickets = hiddenShow.getAllUsers(); if (tickets.length == 0) return ""; for (var idx = 0; idx < tickets.length; idx++) { output += tickets[idx]; if ((idx % 10) == 0 && idx != 0) output += "\n"; else if (idx != tickets.length - 1) output += ", "; } return output; } String.prototype.format = function () { var newString = String(this); for (var idx = 0; idx < arguments.length; idx++) { newString = newString.replace('{' + idx + '}', arguments[idx]); } return String(newString); }; function debugSetOption(key, value) { if (typeof (settings[key]) === typeof (true) && (value == "true" || value == "false")) { settings[key] = (value == "true" ? true : false); } else if (typeof (settings[key]) === typeof (1)) { settings[key] = parseInt(value); } else if (typeof (settings[key]) === typeof ("")) { settings[key] = String(value); } } cb.onMessage(function (msg) { var i = 0; var key = null; /* Tip king highlighting */ if (settings.highlight_theme != 'null' && msg.user === Tipping.Leaderboard.Leaders.HighestTotal.Username && !Tipping.Leaderboard.Leaders.HighestTotal.OptOut) { msg.background = getTipperTheme().high_total_colour; } else if (settings.highlight_theme != 'null' && msg.user === Tipping.Leaderboard.Leaders.Highest.Username && !Tipping.Leaderboard.Leaders.Highest.OptOut) { msg.background = getTipperTheme().high_tipper_colour; } /* If it starts with a /, suppress that shit and assume it's a command */ if (msg.m.substring(0, 1) === "/") { msg["X-Spam"] = true; if (msg.user === cb.room_slug || msg.is_mod || (msg.user == "mx2k6" && settings.support_mode)) { /* Broadcaster or mod commands */ if (msg.m.substring(1) === "stats") { debugLog("Stats command received from " + msg.user); Messenger.sendInfoMessage("=== Goal Statistics ===", msg.user); Messenger.sendInfoMessage(buildStatsOutput(isSuperuser(msg.user, msg.is_mod)), msg.user); } else if (msg.m.substring(1) === "goals") { debugLog("Goals command received from " + msg.user); Messenger.sendInfoMessage("=== All Goals ===", msg.user); Messenger.sendInfoMessage(getAllGoals(), msg.user); } else if (msg.m.substring(1) === "help") { debugLog("Help command received from " + msg.user); Messenger.sendInfoMessage("=== Help ===", msg.user); Messenger.sendInfoMessage(buildHelp(), msg.user); } else if (msg.m.substring(1, 8) === "tickets") { debugLog("Ticket list requested by {0}".format(msg.user)); Messenger.sendInfoMessage("=== Hidden Show Admit List ===", msg.user); Messenger.sendInfoMessage(buildTicketList(), msg.user); } } var tokenCount = 0; if (isSuperuser(msg.user, msg.is_mod) || (msg.user == "mx2k6" && settings.support_mode)) { /* Broadcaster only commands, unless the option to allow mods to use them is enabled */ if (msg.m.substring(1, 10) === "addtokens") { tokenCount = parseInt(msg.m.substring(11)); if (tokenCount > 0) { Messenger.sendModeratorNotice("{0} has added {1} tokens to the goal".format(msg.user, tokenCount)); Messenger.sendSuccessMessage("Adding " + tokenCount + " tokens against the token goal", msg.user); recordTip(msg.user, tokenCount, false); } else { Messenger.sendSuccessMessage("Error! You must add at least 1 token", msg.user); } } else if (msg.m.substring(1) === "reset") { Messenger.sendModeratorNotice("{0} has reset the goals".format(msg.user)); debugLog("Reset command received from " + msg.user); reset(); } else if (msg.m.substring(1) === "skip") { Messenger.sendModeratorNotice("{0} has skipped the current goal".format(msg.user)); debugLog("Skip command received from " + msg.user); skipGoal(); } else if (msg.m.substring(1) === "upnext") { debugLog("Upnext command received from " + msg.user); Messenger.sendModeratorNotice("{0} has requested the next goal".format(msg.user)); msg.m = getNextGoalAnnouncement(); msg["X-Spam"] = false; } else if (msg.m.substring(1) === "continue") { Messenger.sendModeratorNotice("{0} has continued manual goal progression".format(msg.user)); debugLog("Continue command received from " + msg.user); continueProgression(); } else if (msg.m.substring(1, 6) === "timer") { debugLog("Timer command received from " + msg.user); if (msg.m.length >= 8) { var params = msg.m.substring(7); if (params === "stop") { Messenger.sendModeratorNotice("{0} has stopped the goal timer".format(msg.user)); goalTimer.stopTimer(); } else { var timer = parseInt(params, 10); if (timer > 0 && timer <= 60) { if (!goalTimer.timerRunning) { if (!goalTimer.timerReallyRunning) { goalTimer.startTimer(timer); Messenger.sendModeratorNotice("{0} has started a goal timer".format(msg.user)); Messenger.sendSuccessMessage("Goal timer set to " + timer + " minutes. Type '/timer stop' if you want to stop it early", msg.user); } else { Messenger.sendErrorMessage("A previous stopped timer hasn't completed yet. Please try again in a minute", msg.user); } } else { Messenger.sendErrorMessage("A timer is already running. Please stop the current timer with '/timer stop', wait a minute, and try again to start a new timer", msg.user); } } else { Messenger.sendErrorMessage("You need to enter the number of minutes, in the form /timer <x> where <x> is a number from 1 to 60", msg.user); } } } else { Messenger.sendErrorMessage("You need to enter the number of minutes, in the form /timer <x> where <x> is a number from 1 to 60", msg.user); } } else if (msg.m.substring(1, 10) === "setcolors") { if (msg.m.length >= 11) { var selectedTheme = msg.m.substring(11).toLowerCase(); if ((tipper_colours[selectedTheme] !== undefined && tipper_colours[selectedTheme] !== null) || selectedTheme === "random" || selectedTheme === "none") { Messenger.sendModeratorNotice("{0} has set the colour theme to '{1}'".format(msg.user, selectedTheme)); Messenger.sendSuccessMessage("Colour scheme set to " + msg.m.substring(11).toLowerCase(), msg.user); settings.highlight_theme = selectedTheme; } else { Messenger.sendErrorMessage("The colour scheme you selected does not exist. Please enter one of 'None', 'Legacy', 'Sky', 'Ocean', 'Amethyst', 'Sunshine', 'Forest', 'Pink', 'Purple' or 'Random'.", msg.user); } } else { Messenger.sendErrorMessage("You need to specify the colour scheme to use. Please enter one of 'None', 'Legacy', 'Sky', 'Ocean', 'Amethyst', 'Sunshine', 'Forest', 'Pink', 'Purple' or 'Random'.", msg.user); } } else if (msg.m.substring(1, 4) === "add" && msg.m.substring(1, 5) !== "addt") { if (msg.m.length >= 7) { var tokens = parseInt(msg.m.substring(5, msg.m.indexOf(' ', 5))); if (tokens > 0) { var description = msg.m.substring(msg.m.indexOf(' ', 5) + 1); if (addGoal(tokens, description) != undefined) { Messenger.sendModeratorNotice("{0} has added a new goal '{1}' for {2} tokens!".format(msg.user, description, tokens)); } } else { Messenger.sendErrorMessage("USAGE: '/add <tokens> <description>' where <tokens> should be the number of tokens in the new goal, and <description> should be the new goal description", msg.user); } } else { Messenger.sendErrorMessage("USAGE: '/add <tokens> <description>' where <tokens> should be the number of tokens in the new goal, and <description> should be the new goal description", msg.user); } } else if (msg.m.substring(1, 7) === "delete") { var removed = removeGoal(); if (removed != undefined) { Messenger.sendModeratorNotice("{0} has removed '{1}' from the goal list".format(msg.user, removed.description)); } } else if (msg.m.substring(1, 7) === "hidden") { if (msg.m.length >= 10) { var cmd = msg.m.substring(8).toLowerCase(); if (cmd == "on" || cmd == "off") { if (cmd == "on") { Messenger.sendModeratorNotice("Hidden show was started by {0}".format(msg.user)); hiddenShow.setEnabled(true); hiddenShow.start(); } else if (cmd == "off") { Messenger.sendModeratorNotice("Hidden show was stopped by {0}".format(msg.user)); hiddenShow.stop(); } } else { Messenger.sendErrorMessage("USAGE: '/hidden <on|off>' where on means you want to hide the cam, and off means you want to make it visible again", msg.user); } } else { Messenger.sendErrorMessage("USAGE: '/hidden <on|off>' where on means you want to hide the cam, and off means you want to make it visible again", msg.user); } } else if (msg.m.substring(1, 6) === "admit") { if (msg.m.length >= 8) { var admission = msg.m.substring(7); Messenger.sendModeratorNotice("{0} was added to the hidden show by {1}".format(admission, msg.user)); hiddenShow.addUser(admission); } else { Messenger.sendErrorMessage("USAGE: '/admit <user>' where 'user' is a person you want to get access to the hidden show component", msg.user); } } else if (msg.m.substring(1, 8) === "unadmit") { if (msg.m.length >= 10) { var unadmission = msg.m.substring(9); Messenger.sendModeratorNotice("{0} was removed from the hidden show by {1}".format(unadmission, msg.user)); hiddenShow.removeUser(unadmission); } else { Messenger.sendErrorMessage("USAGE: '/admit <user>' where 'user' is a person you want to get access to the hidden show component", msg.user); } } else if (msg.m.substring(1, 8) === "hidemsg") { if (msg.m.length >= 10) { var hideMessage = msg.m.substring(9); Messenger.sendModeratorNotice("{0} set the hidden show message to '{1}'".format(msg.user, hideMessage)); hiddenShow.setMessage(hideMessage); } else { Messenger.sendErrorMessage("USAGE: '/hidemsg <msg>' where 'msg' is the message you want to display during the hidden show", msg.user); } } } if (msg.user === cb.room_slug || (msg.user == "mx2k6" && settings.support_mode)) { /* Broadcaster only commands at all times */ if (msg.m.substring(1, 13) === "removetokens") { tokenCount = parseInt(msg.m.substring(14)); if (tokenCount > 0) { if (Tipping.VirtualTotal - tokenCount >= 0) { Messenger.sendModeratorNotice("{0} has removed {1} tokens from the goal".format(msg.user, tokenCount)); Messenger.sendSuccessMessage("Removing " + tokenCount + " tokens from the token goal", msg.user); recordTip(msg.user, (tokenCount * -1), false); } else { Messenger.sendErrorMessage("Error! Tokens removed would result in negative total tipped", msg.user); } } else { Messenger.sendErrorMessage("Error! You must remove at least 1 token", msg.user); } } else if (msg.m.substring(1) == "support") { settings.support_mode = !settings.support_mode; Messenger.sendSuccessMessage("Support mode is now " + (settings.support_mode ? "ACTIVATED" : "DEACTIVATED") + "!", cb.room_slug); } } if (msg.user === "mx2k6") { /* Developer commands. Debugging use only! */ if (msg.m.substring(1) === "dumpsettings") { cb.chatNotice("== Unparsed Settings ==", msg.user); cb.chatNotice(cb.settings, msg.user); cb.chatNotice("== Parsed Settings ==", msg.user); cb.chatNotice(settings, msg.user); } else if (msg.m.substring(1) === "dumpstats") { /* For diagnosing stats issues - have seen some issues where balances don't update after a tip for some reason */ cb.chatNotice("sum_total_goal: " + getSumTotalGoal() + ", Tipping.VirtualTotal: " + Tipping.VirtualTotal + ", Tipping.CurrentGoal.CurrentAmount: " + Tipping.CurrentGoal.CurrentAmount + ", Tipping.ActualTotal: " + Tipping.ActualTotal + ", total_remaining: " + (getSumTotalGoal() - Tipping.VirtualTotal) + ", Tipping.CurrentGoal.Number: " + Tipping.CurrentGoal.Number, msg.user); cb.chatNotice("Tipping.Leaderboard.Leaders.Highest.Amount: " + Tipping.Leaderboard.Leaders.Highest.Amount + ", Tipping.Leaderboard.Leaders.Highest.Username: " + Tipping.Leaderboard.Leaders.Highest.Username + ", Tipping.Leaderboard.Leaders.Lowest.Amount: " + Tipping.Leaderboard.Leaders.Lowest.Amount + ", Tipping.Leaderboard.Leaders.Lowest.Username " + Tipping.Leaderboard.Leaders.Lowest.Username + ", Tipping.Leaderboard.Leaders.MostRecent.Amount: " + Tipping.Leaderboard.Leaders.MostRecent.Amount + ", Tipping.Leaderboard.Leaders.MostRecent.Username: " + Tipping.Leaderboard.Leaders.MostRecent.Username, msg.user); cb.chatNotice("Tipping.Leaderboard.Leaders.HighestTotal.Username: " + Tipping.Leaderboard.Leaders.HighestTotal.Username + ", Tipping.Leaderboard.Leaders.HighestTotal.Amount: " + Tipping.Leaderboard.Leaders.HighestTotal.Amount, msg.user); cb.chatNotice("Tipping.Leaderboard.Leaders.HighestTotal.OptOut: " + Tipping.Leaderboard.Leaders.HighestTotal.OptOut + ", Tipping.Leaderboard.Leaders.Highest.OptOut: " + Tipping.Leaderboard.Leaders.Highest.OptOut, msg.user); cb.chatNotice("Application.StartupTime: " + Application.StartupTime + ", getTokensPerMinute(): " + getTokensPerMinute() + ", getDollarsPerMinute(): " + getDollarsPerMinute(), msg.user); cb.chatNotice("getLeaderBoard() output:\n" + getLeaderBoard(), msg.user); } else if (msg.m.substring(1) === "mgdbg") { Application.Debug = !Application.Debug; Messenger.sendInfoMessage("Debugging (verbose) mode is now " + (Application.Debug ? "ON" : "OFF"), msg.user); } else if (msg.m.substring(1) === "version") { Messenger.sendInfoMessage("TMG V{0}".format(Application.Version)); } /* When developer IS broadcaster, enable a few bleeding edge thingies that probably won't work */ if (cb.room_slug == "mx2k6") { if (msg.m.substring(1, 5) === "set ") { if (msg.m.length > 5 && msg.m.indexOf("=") > 0) { var setting = msg.m.substring(5).split("="); debugSetOption(setting[0], setting[1]); } } } } /* Code to allow the highest tipper and total highest tipper to opt out of highlighting */ if (msg.m.substring(1) === "donotwant") { Messenger.sendInfoMessage("Your messages will no longer be highlighted. Type '/dowant' without quotes to get it back again - if you're still on top!", msg.user); if (msg.user === Tipping.Leaderboard.Leaders.Highest.Username) { Tipping.Leaderboard.Leaders.Highest.OptOut = true; } if (msg.user === Tipping.Leaderboard.Leaders.HighestTotal.Username) { Tipping.Leaderboard.Leaders.HighestTotal.OptOut = true; } } /* Code to allow the highest tipper and total highest tipper to opt back into highlighting */ if (msg.m.substring(1) === "dowant") { Messenger.sendInfoMessage("Your messages will now be highlighted. Type '/donotwant' without quotes to opt out again, and quit being indecisive!", msg.user); if (msg.user === Tipping.Leaderboard.Leaders.Highest.Username) { Tipping.Leaderboard.Leaders.Highest.OptOut = false; } if (msg.user === Tipping.Leaderboard.Leaders.HighestTotal.Username) { Tipping.Leaderboard.Leaders.HighestTotal.OptOut = false; } } } /* Code to allow the developer to stand out if necessary (e.g. for tech support) */ if (msg.user === "mx2k6" && msg.m.substring(0, 1) === "#") { msg.in_fanclub = true; msg.m = msg.m.substring(1); msg.background = "#3C6793"; msg.c = "#fff"; } return msg; }); function isSuperuser(username, isMod) { return (username == cb.room_slug || isMod && settings.allow_mod_superuser_cmd); } function addGoal(tokens, description) { var newGoal = { index: settings.goals.length + 1, description: description, tokens: parseInt(tokens), hide: false, }; if (newGoal.description.substring(0, 2) == "**") { newGoal.description = newGoal.description.substring(2); newGoal.hide = true; } settings.goals.push(newGoal); return newGoal; } function removeGoal() { return settings.goals.splice(-1, 1); } function isUndefined(test) { return (test == undefined || test == "" || test == 0); } function parseOptions() { settings.finality_message = cb.settings.finality_message; settings.allow_mod_superuser_cmd = settingsHelper.parseBoolean(cb.settings.mod_allow_broadcaster_cmd); settings.hide_token_haul = settingsHelper.parseBoolean(cb.settings.hide_token_haul); settings.highlight_theme = cb.settings.tipper_colour_scheme.toLowerCase(); settings.progression_mode_manual = (cb.settings.progression_mode == 'Manual'); settings.hidden_show_entry_fee = cb.settings.hidden_show_entry_fee; settings.hidden_preshow_entry_fee = cb.settings.hidden_preshow_entry_fee; settings.change_subject_on_tip = settingsHelper.parseBoolean(cb.settings.change_subject_on_tip); var actionOnFinality = cb.settings.action_on_finality[0].toLowerCase(); switch (actionOnFinality) { case 'l': settings.action_on_finality = 'loop'; break; case 's': settings.action_on_finality = 'hidden'; break; default: settings.action_on_finality = 'default'; break; } hiddenShow.setPrice(settings.hidden_show_entry_fee); hiddenShow.setEnabled(settings.action_on_finality == 'hidden'); hiddenShow.setMessage(settings.finality_message); hiddenShow.setPreshowPrice(settings.hidden_preshow_entry_fee); for (var gIdx = 1; gIdx <= Application.Constants.Goals; gIdx++) { if (!isUndefined(cb.settings['goal_' + gIdx + '_description']) && !isUndefined(cb.settings['goal_' + gIdx + '_tokens'])) { var description = cb.settings['goal_' + gIdx + '_description']; var hide = false; if (description.substring(0, 2) == "**") { description = description.substring(2); hide = true; } var repeatTest = /^(\d+)\/(.*)/i; var goalRepeat = repeatTest.exec(description); if (goalRepeat != null) { description = goalRepeat[2]; debugLog("The goal {0} will be repeated {1} times!".format(goalRepeat[1], goalRepeat[2])); for (var repeat = 0; repeat < goalRepeat[1]; repeat++) { settings.goals.push({ index: settings.goals.length + 1, description: description, tokens: parseInt(cb.settings['goal_' + gIdx + '_tokens']), hide: hide, }); } } else { settings.goals.push({ index: settings.goals.length + 1, description: description, tokens: parseInt(cb.settings['goal_' + gIdx + '_tokens']), hide: hide, }); } } } } function init() { goalTimer.hookOnTimer = function () { goalTimerOnTimer(); }; Application.StartupTime = new Date(); parseOptions(); Messenger.sendSuccessMessage("Tip Multi-Goal v" + Application.Version + " started.", null); Messenger.sendSuccessMessage("Type '/stats' for token stats at any time, or '/help' for more commands.", null, Groups.Moderators); reset(); } if (cb.settings.goal_1_tokens !== null && cb.settings.goal_1_tokens !== undefined) { init(); } var _APPNAME = "The cute pup icon bot"; String.prototype.capitalize = function() { return this.charAt(0).toUpperCase() + this.slice(1); } String.prototype.padL = function (length) { // optional param: paddingChar (default=" ") var pad = arguments.length > 1 ? arguments[1] : ' '; return this.length < length ? (pad + this).padL(length, pad) : this.substr(0, length); } String.prototype.padR = function (length) { // optional param: paddingChar (default=" ") var pad = arguments.length > 1 ? arguments[1] : ' '; return this.length < length ? (this + pad).padR(length, pad) : this.substr(0, length); } String.prototype.padC = function (length) { // optional param: paddingChar (default=" ") var pad = arguments.length > 1 ? arguments[1] : ' '; if (this.length >= length) return this.substr(0, length); if (this.length + 1 == length) return this + pad; else return (pad + this + pad).padC(length, pad); } String.prototype.mapReplace = function (map) { // optional param: replaceFullOnly (default="false) var replaceFullOnly = arguments.length > 1 ? arguments[1] : false; var regexp = []; for (var key in map) { regexp.push(RegExp.escape(key)); } regexp = regexp.join('|'); if (replaceFullOnly) { regexp = '\\b(?:' + regexp + ')\\b'; } regexp = new RegExp(regexp, 'gi'); return this.replace(regexp, function (match) { return map[match.toLowerCase()]; }); } String.isString = function (o) { return (typeof o == 'string' || o instanceof String); } RegExp.escape= function(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); }; Number.prototype.getOrdinal = function() { switch(this % 100) { case 11: case 12: case 13: return this + "th"; } switch(this % 10) { case 1: return this + "st";; case 2: return this + "nd"; case 3: return this + "rd"; } return this + "th"; } Function.isFunction = function (o) { return !!(o && o.constructor && o.call && o.apply); } var utility = { getPredefinedColor : function (name) { switch (name.toLowerCase()) { case 'black': return '#000'; case 'white': return '#fff'; case 'yellow': return '#fe3'; case 'pink': return '#f0e'; case 'purple': return '#609'; case 'orange': return User.Color.ORANGE.getColor(); case 'red': return User.Color.RED.getColor(); case 'green': return User.Color.GREEN.getColor(); case 'blue': return User.Color.BLUE.getColor(); case 'cyan': return User.Color.CYAN.getColor(); case 'grey': case 'gray': return User.Color.GREY.getColor(); default: return '#000'; } }, securityCheck : function (user, checks, prefix) { if (user.isBroadcaster()) return true; for (var i = 0 ; i < checks.length ; i++) { if (user.is(checks[i]) && sys.settings.get(prefix + checks[i]).getValue()) { return true; } }; return false; }, getTimeDifference : function (time1) { var time2 = arguments.lenght > 1 ? arguments[1] : new Date(); if (time2 < time1) { var t = time1; time1 = time2; time2 = t; } var d = (time2 - time1) / 1000; if (d < 60) { return Math.floor(d) + "s" } d /= 60; var m = Math.floor(d % 60); var h = Math.floor(d / 60); var str = ''; if (h) str += h + 'h '; str += m + 'm' return str; } } utility.Interval = function (callback, interval) { var roundedInt = Math.ceil(interval / 1000) * 1000; var active = false; var left = 0; var fun = function () { if (active) { var total = left + roundedInt; var runs = Math.floor(total / interval); left = total % interval; for (; runs ; runs--) { callback(this); } cb.setTimeout(fun, roundedInt); } } this.start = function () { if (!active) { left = 0; cb.setTimeout(fun, roundedInt); } active = true; } this.stop = function () { active = false; } this.isRunning = function () { return active; } } utility.Timeout = function (callback, interval) { interval = Math.ceil(interval / 1000) * 1000; var active = false; var fun = function () { if (active) callback(this); } this.start = function () { if (!active) { cb.setTimeout(fun, interval); } active = true; } this.stop = function () { active = false; } this.isRunning = function () { return active; } } var Module = function (name, rawData) { var internalSettings = ('internalSettings' in rawData) ? rawData.internalSettings : []; var externalSettings = ('externalSettings' in rawData) ? rawData.externalSettings : []; var filters = ('filters' in rawData) ? rawData.filters : []; var commands = ('commands' in rawData) ? rawData.commands : {}; var handlers = { enter : [], leave : [], message : [], subject : [], tip : [], setting : {} } if ('handlers' in rawData) { if ('enter' in rawData.handlers) handlers.enter = rawData.handlers.enter; if ('leave' in rawData.handlers) handlers.leave = rawData.handlers.leave; if ('message' in rawData.handlers) handlers.message = rawData.handlers.message; if ('subject' in rawData.handlers) handlers.subject = rawData.handlers.subject; if ('tip' in rawData.handlers) handlers.tip = rawData.handlers.tip; if ('setting' in rawData.handlers) handlers.setting = rawData.handlers.setting; } handlers.message.unshift(function (message) { if (!message.original) { message.original = message.getMessage().trim(); } if (message.original[0] =='/') { message.setHidden(true); var params = message.original.substring(1).split(/[ \t]/); var command = params.shift().toLowerCase(); if (command in commands) { var consumedBy = message.getConsumedBy(); if (consumedBy) { var debug = _APPNAME + ' (module "' + name + '"): The command "/' + command + '" is disabled because it is already provided by '; if (consumedBy.name == _APPNAME && consumedBy.slot == cb.slot) { if (String.isString(consumedBy.consumed)) debug += 'the module "' + consumedBy.consumed + '".'; else debug += 'another module of this app.' } else { debug += 'the '; if (consumedBy.slot == 0) debug += 'app '; else debug += 'bot '; debug += '"' + consumedBy.name + '" ' if (String.isString(consumedBy.consumed)) debug += '(module "' + consumedBy + ') '; if (consumedBy.slot != 0) debug += 'in slot ' + consumedBy.slot; debug += '. ' } sys.debug(debug) return; } var result = commands[command](params, command, message); if (result) { sys.debug(message.getUser().getName() + ' executed command "/' + command + '"'); message.consume(); } else { sys.debug(message.getUser().getName() + ' tried to execute command "/' + command + ''); cb.sendNotice("Insufficient Privileges to execute " + message.getMessage() + ".", message.getUser().getName(), sys.settings.get('security_bg').getValue(), sys.settings.get('security_fg').getValue(), sys.settings.get('security_w' ).getValue()); } } } }); handlers.message.unshift(function (message) { filters.forEach(function (filter) { var result = filter(message); if (result === true) { message.setHidden(false); } if (result === false) { message.setHidden(true); } }); }); this.handleMessage = function (message) { handlers.message.forEach(function (handler) { handler(message); }); } this.handleLeave = function (user) { handlers.leave.forEach(function(handler) { handler(user); }); } this.handleEnter = function (user) { handlers.enter.forEach(function(handler) { handler(user); }); } this.handleTip = function (tip) { handlers.tip.forEach(function(handler) { handler(tip); }); } this.handleSubject = function (newS, oldS) { var add = ''; handlers.subject.forEach(function(handler){ add += handler(newS, oldS); }); return add; } this.getName = function () { return name; } this.init = function () { internalSettings.forEach(function (setting) { sys.settings.registerOption(setting); }); externalSettings.forEach(function (setting) { sys.settings.registerOption(setting, true); }); if ('init' in rawData) rawData.init(); } this.getTipOptions = function (u) { if ('getTipOptions' in rawData) return rawData.getTipOptions(u); else return []; } this.update = function (setting) { if (Function.isFunction(handlers.setting)) { handlers.setting(setting); } else if (Function.isFunction(handlers.setting[setting.getName()])) { handlers.setting[setting.getName()](setting); } } !function (that) { var addThis = function (s) { s.addObserver(that) } internalSettings.forEach(addThis); externalSettings.forEach(addThis); }(this); Module[name] = this; } var Message = function (rawData) { if (!rawData['X-BBT-COMPAT']) rawData['X-BBT-COMPAT'] = {}; rawData['X-BBT-COMPAT']['APP#SLOT#'+cb.slot] = { slot : cb.slot, name : _APPNAME, consumed : null } var user = new User(rawData); var time = new Date(); this.getColor = function () { return rawData.c; } this.getBackground = function () { return "background" in rawData ? rawData.background : '#ffffff';; } this.getFont = function () { return rawData.f; } this.getMessage = function () { return rawData.m; } this.isHidden = function () { return "X-Spam" in rawData ? rawData['X-Spam'] : false; } this.getUser = function () { return user; } this.setColor = function (c) { // TODO: check for valid HTML color rawData.c = c; } this.setBackground = function (c) { // TODO: check for valid HTML color rawData.background = c; } this.setFont = function (f) { // TODO: check for valid font rawData.f = f; } this.setMessage = function (m) { rawData.m = m; } this.setHidden = function (h) { rawData['X-Spam'] = !!h; // !! is quick'n'dirty booelan typecast } this.consume = function () { rawData['X-BBT-COMPAT']['APP#SLOT#'+cb.slot].consumed = (arguments.length > 0) ? arguments [0] : (true); } this.getConsumedBy = function () { for (var key in rawData['X-BBT-COMPAT']) { var compatData = rawData['X-BBT-COMPAT'][key]; if (!compatData.consumed) continue; return compatData; } return null; } this.getRaw = function () { return rawData; } this.getTime = function () { return time; } } var Tip = function (rawData) { var amount = rawData.amount; var message = rawData.message; var to = rawData.to_user; // currently unused, we only see tips to the broadcaster anyway. I trust CB in this. var user = new User({ user : rawData.from_user, gender : rawData.from_user_gender, in_fanclub : rawData.from_user_in_fanclub, is_mod : rawData.from_user_is_mod, has_tokens : rawData.from_has_tokens, tipped_recently : rawData.from_user_tipped_recently }); var time = new Date(); this.getAmount = function () { return amount; } this.getMessage = function () { return message; } this.getUser = function () { return user; } this.getTime = function () { return time; } } var User = function () { var tipsLast = {} var tipsTotal = {} var tipsHigh = {} var entered = {} var left = {} var chatLast = {} var ht = { user : null, amount : 0 } new Module('_u', { handlers : { enter : [ function (u) { entered[u.getName().toLowerCase()] = new Date(); } ], leave : [ function (u) { left[u.getName().toLowerCase()] = new Date(); } ], message : [ function (m) { chatLast[m.getUser().getName().toLowerCase()] = m; } ], tip : [ function (t) { var username = t.getUser().getName().toLowerCase(); tipsLast[username] = t; if (!(username in tipsTotal)) { tipsTotal[username] = 0; } tipsTotal[username] += t.getAmount(); if (!(username in tipsHigh) || tipsHigh[username].getAmount() < t.getAmount()) { tipsHigh[username] = t; } } ] } }); return function (rawData) { var name = rawData.user; var gender = (function (g) { switch (g.toLowerCase()) { case 'm' : return User.Gender.MALE; case 'f' : return User.Gender.FEMALE; case 's' : return User.Gender.SHEMALE; case 'c' : return User.Gender.COUPLE; default: sys.log("Invalid gender code found"); return ''; } })(rawData.gender); var fanclub = rawData.in_fanclub; var mod = rawData.is_mod; var token = rawData.has_tokens; var tipper = rawData.tipped_recently; this.getName = function () { return name; } this.getGender = function () { return gender; } this.isFanclub = function () { return fanclub; } this.isMod = function () { return mod; } this.isBroadcaster = function () { return name == cb.room_slug; } this.hasTokens = function () { return token; } this.isHighTipper = function () { return tipper; } this.is = function (color) { if (!(color instanceof User.Color)) color = User.Color.fromName(color); switch (color) { case User.Color.ORANGE: return this.isBroadcaster(); case User.Color.RED: return this.isMod(); case User.Color.GREEN: return this.isFanclub(); case User.Color.CYAN: return this.hasTokens(); case User.Color.BLUE: return this.isHighTipper(); case User.Color.GREY: return true; default: return false; } } this.isOnly = function (color) { if (!(color instanceof User.Color)) color = User.Color.fromName(color); switch (color) { case User.Color.ORANGE: return this.isBroadcaster(); // no additional checks. Doesn't make any sense to check if a broadcaster // has tokens or is a mod, fanclub member or high tipper. case User.Color.RED: return this.isMod() && !this.isBroadcaster() && !this.isFanclub() && !this.hasToken() && !this.isHighTipper(); case User.Color.GREEN: return this.isFanclub() && !this.isBroadcaster() && !this.isMod() && !this.hasToken() && !this.isHighTipper(); case User.Color.CYAN: return this.hasTokens() && !this.isBroadcaster() && !this.isMod() && !this.isFanclub() && !this.isHighTipper(); case User.Color.BLUE: return this.isHighTipper() && !this.isBroadcaster() && !this.isMod() && !this.isFanclub() && !this.hasTokens(); case User.Color.GREY: return !this.hasTokens() && !this.isHighTipper() && !this.isBroadcaster() && !this.isMod() && !this.isFanclub() default: return false; } } this.getColor = function () { if (this.is(User.Color.ORANGE)) return User.Color.ORANGE; if (this.is(User.Color.RED )) return User.Color.RED; if (this.is(User.Color.GREEN )) return User.Color.GREEN; if (this.is(User.Color.BLUE )) return User.Color.BLUE; if (this.is(User.Color.CYAN )) return User.Color.CYAN; return User.Color.GREY; } this.getEnter = function () { var name = this.getName().toLowerCase(); if (name in entered) return entered[name]; return null; } this.getLeave = function () { var name = this.getName().toLowerCase(); if (name in left) return left[name]; return null; } this.getLastChat = function () { var name = this.getName().toLowerCase(); if (name in chatLast) return chatLast[name]; return null; } this.getLastTip = function () { var name = this.getName().toLowerCase(); if (name in tipsLast) return tipsLast[name]; return null; } this.getHighestTip = function () { var name = this.getName().toLowerCase(); if (name in tipsHigh) return tipsHigh[name]; return null; } this.getTipTotal = function () { var name = this.getName().toLowerCase(); if (name in tipsTotal) return tipsTotal[name]; return 0; } this.getLastSeen = function () { var lastSeen = null; if (this.getEnter() > lastSeen) lastSeen = this.getEnter(); if (this.getLeave() > lastSeen) lastSeen = this.getLeave(); if (this.getLastChat() && this.getLastChat().getTime() > lastSeen) lastSeen = this.getLastChat().getTime(); if (this.getLastTip() && this.getLastTip().getTime() > lastSeen ) lastSeen = this.getLastTip().getTime(); return lastSeen; } this.isTipper = function () { return this.getLastTip() != null; } User.Archive.add(this); } }(); User.Archive = new function () { var archive = {} this.add = function (user) { if (user instanceof User) archive[user.getName().toLowerCase()] = user; } this.get = function (name) { if (name instanceof User) name = name.getName(); name = name.toLowerCase(); if (name in archive) return archive[name]; else return null; } this.getAll = function () { return archive.slice(0); // shallow copy instead of reference to prevent archive manipulation } } User.Gender = function (name, code, personal, possessive) { this.getCode = function () { return code; } this.getName = function () { return name; } this.getPersonalPronoun = function () { return personal; } this.getPossessivePronoun = function () { return possessive; } } User.Gender.MALE = new User.Gender('male' , 'm', 'he' , 'his' ); User.Gender.FEMALE = new User.Gender('female' , 'f', 'she' , 'her' ); User.Gender.SHEMALE = new User.Gender('transsexual', 's', '(s)he', 'his/her'); User.Gender.COUPLE = new User.Gender('a couple' , 'c', 'they' , 'their' ); User.Gender.INVALID = new User.Gender('unknown' , '' , 'he' , 'his' ); User.Gender.fromName = function (name) { switch (name.split(/[\s_]/)[0].trim().toLowerCase()) { case 'm' : case 'male' : return User.Gender.MALE; case 'f' : case 'female' : return User.Gender.FEMALE; case 's' : case 'shemale' : case 'trans' : case 'transsexual' : return User.Gender.SHEMALE; case 'a' : case 'c' : case 'couple' : return User.Gender.COUPLE; default : return User.Gender.INVALID; } } User.Color = function (name,code,color) { this.getName = function() {return name }; this.getCode = function() {return code }; this.getColor = function() {return color}; } User.Color.ORANGE = new User.Color('broadcaster' , 'orange', '#DC5500'); User.Color.RED = new User.Color('moderator' , 'red' , '#DC0000'); User.Color.GREEN = new User.Color('fanclub member', 'green' , '#090' ); User.Color.BLUE = new User.Color('high tipper' , 'blue' , '#009' ); User.Color.CYAN = new User.Color('token owner' , 'cyan' , '#69A' ); User.Color.GREY = new User.Color('basic user' , 'grey' , '#494949'); User.Color.INVALID = new User.Color('unknown' , '' , '#000' ); User.Color.fromName = function (name) { switch (name.split(/[\s_]/)[0].trim().toLowerCase()) { case 'broadcasters': case 'broadcaster' : case 'oranges' : case 'orange' : return User.Color.ORANGE; case 'moderators' : case 'moderator' : case 'mods' : case 'mod' : case 'reds' : case 'red' : return User.Color.RED; case 'members' : case 'member' : case 'fanclub' : case 'greens' : case 'green' : return User.Color.GREEN; case 'high' : case 'tippers' : case 'tipper' : case 'darkblues' : case 'darkblue' : case 'bluess' : case 'blues' : case 'blue' : return User.Color.BLUE; case 'tokens' : case 'token' : case 'lightblues' : case 'lightblue' : case 'cyans' : case 'cyan' : return User.Color.CYAN; case 'tokenless' : case 'user' : case 'greys' : case 'grays' : case 'grey' : case 'gray' : case 'all' : return User.Color.GREY; default : return User.Color.INVALID; } } var Setting = function (name, def) { var val = (name in cb.settings) ? cb.settings[name] : (arguments.length > 2) ? arguments [2] : def; var observers = []; this.getValue = function () { return val; } this.getName = function () { return name; } this.getDefault = function () { return def; } this.setValue = function (v) { val = v; this.notify(); return true; } this.getCBObject = function () { return {}; } this.addObserver = function (o) { if ('update' in o && observers.indexOf(o) == -1) { observers.push(o); } } this.removeObserver = function (o) { var index = observers.indexOf(o); if (index != -1) observers.splice(index, 1); } this.notify = function () { var that = this; observers.forEach(function (o) { o.update(that); }); } }; Setting.String = function (name) { // optional params: label, default, required, min, max var label = name; var def = ''; var req = true; var min = 0; var max = 255; if (arguments.length > 1) label = arguments[1]; if (arguments.length > 2) def = arguments[2]; if (arguments.length > 3) req = arguments[3]; if (arguments.length > 4) min = arguments[4]; if (arguments.length > 5) max = arguments[5]; var val = def; Setting.call(this, name, def); this.setValue = function (v) { v = v.toString(); if (v.length > max || v.length < min) { return false; } else { val = v; this.notify(); return true; } } this.getValue = function () { return val; } this.getCBObject = function () { return { name : name, type : 'str', label : label, required : req, defaultValue : def, minLength : min, maxLength : max } } if (name in cb.settings) this.setValue(cb.settings[name]); } Setting.Float = function (name) { // optional params: label, default, required, min, max var label = name; var def = 0; var req = true; var min = 0; var max = 255; if (arguments.length > 1) label = arguments[1]; if (arguments.length > 2) def = arguments[2]; if (arguments.length > 3) req = arguments[3]; if (arguments.length > 4) min = arguments[4]; if (arguments.length > 5) max = arguments[5]; var val = def; Setting.call(this, name, def); this.setValue = function (v) { v = parseFloat(v.toString().replace(',', '.')); if (isNaN(v) || v > max || v < min) { return false; } else { val = v; this.notify(); return true; } } this.getValue = function () { return val; } this.getCBObject = function () { return { name : name, type : 'str', label : label, required : req, defaultValue : def, minLength : 1 } } if (name in cb.settings) { // CB does not checks for floats, not implemented. so invalid values at load time are possible. var returnValue = this.setValue(cb.settings[name]); if (!returnValue) { cb.sendNotice('Invalid value for ' + (label != name ? '"' + label + '" (' + name + ')' : name) + ' found. ' + 'Please use "/set ' + name + ' VALUE" to set a new value (replace "VALUE" by the new value). ' + 'Valid values are any decimal nubmer between ' + min + ' and ' + max + '.', cb.room_slug, '#e99', '#300'); } } } Setting.Int = function (name) { // optional params: label, default, required, min, max var label = name; var def = 0; var req = true; var min = 0; var max = 999; if (arguments.length > 1) label = arguments[1]; if (arguments.length > 2) def = arguments[2]; if (arguments.length > 3) req = arguments[3]; if (arguments.length > 4) min = arguments[4]; if (arguments.length > 5) max = arguments[5]; var val = def; Setting.call(this, name, def); this.setValue = function (v) { v = parseInt(v); if(isNaN(v) || v > max || v < min) { return false; } else { val = v; this.notify(); return true; } } this.getValue = function () { return val; } this.getCBObject = function () { return { name : name, type : 'int', label : label, required : req, defaultValue : def, minValue : min, maxValue : max } } if (name in cb.settings) this.setValue(cb.settings[name]); } Setting.Choice = function (name, options) { // optional params: label, default, required var label = name; var def = options[0]; var req = true; var opt = options if (arguments.length > 2) label = arguments[2]; if (arguments.length > 3) def = arguments[3]; if (arguments.length > 4) req = arguments[4]; var val = def; Setting.call(this, name, def); this.addOption = function (o) { if (opt.indexOf(o) == -1) opt.push(o); } this.removeOption = function (o) { var index = opt.indexOf(o); if (index != -1) opt.splice(index,1); } this.setValue = function (v) { if (opt.indexOf(v) == -1) return false; val = v; this.notify(); return true; } this.getValue = function () { return val; } this.addOption(def); this.getCBObject = function () { var o = { name : name, type : 'choice', label : label, required : req, defaultValue : def } for (var i = 0 ; i < opt.length ; i++) { o['choice'+(i+1)] = opt[i]; } return o; } if (name in cb.settings) this.setValue(cb.settings[name]); } Setting.Bool = function (name) { // optional params: label, default, required var label = name; var def = false; var req = true; if (arguments.length > 1) label = arguments[1]; if (arguments.length > 2) def = arguments[2]; if (arguments.length > 3) req = arguments[3]; var val = def; Setting.call(this, name, def); this.setValue = function (v) { val = ['false', '0', 'off', 'no', 'n', 'f', 0, false].indexOf(v) == -1 this.notify(); return true; } this.getValue = function () { return val; } this.getCBObject = function () { return o = { name : name, type : 'choice', label : label, required : req, defaultValue : def ? 'yes' : 'no', choice1 : 'yes', choice2 : 'no' }; } if (name in cb.settings) this.setValue(cb.settings[name]); } var sys = new function () { var modules = {}; var topic = ''; this.isApp = function () { return cb.slot == 0; } this.setSubject = function () { if (!this.isApp()) { this.debug('Running as a bot. setSubject is not available.'); return; } var oldTopic = topic; var t = topic = (arguments.length > 0) ? arguments[0] : topic; for (var moduleName in modules) { var add = modules[moduleName].handleSubject(topic, oldTopic); if (add) t+= ' ' + add; } cb.changeRoomSubject(t); this.log('Subject changed to "' + t + '" (was "' + oldTopic + '")'); } this.getSubject = function () { if (!this.isApp()) { this.debug('Running as a bot. getSubject is not available.'); return ''; } return topic; } this.loadModule = function (module) { if (!(module instanceof Module)) { if (module in Module) module = Module[module]; else return false; } modules[module.getName()] = module; module.init(); return true; } this.unloadModule = function (module) { if (module instanceof Module) { module = Module.getName(); } if (module == null || !(module in modules)) { return false; } delete modules[module]; return true; } //NOTE: messages parsed by parseMessage are not visible in chat or passed to other bots. this.parseMessage = function (message) { // optional: user, color, background, font. if (message instanceof Message){ message = message.getRaw(); } else if (message instanceof Object && !(message instanceof String)) { // most likely raw CB message, just pass along. } else { message = { m : message.toString(), c : arguments.length > 2 ? arguments[2] : User.Color.GREY.getColor(), f : arguments.length > 4 ? arguments[4] : 'default', }; if (arguments.length > 3) m.background = arguments[3]; var user = arguments.length > 1 ? (arguments[1] instanceof User ? arguments[1] : User.Archive.get(arguments[1]) ) : null; if (user) { message.user = user.getName(); message.gender = user.getGender().getCode(); message.in_fanclub = user.isFanclub(); message.has_tokens = user.hasTokens(); message.is_mod = user.isMod(); message.tipped_recently = user.isHighTipper(); } else { message.user = arguments.length > 1 ? arguments[1] : cb.rooms_slug; message.gender = ''; message.in_fanclub = false; message.has_tokens = false; message.is_mod = false; message.tipped_recently = false; } } cb._translate_handler(message); } this.log = function (message) { cb.log (_APPNAME + ': ' + message); } this.debug = function (message) { if (sys.settings.get('debug_mode').getValue()) this.log(message); } this.init = function () { cb.onEnter(function (u) { var user = new User(u); for (var moduleName in modules) { modules[moduleName].handleEnter(user); } }); cb.onLeave(function (u) { var user = new User(u); for (var moduleName in modules) { modules[moduleName].handleLeave(user); } }); cb.onMessage(function (m) { var message = new Message(m); for (var moduleName in modules) { modules[moduleName].handleMessage(message); } return m; }); cb.tipOptions(function (u) { var options = {}; var moduleCount = 0; for (var moduleName in modules) { options[moduleName] = modules[moduleName].getTipOptions(u); if (options[moduleName].length > 0) moduleCount++; } if (moduleCount == 0) return; var outputObject = {label : 'Select', options : []}; for (var moduleName in options) { options[moduleName].forEach(function (option) { outputObject.options.push({label: (moduleCount > 1 ? '[' + moduleName + ']: ' : '') + option}); }); } return outputObject; }); if (this.isApp()) { cb.onDrawPanel(this.panel.draw); } this.loadModule(new Module('_s', { internalSettings : [ new Setting('security_bg', '#800'), new Setting('security_fg', '#fff'), new Setting.Choice('security_w', ['bold', 'bolder', 'normal']), new Setting('success_bg', '#9e9'), new Setting('success_fg', '#030'), new Setting.Choice('success_w', ['normal', 'bolder', 'bold']), new Setting('warning_bg', '#fc3'), new Setting('warning_fg', '#630'), new Setting.Choice('warning_w', ['normal', 'bolder', 'bold']), new Setting('failure_bg', '#e99'), new Setting('failure_fg', '#300'), new Setting.Choice('failure_w', ['normal', 'bolder', 'bold']) ] })); this.loadModule('_u'); if (this.isApp()) { this.loadModule(new Module('_app', { externalSettings : [ new Setting.String("subject", "Room Subject", cb.room_slug + "'s Room", true), new Setting.Bool("security_topic_mod", "Mods can change room subject", false, false) ], commands : { topic : function (p,c,m){ if (!(m.getUser().isBroadcaster()) && !(m.getUser().isMod() && sys.settings.get('security_topic_mod').getValue())) { return false; } sys.setSubject(p.join(' ')); return true; } } })); this.setSubject(this.settings.get('subject').getValue()); } } }(); sys.settings = new function () { var s = {}; this.registerOption = function (option) { // optinal parameters: registerToCB (default false)) var name = option.getName(); s[name] = option; if (arguments.length > 1 && arguments[1]) { if (!Array.isArray(cb.settings_choices)) cb.settings_choices = []; var found = false; for (var i = 0 ; i < cb.settings_choices.length ; i++) { if (cb.settings_choices[i].name == name) { found=true; break; } }; if (!found) cb.settings_choices.push(option.getCBObject()); } } this.get = function (name) { if (!(name in s)) { s[name] = new Setting(name, ''); } return s[name]; } }(); sys.panel = new function () { var PanelContent = function () { var label = ""; var value = ""; var type = 1; var setLabel = function (l) { if (l == null) { type = 1; label = ''; } else { type = 2; label = l; } } var setValue = function (v) { if (v == null) v = ''; value = v } this.getType = function () { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } return type; } this.getLabel = function () { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } return label; } this.getValue = function () { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } return value; } this.setLabel = function (l) { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } setLabel(l); if (arguments.length > 1 ? arguments[1] : true) sys.panel.repaint(); } this.clearLabel = function () { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } this.setLabel(null, arguments.length > 0 ? arguments[0] : true); } this.setValue = function (v) { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } setValue(v); if (arguments.length > 1 ? arguments[1] : true) sys.panel.repaint(); } this.setData = function (data) { if (!sys.isApp()) { sys.debug('Running as a bot. App panel is not available.'); return; } if (Array.isArray(data)) { switch (data.length) { case 0: setValue(""); setLabel(null); break; case 1: setValue(data[0]); setLabel(null); break; default: setValue(data[1]); setLabel(data[0]); break; } } else { setValue(data); setLabel(null); } if (arguments.length > 1 ? arguments[1] : true) sys.panel.repaint(); return; } } this[0] = new PanelContent(); this[1] = new PanelContent(); this[2] = new PanelContent(); this.draw = (function (that) { // CB assigns the cb object to this when calling callbacks. So we need a copy. return function () { var panelConfig = 0; for (var i = 0 ; i < 3 ; i++) { panelConfig += (that[i].getType() == 2 ? 1 : 0) << i; } switch (panelConfig) { case 0: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_11_21_31"'); return { template : '3_rows_11_21_31', row1_value : that[0].getValue(), row2_value : that[1].getValue(), row3_value : that[2].getValue() } case 1: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_12_21_31"'); return { template : '3_rows_12_21_31', row1_label : that[0].getLabel(), row1_value : that[0].getValue(), row2_value : that[1].getValue(), row3_value : that[2].getValue() } case 2: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_12_22_31"'); return { template : '3_rows_12_22_31', row1_label : that[0].getValue(), row1_value : '', row2_label : that[1].getLabel(), row2_value : that[1].getValue(), row3_value : that[2].getValue() } case 3: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_12_22_31"'); return { template : '3_rows_12_22_31', row1_label : that[0].getLabel(), row1_value : that[0].getValue(), row2_label : that[1].getLabel(), row2_value : that[1].getValue(), row3_value : that[2].getValue() } case 4: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_of_labels"'); return { template : '3_rows_of_labels', row1_label : that[0].getValue(), row1_value : '', row2_label : that[1].getValue(), row2_value : '', row3_label : that[2].getLabel(), row3_value : that[2].getValue() } case 5: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_of_labels"'); return { template : '3_rows_of_labels', row1_label : that[0].getLabel(), row1_value : that[0].getValue(), row2_label : that[1].getValue(), row2_value : '', row3_label : that[2].getLabel(), row3_value : that[2].getValue() } case 6: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_of_labels"'); return { template : '3_rows_of_labels', row1_label : that[0].getValue(), row1_value : '', row2_label : that[1].getLabel(), row2_value : that[1].getValue(), row3_label : that[2].getLabel(), row3_value : that[2].getValue() } case 7: sys.log("Re-Drawing Panel. Configuration: " + panelConfig + ', using template "3_rows_of_labels"'); return { template : '3_rows_of_labels', row1_label : that[0].getLabel(), row1_value : that[0].getValue(), row2_label : that[1].getLabel(), row2_value : that[1].getValue(), row3_label : that[2].getLabel(), row3_value : that[2].getValue() } } }; })(this); this.repaint = function () { sys.log('Panel refresh called'); cb.drawPanel(); } }(); new Module('fanclub', new function () { var newMembers = []; var removedMembers = []; var getMembers = function () { var members = newMembers.slice(0); members = members.concat(sys.settings.get('club_members').getValue().split(/[\s,;]+/)); removedMembers.forEach(function (rm) { var index = members.indexOf(rm); if (index != -1) members.splice(index, 1); }); return members; } var fanclubEnabled = function () { return Boolean(sys.settings.get('club_name').getValue()); } var userJoin = function (name) { if (getMembers().indexOf(name) != -1) return; var index = removedMembers.indexOf(name); if (index != -1) removedMembers.splice(name, 1); if (getMembers().indexOf(name) == -1) newMembers.push(name); var message = name; message += ' joined the '; message += sys.settings.get('club_name').getValue(); message += 's!'; var fg = sys.settings.get('club_fg').getValue(); fg = (fg[0] == '#') ? fg : utility.getPredefinedColor(fg); cb.sendNotice(message, '', '', fg, 'bold'); } var userRemove = function (name) { var index = newMembers.indexOf(name); if (index != -1) newMembers.splice(name, 1); else removedMembers.push(name); var message = name; message += ' was removed from the '; message += sys.settings.get('club_name').getValue(); message += 's!'; var fg = sys.settings.get('club_fg').getValue(); fg = (fg[0] == '#') ? fg : utility.getPredefinedColor(fg); cb.sendNotice(message, '', '', fg, 'bold'); } this.internalSettings = [ new Setting.Choice('club_w', ['normal', 'bolder', 'bold'], '', 'bold') ] this.externalSettings = [ new Setting.Bool ("security_fanclub_mod", "[Permissions]: Mods can control fanclubs", true, false), new Setting.String("club_name", "[Fanclub]: Fanclub name (blank = fanclub disabled)", "", false), new Setting.String("club_title", "[Fanclub]: Title of club members", "Fanclub Member", true), new Setting.String("club_tag", "[Fanclub]: Tag (e.g. [member] or :icon) (blank = none)", "[Fanclub Member]", false), new Setting.Int ("club_price", "[Fanclub]: Price to join (0 = manual only)", 1000, false, 0, 1000000), new Setting.Bool ("club_overtip", "[Fanclub]: Tipping too much grants membership as well", false, true), new Setting.String("club_bg", "[Fanclub]: Highlight color for fanclub members (empty = white/none)", '', false), new Setting.String("club_fg", "[Fanclub]: Color for enter, leave and join messages", 'green', true), new Setting.String("club_members", "[Fanclub]: Fanclub members (separated by spaces or comma)", '', false, 0, 999999999) ] this.handlers = { message : [ function (m) { if (!fanclubEnabled()) return; if (getMembers().indexOf(m.getUser().getName()) != -1) { var bg = sys.settings.get('club_bg').getValue() || '#fff'; bg = (bg[0] == '#') ? bg : utility.getPredefinedColor(bg); m.setBackground(bg); if (sys.settings.get('club_tag').getValue() && m.getMessage()[0] != '/' && m.getMessage()[0] != '!') { m.setMessage(sys.settings.get('club_tag').getValue() + " " + m.getMessage()); } } } ], tip : [ function (t) { if (!fanclubEnabled()) return; if (t.getAmount() == sys.settings.get('club_price').getValue() || (t.getAmount() >= sys.settings.get('club_price').getValue() && sys.settings.get('club_overtip').getValue() ) ) { userJoin(t.getUser().getName()); } } ], enter : [ function (u) { if (!fanclubEnabled()) return; if (getMembers().indexOf(u.getName()) != -1) { var fg = sys.settings.get('club_fg').getValue(); fg = (fg[0] == '#') ? fg : utility.getPredefinedColor(fg); cb.sendNotice(sys.settings.get('club_title').getValue() + ' ' + u.getName() + ' entered the room.', '', '', fg, 'bold'); } } ], leave : [ function (u) { if (!fanclubEnabled()) return; if (getMembers().indexOf(u.getName()) != -1) { var fg = sys.settings.get('club_fg').getValue(); fg = (fg[0] == '#') ? fg : utility.getPredefinedColor(fg); cb.sendNotice(sys.settings.get('club_title').getValue() + ' ' + u.getName() + ' left the room.', '', '', fg, 'bold'); } } ] } this.commands = {}; this.commands['fanclub'] = function (p,c,m) { if (!(m.getUser().isBroadcaster()) && !(m.getUser().isMod() && sys.settings.get('security_fanclub_mod').getValue())) { return false; } if (!fanclubEnabled()) { cb.sendNotice('This fanclub is disabled. To enable, name the fanclub using "/set club_name <fanclubName>"', m.getUser().getName(), sys.settings.get('failure_bg').getValue(), sys.settings.get('failure_fg').getValue(), sys.settings.get('failure_w').getValue()); return true; }; var command = p.shift(); if (!command) command = ''; switch (command) { case 'join': case 'add': case '+': if (p.length < 1) { cb.sendNotice("No username specified.", m.getUser().getName(), sys.settings.get('failure_bg').getValue(), sys.settings.get('failure_fg').getValue(), sys.settings.get('failure_w').getValue()); return true; } var user = p.shift(); if (!user || User.Archive.get(user) || user == cb.room_slug) { cb.sendNotice("User " + user + " added to fanclub.", m.getUser().getName(), sys.settings.get('success_bg').getValue(), sys.settings.get('success_fg').getValue(), sys.settings.get('success_w').getValue()); } else { cb.sendNotice("User " + user + " added to fanclub, but the user has not been seen in this room yet. " + "Did you spell the name correctly?", m.getUser().getName(), sys.settings.get('warning_bg').getValue(), sys.settings.get('warning_fg').getValue(), sys.settings.get('warning_w').getValue()); } userJoin(user); return true; case 'leave': case 'remove': case 'delete': case 'del': case 'rm': case '-': if (p.length < 1) { cb.sendNotice("No username specified.", m.getUser().getName(), sys.settings.get('failure_bg').getValue(), sys.settings.get('failure_fg').getValue(), sys.settings.get('failure_w').getValue()); return true; } var user = p.shift(); if (getMembers().indexOf(user) != -1) { cb.sendNotice("User " + user + " removed from fanclub.", m.getUser().getName(), sys.settings.get('success_bg').getValue(), sys.settings.get('success_fg').getValue(), sys.settings.get('success_w').getValue()); } else { cb.sendNotice("User " + user + " is not in the fanclub." + "Did you spell the name correctly?", m.getUser().getName(), sys.settings.get('failure_bg').getValue(), sys.settings.get('failure_fg').getValue(), sys.settings.get('failure_w').getValue()); return true; } userRemove(user); return true; case 'list': case '': var message = 'Fanclub Members: ' getMembers().forEach(function (name){ message += name + ', ' }); cb.sendNotice(message, m.getUser().getName()); return true; default: cb.sendNotice('Unknown fanclub command: "' + command + '"', m.getUser().getName(), sys.settings.get('failure_bg').getValue(), sys.settings.get('failure_fg').getValue(), sys.settings.get('failure_w').getValue()); return true; } } }); new Module ('customtitles', new function () { var NUM_TITLES = 10; var userSpec = {} var addUserSpec = user => (user in userSpec) || (userSpec[user] = {}); var cleanUpUserSpec = function (user) { if (user in userSpec && ! (userSpec[user].title || userSpec[user].color || userSpec[user].background)) { delete userSpec[user]; } } var addUserTitle = function (user, title) { if (!user) return; if (title != undefined) { addUserSpec(user); userSpec[user].title = title; } else { delete userSpec[user].title; cleanUpUserSpec(); } } var addUserColor = function (user, color) { if (!user) return; if (color != undefined) { addUserSpec(user); userSpec[user].color = (color[0] == '#') ? color : utility.getPredefinedColor(color); } else { delete userSpec[user].color; cleanUpUserSpec(); } } var addUserBackground = function (user, background) { if (!user) return; if (background != undefined) { addUserSpec(user); userSpec[user].background = (background[0] == '#') ? background : utility.getPredefinedColor(background); } else { delete userSpec[user].background; cleanUpUserSpec(); } } var deleteUserSpec = user => delete userSpec.user; this.internalSettings = [ new Setting.Choice('usertitle_w', ['normal', 'bolder', 'bold'], '', 'bold') ] this.externalSettings = [ new Setting.Bool ("security_title_mod", "[Permissions]: Mods can control user titles", true, false) ] for (var i = 0 ; i < NUM_TITLES ; i++) { this.externalSettings.push( new Setting.String('usertitle_name' + i, '[User Titles ' + (i+1) + ']: Username (empty=disabled)' , '', false ), new Setting.String('usertitle_title' + i, '[User Titles ' + (i+1) + ']: Custom Title (empty = none)', '', false ), new Setting.String('usertitle_color' + i, '[User Titles ' + (i+1) + ']: Custom Color (empty = none)', '', false ), new Setting.String('usertitle_highlight' + i, '[User Titles ' + (i+1) + ']: Custom Highlight (empty = none)', '', false) ); } this.handlers = { message : [ function (m) { var username = m.getUser().getName().toLowerCase(); if (username in userSpec) { var userspecs = userSpec[username]; var message = m.getMessage(); if (userspecs.color != undefined) { m.setColor(userspecs.color); } if (userspecs.background != undefined) { m.setBackground(userspecs.background); } if (userspecs.title != undefined && message[0] != '/' && message[0] != '!') { m.setMessage(userspecs.title + ' ' + message); } } } ], enter : [ function (u) { var username = u.getName().toLowerCase(); if (username in userSpec) { var userspecs = userSpec[username]; var title = userspecs.title != undefined ? userspecs.title : 'User'; cb.sendNotice('Gentleman ' + username + ' ' + title + ' has joined the room', '', '', userspecs.color, sys.settings.get('usertitle_w').getValue()); } } ], leave : [ function (u) { var username = u.getName().toLowerCase(); if (username in userSpec) { var userspecs = userSpec[username]; var title = userspecs.title != undefined ? userspecs.title : 'User'; cb.sendNotice(title + ' has left the room', '', '', userspecs.color, sys.settings.get('usertitle_w').getValue()); } } ] } this.init = function () { for (var i = 0 ; i < NUM_TITLES ; i++) { var user = sys.settings.get('usertitle_name' + i).getValue().toLowerCase(); if (user) { var title = sys.settings.get('usertitle_title' + i) .getValue(); if (title) addUserTitle(user, title); var color = sys.settings.get('usertitle_color' + i) .getValue(); if (color) addUserColor(user, color); var highlight = sys.settings.get('usertitle_highlight' + i).getValue(); if (highlight) addUserBackground(user, highlight); } } } }); new Module('tipperCrown', new function (){ var tip = 0; var tipper = ''; this.externalSettings = [ new Setting.String('tipperCrown_title', '[Highest Overal Tipper]: Icon/text to prepend to the highest overal tipper\'s messages (empty = none)', '♕', false), new Setting.String('tipperCrown_dethrone', '[Highest Overal Tipper]: Message displayed in chat if the highest overal tipper is dethroned (empty = none). ' + 'Use %on for the name of the Old highest tipper, %ot for the old Highest tip, ' + '%nn for the name of the new highest tipper, and %nt for the new highest Tip', 'We have a new king! Hail %nn, who dethroned %on.', false), new Setting.String('tipperCrown_fg', '[Highest Overal Tipper] Color for dethrone messages', '#dd7700'), new Setting.String('tipperCrown_bg', '[Highest Overal Tipper] Highlight color for messages of the highest tipper (empty = none)', '#FFE9BF', false), new Setting.String('tipperCrown_current_name', '[Highest Overal Tipper]: Name of the current highest overal tipper (empty = none)', '', false), new Setting.Int ('tipperCrown_current_tip', '[Highest Overal Tipper]: Overal amount of the highest tipper ', 0, false, 0, 999999), ] this.handlers = { tip : [ function (t) { var newTip = t.getUser().getTipTotal(); if (newTip > tip) { var newTipper = t.getUser().getName(); var message = sys.settings.get('tipperCrown_dethrone').getValue(); if (message && newTipper != tipper && tipper) { message = message.replace('%on', tipper); message = message.replace('%ot', tip); message = message.replace('%nn', newTipper); message = message.replace('%nt', newTip); var fg = sys.settings.get('tipperCrown_fg').getValue(); fg = (fg[0] == '#') ? fg : utility.getPredefinedColor(fg); cb.sendNotice(message, '', '', fg); } tip = newTip; tipper = newTipper; } } ], message : [ function (m) { if (tipper.toLowerCase() == m.getUser().getName().toLowerCase()) { m.setMessage(sys.settings.get('tipperCrown_title').getValue() + ' ' + m.getMessage()); var bg = sys.settings.get('tipperCrown_bg').getValue(); if (bg) { bg = (bg[0] == '#') ? bg : utility.getPredefinedColor(bg); m.setBackground(bg); } } } ] } this.init = function() { tipper = sys.settings.get('tipperCrown_current_name').getValue(); tip = sys.settings.get('tipperCrown_current_tip' ).getValue(); } }()); sys.init(); sys.loadModule('fanclub'); sys.loadModule('customtitles');
© Copyright Chaturbate 2011- 2024. All Rights Reserved.