Apps Home
|
Create an App
Multi3 beta Aug 2020
Author:
rhend98
Description
Source Code
Launch App
Current Users
Created by:
Rhend98
/* Title: "TipMultiGoalApp3" app Author: rhend99 @ chaturbate Version: 0.90 (7/2019) App combined to display current sub-goal and overall goal Auto Thank you, randomized gifs or text: for tips > min, < 25 Ty1 + name for tips >25 < 99 Ty1 + name + Ty2 for tips > 100 :awetip + Ty1 + name + broadcaster specific text/gif (otherwise Ty2) delay of 4000 milliseconds Version: 0.91 (1/24/2020) edited negative tokens test in line 2 of the blue box, used getSumTotalGoal() instead of "Overall Goal" from Launch screen added automatic YellowWall display (6 in 4 seconds) updated list of Thank-you gifs changed /add [tokens] [description] to /addnewgoal [tokens] [description] to avoid conflict with Fembot Version 0.92 (8/6/2020) Edited /stats display to include tokens/hr, $/hr, time since launch Reduced Room Title update interval from 25 to 15 seconds */ /* Title: "codgod69-multigoalApp2" app Author: cb_kyle @ chaturbate Version: 0.9 (12/13/2018) App combined save bot spaces for codgot69bot2: Modifications: 20181213: Added xxx. */ /* Name: Tip Multi-Goal Author: mx2k6 Last Modified: 2016-12-30 19:21:00+13:00 For support enquiries, contact c9max69@gmail.com The moral right of "mx2k6" to be identified as the author of this work has been asserted. Version History ============================================ v1.0 08/02/2013: First release v1.1 22/02/2013: Added /stats and /reset commands. See Description for more v1.2 08/03/2013: Added /skip (suggestion from jcummings) v1.3 12/03/2013: Added /goals command for moderators and /upnext command for broadcasters. See Description for more v1.4 13/03/2013: Minor cleanup to make code more readable. Modified /upnext to actually use the broadcaster's message instead of chat notice v1.5 07/04/2013: Added some debugging code so I can get to the bottom of why chat commands don't always respond v1.6 13/04/2013: Fixed an embarrassing bug where all broadcaster and debug commands were ignored v1.7 20/04/2013: Tipper leaderboard now available on /stats output - see who your top 10 tippers are v1.8 21/04/2013: Goal timer function now available - /timer x now sets a countdown to when the goal needs tp be in by v1.9 05/05/2013: Stats junkies rejoice! /stats will now display an approximate token count per minute, to help you work out whether you'll make more tokens camming or flipping burgers. Also, tip goal king now stands out more! v1.10 05/05/2013: Now optionally highlight your highest total tipper (light purple currently - make suggestions people!) so the one who contributes the most can show off too! v1.11 05/05/2013: Timer stability improvements. Added timer debugging code. Type '/verbose' (broadcaster ONLY) to get precise dates and times of timer interval for when requesting support v1.12 18/05/2013: Clarified stats text relating to per minute figures. Changed highest tipper to no longer have a green "fanclub" name until such time as we can change the colour to a custom one v1.13 31/05/2013: Added ability for high tippers to "opt out" of being highlighted by typing "/donotwant" after they tip v1.14 05/06/2013: Added ability to directly manipulate tokens toward goals with /addtokens and /removetokens. See /help for more v1.15 02/07/2013: Timer now automatically clears if a goal is met rather than continuing on to zero v1.16 03/07/2013: Added a sixth goal... HOW MANY DO YOU PEOPLE NEED?!? Also, fixed a defect where total highest tipper got notified even when their highlight is disabled v1.17 04/07/2013: Allowed looping final goal if broadcaster chooses to v1.18 05/07/2013: Time's up message (when timer is in use) now uses a graphic to draw attention to it v1.19 19/07/2013: Notices (where applicable) now stand out more v1.20 10/08/2013: Colour schemes for the tipper highlights (suggestion from ashley93bad), defect fixes for opt-out v1.21 17/08/2013: More colour schemes added v1.22 28/09/2013: Fixed an issue with /skip which could reduce ongoing goal token quantities. Added manual progression mode- app eats tips once each goal is met until superuser types /continue command v1.23 16/11/2013: Added "Random" colour scheme v1.24 07/12/2013: Added "Purple" colour scheme, changed "Timer Countdown" warnings to be orange (stand out more) v1.25 21/12/2013: Changed to use new cb.sendNotice instead of cb.chatNotice, so we can send messages to entire groups v1.26 06/01/2014: Removed redundant "Amethyst" and "Ocean" schemes. Cleaned up colour codes so I can add new themes easier v1.27 10/01/2014: Added debugging code in case of an error I've been alerted to. IF YOU SEE THE NASTY ERROR MESSAGE, LET ME KNOW! v1.28 20/01/2014: Updated parseOptions to quit parsing goals if a blank goal is found, which occurs if settings are incorrectly filled in v1.29 01/02/2014: Added new "hide total tips" option, stats beefed up a (little) bit. Added '/support' command for broadcaster to grant developer broadcaster access. See notes! v1.30 03/02/2014: Removed "/verbose" command. The verbose data was effectively useless anyway, so now if verbose is activated by the developer cb.log() is used instead. Also, fix for potential crash if random colour scheme used. Also ran Resharper over the code to clean it up a bit v1.31 06/02/2014: Fixed defect which caused panel to not appear until someone tipped v1.32 09/03/2014: Added notification functions so that important actions are notified v1.33 01/05/2014: Added ability to add goals at runtime with /add <tokens> <description>, or remove the final goal with /delete v1.34 23/05/2014: Fixed a grammatical error in the panel that's been pissing me off for months v1.35 25/05/2014: Added options for what to do at end of final goal, go hidden/repeat/default v1.36 26/05/2014: Added ability to start hidden show at any time, no longer count entry fees towards primary goal v1.37 27/05/2014: Fixed defect where crash could occur at final goal v1.38 31/05/2014: Fixed defect where moderator notice didn't go to moderators. Also made it so fanclub members and mods get free admission to hidden show components. Fixed where hidden show wouldn't start unless it was set as the end goal action v1.39 21/06/2014: Rationalised some settings. Slight refactoring in preparation for much larger refactoring. Fixed defect where mod notices went to entirely too many people. Goal setup a little more resilient v1.40 24/11/2014: Changed so moderator messages go to room owner as well v1.41 20/01/2015: Moderators now auto-added to hidden show (if they enter the room after the app starts. I can't detect them otherwise, and I'm not wasting CPU cycles checking the ACL every time they speak) v1.42 08/02/2015: Changed so moderator messages only go to room owner once. For fuck sake. Also, starting a goal description with a number and a / repeats it that number times v1.43 12/03/2015: Added preshow entry fee option. Also, fuck yo shit messaging. I think it works now. It probably doesn't. Whatever. v1.44 13/03/2015: /addtokens now works for moderators if enabled. /removetokens does NOT. v1.45 30/11/2015: New setting so that you can choose NOT to change the room subject on each tip. I think. Unless I got it backwards, or something. v1.46 17/01/2016: Fixed defect where the application would crash if /upnext were used on the final goal v1.47 31/01/2016: We no longer change the subject if it has been less than a defined number of tokens worth of one token tips. Yellow walls yo (thanks MissJanieJones) v1.48 18/06/2016: Add a 60 second delay to goal-driven Hidden Show, because it's a little abrupt right now. Some refactoring relating to timers which will be rolled out more in v2 v1.49 19/06/2016: Add a "hashtags" setting to drop hashtags or other stuff at the end of the subject (after the tokens left counter) v1.50 20/06/2016: Bug fixes v1.51 26/06/2016: More bug fixes, added support for gifting Hidden Show tickets v1.52 28/08/2016: Notice when hidden cam show is going to start to encourage tipping to join. Repeats at 30 second marker and at start v1.53 30/12/2016: Bug fixes. Like an Uber update but less descriptive (somehow) To-Do ============================================ * Add minimum tip to count towards hidden show? * Alter un-goal to allow decrementing to shut down a hidden show if not manually started 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. */ // kyle - begin var SmartSubjectUpdateThreshold = 25; // seconds var SmartSubjectUpdateLastRun = 0; var IsSmartSubjectUpdating = false; var SmartSubjectUpdateText = ''; function updateSmartSubject() { IsSmartSubjectUpdating = false; cb.changeRoomSubject(SmartSubjectUpdateText); } // kyle - end var Application = { Name: "Tip Multi-Goal", // The name of the application Version: 1.53, // 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: false, // 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: 8 // The number of goals to permit configuration of in the startup settings window - add to UserConstants below to override per user }, }; var TimerManager = { Timers: [], onElapsed: function () { var now = new Date(); now.setMilliseconds(0); for (var timerId in TimerManager.Timers) { if (!(TimerManager.Timers[timerId] instanceof Timer)) { delete TimerManager.Timers[timerId]; continue; } else { var timer = TimerManager.Timers[timerId]; if (now <= timer.getDeadline() && now >= timer.getDeadline()) { debugLog("Timer {0} matches current time, calling onElapsed event".format(timerId)); timer.onElapsed(); delete TimerManager.Timers[timerId]; break; } } } cb.setTimeout(TimerManager.onElapsed, 1000); }, addTimer: function (id, timer) { if (!(timer instanceof Timer)) return false; debugLog("Adding timer with id {0}".format(id)); TimerManager.Timers[id] = timer; return true; }, removeTimer: function (id) { if (isUndefined(TimerManager.Timers[id])) return true; delete TimerManager.Timers[id]; if (isUndefined(TimerManager.Timers[id])) return true; return false }, initialise: function () { cb.setTimeout(TimerManager.onElapsed, 1000); }, }; /* Setup some common objects - these will be utilised more as we approach v2 */ function Timer(elapsed, seconds) { var now = new Date(); now.setSeconds(seconds); now.setMilliseconds(0); this.deadline = now; this.onElapsed = elapsed; } Timer.prototype.getDeadline = function () { return this.deadline; }; var UserConstants = { "mx2k6": { Goals: 3 }, "lbow": { Goals: 16 }, "chessslut": { Goals: 10 }, "TheLadyRousseau": { Goals: 25 }, "420stonerchick": { Goals: 10 }, }; if (cb != null) { if (UserConstants[cb.room_slug] != undefined) { Application.Constants = UserConstants[cb.room_slug]; Application.Constants.Overriden = true; } } 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, AdvanceTracker: { Count: 0, shouldAdvance: function (tokens) { if (this.Count >= 15 || tokens > settings.yellow_wall_threshold) { this.Count = 0; return true; } else { if (this.Count == 1) { Messenger.sendInfoMessage("A yellow wall has started! We're turning off room subject updates until either 15 tips have been received, or someone tips more than {0}.".format(settings.yellow_wall_threshold), cb.room_slug, null); } this.Count++; return false; } } } }; var settingsHelper = { parseBoolean: function (str) { return (str == "Yes"); } }; // vars // begin added Rhend99 var ArTy1 = []; // Array of Thanks var ArTy2 = []; // Array of Compliments var minT = 0; // Threshold Tip to get a Response var ignoreTokens = 0; // For future use, to chage on the fly var tipAmt = 0; // Current Tip amount var modelSpecial1 = 'waynenorth'; // to get special gifs, all lowercase letters var modelSpecial2 = 'rhend 99'; // to get special gifs, all lowercase letters // var s3 Output string for chat // var s4 Output string for chat, for special model var thankName = '' // to accommodate anonymous tipping var now2 // used in calc of hoursSinceLaunch var timespan2 // used in calc of hoursSinceLaunch var hoursSinceLaunch // used in function getHoursSinceLaunch var overall_goal_description /// used for dispaly of overall goal in blue box // end added Rhend99 var subject_is_final = 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' }, { name: 'yellow_wall_threshold', label: 'Yellow wall threshold (this or lower is yellow wall and will not change subject - 0 to disable)', type: 'int', defaultValue: 0, required: true }, { name: 'subject_suffix', label: 'Hashtags (appended after the goal and token counter to the room subject)', type: 'str', required: false }, // begin insert Rhend99 {name: 'minT', label: 'Minimum Tip to receive a Thank-You', type: 'int', minValue: 1, default: 5}, {name: 'ty1', label: 'First Thank-You phrase (use an asterisk as separator)', type: 'str', minLength: 0, maxLength: 1000, defaultValue: ':thanks3*:thanks*:thanksfortipping*:THANKYOU*:thankyou2*:Thanks*:thankyoumat*:ty77*:ty7*:ty3*:tyx*:s-ty4*:herothanks*:b-thanks3*:mc-thanks1*:tx5*Thanks*Thanks very much*:mspty5*:mspty8*:mspty18*:mspty13*:mspty17*:mspty10*:mspty12*:mspty9*:mspty4*:mspty3*:mspty11'}, {name: 'ty2', label: 'Second Thank-You phrase (use an asterisk as separator)', type: 'str', minLength: 0, maxLength: 1000, defaultValue: ':yourock*:yourock1*:youdaman*:wow*:kisses4u*:thumbup*:wowx*:boww*:yay*:bow*:thumbsup*:cool*:nicetip47*:nicetip9*Much appreciated!*Thanks for your support!'}, {name: 'overall_goal_description', label: 'Short Overall Goal Description. (Maximum 11 characters) (Total tokens will be calculated automatically)', type: 'str', minLength: 1, maxLength: 11}, // end insert Rhend99 ]; 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, yellow_wall_threshold: 0, subject_suffix: '', 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 (!isUndefined(recipient) && !isUndefined(group)) { cb.sendNotice(str, null, background, colour, 'bold', group); cb.sendNotice(str, recipient, background, colour, 'bold', null); } if (!isUndefined(recipient) && isUndefined(group)) cb.sendNotice(str, recipient, background, colour, 'bold', null); if (isUndefined(recipient) && !isUndefined(group)) cb.sendNotice(str, null, background, colour, 'bold', group); if (isUndefined(recipient) && isUndefined(group)) 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() { if (!isUndefined(settings.goals[Tipping.CurrentGoal.Number])) { return settings.goals[Tipping.CurrentGoal.Number].description; } else { return "[unknown goal]"; } } 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() + " " + cb.settings.overall_goal_description; 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 = (Tipping.ActualTotal / (timespan / 1000 / 60)); // edited by rhend99 return (Math.round(tokensPerMin * 10) / 10); } // begin added Rhend99 8-2020 function getTokensPerHour() { return (60 * getTokensPerMinute()).toFixed(); } function getDollarsPerHour() { return (60 * getDollarsPerMinute()).toFixed(2); } // function getHoursSinceLaunch() { // var now2 = new Date(); // var timespan2 = now2 - Application.StartupTime; // var hoursSinceLaunch = (Math.round(timespan2 / 1000 / 60 / 60 * 10) / 10) // return (Math.round(hoursSinceLaunch)).toFixed(1); // } // end added Rhend99 // added by rhend99 function getLengthOfShow() { var now = new Date(); var timespan = now - Application.StartupTime; var lengthOfShow = (timespan / 1000 / 60 / 60) ; return (Math.round(lengthOfShow * 10) / 10); } // end added by rhend99 function getDollarsPerMinute() { return (0.05 * getTokensPerMinute()).toFixed(2); // edited by rhend99 } function getTotalDollars() { return (0.05 * Tipping.VirtualTotal).toFixed(2); } function skipGoal() { Tipping.CurrentGoal.CurrentAmount = 0; nextGoal(); checkFinality(true); recachePanel(); } function nextGoal() { Tipping.CurrentGoal.Number++; if (settings.goals[Tipping.CurrentGoal.Number] != undefined && settings.goals[Tipping.CurrentGoal.Number].hide) { Messenger.sendWarningMessage("Hidden Show will start in 5 seconds! Tip {0} before the start to join!".format(hiddenShow.getPreshowPrice())); for (var user in hiddenShow.getAllUsers()) { if (!isUndefined(user)) Messenger.sendSuccessMessage("You are in the Hidden Show! Thanks for your contribution!", user); } TimerManager.addTimer("startHiddenShow" + Tipping.CurrentGoal.toString(), new Timer(function () { Messenger.sendWarningMessage("Hidden Show has started! Tip {0} to join!".format(hiddenShow.getPrice())); hiddenShow.start(); }, 5)); TimerManager.addTimer("warnHiddenShow" + Tipping.CurrentGoal.toString(), new Timer(function () { Messenger.sendWarningMessage("Hidden Show will start in 5 seconds! Tip {0} before the start to join!".format(hiddenShow.getPreshowPrice())); }, 5)); } } 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(shouldSetSubject) { 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--; Tipping.Finality = false; } else if (settings.action_on_finality == 'default') { Tipping.Finality = true; } else if (settings.action_on_finality == 'hidden') { if (!Tipping.Finality) { TimerManager.addTimer("startHiddenShowFinal", new Timer(function () { hiddenShow.start(); }, 60)); Messenger.sendWarningMessage("The final goal has been met! The Hidden Show starts in 5 seconds!"); for (var user in hiddenShow.getAllUsers()) { Messenger.sendSuccessMessage("You are in the Hidden Show! Thanks for your contribution!", user); } } Tipping.Finality = true; } } else { Tipping.Finality = false; } if (shouldSetSubject) { updateRoomSubject(); } recachePanel(); } 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 && Tipping.Finality) { return; } if (Tipping.Finality) { 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 = "[" + getGoalTokensRemaining() + " Left] " + getCurrentGoalDescription() ; // kyle changed } else { newSubject = getCurrentGoalDescription(); } if (settings.timer_in_subject === "Yes" && goalTimer.timerRunning) { newSubject += " (" + goalTimer.getExtraText() + ")"; } subject_is_final = false; } if (!isUndefined(settings.subject_suffix) && settings.subject_suffix != '') { newSubject += " {0}".format(settings.subject_suffix); } debugLog("Changing subject to: " + newSubject); //kyle altered: cb.changeRoomSubject(newSubject); { if (!IsSmartSubjectUpdating) { var currentTime = Date.now(); var timePassed = currentTime - SmartSubjectUpdateLastRun; if (timePassed > (SmartSubjectUpdateThreshold * 1000)) { SmartSubjectUpdateLastRun = currentTime; cb.changeRoomSubject(newSubject); } else { // update newSubject in text. IsSmartSubjectUpdating = true; SmartSubjectUpdateText = newSubject; cb.setTimeout(updateSmartSubject, (SmartSubjectUpdateThreshold * 1000) ); // cb.setTimeout(runSubgoalSmartCount, ((SubgoalSmartCountThreshold * 1000) - timePassed)); } } else { SmartSubjectUpdateText = newSubject; } } } function recordTip(username, tokens, countsToActual) { var tipperFound = false; var shouldSetSubject = (settings.change_subject_on_tip && Tipping.AdvanceTracker.shouldAdvance(tokens)); 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()) { //kyle disabled: Messenger.sendSuccessMessage("Thanks, {0}! Your tip gets you into the hidden show! You can also gift this ticket to someone else with '/gift <user>'. Enjoy!".format(username), username); // kyle: disabled Messenger.sendSuccessMessage("Thanks, {0}! Your tip gets you into the hidden show! Enjoy!".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 (!Tipping.Finality) { 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(shouldSetSubject); return; } 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 (Tipping.Finality) 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(shouldSetSubject); } 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; Tipping.AdvanceTracker.Count = 0; Tipping.Finality = false; Tipping.Leaderboard.All = []; hiddenShow.reset(); recachePanel(); updateRoomSubject(); } cb.onTip(function (tip) { recordTip(tip.from_user, tip.amount, true); // begin added Rhend99 c_ayw.onTip(); // for AutoYellowWall ignoreTokens = cb.settings.minT modelName = tip['to_user'] tipAmt = tip['amount'] if(tip['is_anon_tip']) { thankName = 'my secret Lover'; } else { thankName = tip['from_user'] } if (tip['amount'] >= ignoreTokens) { apply_ty(tip['from_user']); } // end added Rhend99 }); cb.onEnter(function (user) { if (user.in_fanclub || user.is_mod) { hiddenShow.addUser(user.user); } }); // begin added Rhend99 // helper functions function apply_ty(sUser) { var COLOR_YELLOW = '#FFFF00'; var COLOR_BLACK = '#000000'; var COLOR_WHITE = '#FFFFFF'; var iTy1Rnd = (Math.round(Math.random() * (ArTy1.length-1))); var iTy2Rnd = (Math.round(Math.random() * (ArTy2.length-1))); var s3 = '' + ArTy1[iTy1Rnd] + ' ' + thankName + '! :blanktwo ' + ArTy2[iTy2Rnd]; var s4 = s3 if (modelName === modelSpecial1) { var s4 = '' + ArTy1[iTy1Rnd] + ' ' + thankName + '! :blanktwo :waynecheers ' ; //special text } if (modelName === modelSpecial2) { var s4 = '' + ArTy1[iTy1Rnd] + ' ' + thankName + '! :blanktwo :waynecheers ' ; //special text } if (tipAmt <= 24) { var s3 = '' + ArTy1[iTy1Rnd] + ' ' + thankName + '! ' ; } if (tipAmt >= 100) { var s3 = '' + ' :awetip \n ' + s4 ; } setTimeout(function(){ cb.sendNotice(s3, '', COLOR_WHITE, COLOR_BLACK, 'bold'); }, 4000); } function init2() { var s1 = '' + cb.settings.ty1; var s2 = '' + cb.settings.ty2; ArTy1 = s1.split('*'); ArTy2 = s2.split('*'); } // end added Rhend99 cb.onLeave(function (user) { if (user.in_fanclub || user.is_mod) { hiddenShow.removeUser(user.user); } }); function recachePanel() { if (Tipping.Finality) { 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 / Sub-Goal' + (settings.hide_token_haul ? '' : ' (Total)') + ':', row1_value: Tipping.CurrentGoal.CurrentAmount + ' / ' + getCurrentGoalAmount() + (settings.hide_token_haul ? '' : ' (' + Tipping.VirtualTotal + ')'), // start inserted Rhend99 // new 1-24-2020 row2_label: ((getSumTotalGoal() - Tipping.VirtualTotal) <= 0) ? '' : 'Remaining Overall Goal:', row2_value: ((getSumTotalGoal() - Tipping.VirtualTotal) <= 0) ? '' : (getSumTotalGoal() - Tipping.VirtualTotal + ' to ' + cb.settings.overall_goal_description) , row3_label: 'Highest Tip:', row3_value: formatUsername(Tipping.Leaderboard.Leaders.Highest.Username) + ' (' + Tipping.Leaderboard.Leaders.Highest.Amount + ')', // end inserted Rhend99 }; 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 += "=== Goal Statistics ===\n\n" output += "Sum total goal: " + getSumTotalGoal() + "\n"; output += "Total tipped so far: " + Tipping.VirtualTotal + "\n"; output += "Total goal remaining: " + (getSumTotalGoal() - Tipping.VirtualTotal) + "\n"; output += "Token Rate: " + getTokensPerMinute() + " tkns/min = " + getTokensPerHour() + " tkns/hr" + "\n"; // edited by rhend99 if (includeExtraInfo) { output += "Total actual tipped (disregarding resets): " + Tipping.ActualTotal + " in " + getLengthOfShow() + " hrs" + "\n"; output += "Dollar rate (@ $0.05/token): $" + getDollarsPerMinute() + "/min = $" + getDollarsPerHour() + "/hr" + "\n"; // edited by rhend99 output += "Total dollars (@ $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 += "=== Command Line Help ===\n\n" 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\n"; output += "=== For more help ===\n\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(); output += "=== Hidden Show Admit List ===\n\n" 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(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 ===\n\n" + getAllGoals(), msg.user); } else if (msg.m.substring(1) === "help") { debugLog("Help command received from " + 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(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); } // begin edited by rhend99 } else if (msg.m.substring(1, 11) === "addnewgoal" && msg.m.substring(1, 5) !== "addt") { if (msg.m.length >= 14) { var tokens = parseInt(msg.m.substring(12, msg.m.indexOf(' ', 12))); if (tokens > 0) { var description = msg.m.substring(msg.m.indexOf(' ', 12) + 1); // end edited by rhend99 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: '/addnewgoal <tokens> <description>' where <tokens> should be the number of tokens in the new goal, and <description> should be the new goal description", msg.user); // edited rhend99 } } else { Messenger.sendErrorMessage("USAGE: '/addnewgoal <tokens> <description>' where <tokens> should be the number of tokens in the new goal, and <description> should be the new goal description", msg.user); // edited rhend99 } } 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; } } /* // kyle disabled: if (msg.m.substring(1, 5) == "gift") { if (hiddenShow.checkUser(msg.user)) { if (msg.m.length >= 6) { var giftee = msg.m.substring(6); if (!hiddenShow.checkUser(giftee)) { hiddenShow.addUser(giftee); if (hiddenShow.checkUser(giftee)) { Messenger.sendSuccessMessage("Congratulations! {0} has gifted you their Hidden Show ticket!".format(msg.user), giftee); Messenger.sendSuccessMessage("Your Hidden Show ticket has been gifted to {0}. Thanks!".format(giftee), msg.user); hiddenShow.removeUser(msg.user); } else { Messenger.sendErrorMessage("Unable to gift your Hidden Show ticket to {0}. An error occurred. Are they in the room?".format(giftee), msg.user); hiddenShow.removeUser(giftee); } } else { Messenger.sendErrorMessage("Unable to gift your Hidden Show ticket to {0}, because they already have one".format(giftee), msg.user) } } else { Messenger.sendErrorMessage("USAGE: '/gift <user>' where 'user' is the person to whom you want to gift your Hidden Show ticket", msg.user); } } else { Messenger.sendErrorMessage("Unable to gift your Hidden Show ticket to {0}, because you do not have one to gift".format(giftee), msg.user) } } */ } /* Code to allow the developer to stand out if necessary (e.g. for tech support) */ /* // kyle disabled: 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); settings.yellow_wall_threshold = cb.settings.yellow_wall_threshold; settings.subject_suffix = cb.settings.subject_suffix; 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, }); } } } } // begin added for yellow wall cb_kyle /* ========= AutoYellowWall ================================= */ cb.settings_choices.push({ name: 'autoYellowWallText', label: 'Enter the emoji or text to display when a yellow wall is triggered (nothing disables). Ex. :YellowWall1 :yellowwall1 ', type: 'str', required: false, defaultValue: ':YellowWall2' }); // ************************************************** class AutoYellowWall { constructor() { this._isOn = false; this._count = 0; this._lastTipTime = 0; this._messageYellowWall = ':YellowWall2 '; } //constants get LAST_TIP_MIN_TIME() { return 4000; } // in MILLISECONDS -- 3 seconds get TRIGGER_THRESHOLD() { return 6; } //no spam wait time variable ? //variables get isOn() { return this._isOn; } set isOn(value) { this._isOn = value;} get count() { return this._count; } set count(value) { this._count = value;} get lastTipTime() { return this._lastTipTime; } set lastTipTime(value) { this._lastTipTime = value; } get messageYellowWall() { return this._messageYellowWall;} set messageYellowWall(value) { this._messageYellowWall = value; } //functions toggle() { this._isOn = ! this._isOn; } onTip() { if (this._isOn) { var currentTipTime = Date.now(); if ((currentTipTime - this._lastTipTime) > this.LAST_TIP_MIN_TIME) { this._count = 0; } else { this._count = this._count + 1; if (this._count > this.TRIGGER_THRESHOLD) { this._count = 0; cb.sendNotice(' ' + this._messageYellowWall); } } this._lastTipTime = currentTipTime; } } } // END -- class AutoYellowWall let c_ayw = new AutoYellowWall(); var sAutoYellowWall = cb.settings['autoYellowWallText']; if (sAutoYellowWall != '') { c_ayw.messageYellowWall = sAutoYellowWall; c_ayw.isOn = true; } // end added for yellow wall function init() { // begin added Rhend99 init2(); //end added Rhend99 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); if ( Application.Constants.Overridden !== undefined && Application.Constants.Overridden ) { Messenger.sendSuccessMessage("Customised application constants are in effect for your room. To change settings such as number of goals, contact {0}".format(Application.Author), cb.room_slug); } TimerManager.initialise(); reset(); } if (cb.settings.goal_1_tokens !== null && cb.settings.goal_1_tokens !== undefined) { if (!Application.Debug || (Application.Debug && cb.room_slug == Application.Author)) { init(); } else { Messenger.sendErrorMessage("Application Error! This {0} version is a DEBUG build and can only be run by {1}. Startup aborted!".format(Application.Name, Application.Author), cb.room_slug); } }
© Copyright Chaturbate 2011- 2024. All Rights Reserved.