Apps Home
|
Create an App
SBC Multi-Goal Tip Counter+ x 25
Author:
salserobachaterocasinero
Description
Source Code
Launch App
Current Users
Created by:
Salserobachaterocasinero
/* [APP] Title: "SBC Multi-Goal Tip Counter+ x 25" Author: SalseroBachateroCasinero Version: 1.0.0 (3/8/2016) This app features a multi-goal tip counter or a basic tip counter, which can be toggled back and forth at any time without losing tip amounts. You can manually skip a goal or adjust the amount of tokens up/down as needed to achieve the proper flow of your show. The multi-goal mode of the tip counter ends at the 'finish line', where additional tips will continue to add to the counter until the show ends and/or the app is deactivated. You can manually set the title of the room as well, in case you decide you are no longer interested in using the app-generated one. A goal list will run on a timer and can be toggled on/off in the settings or while the app is running. You can also display the next goal as a separate notification at any time. Future features are planned, but this is a complete app as-is at this point. Type '/sbc-mgtc toggle' to toggle between "basic tip counter" and "multi-goal tip counter" mode. Type '/sbc-mgtc toggle goallist' to toggle the "goal list" feature on or off. <Multi-Goal Tip Counter Mode Only> Type '/sbc-mgtc show goallist' to show the "goal list" immediately. <Multi-Goal Tip Counter Mode Only> Type '/sbc-mgtc show nextgoal' to show the "next goal" immediately. <Multi-Goal Tip Counter Mode Only> Type '/sbc-mgtc skip' to increase the tip counter by enough tokens to move to the "next goal" immediately. <Multi-Goal Tip Counter Mode Only> Type '/sbc-mgtc adjust [#]' to manually adjust the tip counter up (where [#] is a positive number) or down (with a negative number). You cannot decrease the count to below zero (even if you try). Type '/sbc-mgtc toptippertotal [U] [#]' to manually set the "highest total" top tipper (where [U] is the user's chat name and [#] is the amount of tokens tipped). Type '/sbc-mgtc toptippersingle [U] [#]' to manually set the "highest single" top tipper (where [U] is the user's chat name and [#] is the amount of tokens tipped). Type '/sbc-mgtc lasttipper [U] [#]' to manually set the last tipper (where [U] is the user's chat name and [#] is the amount of tokens tipped). Type '/sbc-mgtc title [X]' to manually change the room title (where [X] is the new room title or leave blank for the default). Type '/sbc-mgtc panel total' to switch the custom tip panel view to "highest total tips" only. Type '/sbc-mgtc panel single' to switch the custom tip panel view to "highest single tip" only. Type '/sbc-mgtc panel rotate' to switch the custom tip panel view to rotate between "highest total tip" and "highest single tip". Type '/sbc-mgtc resetgoal' to reset the app (to "multi-goal tip counter" mode) and launch the next goal sequence.".toReplace(Command['']), true); Type '/sbc-mgtc stats' to view the running time and tipping statistics for the current session. <Broadcaster Only> Type '/sbc-mgtc help' if you forget the above commands. */ var App = { Name: 'SBC Multi-Goal Tip Counter+ x 25', // The name of the app Version: '1.0.0 (3/8/2016)', // The current version of the app Author: 'SalseroBachateroCasinero', // The author of this version // Don't change the author unless you've modified something! Command: 'sbc-mgtc', // The "/" command of the app Constants: { Goals: 25 }, // The number of possible goals } /* ************************************************** */ var DebugMode = false; var DebugCount = 1; var VerboseDebugMode = false; /* ************************************************** */ var ShowDebugMessage = function (out) { if (DebugMode) { out = out.trim(); output = '<{{0}}> Debug ~ {{1}}: '; output = output.toReplace(String(DebugCount).toPad(2), App.Name); if (out.charAt(0) == '*') { cb.sendNotice(output + String(out.substring(1).trim()), App.Author.toLowerCase(), '#FFFFFF', '#FF0000', 'bolder'); DebugCount++; } else if (VerboseDebugMode) { cb.sendNotice(output + String(out), App.Author.toLowerCase(), '#FFFFFF', '#7F0000', 'bolder'); DebugCount++; } } return DebugMode; }; String.prototype.toReplace = function () { var newString = String(this); for (var idx = 0;idx < arguments.length;idx++) { newString = newString.replace('{{'+idx + '}}', arguments[idx]); } return String(newString); }; String.prototype.toLowerCaseTrim = function () { return String(this).toLowerCase().trim(); }; String.prototype.toPad = function (digits) { var newString = String(this); while (newString.length < digits) { newString = '0' + newString; } return newString; }; Number.prototype.fromSecondsNowMilliseconds = function () { return Number(this) * 1000; // Seconds -> Milliseconds }; Number.prototype.fromMinutesNowMilliseconds = function () { return Number(this) * 60 * 1000; // Minutes -> Seconds -> Milliseconds }; var TheInfo = '{{0}} v{{1}} by {{2}}'; TheInfo = TheInfo.toReplace(App.Name, App.Version, App.Author); var FormatCommand = function (info,doublespace) { var newString = "Type '/{{0}} {{1}}" + '\n'; if (doublespace) { newString += '\n'; } return String(newString.toReplace(App.Command, info)); }; var Command = {}; Command['toggle'] = 'toggle'; Command['show'] = 'show'; Command['goallist'] = 'goallist'; Command['nextgoal'] = 'nextgoal'; Command['skip'] = 'skip'; Command['adjust'] = 'adjust'; Command['toptipperT'] = 'toptippertotal'; Command['toptipperS'] = 'toptippersingle'; Command['lasttipper'] = 'lasttipper'; Command['title'] = 'title'; Command['panel'] = 'panel'; Command['panelT'] = 'total'; Command['panelS'] = 'single'; Command['panelR'] = 'rotate'; Command['resetgoal'] = 'resetgoal'; Command['stats'] = 'stats'; Command['help'] = 'help'; var TheCommands = '\n'; TheCommands += FormatCommand("{{0}}' to toggle between 'basic tip counter' and 'multi-goal tip counter' mode.".toReplace(Command['toggle']), true); TheCommands += FormatCommand("{{0}}' to toggle the 'goal list' feature on or off. <Multi-Goal Tip Counter Mode Only>".toReplace(Command['toggle'] + ' ' + Command['goallist']), true); TheCommands += FormatCommand("{{0}} {{1}}' to show the 'goal list' immediately. <Multi-Goal Tip Counter Mode Only>".toReplace(Command['show'], Command['goallist']), false); TheCommands += FormatCommand("{{0}} {{1}}' to show the 'next goal' immediately. <Multi-Goal Tip Counter Mode Only>".toReplace(Command['show'], Command['nextgoal']), true); TheCommands += FormatCommand("{{0}}' to increase the tip counter by enough tokens to move to the 'next goal' immediately. <Multi-Goal Tip Counter Mode Only>".toReplace(Command['skip']), true); TheCommands += FormatCommand("{{0}} [#]' to manually adjust the tip counter up (where [#] is a positive number) or down (with a negative number). You cannot decrease the count to below zero (even if you try).".toReplace(Command['adjust']), true); TheCommands += FormatCommand("{{0}} [U] [#]' to manually set the 'highest total' top tipper (where [U] is the user's chat name and [#] is the amount of tokens tipped).".toReplace(Command['toptipperT']), false); TheCommands += FormatCommand("{{0}} [U] [#]' to manually set the 'highest single' top tipper (where [U] is the user's chat name and [#] is the amount of tokens tipped).".toReplace(Command['toptipperS']), false); TheCommands += FormatCommand("{{0}} [U] [#]' to manually set the last tipper (where [U] is the user's chat name and [#] is the amount of tokens tipped).".toReplace(Command['lasttipper']), true); TheCommands += FormatCommand("{{0}} [X]' to manually change the room title (where [X] is the new room title or leave blank for the default).".toReplace(Command['title']), true); TheCommands += FormatCommand("{{0}} {{1}}' to switch the custom tip panel view to 'highest total tips' only.".toReplace(Command['panel'], Command['panelT']), false); TheCommands += FormatCommand("{{0}} {{1}}' to switch the custom tip panel view to 'highest single tip' only.".toReplace(Command['panel'], Command['panelS']), false); TheCommands += FormatCommand("{{0}} {{1}}' to switch the custom tip panel view to rotate between 'highest total tip' and 'highest single tip'.".toReplace(Command['panel'], Command['panelR']), true); TheCommands += FormatCommand("{{0}}' to reset the app (to 'multi-goal tip counter' mode) and launch the next goal sequence.".toReplace(Command['resetgoal']), true); TheCommands += FormatCommand("{{0}}' to view the running time and tipping statistics for the current session. <Broadcaster Only>".toReplace(Command['stats']), true); TheCommands += FormatCommand("{{0}}' if you forget the above commands.".toReplace(Command['help']), false); var TheHelp = '\n' + '*** {{0}} ***' + '\n'; TheHelp += '{{1}}'; TheHelp = TheHelp.toReplace(TheInfo, TheCommands); cb.settings_choices = [ { name: 'tipcounter', type: 'choice', label: "Start the tip counter in 'basic tip counter' or 'multi-goal tip counter' mode by default?", choice1: 'Multi-Goal Tip Counter', choice2: 'Basic Tip Counter', defaultValue: 'Multi-Goal Tip Counter' }, { name: 'support', type: 'choice', label: 'Enable support mode?', choice1: 'Yes', choice2: 'No', defaultValue: 'No' }, { name: 'moderation', type: 'choice', label: 'Allow moderators to access this feature?', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'alwaysallowedusers', type: 'str', required: false, label: 'List of users that will have moderator access at all times, regardless of the previous question (separate with ; for more than 1 or leave blank for none)', defaultValue: '' }, { name: 'noticebackground', type: 'str', label: 'Notification background color in hex #RrGgBb format (default is white / #FFFFFF)', defaultValue: '#FFFFFF' }, { name: 'noticetext', type: 'str', label: 'Notification text color in hex #RrGgBb format (default is red / #FF0000)', defaultValue: '#FF0000' }, { name: 'noticeweight', type: 'choice', label: 'Notification weight', choice1: 'Normal', choice2: 'Bold', choice3: 'Bolder', defaultValue: 'Bolder' }, { name: 'roomtitle', type: 'str', label: 'Basic tip counter room title', defaultValue: 'My Room!' }, { name: 'goallist', type: 'choice', label: 'Enable the goal list feature by default?', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'goallistname', type: 'str', label: 'Goal list title', defaultValue: '*** GOAL LIST ***' }, { name: 'goallistinterval', type: 'int', label: 'Interval between goal list notifications (in minutes / minimum = 1)', minValue: 1, defaultValue: 5 }, { name: 'amountlocation', type: 'choice', choice1: 'Before', choice2: 'After', label: 'Show goal amount before or after description?', defaultValue: 'After' }, { name: 'separatorcharacter', type: 'str', required: false, label: 'Goal separator for single-line goal list (try a : graphic or leave blank for a multi-line goal list)', defaultValue: '|' }, { name: 'nextgoalname', type: 'str', label: 'Next Goal title', defaultValue: '*** NEXT GOAL ***' }, { name: 'finishlinedescription', type: 'str', label: "Description of 'finish line' to show when the next goal is the 'finish line'", defaultValue: 'Finish Line!' }, { name: 'finishlinemode', type: 'choice', label: "Switch to 'basic tip counter' when the 'finish line' is reached?", choice1: 'Yes', choice2: 'No', defaultValue: 'No' }, { name: 'goalintitle', type: 'choice', label: 'Show current goal in room title?', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'tokensremainingintitle', type: 'choice', label: 'Show tokens remaining for current goal in room title?', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'goalintitleplacement', type: 'choice', label: 'Show current goal before or after tokens remaining in room title (only applies when both are enabled)?', choice1: 'Before', choice2: 'After', defaultValue: 'Before' }, { name: 'titleprefix', type: 'str', required: false, label: 'Prefix to always show in room title', defaultValue: 'My Room ~' }, { name: 'titlesuffix', type: 'str', required: false, label: 'Suffix to always show in room title', defaultValue: '~ #hashtag #examples' }, { name: 'whichcustompanel', type: 'choice', label: "Show 'top tipper' based on 'highest total tips', 'highest single tip', or rotate between the two?", choice1: 'Highest Total Tips', choice2: 'Highest Single Tip', choice3: 'Rotate', defaultValue: 'Yes' }, { name: 'custompanelinterval', type: 'int', label: 'Interval for rotation (in seconds / minimum = 5 / maximum = 60)', minValue: 5, maxValue: 60, defaultValue: 5 }, { name: 'custompanel', type: 'choice', label: "Customize the tip token panel (using the next 6 inputs -- if any are left blank, this will default to 'No')?", choice1: 'Yes', choice2: 'No', defaultValue: 'Yes' }, { name: 'alltipslabel', type: 'str', label: '1. Label to indicate the total amount of tokens received (during this app session)', required: false, maxLength: 20, defaultValue: 'Total Tips Received' }, { name: 'hightiplabel', type: 'str', label: '2a. Label to indicate the highest total tipper', required: false, maxLength: 10, defaultValue: 'Most Tips' }, { name: 'blankhigh', type: 'str', label: '2b. What to show when there is no highest total tipper yet (upon app startup)', required: false, maxLength: 14, defaultValue: 'N/A' }, { name: 'goodtiplabel', type: 'str', label: '3a. Label to indicate the highest single tipper', required: false, maxLength: 10, defaultValue: 'Top Tipper' }, { name: 'blankgood', type: 'str', label: '3b. What to show when there is no highest single tipper yet (upon app startup)', required: false, maxLength: 14, defaultValue: 'N/A' }, { name: 'lasttiplabel', type: 'str', label: '4a. Label to indicate the most recent tipper', required: false, maxLength: 10, defaultValue: 'Latest Tip' }, { name: 'blanklast', type: 'str', label: '4b. What to show when there is no most recent tipper yet (upon app startup)', required: false, maxLength: 14, defaultValue: 'N/A' }, { name: 'goaltipslabel', type: 'str', label: "5. Custom tip panel label for current goal progress (you may use '{' to represent the current goal and/or '}' to represent the total goals e.g. 'Goal { of }' = 'Goal 3 of 5' during the third goal out of five total)", required: false, maxLength: 20, defaultValue: 'Goal Progress [{/}]' }, { name: 'finishlinelabel', type: 'str', label: "6. Custom tip panel label once the 'finish line' is reached", required: false, maxLength: 20, defaultValue: 'Finish Line Reached!' }, ]; var MultiGoalTipCounterMode = cb.settings['tipcounter'] == 'Multi-Goal Tip Counter'; var SwitchFinishLineMode = cb.settings['finishlinemode'] == 'Yes'; // Dynamically Build Goal Fields // var GoalItems = []; for (m=1;m<=App.Constants.Goals;m++) { GoalItems.push({ name: 'goal' + m, type: 'str', required: (m === 1), label: 'Goal ' + m + ' Description' + (m > 1 ? ' (Blank = Disabled)' : '' ), defaultValue: (m === 1 ? 'Just One Goal!' : '' ) }); GoalItems.push({ name: 'goal' + m + 'price', type: 'int', required: (m === 1), label: 'Goal ' + m + ' Price' + (m === 1 ? '(goals are cumulative, so each subsequent goal amount will be added to the previous e.g. a goal of 250 followed by a goal of 250 will mean each goal will be met at the next 250: goal 1 at 250 and goal 2 at 500)' : '' ) + (m > 1 ? ' (0 = Disabled)' : '' ), minValue: (m === 1 ? 1 : 0 ), defaultValue: 250 }); } cb.settings_choices = cb.settings_choices.concat(GoalItems); // Dynamically Build Goal Fields // var SupportMode = cb.settings['support'] == 'Yes'; var NoticeStyle = []; NoticeStyle['BackgroundColor'] = cb.settings['noticebackground']; NoticeStyle['TextColor'] = cb.settings['noticetext']; NoticeStyle['Weight'] = cb.settings['noticeweight']; var TheBroadcaster = cb['room_slug']; var ActiveBroadcaster = SupportMode ? App.Author.toLowerCase() : TheBroadcaster; var CurrentUser; // Moderators "Allowed" To Use This App // var ModeratorsAllowed = cb.settings['moderation'] == 'Yes'; // Moderators "Allowed" To Use This App // // Users "Always Allowed" To Use This App // var AlwaysAllowedUsers = cb.settings['alwaysallowedusers'] == null ? [] : cb.settings['alwaysallowedusers'].length == 0 ? [] : cb.settings['alwaysallowedusers'].split(';'); // Users "Always Allowed" To Use This App // var UserIs = []; UserIs.ThisUser = function (CompareUser) { return CurrentUser == CompareUser; }; // User Is The App Author // UserIs.Author = function () { return UserIs.ThisUser(App.Author.toLowerCase()); }; // User Is The App Author // // User Is The Actual Broadcaster // UserIs.TheBroadcaster = function () { return UserIs.ThisUser(TheBroadcaster); }; // User Is The Actual Broadcaster // // User Is The Active Broadcaster (Spoofed To App Author When Support Mode Is Active) // UserIs.ActiveBroadcaster = function () { return UserIs.ThisUser(ActiveBroadcaster); }; // User Is The Active Broadcaster (Spoofed To App Author When Support Mode Is Active) // // User Is An "Allowed" Moderator // UserIs.AllowedModerator = function (msg) { return (UserIs.Moderator(msg) && ModeratorsAllowed); }; // User Is An "Allowed" Moderator // // User Exists In The "Always-Allowed Users" List // UserIs.AlwaysAllowed = function () { if (SupportMode && UserIs.TheBroadcaster()) { return true; } for (i=0;i<AlwaysAllowedUsers.length;i++) { if (UserIs.ThisUser(AlwaysAllowedUsers[i].toLowerCase().trim())) { return true; break; } } return false; }; // User Exists In The "Always-Allowed Users" List // // User Is A Moderator // UserIs.Moderator = function (msg) { return msg['is_mod']; }; // User Is A Moderator // // User Is A Fan Club Member // UserIs.FanClubMember = function (msg) { return msg['in_fanclub']; }; // User Is A Fan Club Member // // User Is A Super Tipper // UserIs.SuperTipper = function (msg) { return msg['tipped_tons_recently']; }; // User Is A Super Tipper // // User Is A Big Tipper // UserIs.BigTipper = function (msg) { return msg['tipped_alot_recently']; }; // User Is A Big Tipper // // User Is A Tipper // UserIs.Tipper = function (msg) { return msg['tipped_recently']; }; // User Is A Tipper // // User Is A Token Owner // UserIs.TokenOwner = function (msg) { return msg['has_tokens']; }; // User Is A Token Owner // // User Is Gray // UserIs.Gray = function (user) { return ((!(UserIs.TokenOwner(user))) && (!(UserIs.FanClubMember(user))) && (!(UserIs.Moderator(user))) && (!(UserIs.AlwaysAllowed())) && (!(UserIs.TheBroadcaster()))); }; // User Is Gray // var SendTo = []; // Send Notice To All Or Specified User/Group As Indicated // SendTo.Default = function (msg,user,group) { if (group == null || group == '') { cb.sendNotice(msg, user, NoticeStyle['BackgroundColor'], NoticeStyle['TextColor'], NoticeStyle['Weight']); } else { cb.sendNotice(msg, user, NoticeStyle['BackgroundColor'], NoticeStyle['TextColor'], NoticeStyle['Weight'], group); } }; // Send Notice To All Or Specified User/Group As Indicated // // Send Notice To The "Always-Allowed Users" List // SendTo.AlwaysAllowedUsers = function (msg,bg,fg,wt) { for (i=0;i<AlwaysAllowedUsers.length;i++) { if (!(UserIs.AllowedModerator(msg)) || (SupportMode && UserIs.TheBroadcaster())) { cb.sendNotice(msg, AlwaysAllowedUsers[i].trim(), bg, fg, wt); } } }; // Send Notice To The "Always-Allowed Users" List // // Send Notice To Broadcaster, Moderators (If "Allowed"), And "Always-Allowed Users" List // SendTo.ImportantUsers = function (msg) { if (SupportMode) { if ((!(UserIs.TheBroadcaster(App.Author.toLowerCase()))) && (!(UserIs.Moderator(App.Author.toLowerCase()) && ModeratorsAllowed)) || (!(UserIs.AlwaysAllowed(App.Author.toLowerCase())))) { // Author Is Broadcaster? ~or~ Author Is A Moderator & Moderators Allowed? ~or~ Author Is Always-Allowed? Don't Do This. SendTo.Default(msg, App.Author.toLowerCase()); } } SendTo.Default(msg, TheBroadcaster); if (ModeratorsAllowed) { SendTo.Default(msg, null, 'red'); } SendTo.AlwaysAllowedUsers(msg, NoticeStyle['BackgroundColor'], NoticeStyle['TextColor'], NoticeStyle['Weight']); }; // Send Notice To Broadcaster, Moderators (If "Allowed"), And "Always-Allowed Users" List // // Send Special Case // SendTo.Special = function (msg,user,group) { cb.sendNotice(msg, user, '#FFFFFF', '#FF0000', 'bolder', group); }; // Send Special Case // var GoalDescription = []; var GoalPriceInterval = []; var GoalPrice = []; var GoalReached = []; var FinishLineDescription = cb.settings['finishlinedescription']; var FinishLineTotal = 0; var FinishLineReached = false; var GoalCount = 0; for (i=1;i<=App.Constants.Goals;i++) { if ((!(cb.settings['goal' + i] == '')) && (!(parseInt(cb.settings['goal' + i + 'price']) == 0))) { GoalCount++; GoalDescription[GoalCount] = cb.settings['goal' + i]; GoalPriceInterval[GoalCount] = cb.settings['goal' + i + 'price']; FinishLineTotal += GoalPriceInterval[GoalCount]; // Running Total Of All Goal Prices (Final = Cumulative Total) GoalPrice[GoalCount] = FinishLineTotal; // Each New Goal Price Added To Previous Goal Price(s) (Increasing Goal Totals) GoalReached[GoalCount] = false; } } var CurrentGoal = 1; // Do Goal List // var DoGoalList = []; // Goal List Notification Feature // var GoalListStatus = cb.settings['goallist'] == 'Yes'; var RememberGoalListStatus = GoalListStatus; var GoalList = ''; DoGoalList.Notification = function (force) { /* ************************************************** */ ShowDebugMessage('* DoGoalList.Notification(force)'); /* ************************************************** */ if (force) { /* ************************************************** */ ShowDebugMessage('Forced'); /* ************************************************** */ // Show Goal List Notification // SendTo.Default(GoalList); // Show Goal List Notification // } else if (GoalListStatus) { /* ************************************************** */ ShowDebugMessage('Default'); /* ************************************************** */ // Show Goal List Notification // SendTo.Default(GoalList); // Show Goal List Notification // // Setup Timer To Repeat // cb.setTimeout(DoGoalList.Notification, cb.settings['goallistinterval'].fromMinutesNowMilliseconds()); // Setup Timer To Repeat // } /* ************************************************** */ ShowDebugMessage('* DoGoalList.Notification(force) Complete'); /* ************************************************** */ }; // Goal List Notification Feature // // Build Goal List // var GoalListItemSeparator = ''; DoGoalList.Build = function () { /* ************************************************** */ ShowDebugMessage('* DoGoalList.Build()'); /* ************************************************** */ // Dynamically Add Goal Items // GoalList = '\n' + cb.settings['goallistname'] + '\n\n'; var x = 0; for (i=1;i<=App.Constants.Goals;i++) { if ((!(cb.settings['goal' + i] == '')) && (!(parseInt(cb.settings['goal' + i + 'price']) == 0))) { x++; if (i >= 2) { GoalList += GoalListItemSeparator; } GoalList += cb.settings['amountlocation'] == 'Before' ? '[{{0}}] {{1}} '.toReplace(GoalPrice[x], cb.settings['goal'+i]) : '{{0}} [{{1}}] '.toReplace(cb.settings['goal'+i], GoalPrice[x]); } } GoalList += '\n'; // Dynamically Add Goal Items // if (MultiGoalTipCounterMode) { DoGoalList.Notification(false); } /* ************************************************** */ ShowDebugMessage('* DoGoalList.Build() Complete'); /* ************************************************** */ }; // Build Goal List // // Do Goal List // // Do Room Title // var AllowBuildTitle = true; var BasicRoomTitle = cb.settings['roomtitle']; var OriginalBasicRoomTitle = BasicRoomTitle; /* ************************************************** */ ShowDebugMessage('AllowBuildTitle=' + AllowBuildTitle); /* ************************************************** */ var DoRoomTitle = []; // Build Room Title // DoRoomTitle.Build = function () { /* ************************************************** */ ShowDebugMessage('* DoRoomTitle.Build()'); /* ************************************************** */ if (FinishLineReached) { AllowBuildTitle = true; } if (AllowBuildTitle) { /* ************************************************** */ ShowDebugMessage('AllowBuildTitle'); /* ************************************************** */ var GoalInTitle = cb.settings['goalintitle'] == 'Yes'; var TokensInTitle = cb.settings['tokensremainingintitle'] == 'Yes'; var Before = cb.settings['goalintitleplacement'] == 'Before'; var TokensLeft = '[{{0}} Left]'; TokensLeft = TokensLeft.toReplace(GoalPrice[CurrentGoal] - AllTipsAmount); var BeforeOrAfter = '{{0}} {{1}}'; BeforeOrAfter = Before ? BeforeOrAfter.toReplace(GoalDescription[CurrentGoal], TokensLeft) : BeforeOrAfter.toReplace(TokensLeft, GoalDescription[CurrentGoal]); var BuildTitle = '{{0}} {{1}} {{2}}'; if (!(FinishLineReached)) { BuildTitle = BuildTitle.toReplace(cb.settings['titleprefix'], (GoalInTitle && TokensInTitle ? BeforeOrAfter : GoalInTitle ? GoalDescription[CurrentGoal] : TokensInTitle ? TokensLeft : null ), cb.settings['titlesuffix']); } else { BuildTitle = BuildTitle.toReplace(cb.settings['titleprefix'], FinishLineDescription, cb.settings['titlesuffix']); } } else { var BuildTitle = BasicRoomTitle; } /* ************************************************** */ ShowDebugMessage('BuildTitle=' + BuildTitle); ShowDebugMessage('* DoRoomTitle.Build() Complete'); /* ************************************************** */ return BuildTitle; }; // Build Room Title // // Do Room Title // // Feature Functions // var Feature = []; Feature.RotatePanel = function () { if (RotatePanel) { ShowGoodTipper = !ShowGoodTipper; ShowHighTipper = !ShowHighTipper; cb.drawPanel(); cb.setTimeout(Feature.RotatePanel, RotateTimer); } }; // Check To See If Goal Has Been Met // var PreviousTotal = -1; Feature.CheckGoal = function (ghost) { /* ************************************************** */ ShowDebugMessage('* Feature.CheckGoal()'); /* ************************************************** */ var SomethingChanged = false; if (PreviousTotal != AllTipsAmount) { /* ************************************************** */ ShowDebugMessage('Tip Total Changed'); /* ************************************************** */ PreviousTotal = AllTipsAmount; SomethingChanged = true; Feature.CheckGoal(ghost); } /* ************************************************** */ ShowDebugMessage('Tip Total Change Check Complete'); /* ************************************************** */ var RoomTitle = DoRoomTitle.Build(); /* ************************************************** */ ShowDebugMessage('FinishLineReached=' + FinishLineReached); ShowDebugMessage('AllTipsAmount=' + AllTipsAmount); ShowDebugMessage('FinishLineTotal=' + FinishLineTotal); ShowDebugMessage('GoalReached[CurrentGoal]=' + GoalReached[CurrentGoal]); ShowDebugMessage('CurrentGoal=' + CurrentGoal); ShowDebugMessage('SomethingChanged=' + SomethingChanged); /* ************************************************** */ output = '\n' + 'Goal Completed! Next Goal Started:' + '\n'; if ((!(FinishLineReached)) && AllTipsAmount >= FinishLineTotal) { // Finish Line Reached/Surpassed /* ************************************************** */ ShowDebugMessage('Finish Line Reached/Surpassed'); /* ************************************************** */ FinishLineReached = true; GoalReached[CurrentGoal] = true; CurrentGoal++; RoomTitle = DoRoomTitle.Build(); output = '\n' + FinishLineDescription + '\n'; if (!ghost) { SendTo.Default(output); } SomethingChanged = true; Feature.CheckGoal(ghost); } else if (FinishLineReached && AllTipsAmount < FinishLineTotal) { // Finish Line Reached/Surpassed, But Manually Undone /* ************************************************** */ ShowDebugMessage('Finish Line Reached/Surpassed, But Manually Undone'); /* ************************************************** */ CurrentGoal--; FinishLineReached = false; GoalReached[CurrentGoal] = false; RoomTitle = DoRoomTitle.Build(); output += cb.settings['amountlocation'] == 'Before' ? '[{{0}}] {{1}}'.toReplace(GoalPrice[CurrentGoal], GoalDescription[CurrentGoal]) + '\n' : '{{0}} [{{1}}]'.toReplace(GoalDescription[CurrentGoal], GoalPrice[CurrentGoal]) + '\n'; if (!ghost) { SendTo.Default(output); } SomethingChanged = true; Feature.CheckGoal(ghost); } else if ((!(GoalReached[CurrentGoal])) && AllTipsAmount >= GoalPrice[CurrentGoal]) { // Current Goal Reached/Surpassed if (CurrentGoal == GoalCount) { // Finishg Line Reached/Surpassed /* ************************************************** */ ShowDebugMessage('Finish Line Reached/Surpassed'); /* ************************************************** */ FinishLineReached = true; GoalReached[CurrentGoal] = true; CurrentGoal++; RoomTitle = DoRoomTitle.Build(); output = '\n' + FinishLineDescription + '\n'; } else { /* ************************************************** */ ShowDebugMessage('Current Goal Reached/Surpassed'); /* ************************************************** */ GoalReached[CurrentGoal] = true; CurrentGoal++; RoomTitle = DoRoomTitle.Build(); output += cb.settings['amountlocation'] == 'Before' ? '[{{0}}] {{1}}'.toReplace(GoalPrice[CurrentGoal], GoalDescription[CurrentGoal]) + '\n' : '{{0}} [{{1}}]'.toReplace(GoalDescription[CurrentGoal], GoalPrice[CurrentGoal]) + '\n'; } if (!ghost) { SendTo.Default(output); } SomethingChanged = true; Feature.CheckGoal(ghost); } else if (CurrentGoal > 1) { if ((!(GoalReached[CurrentGoal])) && AllTipsAmount < GoalPrice[CurrentGoal-1]) { /* ************************************************** */ ShowDebugMessage('Current Goal Manually Undone'); /* ************************************************** */ CurrentGoal--; GoalReached[CurrentGoal] = false; RoomTitle = DoRoomTitle.Build() output += cb.settings['amountlocation'] == 'Before' ? '[{{0}}] {{1}}'.toReplace(GoalPrice[CurrentGoal], GoalDescription[CurrentGoal]) + '\n' : '{{0}} [{{1}}]'.toReplace(GoalDescription[CurrentGoal], GoalPrice[CurrentGoal]) + '\n'; if (!ghost) { SendTo.Default(output); } SomethingChanged = true; Feature.CheckGoal(ghost); } } /* ************************************************** */ ShowDebugMessage('RoomTitle=' + RoomTitle); ShowDebugMessage('Something Changed Check Complete'); /* ************************************************** */ if (SomethingChanged) { cb.changeRoomSubject(RoomTitle); if (FinishLineReached && SwitchFinishLineMode) { MultiGoalTipCounterMode = false; GoalListStatus = false; } } /* ************************************************** */ ShowDebugMessage('* Feature.CheckGoal() Complete'); /* ************************************************** */ }; // Check To See If Goal Has Been Met // // Show Next Goal Feature // Feature.ShowNextGoal = function () { /* ************************************************** */ ShowDebugMessage('* Feature.ShowNextGoal()'); /* ***************************************************/ // Identify Next Goal // var NextGoal = '\n' + cb.settings['nextgoalname'] + '\n\n'; var x = 0; for (i=1;i<=App.Constants.Goals;i++) { if ((!(cb.settings['goal' + (i+1)] == '')) && (!(parseInt(cb.settings['goal' + (i+1) + 'price']) == 0))) { x++; /* ************************************************** */ ShowDebugMessage('x=' + x); ShowDebugMessage('CurrentGoal=' + CurrentGoal); ShowDebugMessage('GoalPrice[x+1]=' + GoalPrice[x+1]); ShowDebugMessage("cb.settings['goal' + (i+1)]=" + cb.settings['goal' + (i+1)]); /* ************************************************** */ if (CurrentGoal < GoalCount) { if (x == CurrentGoal) { NextGoal += cb.settings['amountlocation'] == 'Before' ? '[{{0}}] {{1}} '.toReplace(GoalPrice[x+1], cb.settings['goal'+(i+1)]) : '{{0}} [{{1}}] '.toReplace(cb.settings['goal'+(i+1)], GoalPrice[x+1]); break; } } else if (CurrentGoal == GoalCount) { NextGoal += cb.settings['finishlinedescription']; break; } } } NextGoal += '\n'; // Identify Next Goal // // Show Next Goal Notification // SendTo.Default(NextGoal); // Show Next Goal Notification // /* ************************************************** */ ShowDebugMessage('* Feature.ShowNextGoal() Complete'); /* ************************************************** */ }; // Show Next Goal Feature // // Manually Adjust Tip Counter // Feature.ManuallyAdjustTipCounter = function (TokenCount) { /* ************************************************** */ ShowDebugMessage("* Feature.ManuallyAdjustTipCounter(TokenCount)"); ShowDebugMessage("TokenCount=" + TokenCount); /* ************************************************** */ if (TokenCount > 0) { if (MultiGoalTipCounterMode) { AllTipsAmount += TokenCount; TotalTokensAllGoalSequences += TokenCount; } AllTokens += TokenCount; output = '\n' + "{{0}}: {{1}} has adjusted all (except the exclusively real tokens) tip counters by adding {{2}} token{{3}}." + '\n'; output = output.toReplace(App.Name, CurrentUser, TokenCount, (TokenCount == 1 ? '' : 's')); SendTo.ImportantUsers(output); cb.drawPanel(); } else if (TokenCount < 0) { TokenCount = TokenCount * -1; if (MultiGoalTipCounterMode) { if (AllTipsAmount - TokenCount < 0) { output = '\n' + "{{0}}: the result would cause the 'current' tip counter to go below zero, so the amount to remove will be set to the total available." + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); TokenCount = AllTipsAmount; SendTo.ImportantUsers(output); } AllTipsAmount -= TokenCount; TotalTokensAllGoalSequences -= TokenCount; AllTokens -= TokenCount; output = '\n' + "{{0}}: {{1}} has adjusted the 'current' tip counter by removing {{2}} token{{3}}." + '\n'; output = output.toReplace(App.Name, CurrentUser, TokenCount, (TokenCount == 1 ? '' : 's')); } else { if (AllTokens - TokenCount < 0) { output = '\n' + "{{0}}: the result would cause the 'total' tip counter to go below zero, so the amount to remove will be set to the total available." + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); TokenCount = AllTokens; SendTo.ImportantUsers(output); } AllTokens -= TokenCount; output = '\n' + "{{0}}: {{1}} has adjusted the 'total' tip counter by removing {{2}} token{{3}}." + '\n'; output = output.toReplace(App.Name, CurrentUser, TokenCount, (TokenCount == 1 ? '' : 's')); } SendTo.ImportantUsers(output); cb.drawPanel(); } else { output = '\n' + "{{0}}: you must add or remove at least 1 token." + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); } /* ************************************************** */ ShowDebugMessage("* Feature.ManuallyAdjustTipCounter(TokenCount) Complete"); /* ************************************************** */ Feature.CheckGoal(true); if (MultiGoalTipCounterMode) { Feature.ShowNextGoal(); } }; // Manually Adjust Tip Counter // // Manually Set Top Tipper // Feature.ManuallySetTipper = function (tippertype, tipper, tippedamount) { if (tippertype == Command['toptipperT']) { HighTipAmountTracker[tipper] = tippedamount; HighTipAmount = HighTipAmountTracker[tipper]; HighTipperTracker[tipper] = tipper; HighTipper = HighTipperTracker[tipper]; var DoesNotExist = true; for (i=0;i<LengthTracker.length;i++) { if (LengthTracker[i] == tipper) { DoesNotExist = false; break; } } if (DoesNotExist) { LengthTracker[LengthTracker.length] = tipper; } } else if (tippertype == Command['toptipperS']) { GoodTipAmount = tippedamount; GoodTipper = tipper; } else if (tippertype == Command['lasttipper']) { LastTipAmount = tippedamount; LastTipper = tipper; } cb.drawPanel(); }; // Manually Set Top Tipper // // Feature Functions // // Utility Functions // var Utility = []; var StartTime = new Date(); Utility.GetUpTime = function () { var CurrentTime = new Date(); var d = (CurrentTime - StartTime) / 1000; if (d < 60) { return Math.floor(d) + " Seconds" } d /= 60; var m = Math.floor(d % 60); var h = Math.floor(d / 60); var str = ''; if (h) str += h + ' Hours, '; str += m + ' Minutes' return str; }; var TotalTokensAllGoalSequences = 0; var TotalGoalSequences = MultiGoalTipCounterMode ? 1 : 0; var TotalActualTokens = 0; var AllTokens = 0; // Show Session Stats // Utility.ShowStats = function () { output = 'Current Session Running Time: {{0}}' + '\n\n'; output += 'Total Real/Artificial Tokens In Tip Counter Since Last Goal Sequence Started: {{1}}' + '\n'; output += 'Total Real/Artificial Tokens Received For {{2}} Total Goal Sequences So Far This Session: {{3}}' + '\n'; output += 'Total Real/Artificial Tokens In Tip Counter (Regardless Of Goals): {{4}}' + '\n'; output += 'Total Real Tokens Actually Received For This Entire Session (Regardless Of Goals): {{5}}' + '\n\n'; output = output.toReplace(Utility.GetUpTime(), AllTipsAmount, TotalGoalSequences, TotalTokensAllGoalSequences, AllTokens, TotalActualTokens); output += 'Highest Total Amount Tipped: {{0}}{{1}}' + '\n'; output = output.toReplace(HighTipAmount, (HighTipper == null ? '' : ' (by {{0}})'.toReplace(HighTipper))); output += 'Highest Single Amount Tipped: {{0}}{{1}}' + '\n'; output = output.toReplace(GoodTipAmount, (GoodTipper == null ? '' : ' (by {{0}})'.toReplace(GoodTipper))); output += 'Latest Single Amount Tipped: {{0}}{{1}}' + '\n'; output = output.toReplace(LastTipAmount, (LastTipper == null ? '' : ' (by {{0}})'.toReplace(LastTipper))); return output; }; // Show Session Stats // // Hide Message From Chat Room // Utility.HideMessageFromChatRoom = function (msg) { msg['X-Spam'] = true; }; // Hide Message From Chat Room // // Command Handler // Utility.CheckForCommand = function (msg) { var CommandParamaters = msg['m'].toLowerCaseTrim().split(" "); var CommandCheck = CommandParamaters[0].substring(1); var InvalidCommand = false; /* ************************************************** */ if (DebugMode) { for (x=0;x<CommandParamaters.length;x++) { ShowDebugMessage(x + '=[' + CommandParamaters[x] + ']'); } } ShowDebugMessage("CommandParamaters[0].charAt(0)=[" + CommandParamaters[0].charAt(0) + "]"); ShowDebugMessage("CommandCheck / CommandParamaters[0].substring(1)=[" + CommandCheck + "]"); /* ************************************************** */ // All Users // switch (CommandParamaters[0].charAt(0)) { // Commands // case ('/'): if (CommandCheck == App.Command) { Utility.HideMessageFromChatRoom(msg); switch (CommandParamaters[1]) { case null: case undefined: break; default: break; } // (End Switch) } break; // Commands // } // (End Switch) // All Users // // Broadcaster, Moderators (If "Allowed"), And "Always-Allowed Users" Only // if (UserIs.TheBroadcaster() || UserIs.ActiveBroadcaster() || UserIs.AllowedModerator(msg) || UserIs.AlwaysAllowed()) { switch (CommandParamaters[0].charAt(0)) { // Commands // case ('/'): // Show "Universal" Help // if (CommandCheck == 'help') { Utility.HideMessageFromChatRoom(msg); SendTo.Default(TheHelp, CurrentUser); InvalidCommand = false; break; } // Show "Universal" Help // if (CommandCheck == App.Command) { switch (CommandParamaters[1]) { // Start New Goal Sequence // case Command['resetgoal']: MultiGoalTipCounterMode = false; var reset = true; // Start New Goal Sequence // // Toggle App Features On/Off // case Command['toggle']: switch (CommandParamaters[2]) { case undefined: // Toggle Multi-Goal Tip Counter Mode vs. Basic Tip Counter Mode // MultiGoalTipCounterMode = !MultiGoalTipCounterMode; /* ************************************************** */ ShowDebugMessage('Toggle Multi-Goal Tip Counter Mode'); ShowDebugMessage('MultiGoalTipCounterMode=' + MultiGoalTipCounterMode); /* ************************************************** */ if (MultiGoalTipCounterMode) { AllTipsAmount = 0; TotalGoalSequences++; GoalListStatus = RememberGoalListStatus; DoGoalList.Notification(false); /* ************************************************** */ ShowDebugMessage('DoGoalList.Notification(false)'); /* ************************************************** */ Feature.CheckGoal(reset); /* ************************************************** */ ShowDebugMessage('Feature.CheckGoal()'); /* ************************************************** */ } else { GoalListStatus = false; cb.changeRoomSubject(BasicRoomTitle); } cb.drawPanel(); if (!(reset)) { output = '\n' + "{{0}}: {{1}} switched the app to '{{2}} tip counter' mode." + '\n'; output = output.toReplace(App.Name, CurrentUser, (MultiGoalTipCounterMode ? 'multi-goal' : 'basic' )); } else { output = '\n' + "{{0}}: {{1}} restarted the app to launch the {{2}} goal sequence." + '\n'; output = output.toReplace(App.Name, CurrentUser, (TotalGoalSequences == 1 ? '1st' : TotalGoalSequences == 2 ? '2nd' : TotalGoalSequences == 3 ? '3rd' : '{{0}}th'.TotalGoalSequences(RememberGoalListStatus) )); } SendTo.ImportantUsers(output); InvalidCommand = false; /* ************************************************** */ ShowDebugMessage('Toggle Multi-Goal Tip Counter Mode Complete'); /* ************************************************** */ break; // Toggle Multi-Goal Tip Counter Mode vs. Basic Tip Counter Mode // case Command['goallist']: if (!(reset)) { if (MultiGoalTipCounterMode) { GoalListStatus = !GoalListStatus; RememberGoalListStatus = GoalListStatus; output = '\n' + "{{0}}: the 'goal list' feature of this app has been {{1}}abled by {{2}}." + '\n'; output = output.toReplace(App.Name, (GoalListStatus ? 'en' : 'dis' ), CurrentUser); SendTo.ImportantUsers(output); if (GoalListStatus) { cb.setTimeout(DoGoalList.Notification, 2000); } InvalidCommand = false; } break; } default: output = '\n' + "{{0}}: '{{1}}' is not a valid feature of this app." + '\n'; output = output.toReplace(App.Name, CommandParamaters[2]); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; } break; // Toggle App Features On/Off // // Show Feature Immediately // case Command['show']: if (MultiGoalTipCounterMode) { switch (CommandParamaters[2]) { case undefined: output = '\n' + "{{0}}: the feature of this app to show was not specified." + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; case Command['goallist']: DoGoalList.Notification(true); InvalidCommand = false; break; case Command['nextgoal']: Feature.ShowNextGoal(); InvalidCommand = false; break; default: output = '\n' + "{{0}}: '{{1}}' is not a valid feature of this app." + '\n'; output = output.toReplace(App.Name, CommandParamaters[2]); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; } } else { output = '\n' + App.Name + ": the command '" + CommandParamaters[1] + ' ' + CommandParamaters[2] + "' is only available in 'multi-line goal tip counter' mode." + '\n'; SendTo.Special(output, CurrentUser); InvalidCommand = true; } break; // Show Feature Immediately // // Skip Current Goal // case Command['skip']: if (MultiGoalTipCounterMode) { if (!(FinishLineReached)) { Feature.ManuallyAdjustTipCounter(GoalPrice[CurrentGoal] - AllTipsAmount); InvalidCommand = false; } else { output = '\n' + "{{0}}: this goal sequence has already reached the 'finish line' (there are no remaining goals to skip)." + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); InvalidCommand = true; } } else { output = '\n' + "{{0}}: the command '{{1}}' is only available in 'multi-line goal tip counter' mode." + '\n'; output = output.toReplace(App.Name, CommandParamaters[1]); SendTo.Special(output, CurrentUser); InvalidCommand = true; } break; // Skip Current Goal // // Manually Adjust The Tip Counter // case Command['adjust']: switch (isNaN(CommandParamaters[2]) || parseInt(CommandParamaters[2])) { case true: if (!(CommandParamaters[2] == null || CommandParamaters[2] == '')) { output = '\n' + "{{0}}: '{{1}}' is not a number, but one is required for this command." + '\n'; output = output.toReplace(App.Name, CommandParamaters[2]); } else { output = '\n' + "{{0}}: you didn't supply a quantity of tokens to add or subtract." + '\n'; output = output.toReplace(App.Name); } SendTo.Special(output, CurrentUser); InvalidCommand = true; break; default: Feature.ManuallyAdjustTipCounter(parseInt(CommandParamaters[2])); InvalidCommand = false; break; } break; // Manually Adjust The Tip Counter // // Manually Set Top Tipper // case Command['toptipperT']: case Command['toptipperS']: case Command['lasttipper']: switch (CommandParamaters[2]) { case undefined: output = '\n' + "{{0}}: you didn't supply the user's name or amount of tokens tipped." + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; default: switch (isNaN(CommandParamaters[3]) || parseInt(CommandParamaters[3])) { case true: if (!(CommandParamaters[3] == null || CommandParamaters[3] == '')) { output = '\n' + "{{0}}: '{{1}}' is not a number, but one is required for this command." + '\n'; output = output.toReplace(App.Name, CommandParamaters[3]); } else { output = '\n' + "{{0}}: you didn't supply the amount of tokens tipped." + '\n'; output = output.toReplace(App.Name); } SendTo.Special(output, CurrentUser); InvalidCommand = true; break; default: Feature.ManuallySetTipper(CommandParamaters[1], CommandParamaters[2], parseInt(CommandParamaters[3])); output = '\n' + "{{0}}: {{1}} was set as the 'highest {{2}}' top tipper for {{3}} tokens by {{4}}." + '\n'; output = output.toReplace(App.Name, CommandParamaters[2], (CommandParamaters[1] == Command['toptipperT'] ? 'total' : 'single' ), CommandParamaters[3], CurrentUser); SendTo.ImportantUsers(output); InvalidCommand = false; break; } break; } break; // Manually Set Top Tipper // // Manually Change The Room Title // case Command['title']: switch (CommandParamaters[2]) { case undefined: if (MultiGoalTipCounterMode) { AllowBuildTitle = true; } else { AllowBuildTitle = false; } cb.changeRoomSubject(DoRoomTitle.Build()); // = Default Title output = '\n' + "{{0}}: room title set to automatic by {{1}}." + '\n'; output = output.toReplace(App.Name, CurrentUser); SendTo.ImportantUsers(output); InvalidCommand = false; break; default: /* ************************************************** */ ShowDebugMessage("msg['m'].substring(1 + CommandParamaters[0].length + 1 + CommandParamaters[1].length + 1)=[" + msg['m'].substring(1 + CommandParamaters[0].length + 1 + CommandParamaters[1].length + 1) + ']'); /* ************************************************** */ cb.changeRoomSubject(msg['m'].substring(CommandParamaters[0].length + 1 + CommandParamaters[1].length + 1).trim()); output = '\n' + "{{0}}: room title changed by {{1}}." + '\n'; output = output.toReplace(App.Name, CurrentUser); SendTo.ImportantUsers(output); InvalidCommand = false; break; } break; // Manually Change The Room Title // // Manually Change The Custom Panel Type // case Command['panel']: switch (CommandParamaters[2]) { case undefined: output = '\n' + "{{0}}: you didn't supply the type of custom panel to use. Please choose from '{{1}}', '{{2}}', or '{{3}}'." + '\n'; output = output.toReplace(App.Name, Command['panelT'], Command['panelS'], Command['panelR']); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; case Command['panelT']: ShowHighTipper = true; ShowGoodTipper = false; RotatePanel = false; cb.setTimeout(Feature.RotatePanel, RotateTimer); output = '\n' + "{{0}}: the type of custom panel has been changed to 'highest total tips' by {{1}}." + '\n'; output = output.toReplace(App.Name, CurrentUser); SendTo.ImportantUsers(output); InvalidCommand = false; break; case Command['panelS']: ShowHighTipper = false; ShowGoodTipper = true; RotatePanel = false; cb.setTimeout(Feature.RotatePanel, RotateTimer); output = '\n' + "{{0}}: the type of custom panel has been changed to 'highest single tip' by {{1}}." + '\n'; output = output.toReplace(App.Name, CurrentUser); SendTo.ImportantUsers(output); InvalidCommand = false; break; case Command['panelR']: RotatePanel = true; cb.setTimeout(Feature.RotatePanel, RotateTimer); output = '\n' + "{{0}}: the type of custom panel has been changed to 'rotating' by {{1}}." + '\n'; output = output.toReplace(App.Name, CurrentUser); SendTo.ImportantUsers(output); InvalidCommand = false; break; default: output = '\n' + "{{0}}: '{{1}}' is not a valid custom panel option. Please choose from '{{2}}', '{{3}}', or '{{4}}'." + '\n'; output = output.toReplace(App.Name, CommandParamaters[2], Command['panelT'], Command['panelS'], Command['panelR']); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; } break; // Manually Change The Custom Panel Type // // Show Stats // case Command['stats']: if (UserIs.TheBroadcaster() || (SupportMode && UserIs.Author())) { output = '\n' + '{{0}}: stats for this session are shown below.' + '\n\n'; output = output.toReplace(App.Name); output += Utility.ShowStats(); SendTo.Default(output, CurrentUser); InvalidCommand = false; } break; // Show Stats // // Show Help // case undefined: case Command['help']: SendTo.Default(TheHelp, CurrentUser); InvalidCommand = false; break; // Show Help // // Toggle Support Mode (Must Be Broadcaster) // case 'support': if (UserIs.TheBroadcaster()) { SupportMode = !SupportMode; ActiveBroadcaster = SupportMode ? App.Author.toLowerCase() : TheBroadcaster; output = '\n' + '{{0}}: support mode {{1}}abled.' + '\n'; output = output.toReplace(App.Name, (SupportMode ? 'en' : 'dis' )); SendTo.Special(output, CurrentUser); if (UserIs.TheBroadcaster() != UserIs.Author()) { SendTo.Special(output, App.Author.toLowerCase()); } InvalidCommand = false; } break; // Toggle Support Mode (Must Be Broadcaster) // // Toggle Debug Mode (Must Be App Author) // case 'debug': if ((!(SupportMode)) && DebugMode && UserIs.Author()) { // Escape Debug Mode If Support Mode Is Prematurely Deactivated DebugMode = false; output = '\n' + '{{0}}: debug mode disabled.' + '\n'; output = output.toReplace(App.Name); SendTo.Special(output, CurrentUser); } if (SupportMode && UserIs.Author()) { DebugMode = !DebugMode; if (DebugMode) { DebugCount = 1; } output = '\n' + '{{0}}: debug mode {{1}}abled.' + '\n'; output = output.toReplace(App.Name, (DebugMode ? 'en' : 'dis' )); SendTo.Special(output, CurrentUser); InvalidCommand = false; } break; // Toggle Debug Mode (Must Be App Author) // // Toggle Verbose Debug Mode (Must Be App Author) // case 'verbose': if ((!(SupportMode)) && DebugMode && VerboseDebugMode && UserIs.Author()) { // Allow Verbose Debug Mode Toggle If Support Mode Is Prematurely Deactivated VerboseDebugMode = !VerboseDebugMode; output = '\n' + '{{0}}: verbose debug mode {{1}}abled.' + '\n'; output = output.toReplace(App.Name, (VerboseDebugMode ? 'en' : 'dis' )); SendTo.Special(output, CurrentUser); } if (SupportMode && UserIs.Author()) { if (DebugMode) { VerboseDebugMode = !VerboseDebugMode; } else { DebugMode = true; VerboseDebugMode = true; } output = '\n' + '{{0}}: verbose debug mode {{1}}abled.' + '\n'; output = output.toReplace(App.Name, (VerboseDebugMode ? 'en' : 'dis' )); SendTo.Special(output, CurrentUser); InvalidCommand = false; } break; // Toggle Verbose Debug Mode (Must Be App Author) // // Invalid Command Option // default: output = '\n' + "{{0}}: invalid command option: '{{1}}'." + '\n' output += '{{2}}'; output = output.toReplace(App.Name, msg['m'].substring(1), TheCommands); SendTo.Special(output, CurrentUser); InvalidCommand = true; break; // Invalid Command Option // } // (End Switch) } break; // Commands // } // (End Switch) } // Broadcaster, Moderators (If "Allowed"), And "Always-Allowed Users" Only // /* ************************************************** */ ShowDebugMessage("InvalidCommand=" + InvalidCommand); /* ************************************************** */ /* Disabled: NOT COMPATIBLE WITH MULTIPLE APP/BOT COMBINATIONS if (CommandCheck == App.Command) { output = "Command {{0}}executed: '{{1}}'"; output = output.toReplace((InvalidCommand ? 'not ' : '' ), msg['m']); msg['m'] = output; } */ }; // Command Handler // // Utility Functions // var AllTipsAmount = 0; var HighTipAmountTracker = {}; var HighTipperTracker = {}; var LengthTracker = []; var HighTipAmount = 0 var HighTipper = null; var GoodTipAmount = 0; var GoodTipper = null; var LastTipAmount = 0; var LastTipper = null; // Build Custom Info Panel // var ShowHighTipper = cb.settings['whichcustompanel'] == 'Highest Total Tips'; var ShowGoodTipper = cb.settings['whichcustompanel'] == 'Highest Single Tip'; var RotatePanel = cb.settings['whichcustompanel'] == 'Rotate'; if (RotatePanel) { ShowGoodTipper = true; ShowHighTipper = false; } var RotateTimer = 0; var NothingEmpty = (!((cb.settings['alltipslabel'] == null || cb.settings['alltipslabel'].length == 0) || (cb.settings['goodtiplabel'] == null || cb.settings['goodtiplabel'].length == 0) || (cb.settings['blankgood'] == null || cb.settings['blankgood'].length == 0) || (cb.settings['hightiplabel'] == null || cb.settings['hightiplabel'].length == 0) || (cb.settings['blankhigh'] == null || cb.settings['blankhigh'].length == 0) || (cb.settings['lasttiplabel'] == null || cb.settings['lasttiplabel'].length == 0) || (cb.settings['blanklast'] == null || cb.settings['blanklast'].length == 0))); var CustomPanel = cb.settings['custompanel'] == 'Yes'; if (NothingEmpty && CustomPanel) { var AllTipsLabel = cb.settings['alltipslabel']; var HighTipLabel = cb.settings['hightiplabel']; var BlankHigh = cb.settings['blankhigh']; var GoodTipLabel = cb.settings['goodtiplabel']; var BlankGood = cb.settings['blankgood']; var LastTipLabel = cb.settings['lasttiplabel']; var BlankLast = cb.settings['blanklast']; var GoalTipsPanelLabel = cb.settings['goaltipslabel']; var FinishLinePanelLabel = cb.settings['finishlinelabel']; } else { var AllTipsLabel = 'Total Tokens Received'; var HighTipLabel = 'Hit It Most'; var BlankHigh = 'Wanna Ride On Top?'; var GoodTipLabel = 'Hit It Hard'; var BlankGood = 'Long-Term Lover!'; var LastTipLabel = 'Hit It Last'; var BlankLast = "I'm Still A Virgin <3"; var GoalTipsPanelLabel = 'Horniness Level [{/}]'; var FinishLinePanelLabel = 'You Rocked My World!!!'; } var GoalTipsPanelLabelReplacements = ''; var ReplacementSearchL = GoalTipsPanelLabel.search('{'); var ReplacementSearchR = GoalTipsPanelLabel.search('}'); if (ReplacementSearchL != -1 && ReplacementSearchR != -1) { if (ReplacementSearchL < ReplacementSearchR) { /* ************************************************** */ ShowDebugMessage('GoalTipsPanelLabel=' + GoalTipsPanelLabel); /* ************************************************** */ GoalTipsPanelLabel = GoalTipsPanelLabel.replace('{', '[[0]]'); GoalTipsPanelLabel = GoalTipsPanelLabel.replace('}', '[[1]]'); GoalTipsPanelLabelReplacements = 'LR'; } else { /* ************************************************** */ ShowDebugMessage('GoalTipsPanelLabel=' + GoalTipsPanelLabel); /* ************************************************** */ GoalTipsPanelLabel = GoalTipsPanelLabel.replace('}', '[[0]]'); GoalTipsPanelLabel = GoalTipsPanelLabel.replace('{', '[[1]]'); GoalTipsPanelLabelReplacements = 'RL'; } } else if (ReplacementSearchL != -1 || ReplacementSearchR != -1) { if (ReplacementSearchL != -1) { /* ************************************************** */ ShowDebugMessage('GoalTipsPanelLabel=' + GoalTipsPanelLabel); /* ************************************************** */ GoalTipsPanelLabel = GoalTipsPanelLabel.replace('{', '[[0]]'); GoalTipsPanelLabelReplacements = 'L'; } else { /* ************************************************** */ ShowDebugMessage('GoalTipsPanelLabel=' + GoalTipsPanelLabel); /* ************************************************** */ GoalTipsPanelLabel = GoalTipsPanelLabel.replace('}', '[[0]]'); GoalTipsPanelLabelReplacements = 'R'; } } GoalTipsPanelLabel = GoalTipsPanelLabel.replace('[[0]]', '{{0}}'); GoalTipsPanelLabel = GoalTipsPanelLabel.replace('[[1]]', '{{1}}'); /* ************************************************** */ ShowDebugMessage('(Final)GoalTipsPanelLabel=' + GoalTipsPanelLabel); /* ************************************************** */ // Build Custom Info Panel // // Chaturbate Info Panel Handler // function onDrawPanel(user) { /* ************************************************** */ ShowDebugMessage('* onDrawPanel(user)'); /* ************************************************** */ CurrentUser = user['user']; var HighTip = HighTipper === null ? BlankHigh : '{{0}} [{{1}}]'.toReplace(HighTipper.substring(0, 14), HighTipAmount); var GoodTip = GoodTipper === null ? BlankGood : '{{0}} [{{1}}]'.toReplace(GoodTipper.substring(0, 14), GoodTipAmount); var LastTip = LastTipper === null ? BlankLast : '{{0}} [{{1}}]'.toReplace(LastTipper.substring(0, 14), LastTipAmount); if (MultiGoalTipCounterMode) { if (FinishLineReached) { /* ************************************************** */ ShowDebugMessage('MultiGoalTipCounterMode / FinishLineReached'); ShowDebugMessage('* onDrawPanel(user) Complete'); /* ************************************************** */ if (ShowHighTipper) { return { 'template': '3_rows_11_21_31', 'row1_value': '{{0}} [{{1}} Total]'.toReplace(FinishLinePanelLabel, AllTipsAmount), 'row2_value': '{{0}}: {{1}}'.toReplace(HighTipLabel, HighTip), 'row3_value': '{{0}}: {{1}}'.toReplace(LastTipLabel, LastTip) }; } else if (ShowGoodTipper) { return { 'template': '3_rows_11_21_31', 'row1_value': '{{0}} [{{1}} Total]'.toReplace(FinishLinePanelLabel, AllTipsAmount), 'row2_value': '{{0}}: {{1}}'.toReplace(GoodTipLabel, GoodTip), 'row3_value': '{{0}}: {{1}}'.toReplace(LastTipLabel, LastTip) }; } } else { var FormattedGoalTipsPanelLabel = GoalTipsPanelLabel; switch (GoalTipsPanelLabelReplacements) { case 'LR': FormattedGoalTipsPanelLabel = GoalTipsPanelLabel.toReplace(CurrentGoal, GoalCount); break; case 'RL': FormattedGoalTipsPanelLabel = GoalTipsPanelLabel.toReplace(GoalCount, CurrentGoal); break; case 'L': FormattedGoalTipsPanelLabel = GoalTipsPanelLabel.toReplace(CurrentGoal); break; case 'R': FormattedGoalTipsPanelLabel = GoalTipsPanelLabel.toReplace(GoalCount); break; } /* ************************************************** */ ShowDebugMessage('FormattedGoalTipsPanelLabel=' + FormattedGoalTipsPanelLabel); ShowDebugMessage('MultiGoalTipCounterMode'); ShowDebugMessage('* onDrawPanel(user) Complete'); /* ************************************************** */ if (ShowHighTipper) { return { 'template': '3_rows_11_21_31', 'row1_value': '{{0}}: {{1}}/{{2}} [Finish: {{3}}]'.toReplace(FormattedGoalTipsPanelLabel, AllTipsAmount, GoalPrice[CurrentGoal], FinishLineTotal), 'row2_value': '{{0}}: {{1}}'.toReplace(HighTipLabel, HighTip), 'row3_value': '{{0}}: {{1}}'.toReplace(LastTipLabel, LastTip) }; } else if (ShowGoodTipper) { return { 'template': '3_rows_11_21_31', 'row1_value': '{{0}}: {{1}}/{{2}} [Finish: {{3}}]'.toReplace(FormattedGoalTipsPanelLabel, AllTipsAmount, GoalPrice[CurrentGoal], FinishLineTotal), 'row2_value': '{{0}}: {{1}}'.toReplace(GoodTipLabel, GoodTip), 'row3_value': '{{0}}: {{1}}'.toReplace(LastTipLabel, LastTip) }; } } } else { /* ************************************************** */ ShowDebugMessage('BasicTipCounterMode'); ShowDebugMessage('* onDrawPanel(user) Complete'); /* ************************************************** */ if (ShowHighTipper) { return { 'template': '3_rows_11_21_31', 'row1_value': '{{0}}: {{1}}'.toReplace(AllTipsLabel, AllTokens), 'row2_value': '{{0}}: {{1}}'.toReplace(HighTipLabel, HighTip), 'row3_value': '{{0}}: {{1}}'.toReplace(LastTipLabel, LastTip) }; } else if (ShowGoodTipper) { return { 'template': '3_rows_11_21_31', 'row1_value': '{{0}}: {{1}}'.toReplace(AllTipsLabel, AllTokens), 'row2_value': '{{0}}: {{1}}'.toReplace(GoodTipLabel, GoodTip), 'row3_value': '{{0}}: {{1}}'.toReplace(LastTipLabel, LastTip) }; } } } cb.onDrawPanel(function(user) { return onDrawPanel(user); }); // Chaturbate Info Panel Handler // // Chaturbate Tip Handler // function onTip(tip) { /* ************************************************** */ ShowDebugMessage("* onTip(tip)"); /* ************************************************** */ LastTipAmount = parseInt(tip['amount']); LastTipper = tip['from_user']; /* ************************************************** */ ShowDebugMessage("LastTipper=" + LastTipper); ShowDebugMessage("LastTipAmount=" + LastTipAmount); /* ************************************************** */ CurrentUser = LastTipper; AllTipsAmount += LastTipAmount; HighTipAmountTracker[LastTipper] = HighTipAmountTracker[LastTipper] >= 0 ? HighTipAmountTracker[LastTipper] + LastTipAmount : LastTipAmount; HighTipperTracker[LastTipper] = LastTipper; var DoesNotExist = true; for (i=0;i<LengthTracker.length;i++) { if (LengthTracker[i] == LastTipper) { DoesNotExist = false; break; } } if (DoesNotExist) { LengthTracker[LengthTracker.length] = LastTipper; } var HighTest = 0; for (i=0;i<LengthTracker.length;i++) { if (HighTipAmountTracker[LengthTracker[i]] > HighTest) { HighTipAmount = HighTipAmountTracker[LengthTracker[i]]; HighTipper = HighTipperTracker[LengthTracker[i]]; HighTest = HighTipAmount; } } if (LastTipAmount > GoodTipAmount) { GoodTipAmount = LastTipAmount; GoodTipper = LastTipper; } if (MultiGoalTipCounterMode) { TotalTokensAllGoalSequences += LastTipAmount; Feature.CheckGoal(); } cb.drawPanel(); TotalActualTokens += LastTipAmount; AllTokens += LastTipAmount; /* ************************************************** */ ShowDebugMessage('* onTip(tip) Complete'); /* ************************************************** */ } cb.onTip(function(tip) { return onTip(tip); }); // Chaturbate Tip Handler // // Chaturbate Welcome Handler // function onEnter(user) { /* ************************************************** */ ShowDebugMessage('* onEnter(user)'); /* ************************************************** */ CurrentUser = user['user']; if (UserIs.Author() && (!(UserIs.TheBroadcaster()))) { output = '{{0}} is being used by {{1}}.' + '\n'; output = output.toReplace(TheInfo, TheBroadcaster); SendTo.Special(output, App.Author.toLowerCase()); } /* ************************************************** */ ShowDebugMessage('* onEnter(user) Complete'); /* ************************************************** */ } cb.onEnter(function(user) { return onEnter(user); }); // Chaturbate Welcome Handler // // Chaturbate Message Handler // function onMessage(msg) { /* ************************************************** */ ShowDebugMessage('* onMessage(msg)'); /* ************************************************** */ CurrentUser = msg['user']; msg['m'] = msg['m'].replace(/\s\s+/g, ' '); /* ************************************************** */ ShowDebugMessage('msg=' + msg['m']); /* ************************************************** */ Utility.CheckForCommand(msg); /* ************************************************** */ ShowDebugMessage('* onMessage(msg) Complete'); /* ************************************************** */ return msg; } cb.onMessage(function (msg) { return onMessage(msg); }); // Chaturbate Message Handler // // Startup Message // var TheStartup = '\n' + '{{0}} has been activated.' + '\n'; TheStartup = TheStartup.toReplace(TheInfo); var Startup = []; Startup.Init = function () { /* ************************************************** */ ShowDebugMessage("* Startup.Init()"); /* ************************************************** */ NoticeStyle['Weight'] = NoticeStyle['Weight'].toLowerCase(); SendTo.Default(TheStartup); cb.setTimeout(Startup.Delay, 2000); /* ************************************************** */ ShowDebugMessage("* Startup.Init() Complete"); /* ************************************************** */ }; Startup.Delay = function () { /* ************************************************** */ ShowDebugMessage('* Startup.Delay()'); /* ************************************************** */ /* cb.setTimeout(SendTo.ImportantUsers(TheCommands), 2000); */ cb.setTimeout(SendTo.ImportantUsers('\n' + FormatCommand("{{0}}' to see a list of the available commands for this app.".toReplace(Command['help']), false)), 2000); RotateTimer = cb.settings['custompanelinterval'].fromSecondsNowMilliseconds(); cb.setTimeout(Feature.RotatePanel, RotateTimer); if (MultiGoalTipCounterMode) { cb.setTimeout(cb.changeRoomSubject(DoRoomTitle.Build(), 4000)); } else { cb.setTimeout(cb.changeRoomSubject(BasicRoomTitle, 4000)); } // Multi-Line vs. Single-Line Display // GoalListItemSeparator = cb.settings['separatorcharacter'].trim() == '' ? '\n' : cb.settings['separatorcharacter']; // Multi-Line vs. Single-Line Display // cb.setTimeout(DoGoalList.Build, 6000); /* ************************************************** */ ShowDebugMessage('* Startup.Delay() Complete'); /* ************************************************** */ }; cb.setTimeout(Startup.Init, 1); // Startup Message //
© Copyright Chaturbate 2011- 2026. All Rights Reserved.