Apps Home
|
Create an App
Ozz App testing
Author:
ozzyzeppelin
Description
Source Code
Launch App
Current Users
Created by:
Ozzyzeppelin
/** Name: Naty's App - Pgoals Author: Ozzyzeppelin Created 05/xx/2021 **/ //** Enable for ESLINT syntax check, Disable for CB compile //var cb = ''; //var cbjs = ''; //** Enable Show1 or Show2 depending on app version since the panel background upload image names are different for each var appVersion = 'Show1'; //var appVersion = 'Show2'; var dummycounter = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61' ]; var backgroundArray = { menu: ['Cube Wave', 'Hearts', 'Blue Floral', 'Aquarium', 'Snakeskin', 'Pastels', 'Clouds', 'Lava Lamp', 'Light Blue', 'Moon', 'My Custom Panel', 'Purple Haze', 'Blue Bars', 'Valentines Pink Bars', 'Valentines 2 Hearts', 'Green and Blue Bars', 'Pink and Blue Bars', 'Green and Green Bars', 'Green and Yellow Bars', 'Purple and Pink Bars', 'Aqua and Pink Bars', 'Pink and Pink Bars', 'Yellow and Yellow Bars', 'Christmas - Silver snowflakes', 'Rainbow in the Clouds', 'Christmas Lights', 'Christmas - Green background', 'Christmas - Blue and Silver', 'Pink Smoke', 'Halloween - spooky pumpkins', 'Halloween - bouncing pumpkin', 'Halloween - Red sky lightning', 'Halloween - orange sky bats', 'Halloween - blue sky bats', 'Halloween - graveyard', 'Halloween - blue with ghosts', 'Halloween - red paper with webs', 'Christmas - BW Snowflakes', 'Christmas - Blue Snowflakes', 'Christmas - Snowflake border', 'Christmas - Red with snow', 'Christmas - Red Santa and snowman', 'Christmas - Wrapping Paper', 'Christmas - Classic', 'Pastel Aqua Stars', 'Black Leather Texture', 'St. Patricks Day 1', 'St. Patricks Day 2', 'St. Patricks Day 3', 'Flowers White', 'Flowers Blue', 'Flowers Cream and Rose', 'Lily Pads', 'Flowers Green and White', 'Green Leaves Pattern', 'Mauve Leaf Pattern', 'White Rose', 'Green Pattern', 'Dark Blue-Green Watercolor', 'Aqua Watercolor', 'Green Diamond Pattern', 'Green Circles Pattern' ], command: ['cubewave', 'hearts', 'bluefloral', 'aquarium', 'snakeskin', 'pastels', 'clouds', 'lavalamp', 'lightblue', 'moon', 'custom', 'purplehaze', 'bluegradient', 'valentines1', 'valentines2', 'greenblue', 'pinkblue', 'greengreen', 'greenyellow', 'purplepink', 'aquapink', 'pinkpink', 'yellowyellow', 'christmas1silversnowflakes', 'rainbowclouds', 'christmas2lights', 'christmas3green', 'christmas4blue', 'pinksmoke', 'halloween1', 'halloween2', 'halloween3', 'halloween4', 'halloween5', 'halloween6', 'halloween7', 'halloween8', 'christmas4bw', 'christmas5bluesnowflakes', 'christmas6snowflakesborder', 'christmas7red1', 'christmas8red2', 'christmas9wrap', 'christmas10classic', 'pastelaqua', 'blackleather', 'stpatrick1', 'stpatrick2', 'stpatrick3', 'flowerswhite', 'flowersblue', 'flowerscreamrose', 'lilypads', 'flowersgreenwhite', 'greenleaf', 'mauveleaf', 'whiterose', 'greenpattern', 'bluegreenwatercolor', 'aquawatercolor', 'greendiamond', 'greencircles' ], devfile: ['17606e52-2215-403d-b052-46a936ca9f92','add', '8b1ebdd0-1e73-4abe-b882-4239b6e55b0d', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'NA', '4b465340-8967-48cc-9945-532f789f73e1', 'add', 'add', 'add', 'add', 'add', 'ca7b4f10-9623-4db8-8f84-70153dc60f61', 'add', 'add', 'add', 'add', 'add', 'add', '1ba66b11-d5b8-4fc2-933d-d6ca4ca4e808', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'cd1d941d-0a4d-4bc1-975c-32a7073f951b', 'add', '6967fc13-202c-49ff-8f15-6b92812c87ff', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add' ], livefile: ['9478eff1-b042-4d33-ad91-f8427b98cad8','18994a59-8a72-44c4-ba3b-ab9e835460b8', 'f9eaf1a2-9530-428e-8c58-1b00bf9e9357', 'e2632788-9b57-48b3-8b63-1880c39efa0d', '80a94656-e4b5-4217-adf3-5a7386dc4023', '8c0b2b81-cb3e-4fb7-9ae3-4f2656c90e4d', 'b3dcdf85-a351-4f50-a686-5a553657981f', '8da93470-9c6a-47c4-83e6-858907d4f84d', '2311527c-85cb-44a0-87d9-adc3097b53d8', '1b53854d-dd02-4e7e-a9cb-fd0e38255cf1', 'NA', '8483963f-416e-4912-b1ee-8e2ddda64e8f', 'a0ad9443-e8df-4286-9f95-ea5f51931b9f', 'f228e2e6-8d58-49f4-b9a4-4662de7290c0', '2a63550e-d6f8-4182-bf35-3a61b24067e5', '1a3e852f-8c39-4c0c-8365-ae7ad8c8ac29', '30182874-b8c9-4004-a145-7ed60f6bfedd', '8e5604f6-5552-4447-a43b-54b97b15f05d', '9817f86f-e3c1-4a1e-b4d7-c06da8fc5639', 'a4e5a8e1-4756-419b-8459-fa971558ebab', 'b12c143a-2ea8-488c-9345-854a36cdd8a3', 'cf4dd491-0f33-4c07-8d22-adec552bc1c9', 'f82bd0a1-5923-401a-bece-6becf546053e', 'a9734d8e-c14d-42ad-a050-a8707dd8b228', '31fe7bd8-c92f-491f-9c4d-e517f6993d06', 'a103491f-8017-4b85-b35c-4f746f13f5d4', 'b9253bf7-d70d-449e-8215-d515d239c42d', '893574c9-64f8-4e26-9747-ff9876f6d266', 'acc9b554-c110-44f8-91f7-1ad0a52466cf', '17361a63-0d94-417a-8337-bdfd2f2f6fee', 'a5110b60-f189-419a-beb9-f4d2a92ac0a1', '70bdd188-e3e4-4571-81f2-8c90eb63b126', '0b2d410c-2e20-41a4-bf39-3af8ac50221b', 'd7b0b19b-e5dc-4eac-8456-b3b1d96a2148', '69b4126f-0455-4a62-b1c4-4e579fbdf52f', '2b10c454-e41e-4718-a80c-2322e9868919', 'a7499e58-3e85-4592-96a6-853f9cccf926', '69888621-3e88-47ac-aa16-2036ec450ee9', '1a008fef-95d1-4ed1-90a7-e440576b73b6', '77b6cd5b-19fe-4dc7-bf3e-4d1d95b205ff', '3d3cd33d-07cb-4535-aca0-a614821c67c2', '47c285d5-e67c-475e-98e6-11570d2aa45c', '355c31d9-6da6-4823-bc68-d08ad20fc656', '2d8b360d-2d6b-47f4-af5a-c52b63576623', '116a887f-5ff6-4620-898e-6e87c268579a', '46c37397-4a07-4d17-b783-dbb6ec53b4ac', '062b95c0-d449-49fb-adcc-f65187288236', 'df5a15a2-0282-48b3-b1d5-edb575483bcb', 'db2081fd-4260-4f01-97c1-464b7093ed51', 'a92f1f21-8d54-47a6-b399-f3fee9c2c25d', 'b75966bb-964d-4a55-89a8-e4dcc0422bd0', 'b9cc280c-6cc2-4df8-bba8-8b2f4134ccfc', '6db9e492-3f3f-43c4-94e7-c280362e4b2b', '0680af9d-39a4-4e88-bac2-9ce7b961bb7f', '061adcba-6c76-40a5-a898-0fd81b456553', '22a5688d-e4e9-4dcc-88df-46839f61f637', '327df6bc-00fb-4a25-b288-8e0af2603f48', '34ef5ca5-1bdc-40c5-98e1-4455777fd8b7', '3f952901-b84f-4a4e-b493-831b419b822f', '9bf44302-3fad-44de-9ad5-c383b1081a5c', 'df9f4214-0b86-4a8b-9ec1-630f522219bf', 'e0c95bf3-cc98-479c-9d8c-3d732ad182e0' ], livefile2: ['958ee720-4d90-48dc-b436-a3af31e139f4','4756f264-2e25-4a62-be6b-067cc20db7e2', '0b0ddbe5-ba0d-492f-96fa-c46cea3512ee', '5420ef5d-f232-4c2b-b8bf-0aade352d924', '3eef9d45-be81-4ed4-92bc-32ac896be906', 'cbae9a73-11c1-4780-939c-438e921640de', '0589d760-9797-4d8b-b649-f57cfef9cb62', 'f04f2c01-7e00-48e7-b45a-bc4e21b4659d', 'fa347bfa-1da1-4c6c-b047-ef1026513ca1', 'd4832372-2b4c-4a17-9ca4-7832e2c4572e', 'NA', '5371691f-0bd8-41ef-b1c5-dc77ca6a6dfd', '1849c10d-8ffc-4058-87d8-b6236e659804', 'b3ec41d2-4d7d-470b-ac85-aa1a5a4d0468', '4411987e-2547-4d0b-8e3d-0633944b6735', '5c123d54-fe96-4734-9ff6-5bf01ba705f2', 'ba4a2483-0850-4d74-a04b-9a1416cf92c9', 'fb4dfb9d-6153-4c39-ac43-480b3c0ecfa2', '2971a31c-b27f-49d3-8903-e11b27303131', '3d702f1c-f111-463b-89a9-38034c74ee50', '9b97c7ee-8193-48d6-b7e9-476813c87ccd', 'b245ae63-593a-4ab8-b66a-095a8ffb26a8', 'd6a33adf-033c-47d8-bb08-8200db361f7c', 'd837bc37-0365-4052-8c0c-d24e2a3e1ba9', '07a19395-84fa-4550-b95e-168fd16ab821', 'e5f491d2-9048-40d9-86b5-0b805dc623a2', '9ed6ab77-fd05-41d2-b54b-2f81462d8591', '50ae4d02-3cf0-4e43-ae41-06bc9d973480', 'c4b304d3-04e4-4fcc-ac70-dd493586bd83', '491fe3b8-64b0-4655-814a-862e941d23db', 'd93fee75-9e90-4378-9b46-d5f2311e61ae', '104f314b-690f-4ff8-a5fd-4a01b82ddb0d', '18c841c1-2aba-48a5-8b48-28e6007542e8', '13da365c-6f14-4ea2-a676-0c598065468f', 'ff58b9d0-957c-46d6-969e-0e4f9a563ab8', '0c3b0b2c-854e-4ff0-88ea-ffb9a3e9cead', '403e2ead-4804-4430-8653-39e4a217fa43', '1e5c8f99-2bec-4b1e-abcf-4ad49e3d1f50', 'b4c879ef-0b33-4491-9d9e-c54fe06e3c54', 'd1eee134-5e13-409d-b600-1feef2e8196d', '56409438-827b-46cf-ae9a-c987789074b5', '76e9adfc-8b62-4de9-ac2c-77dc58b37b3a', '369659f9-93fa-40c5-a92d-f2b1bc1b1b0d', 'e6371053-6fef-41b5-80d0-8482107a2755', '51feda0b-0968-469d-9b22-0dbf1a98a7fe', '7696cce8-a2d2-495e-b91f-abc19b71417d', 'fa6e0a0e-3ad1-4f03-a49b-788ed1e5fa95', '5a70d72a-1a3f-47b0-b372-fe91e068cccc', 'd00470e7-872e-4648-ba91-bc3c6323adc0', 'a33e673f-5c21-4f7d-b0ca-2c92ff7b8aef', 'aa3ef8a1-cf12-4649-8cdb-c93170790209', '343613db-8c48-46c6-b20a-9fcdeb20c191', 'a126255a-4911-44a1-841d-0ef25389b48a', '3ab902c3-dabe-4c02-a14c-be85fd05edfd', 'd7c510c9-9ce1-4836-b289-89f4c098ce66', '8b216af5-358a-4588-868a-dde02978502e', '66e53694-bd46-4d4b-9046-20133ae2b8bf', '2b814f75-0154-4bbe-87eb-79f6d26fc641', '1913087b-9741-4efa-ae81-ddb921567471', '39a1fe8d-d8ef-4fd8-9223-38eec36f480a', '8f9e513f-3d3e-4496-a7bf-96a3dd7e276b', 'fde02895-f88b-4550-8f06-ca8c8cfd9da8' ] }; var botPanel = false; var t01brightrainbow = 'linear-gradient(to right, #ff6767, #ffdb9a, #ffff9d, #5fd85f, #9f9fff, #ff9bff)'; var t02pastelrainbow = 'linear-gradient(to right, #ff9a9a, #ffe4b4, #ffffb4, #bfd9bf, #add8e6, #e0d6f4)'; var t03valentines = 'linear-gradient(to right bottom, #ff7373, #ffe1f1)'; var t04christmas = 'linear-gradient(to right bottom, #6bdb6b, #ff9a9a)'; var t05halloween = 'linear-gradient(to right bottom, #ffba67, #ffffb4)'; var t06blue = 'linear-gradient(to right bottom, #a9c4f5, #d4ebf2)'; var t07green = 'linear-gradient(to right bottom, #9fc69f, #cfe3cf)'; var t08pink = 'linear-gradient(to right bottom, #ffb6da, #ffe9ec)'; var t09purple = 'linear-gradient(to right bottom, #c1adea, #dfd5f5)'; var t10blackgray = 'linear-gradient(to right bottom, #c6c6c6, #ececec)'; var t11sunset = 'linear-gradient(to right, #9489c2, #c600fb, #ffa200, #c600fb, #9489c2)'; var t12abv = 'linear-gradient(to right bottom, #00e0ff, #c600fb, #9489c2)'; var t13rwb = 'linear-gradient(to right bottom, #ff0000, #ffffff, #0027c1)'; var t14stpat = 'linear-gradient(to right bottom, #0e7312, #c0e6c3, #0e7312)'; var themeArray = { name: ['Bright Rainbow', 'Pastel Rainbow', 'Valentines', 'Christmas', 'Halloween', 'Shades of Blue', 'Shades of Green', 'Shades of Pink', 'Shades of Purple', 'Black and Gray', 'Sunset', 'Aqua/Blue/Violet', 'Red/White/Blue', 'St Patrick\'s Day' ], shortcut: ['brightrainbow', 'pastelrainbow', 'valentines', 'christmas', 'halloween', 'shadesblue', 'shadesgreen', 'shadespink', 'shadespurple', 'blackgray', 'sunset', 'aquablue', 'redwhiteblue', 'stpat' ], colorID: [t01brightrainbow, t02pastelrainbow, t03valentines, t04christmas, t05halloween, t06blue, t07green, t08pink, t09purple, t10blackgray, t11sunset, t12abv, t13rwb, t14stpat ], textcolor: ['#00008b', '#00008b', '#c7006b', '#ffffff', '#000000', '#00008b', '#006400', '#d00068', '#663399', '#000000', '#000000', '#d4ebf2', '#000000', '#024604' ]}; var textColorArray = { dispname: ['White/No Color', 'Black', 'Dark Grey', 'Dark Red', 'Dark Orange', 'Dark Green', 'Dark Aqua', 'Dark Blue', 'Dark Purple', 'Dark Pink', 'Dark Gold', 'Dark Teal', 'Dark Brown', 'Dark Bronze', 'Dark Periwinkle', 'Dark Fuschia', 'Dark Lime', 'Dark Plum', 'Bright Rainbow', 'Pastel Rainbow', 'Valentines', 'Christmas', 'Halloween', 'Shades of Blue', 'Shades of Green', 'Shades of Pink', 'Shades of Purple', 'Black and Gray', 'Sunset', 'Aqua/Blue/Violet', 'Red/White/Blue', 'St Patrick\'s Day' ], name: ['white', 'black', 'darkgrey', 'darkred', 'darkorange', 'darkgreen', 'darkaqua', 'darkblue', 'darkpurple', 'darkpink', 'darkgold', 'darkteal', 'darkbrown', 'darkbronze', 'darkperiwinkle', 'darkfuschia', 'darklime', 'darkplum', 'Bright Rainbow', 'Pastel Rainbow', 'Valentines', 'Christmas', 'Halloween', 'Shades of Blue', 'Shades of Green', 'Shades of Pink', 'Shades of Purple', 'Black and Gray', 'Sunset', 'Aqua/Blue/Violet', 'Red/White/Blue', 'St Patrick\'s Day' ], colorID: ['#FFFFFF', '#000000', '#737373', '#cc0000', '#e77400', '#006600', '#006767', '#0629AC', '#3d003d', '#FF6680', '#998100', '#003f1f', '#582c00', '#a56728', '#155bd7', '#d6155c', '#6b790c', '#7f13bf', '#00008b', '#00008b', '#c7006b', '#ffffff', '#000000', '#00008b', '#006400', '#d00068', '#663399', '#000000', '#000000', '#d4ebf2', '#000000', '#024604' ]}; var bgColorArray = { dispname: ['White/No Color', 'Light Yellow', 'Light Blue', 'Light Pink', 'Light Red', 'Light Green', 'Light Purple', 'Light Orange', 'Light Grey', 'Light Aqua', 'Light Teal', 'Cream', 'Light Bronze', 'Light Periwinkle', 'Light Fuschia', 'Light Lime', 'Light Plum', 'Bright Rainbow', 'Pastel Rainbow', 'Valentines', 'Christmas', 'Halloween', 'Shades of Blue', 'Shades of Green', 'Shades of Pink', 'Shades of Purple', 'Black and Gray', 'Sunset', 'Aqua/Blue/Violet', 'Red/White/Blue', 'St Patrick\'s Day' ], name: ['white', 'lightyellow', 'lightblue', 'lightpink', 'lightred', 'lightgreen', 'lightpurple', 'lightorange', 'lightgrey', 'lightaqua', 'lightteal', 'cream', 'lightbronze', 'lightperiwinkle', 'lightfuschia', 'lightlime', 'lightplum', 'Bright Rainbow', 'Pastel Rainbow', 'Valentines', 'Christmas', 'Halloween', 'Shades of Blue', 'Shades of Green', 'Shades of Pink', 'Shades of Purple', 'Black and Gray', 'Sunset', 'Aqua/Blue/Violet', 'Red/White/Blue', 'St Patrick\'s Day' ], colorID: ['#FFFFFF', '#ffff94', '#d1eaee', '#FFE6EA', '#ff9a9a', '#94e594', '#f2cdff', '#ffd9b3', '#e6e6e6', '#adeaea', '#d7fbee', '#f9f6ed', '#ebccad', '#d7e4fb', '#fbd7e4', '#ecf6a7', '#e3c0f9', t01brightrainbow, t02pastelrainbow, t03valentines, t04christmas, t05halloween, t06blue, t07green, t08pink, t09purple, t10blackgray, t11sunset, t12abv, t13rwb, t14stpat ]}; cb.settings_choices = [ {name: 'intro', label: '******************* INTRODUCTION ********************* Latest Update: Xxxx xx, 2022 (version 1.0) ********************************************************* Welcome to Naty\'s App, If you are not Naty and have somehow made it to this screen, please contact OzzyZeppelin on twitter @red_zeppelin77 to let him know how the heck you did that!!!', type: 'choice',required: false}, // *** General Settings {name: 'general', label: '---------------------------------------------------------------------------------- PART 1: GENERAL SETUP ---------------------------------------------------------------------------------- Section 1 will setup the general features of the app for use in all features.', type: 'choice',required: false}, {name: 'bctext', label: '1A. Broadcaster Display Name -- Enter the name you would like to be used for yourself in notices, if you leave blank it will use "The Broadcaster".', type: 'str',required: false, minLength: 1, maxLength: 50, defaultValue: 'Naty'}, {name: 'whichApp', label: '1B. Which Goal Type / Show Type would you like to begin the show with?', type: 'choice', choice1: 'Single/Progressive Goals', choice2: 'Goal Counter', choice3: 'Goal Race', choice4: 'Auto-Reset Goal', choice5: 'UltraApp Ticket Show', choice6: 'Spank-a-thon', choice7: 'None', defaultValue: 'Single/Progressive Goals'}, {name: 'enableEntryMessage', label: '1C. Announcement? -- Display a notification to users when they enter? It is ideal if your welcome message is configured in your Naty DotBot but can also be set in setting 1D.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'entryMessage', label: '1D. Announcement Text -- Enter the message to display. The substitution identifiers {username} and {n} can be used within your welcome text Leave this field blank if you\'ve configured a general welcome message in your bot', type: 'str',required: false, minLength: 1, maxLength: 1000, defaultValue: ''}, {name: 'botModList', label: '1E. App Mods -- Enter the names of any users you would like to grant moderator privileges without assigning Chaturbate moderator status. Can also be added during the show with the "/addmod" command', type: 'str', minLength: 1, maxLength: 1000, defaultValue: 'n_ozzyzeppelin, ozzyzeppelin', required: false}, {name: 'VIPList', label: '1F. VIP List -- Enter the names of any VIP List users you would like to grant special privileges, such as free or reduced prices to ticket shows. User names should be separated by a comma with no spaces', type: 'str', minLength: 1, maxLength: 1000, defaultValue: '', required: false}, {name: 'VIPname', label: '1G. VIP List Name -- Enter the Display Name of the VIP List if you have a personalized one. Default is "VIP List" if you do not enter one.', type: 'str', minLength: 1, maxLength: 50, required: false, defaultValue: 'Lotofagos'}, {name: 'genericRoomSubjectType', label: '1H1. Room Title Format -- What information should be displayed in the room title? Setting to "All" will display all three types of info: the user entered overall show text in setting 1H2, the app specific text entered in 2A, 3A, etc, and the normally hardcoded text about current and next goals. Setting to "All - Current goal only" will also display all three types, but only the current goal info. Setting to "User-entered text only" will exclude the pre-defined current goal and next goal information and only display the user entered text from 1H2 and the individual app features.', type: 'choice', choice1: 'All', choice2: 'All - Current goal only', choice3: 'User-entered text only', defaultValue: 'All'}, {name: 'genericRoomSubjectSfx', label: '1H2. General Room Title Text -- Enter any show description text or hashtags you would like appended to the goal or ticket show specific descriptions from each section below. This part of the room title will be shown at all times regardless of which goal type is used or if the ticket show feature is started. Keep in mind you have a total of 200 characters for all Room Title text.', type: 'str',required: false, minLength: 1, maxLength: 200}, {name: 'genericRoomSubjectPosn', label: '1H3. Title Text Position -- Show the General Room Title Text before or after the current goal and next goal information (if used)', type: 'choice', choice1: 'Beginning', choice2: 'End', defaultValue: 'End'}, {name: 'genericRoomSubjectRemGoal', label: '1H4. Room Title Includes Tokens left to Goal? -- Should the number of tokens left to a goal be displayed in the room title? This is not recommended since it will then post a chat message about the updated room title after every tip.', type: 'choice', choice1: 'No', choice2: 'Yes, at beginning', choice3: 'Yes, at end', defaultValue: 'No'}, {name: 'panelBackground', label: '1J. Background Panel Graphic -- Which background panel type would you like to display? Note you cannot use "My Custom Panel" background unless one has been created for your room, otherwise it will use the default if that is selected', type: 'choice', choice1: 'default - no image', /*choice2: backgroundArray.menu[0], choice3: backgroundArray.menu[1], choice4: backgroundArray.menu[2], choice5: backgroundArray.menu[3], choice6: backgroundArray.menu[4], choice7: backgroundArray.menu[5], choice8: backgroundArray.menu[6], choice9: backgroundArray.menu[7], choice10: backgroundArray.menu[8], choice11: backgroundArray.menu[9], choice12: backgroundArray.menu[10], choice13: backgroundArray.menu[11], choice14: backgroundArray.menu[12], choice15: backgroundArray.menu[13], choice16: backgroundArray.menu[14], choice17: backgroundArray.menu[15], choice18: backgroundArray.menu[16], choice19: backgroundArray.menu[17], choice20: backgroundArray.menu[18], choice21: backgroundArray.menu[19], choice22: backgroundArray.menu[20], choice23: backgroundArray.menu[21], choice24: backgroundArray.menu[22], choice25: backgroundArray.menu[23], choice26: backgroundArray.menu[24], choice27: backgroundArray.menu[25], choice28: backgroundArray.menu[26], choice29: backgroundArray.menu[27], choice30: backgroundArray.menu[28], choice31: backgroundArray.menu[29], choice32: backgroundArray.menu[30], choice33: backgroundArray.menu[31], choice34: backgroundArray.menu[32], choice35: backgroundArray.menu[33], choice36: backgroundArray.menu[34], choice37: backgroundArray.menu[35], choice38: backgroundArray.menu[36], choice39: backgroundArray.menu[37], choice40: backgroundArray.menu[38], choice41: backgroundArray.menu[39], choice42: backgroundArray.menu[40], choice43: backgroundArray.menu[41], choice44: backgroundArray.menu[42], choice45: backgroundArray.menu[43], choice46: backgroundArray.menu[44], choice47: backgroundArray.menu[45], choice48: backgroundArray.menu[46], choice49: backgroundArray.menu[47], choice50: backgroundArray.menu[48], choice51: backgroundArray.menu[49], choice52: backgroundArray.menu[50], choice53: backgroundArray.menu[51], choice54: backgroundArray.menu[52], choice55: backgroundArray.menu[53], choice56: backgroundArray.menu[54], choice57: backgroundArray.menu[55], choice58: backgroundArray.menu[56], choice59: backgroundArray.menu[57], choice60: backgroundArray.menu[58], choice61: backgroundArray.menu[59], choice62: backgroundArray.menu[60], choice63: backgroundArray.menu[61],*/ defaultValue: 'default - no image'}, {name: 'panelTextColor', label: '1K. Panel Text Color -- Choose from the menu or select "Custom" and input a color code below',type: 'choice',choice1: 'White/No Color',choice2: 'Black',choice3: 'Dark Grey',choice4: 'Dark Red',choice5: 'Dark Orange',choice6: 'Dark Green',choice7: 'Dark Aqua',choice8: 'Dark Blue',choice9: 'Dark Purple',choice10: 'Dark Pink',choice11: 'Dark Gold',choice12: 'Dark Teal',choice13: 'Dark Brown',choice14: 'Dark Bronze',choice15: 'Dark Periwinkle',choice16: 'Dark Fuschia',choice17: 'Dark Lime',choice18: 'Dark Plum',choice19: 'Custom',defaultValue: 'Dark Blue'}, {name: 'panelCustomTextColor', label: '1L. Panel Text Custom Color -- If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'noAppRunningDisplay', label: '1M. No App Feature Active Display -- If the configured App Feature could not be started due to invalid settings, or you\'ve deliberately disabled any feature, should the panel show the "Welcome to my room" message (which you can edit with commands), or the highest, last, and total tipper stats?', type: 'choice', choice1: 'Welcome to my room', choice2: 'Tipper Stats', defaultValue: 'Tipper Stats'}, {name: 'autoStartTicket', label: '1N. Automatically start Ticket Show sales after the last goal is completed? If enabled, a 30 second countdown will start after finishing the last goal and switch the active app to Ticket Show, is sales were already started it will just immediately switch the active app. Note: If you change your mind, you can cancel the start of ticket sales with the command /cancelticket before the timer runs out.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'showTotals', label: '1P. Display Total Tips -- Show the total tips so far for the current app in the draw panel?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'fembotRunning', label: '1Q. Suppress command errors in App -- Are you using Naty\'s Dotbot alongside the Naty App? If not, you should be! :) This setting is used to suppress error messages from the app for invalid commands as it is expected the bot will handle them.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'noticeSepStyle', label: '1R1. Notice Border Style -- This controls how the top and bottom border of the recurring notices are displayed. You can choose from the defined options here, or use the below setting for "1Y2" to define your own emoji or other character. Depending on the type you choose, the spacing of the border will be different, so be sure to select the matching type here for the emoji or character you set below.', type: 'choice', choice1: 'No Border', choice2: 'Light Dashed Line', choice3: 'Heavy Dashed Line', choice4: 'Custom Emoji Below', choice5: 'Custom Unicode Character Below', defaultValue: 'Light Dashed Line'}, {name: 'separatorEmoji', label: '1R2. Notice Border emoji or text/unicode character -- For an emoji or unicode character, paste the actual emoji/image here, not the code (not \u2580 for example). Note that most unicode characters have the benfit of being the same color as your text. Do NOT use a CB icon here, if you do, it will default to "Heavy Dashed Line".', type: 'str',minLength: 0,maxLength: 2,required: false}, // *** Single or Progressive Goals {name: 'progressivegoals', label: '---------------------------------------------------------------------------------- PART 2: SINGLE OR PROGRESSIVE GOALS ---------------------------------------------------------------------------------- You can set up a Single goal or multiple goals. Each goal amount is specific to that goal, it is not the cumulative total. Please clear out the descriptions and amounts for the goals that are not being used, or you can set the goal amount to be negative to keep it in your settings for later but not use it for this show.', type: 'choice',required: false}, {name: 'progressiveRoomSubjectSfx', label: '2A. Title? -- Enter any show description text or hashtags you would like appended to the individual goal descriptions shown in the room title', type: 'str',required: false, minLength: 1, maxLength: 125, defaultValue: '#natural #tease #dance'}, {name: 'progressiveAutoNext', label: '2B. Next Goal Selection -- There are three ways the app can be configured for controlling what happens when you finish a goal: (1) automatically start next goal (2) await the broadcaster or a moderator to manually start the next goal with the "/next" command, or (3) allow the broadcaster or a moderator to select the next goal from your goal list. Note that with the latter two, which are manual start, tips that exceed the current goal are not counted toward the next goal, tips start counting again once the new goal is started/selected. With auto-start, the excess amount is rolled over to the next goal. When set to "select" the next goal, the recurring notice for remaining goals will be disabled, and the app will never automatically complete all goals, you are always prompted to select the next.', type: 'choice', choice1: 'Auto-start next', choice2: 'Manually start next', choice3: 'Select next goal from list', defaultValue: 'Auto-start next'}, {name: 'progressiveGoalNoticeInterval', label: '2C1. Notice Interval -- Time interval for displaying the Goals Notice in the chat, in minutes. Decimals are ok as long as they are greater than 1. For example, 1.5 = "One minute and 30 second" intervals. Set to 0 to disable the notice.',required: false,type: 'str',defaultValue: 4.1}, {name: 'progressiveIncludeGoals', label: '2C2. Include Goals in Notice -- Choose if you want to include a list of the remaining goals in the recurring Goal Notice. If set to "No" the notice will just contain a summary of the Progressive Goals feature', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'minTippersToggle', label: '2D1. Include minimum number of tippers needed to tip before final goal can be reached. If set to Yes, the final goal will not count as completed until the number of users who have tipped is equaul to or greater than the value in setting 2D2.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'minTippersGoal', label: '2D2. Minimum number of tippers needed to tip before final goal can be completed. Value must be at least 2, will be ignored if setting 2D1 is set to No.', type: 'int', defaultValue: 2, minValue: 2, maxValue: 99999}, {name: 'progressiveGoalDescription1', label: 'Goal #1 Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Shirt Off'}, {name: 'progressiveGoalAmount1', label: 'Goal #1 amount (use negative sign "-" in front of amount to temporarily disable a goal)', type: 'int',required: false, minValue: -99999, maxValue: 99999, defaultValue: 2323}, {name: 'progressiveGoalDescription2', label: 'Goal #2 Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Shorts Off'}, {name: 'progressiveGoalAmount2', label: 'Goal #2 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999, defaultValue: 2323}, {name: 'progressiveGoalDescription3', label: 'Goal #3 Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Ticket Sales Start'}, {name: 'progressiveGoalAmount3', label: 'Goal #3 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999, defaultValue: 2323}, {name: 'progressiveGoalDescription4', label: 'Goal #4 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount4', label: 'Goal #4 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription5', label: 'Goal #5 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount5', label: 'Goal #5 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription6', label: 'Goal #6 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount6', label: 'Goal #6 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription7', label: 'Goal #7 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount7', label: 'Goal #7 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription8', label: 'Goal #8 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount8', label: 'Goal #8 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription9', label: 'Goal #9 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount9', label: 'Goal #9 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription10', label: 'Goal #10 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount10', label: 'Goal #10 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalTextColor', label: '2F1. Notice Text Color -- Text color for chat notices related to the Progressive Goals feature',type: 'choice',choice1: textColorArray.dispname[0],choice2: textColorArray.dispname[1],choice3: textColorArray.dispname[2],choice4: textColorArray.dispname[3],choice5: textColorArray.dispname[4],choice6: textColorArray.dispname[5],choice7: textColorArray.dispname[6],choice8: textColorArray.dispname[7],choice9: textColorArray.dispname[8],choice10: textColorArray.dispname[9],choice11: textColorArray.dispname[10],choice12: textColorArray.dispname[11],choice13: textColorArray.dispname[12],choice14: textColorArray.dispname[13],choice15: textColorArray.dispname[14],choice16: textColorArray.dispname[15],choice17: textColorArray.dispname[16],choice18: textColorArray.dispname[17],choice19: textColorArray.dispname[18],choice20: textColorArray.dispname[19],choice21: textColorArray.dispname[20],choice22: textColorArray.dispname[21],choice23: textColorArray.dispname[22],choice24: textColorArray.dispname[23],choice25: textColorArray.dispname[24],choice26: textColorArray.dispname[25],choice27: textColorArray.dispname[26],choice28: textColorArray.dispname[27],choice29: textColorArray.dispname[28],choice30: textColorArray.dispname[29],choice31: textColorArray.dispname[30],choice32: textColorArray.dispname[31],choice33: 'Custom',defaultValue: textColorArray.dispname[5]}, {name: 'progressiveGoalCustomTextColor', label: '2F2. Notice Custom Text Color -- If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'progressiveGoalBgColor', label: '2F3. Notice Background Color -- Background/Highlight color for chat notices related to the Progressive Goals feature', type: 'choice', choice1: bgColorArray.dispname[0],choice2: bgColorArray.dispname[1],choice3: bgColorArray.dispname[2],choice4: bgColorArray.dispname[3],choice5: bgColorArray.dispname[4],choice6: bgColorArray.dispname[5],choice7: bgColorArray.dispname[6],choice8: bgColorArray.dispname[7],choice9: bgColorArray.dispname[8],choice10: bgColorArray.dispname[9],choice11: bgColorArray.dispname[10],choice12: bgColorArray.dispname[11],choice13: bgColorArray.dispname[12],choice14: bgColorArray.dispname[13],choice15: bgColorArray.dispname[14],choice16: bgColorArray.dispname[15],choice17: bgColorArray.dispname[16],choice18: bgColorArray.dispname[17],choice19: bgColorArray.dispname[18],choice20: bgColorArray.dispname[19],choice21: bgColorArray.dispname[20],choice22: bgColorArray.dispname[21],choice23: bgColorArray.dispname[22],choice24: bgColorArray.dispname[23],choice25: bgColorArray.dispname[24],choice26: bgColorArray.dispname[25],choice27: bgColorArray.dispname[26],choice28: bgColorArray.dispname[27],choice29: bgColorArray.dispname[28],choice30: bgColorArray.dispname[29],choice31: bgColorArray.dispname[30],choice32: 'Custom',defaultValue: bgColorArray.dispname[5]}, {name: 'progressiveGoalCustomBgColor', label: '2F4. Notice Custom Background Color -- If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, // *** Goal Counter {name: 'goalcounter', label: '---------------------------------------------------------------------------------- PART 3: GOAL COUNTER ---------------------------------------------------------------------------------- With this Feature, smaller goals are set, and prizes are performed at each number of goals met, such as top off at 5 goals, bottoms off at 10 goals, etc. Only fill in the label and goal threshold for the prizes you would like to use and leave the rest blank. Note that the prize thresholds are the total number of goals, and not the incremental number of goals.', type: 'choice',required: false}, {name: 'goalcounterRoomSubjectSfx', label: '3A. Title? -- Enter any show description text or hashtags you would like appended to the list of individual goal thresholds that shown by default in the room title', type: 'str',required: false, minLength: 1, maxLength: 125, defaultValue: '#natural #tease #dance'}, {name: 'goalcounterPerGoalAmount', label: '3B. Individual goal amount', type: 'int',required: false, minValue: 1, maxValue: 10000, defaultValue: 23}, {name: 'goalcounterNoticeInterval', label: '3C1. Notice Interval -- Time interval for displaying the Goal Counter Notice in the chat, in minutes. Decimals are ok as long as they are greater than 1. For example, 1.5 = "One minute and 30 second" intervals. Set to 0 to disable the notice',required: false,type: 'str',defaultValue: 3.7}, {name: 'goalcounterIncludeGoals', label: '3C2. Include Goals in Notice -- Choose if you want to include a list of the remaining goals in the recurring Goal Notice. If set to "No" the notice will just contain a summary of the Goal Counter feature', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'goalcounterGoalDescription1', label: 'Prize Description #1', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Dress Off'}, {name: 'goalcounterGoalAmount1', label: 'Prize Threshold #1 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000, defaultValue: 100}, {name: 'goalcounterGoalDescription2', label: 'Prize Description #2', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Bra Off'}, {name: 'goalcounterGoalAmount2', label: 'Prize Threshold #2 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000, defaultValue: 250}, {name: 'goalcounterGoalDescription3', label: 'Prize Description #3', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Panties off and Ticket Sales Start'}, {name: 'goalcounterGoalAmount3', label: 'Prize Threshold #3 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000, defaultValue: 500}, {name: 'goalcounterGoalDescription4', label: 'Prize Description #4', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount4', label: 'Prize Threshold #4 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription5', label: 'Prize Description #5', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount5', label: 'Prize Threshold #5 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription6', label: 'Prize Description #6', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount6', label: 'Prize Threshold #6 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription7', label: 'Prize Description #7', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount7', label: 'Prize Threshold #7 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription8', label: 'Prize Description #8', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount8', label: 'Prize Threshold #8 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription9', label: 'Prize Description #9', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount9', label: 'Prize Threshold #9 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription10', label: 'Prize Description #10', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount10', label: 'Prize Threshold #10 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterTextColor', label: '3D1. Notice Text Color -- Text color for chat notices related to the Goal Counter Feature',type: 'choice',choice1: textColorArray.dispname[0],choice2: textColorArray.dispname[1],choice3: textColorArray.dispname[2],choice4: textColorArray.dispname[3],choice5: textColorArray.dispname[4],choice6: textColorArray.dispname[5],choice7: textColorArray.dispname[6],choice8: textColorArray.dispname[7],choice9: textColorArray.dispname[8],choice10: textColorArray.dispname[9],choice11: textColorArray.dispname[10],choice12: textColorArray.dispname[11],choice13: textColorArray.dispname[12],choice14: textColorArray.dispname[13],choice15: textColorArray.dispname[14],choice16: textColorArray.dispname[15],choice17: textColorArray.dispname[16],choice18: textColorArray.dispname[17],choice19: textColorArray.dispname[18],choice20: textColorArray.dispname[19],choice21: textColorArray.dispname[20],choice22: textColorArray.dispname[21],choice23: textColorArray.dispname[22],choice24: textColorArray.dispname[23],choice25: textColorArray.dispname[24],choice26: textColorArray.dispname[25],choice27: textColorArray.dispname[26],choice28: textColorArray.dispname[27],choice29: textColorArray.dispname[28],choice30: textColorArray.dispname[29],choice31: textColorArray.dispname[30],choice32: textColorArray.dispname[31],choice33: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'goalcounterCustomTextColor', label: '3D2. Notice Custom Text Color -- If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'goalcounterBgColor', label: '3D3. Notice Background Color -- Background/Highlight color for chat notices related to the Goal Counter feature', type: 'choice', choice1: bgColorArray.dispname[0],choice2: bgColorArray.dispname[1],choice3: bgColorArray.dispname[2],choice4: bgColorArray.dispname[3],choice5: bgColorArray.dispname[4],choice6: bgColorArray.dispname[5],choice7: bgColorArray.dispname[6],choice8: bgColorArray.dispname[7],choice9: bgColorArray.dispname[8],choice10: bgColorArray.dispname[9],choice11: bgColorArray.dispname[10],choice12: bgColorArray.dispname[11],choice13: bgColorArray.dispname[12],choice14: bgColorArray.dispname[13],choice15: bgColorArray.dispname[14],choice16: bgColorArray.dispname[15],choice17: bgColorArray.dispname[16],choice18: bgColorArray.dispname[17],choice19: bgColorArray.dispname[18],choice20: bgColorArray.dispname[19],choice21: bgColorArray.dispname[20],choice22: bgColorArray.dispname[21],choice23: bgColorArray.dispname[22],choice24: bgColorArray.dispname[23],choice25: bgColorArray.dispname[24],choice26: bgColorArray.dispname[25],choice27: bgColorArray.dispname[26],choice28: bgColorArray.dispname[27],choice29: bgColorArray.dispname[28],choice30: bgColorArray.dispname[29],choice31: bgColorArray.dispname[30],choice32: 'Custom',defaultValue: bgColorArray.dispname[2]}, {name: 'goalcounterCustomBgColor', label: '3D4. Notice Custom Background Color -- If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, // *** Goal Race {name: 'goalrace', label: '---------------------------------------------------------------------------------- PART 4: GOAL RACE ---------------------------------------------------------------------------------- With this Feature, you define two goals that the viewers can vote for when tipping. The App keeps track of the progress toward the two goals until one of the goals wins. There are commands to change the room subject, panel subject, goal amounts, and goal descriptions during the show.', type: 'choice',required: false}, {name: 'goalraceSubjectText', label: '4A. Room Title? -- Enter any show description text or hashtags you would like to appear in the configurable part of the room title.', type: 'str',required: false, minLength: 1, maxLength: 125, defaultValue: 'Sex Show at Final Goal #couple #goals'}, {name: 'goalracePanelText', label: '4B. Panel Description? -- Shorter description that will appear in the panel, must be less than 30 characters.', type: 'str',required: false, minLength: 1, maxLength: 30, defaultValue: 'Panel Description'}, {name: 'goalraceDesc1', label: '4C. Goal Description #1', type: 'str',required: false, minLength: 1, maxLength: 20, defaultValue: 'Oral for him'}, {name: 'goalraceAmount1', label: '4D. Goal Amount #1', type: 'int',required: false, minValue: 1, maxValue: 10000, defaultValue: 500}, {name: 'goalraceDesc2', label: '4E. Goal Description #2', type: 'str',required: false, minLength: 1, maxLength: 20, defaultValue: 'Oral for her'}, {name: 'goalraceAmount2', label: '4F. Goal Amount #2', type: 'int',required: false, minValue: 1, maxValue: 10000, defaultValue: 500}, {name: 'goalraceAllowClaim', label: '4G. Allow users to "claim" their unspecified votes later in the goal race.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'goalraceBonusFC', label: '4H. Percent bonus for CB Fan Club tips when voting. Leave as 1 for all votes to count the same. Use 20 for 20% bonus, 50 for 50% bonus, etc (100% doubles their tip amount). Allowable values from 0 to 100%', type: 'int',minValue: 0,maxValue: 100, defaultValue: 0,required: false}, {name: 'goalraceBonusVIP', label: '4J. Percent bonus for VIP tips when voting. Leave as 1 for all votes to count the same. Use 20 for 20% bonus, 50 for 50% bonus, etc (100% doubles their tip amount). Allowable values from 0 to 100%', type: 'int',minValue: 0,maxValue: 100,defaultValue: 0,required: false}, {name: 'goalRaceNoticeInterval', label: '4K. Time interval for displaying the recurring Goal Race feature notice/reminder in the chat, in minutes. Decimals are ok as long as they are greater than 1. For example, 1.5 = "One minute and 30 second" intervals. Set to 0 to disable the notice',required: false,type: 'str',defaultValue: 3.2}, {name: 'goalraceTextColor', label: '4L1. Text color for chat notices related to the Goal Race feature',type: 'choice',choice1: textColorArray.dispname[0],choice2: textColorArray.dispname[1],choice3: textColorArray.dispname[2],choice4: textColorArray.dispname[3],choice5: textColorArray.dispname[4],choice6: textColorArray.dispname[5],choice7: textColorArray.dispname[6],choice8: textColorArray.dispname[7],choice9: textColorArray.dispname[8],choice10: textColorArray.dispname[9],choice11: textColorArray.dispname[10],choice12: textColorArray.dispname[11],choice13: textColorArray.dispname[12],choice14: textColorArray.dispname[13],choice15: textColorArray.dispname[14],choice16: textColorArray.dispname[15],choice17: textColorArray.dispname[16],choice18: textColorArray.dispname[17],choice19: textColorArray.dispname[18],choice20: textColorArray.dispname[19],choice21: textColorArray.dispname[20],choice22: textColorArray.dispname[21],choice23: textColorArray.dispname[22],choice24: textColorArray.dispname[23],choice25: textColorArray.dispname[24],choice26: textColorArray.dispname[25],choice27: textColorArray.dispname[26],choice28: textColorArray.dispname[27],choice29: textColorArray.dispname[28],choice30: textColorArray.dispname[29],choice31: textColorArray.dispname[30],choice32: textColorArray.dispname[31],choice33: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'goalraceCustomTextColor', label: '4L2. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'goalraceBgColor', label: '4L3. Background/Highlight color for chat notices related to the Goal Race feature', type: 'choice', choice1: bgColorArray.dispname[0],choice2: bgColorArray.dispname[1],choice3: bgColorArray.dispname[2],choice4: bgColorArray.dispname[3],choice5: bgColorArray.dispname[4],choice6: bgColorArray.dispname[5],choice7: bgColorArray.dispname[6],choice8: bgColorArray.dispname[7],choice9: bgColorArray.dispname[8],choice10: bgColorArray.dispname[9],choice11: bgColorArray.dispname[10],choice12: bgColorArray.dispname[11],choice13: bgColorArray.dispname[12],choice14: bgColorArray.dispname[13],choice15: bgColorArray.dispname[14],choice16: bgColorArray.dispname[15],choice17: bgColorArray.dispname[16],choice18: bgColorArray.dispname[17],choice19: bgColorArray.dispname[18],choice20: bgColorArray.dispname[19],choice21: bgColorArray.dispname[20],choice22: bgColorArray.dispname[21],choice23: bgColorArray.dispname[22],choice24: bgColorArray.dispname[23],choice25: bgColorArray.dispname[24],choice26: bgColorArray.dispname[25],choice27: bgColorArray.dispname[26],choice28: bgColorArray.dispname[27],choice29: bgColorArray.dispname[28],choice30: bgColorArray.dispname[29],choice31: bgColorArray.dispname[30],choice32: 'Custom',defaultValue: bgColorArray.dispname[2]}, {name: 'goalraceCustomBgColor', label: '4L4. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, // *** Auto-Reset Goal {name: 'autoresetgoal', label: '---------------------------------------------------------------------------------- PART 5: AUTO RESET GOAL ---------------------------------------------------------------------------------- You can set up a Single goal that will continually repeat, recylcing each time the goal is hit, and rolling any excess tips into the next cycle.', type: 'choice',required: false}, {name: 'autoresetSubjectSfx', label: '5A. Room Title -- (Optional) Enter any show description text or hashtags you would like appended to the goal description shown in the room title', type: 'str',required: false, minLength: 1, maxLength: 125, defaultValue: 'Welcome to our show! #couple #goals #sextease'}, {name: 'autoresetGoalAmount', label: '5B. Individual Goal Amount -- (Required) Each time this goal is hit, it recycles. It can keep going forever, or a limit on the number of goals may be set below', type: 'int',minValue: 1, maxValue: 99999, defaultValue: 2323}, {name: 'autoresetEachGoal', label: '5C. Individual Goal Prize -- (Required) Prize Description used for the individual goals (all goals have the same description until the final goal).', type: 'str',minLength: 0, maxLength: 100, defaultValue: 'Take Something Off'}, {name: 'autoresetEndAfter', label: '5D. End After X Goals -- (Optional) If you want to recycle the goals a specific number of times (usually to do a final prize at the last goal), define the total number of goals. Otherwise leave this as zero to keep repeating the goal without end.', type: 'int',minValue: 0,maxValue: 9999,defaultValue: 5,required: false}, {name: 'autoresetFinalGoal', label: '5E. Final Goal Prize -- (Optional) If you plan to do a grand prize after the total goals specified above, put the Prize Description here', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Naked Tease and Ticket Sales Start'}, {name: 'resetShowCount', label: '5F. Show Count? -- Show the number of goals that have been hit so far in the panel and Goal Reached announcement?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'autoresetNoticeInterval', label: '5G. Time interval for displaying the Auto Reset Goal notice in the chat, in minutes. Decimals are ok as long as they are greater than 1. For example, 1.5 = "One minute and 30 second" intervals. Set to 0 to disable the notice',required: false,type: 'str',defaultValue: 4.2}, {name: 'autoresetTextColor', label: '5H1. Text color for chat notices related to the Auto-Reset Goal feature',type: 'choice',choice1: textColorArray.dispname[0],choice2: textColorArray.dispname[1],choice3: textColorArray.dispname[2],choice4: textColorArray.dispname[3],choice5: textColorArray.dispname[4],choice6: textColorArray.dispname[5],choice7: textColorArray.dispname[6],choice8: textColorArray.dispname[7],choice9: textColorArray.dispname[8],choice10: textColorArray.dispname[9],choice11: textColorArray.dispname[10],choice12: textColorArray.dispname[11],choice13: textColorArray.dispname[12],choice14: textColorArray.dispname[13],choice15: textColorArray.dispname[14],choice16: textColorArray.dispname[15],choice17: textColorArray.dispname[16],choice18: textColorArray.dispname[17],choice19: textColorArray.dispname[18],choice20: textColorArray.dispname[19],choice21: textColorArray.dispname[20],choice22: textColorArray.dispname[21],choice23: textColorArray.dispname[22],choice24: textColorArray.dispname[23],choice25: textColorArray.dispname[24],choice26: textColorArray.dispname[25],choice27: textColorArray.dispname[26],choice28: textColorArray.dispname[27],choice29: textColorArray.dispname[28],choice30: textColorArray.dispname[29],choice31: textColorArray.dispname[30],choice32: textColorArray.dispname[31],choice33: 'Custom',defaultValue: textColorArray.dispname[5]}, {name: 'autoresetCustomTextColor', label: '5H2. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'autoresetBgColor', label: '5H3. Background/Highlight color for chat notices related to the Auto-Reset Goal feature', type: 'choice', choice1: bgColorArray.dispname[0],choice2: bgColorArray.dispname[1],choice3: bgColorArray.dispname[2],choice4: bgColorArray.dispname[3],choice5: bgColorArray.dispname[4],choice6: bgColorArray.dispname[5],choice7: bgColorArray.dispname[6],choice8: bgColorArray.dispname[7],choice9: bgColorArray.dispname[8],choice10: bgColorArray.dispname[9],choice11: bgColorArray.dispname[10],choice12: bgColorArray.dispname[11],choice13: bgColorArray.dispname[12],choice14: bgColorArray.dispname[13],choice15: bgColorArray.dispname[14],choice16: bgColorArray.dispname[15],choice17: bgColorArray.dispname[16],choice18: bgColorArray.dispname[17],choice19: bgColorArray.dispname[18],choice20: bgColorArray.dispname[19],choice21: bgColorArray.dispname[20],choice22: bgColorArray.dispname[21],choice23: bgColorArray.dispname[22],choice24: bgColorArray.dispname[23],choice25: bgColorArray.dispname[24],choice26: bgColorArray.dispname[25],choice27: bgColorArray.dispname[26],choice28: bgColorArray.dispname[27],choice29: bgColorArray.dispname[28],choice30: bgColorArray.dispname[29],choice31: bgColorArray.dispname[30],choice32: 'Custom',defaultValue: bgColorArray.dispname[1]}, {name: 'autoresetCustomBgColor', label: '5H4. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, // *** Hidden Ticket Show {name: 'ticketshow', label: '---------------------------------------------------------------------------------- PART 6: TICKET SHOWS ---------------------------------------------------------------------------------- When using this Ultra App in combination with Dorothy\'s Ultra Fembot, be sure to set the ticket show type in the Fembot to "Dorothy\'s UltraApp". Ticket show Pre-sales should also be performed here in the UltraApp, but the backup ticket list as well as all of the automated features around menus, polls, adding users from leaderboard, etc. can be performed or maintained in the Fembot.', type: 'choice',required: false}, {name: 'ticketRoomSubjectSfx', label: '6A. Title? -- Description of the show to appear as part of the room title (optional). You can include hashtags for keywords you want to be searchable', type:'str', minLength: 1,maxLength: 200, required: false, defaultValue: 'Naked Naughty Time!'}, {name: 'ticketShowPrice', label: '6B. Ticket Show Price? -- The Hidden Show feature cannot be enabled without a price defined. Discounted price for Fan Club is defined later below', type: 'int',minValue: 1,maxValue: 99999,defaultValue: 272,required: false}, {name: 'ticketShowStartPrice', label: '6C. New Price at Start of Hidden Show? -- You can choose to have the price increase once at the time the show starts from the original price above to a higher price. Notice that if this is done, the backup ticket list in an external bot such as Dorothy\'s Ultra Fembot will no longer be able to track tickets sold at the updated price. Only used if configured to a value greater than the ticket price above.', type: 'int',minValue: 0,maxValue: 99999,defaultValue: 0,required: false}, {name: 'ticketShowCumulative', label: '6D. Accumulate tips? -- Once ticket sales have started (including Pre-sales if used), accumulate tips toward ticket price? -- Recommend set to "Yes" to avoid need for many manual adds. If set to "No", user can only tip the ticket amount or higher to be added. Note that cumulative tips to buy a ticket will not add a user to the backup ticket list in the Fembot', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowStartMode', label: '6E. Start Mode? -- Which mode is used for determining when to start the show? This setting works in combination with the next setting below to define what will trigger the start of show, and whether it starts automatically', type: 'choice', choice1: 'Start Show Anytime', choice2: 'Start Show after Timer', choice3: 'Start Show after Ticket Goal', choice4: 'Start Show after Token Goal', defaultValue: 'Start Show Anytime'}, {name: 'ticketShowGoal', label: '6F. Goal Amount? -- If using a goal for the total sales before starting the show, set goal amount here (in terms of tickets or tokens based on type of show start goal defined above). When using a timed start, this setting is not used', type: 'int',minValue: 1,maxValue: 5000,defaultValue: 1000,required: false}, {name: 'ticketShowStartAuto', label: '6G. AutoStart? -- If using a timer or goal to determine start of show, does the Broadcaster (or moderator) start the show manually with the /startshow command, or does the show start automatically when timer runs out or goal is reached?', type: 'choice', choice1: 'Start from Command', choice2: 'Automated Start', defaultValue: 'Start from Command'}, {name: 'ticketShowStartTimer', label: '6H. Default Timer -- If the start mode is set for using an automatic timer, define the number of minutes for the countdown to show start here. Timer will start immediately upon starting the broadcast, or when the Hidden Ticket Show feature is turned on later. For manual timer control, start the show in "Start Show Anytime" mode, turn off "Autostart", and use "/ticketstarttimer" to do a countdown', type: 'int',minValue: 1,maxValue: 120,defaultValue: 15,required: false}, {name: 'ticketShowFanAppreciation', label: '6J. Fan Appreciation Mode? When turned on, a special ticket show is used where only the members defined below for free shows are able to access. There will be no ticket sales available.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'ticketShowFreeFC', label: '6K. Give a free ticket to CB Fanclub members?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowFreeMods', label: '6L. Give a free ticket to moderators?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowFreeVIP', label: '6M. Give a free ticket to VIP List members? Even if the VIP list has been setup in the Fembot, it must also be duplicated here (see General Setup section above) to provide ticket show privileges', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowPriceFC', label: '6N. Discounted Price for a ticket to the show for CB Fan Club members if not set for a free ticket above. If no price is set, the Fanc Club ticket price is the same as regular viewers. This value is not used if the above setting grants them a free ticket.', type: 'int',minValue: 1,maxValue: 300,defaultValue: 35,required: false}, {name: 'ticketShowPriceVIP', label: '6P. Discounted Price for a ticket to the show for VIP List members. If no price is set, the VIP List ticket price is the same as regular viewers. This value is not used if the above setting grants them a free ticket.', type: 'int',minValue: 0,maxValue: 300,required: false}, {name: 'ticketShowModsAdd', label: '6Q. Allow moderators to use the "add" and "remove" commands? Note that enabling this will allow them to add themselves to a show even if the above setting for giving them a free ticket is set to "No". This setting overrides the general setting for mod authority specifically for the ticket show "/add" functions.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowModsChgPrice', label: '6R. Allow moderators to change the price of a ticket? This setting overrides the general setting for mod authority specifically for the ticket show price functions', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowAfterNotice', label: '6S. Message to display in the Room Subject and Notice after the show has ended', type:'str',minLength: 1,maxLength: 100,required: false,defaultValue: 'After show hangout, please follow on twitter @cutenataly18.'}, {name: 'ticketShowReducePriceWarn', label: '6T. Late addition Ticket Show Sale? -- Reduce Ticket Price by this amount when the /showwarn command is used to indicate the show is nearly over. Set to 0 to not use this feature. Note this is the price reduction, not the actual new price', type: 'int',minValue: 0,maxValue: 300,defaultValue: 0,required: false}, {name: 'ticketshowEnableIcon', label: '6U. Show the ticket icon next to names that have bought a ticket to the show? Defaulted to Yes, but can be diabled if it interferes with other icons (can only have a max of 3 icons/gifs per message)', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketNoticeInterval', label: '6V. Time interval for displaying the Ticket Show Price Notice in the chat, in minutes. Decimals are ok as long as they are greater than 1. For example, 1.5 = "One minute and 30 second" intervals.',required: false,type: 'str',defaultValue: 1.2}, {name: 'ticketShowTextColor', label: '6W. Text color for chat notices related to the Ticket Show',type: 'choice',choice1: textColorArray.dispname[0],choice2: textColorArray.dispname[1],choice3: textColorArray.dispname[2],choice4: textColorArray.dispname[3],choice5: textColorArray.dispname[4],choice6: textColorArray.dispname[5],choice7: textColorArray.dispname[6],choice8: textColorArray.dispname[7],choice9: textColorArray.dispname[8],choice10: textColorArray.dispname[9],choice11: textColorArray.dispname[10],choice12: textColorArray.dispname[11],choice13: textColorArray.dispname[12],choice14: textColorArray.dispname[13],choice15: textColorArray.dispname[14],choice16: textColorArray.dispname[15],choice17: textColorArray.dispname[16],choice18: textColorArray.dispname[17],choice19: textColorArray.dispname[18],choice20: textColorArray.dispname[19],choice21: textColorArray.dispname[20],choice22: textColorArray.dispname[21],choice23: textColorArray.dispname[22],choice24: textColorArray.dispname[23],choice25: textColorArray.dispname[24],choice26: textColorArray.dispname[25],choice27: textColorArray.dispname[26],choice28: textColorArray.dispname[27],choice29: textColorArray.dispname[28],choice30: textColorArray.dispname[29],choice31: textColorArray.dispname[30],choice32: textColorArray.dispname[31],choice33: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'ticketShowCustomTextColor', label: '6X. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'ticketShowBgColor', label: '6Y. Background/Highlight color for chat notices related to the Ticket Show', type: 'choice', choice1: bgColorArray.dispname[0],choice2: bgColorArray.dispname[1],choice3: bgColorArray.dispname[2],choice4: bgColorArray.dispname[3],choice5: bgColorArray.dispname[4],choice6: bgColorArray.dispname[5],choice7: bgColorArray.dispname[6],choice8: bgColorArray.dispname[7],choice9: bgColorArray.dispname[8],choice10: bgColorArray.dispname[9],choice11: bgColorArray.dispname[10],choice12: bgColorArray.dispname[11],choice13: bgColorArray.dispname[12],choice14: bgColorArray.dispname[13],choice15: bgColorArray.dispname[14],choice16: bgColorArray.dispname[15],choice17: bgColorArray.dispname[16],choice18: bgColorArray.dispname[17],choice19: bgColorArray.dispname[18],choice20: bgColorArray.dispname[19],choice21: bgColorArray.dispname[20],choice22: bgColorArray.dispname[21],choice23: bgColorArray.dispname[22],choice24: bgColorArray.dispname[23],choice25: bgColorArray.dispname[24],choice26: bgColorArray.dispname[25],choice27: bgColorArray.dispname[26],choice28: bgColorArray.dispname[27],choice29: bgColorArray.dispname[28],choice30: bgColorArray.dispname[29],choice31: bgColorArray.dispname[30],choice32: 'Custom',defaultValue: bgColorArray.dispname[9]}, {name: 'ticketShowCustomBgColor', label: 'Z. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, // *** Spank-a-thon {name: 'spankathon', label: '---------------------------------------------------------------------------------- PART 7: SPANKATHON ---------------------------------------------------------------------------------- The "Spank-a-thon" is a is a show format that allows you to setup a spank tip menu and spank goals so people can tip for different types of spanks, the total count is tracked for each type, and then also track progress toward goals at which the spanking occurs. Note that the App Feature will not start if there are duplicate prices in the Spank Tip Menu, and each individual goal has it\'s own total (it is NOT a cumulative amount).', type: 'choice',required: false}, {name: 'spankSubjectText', label: '7A. Title? -- Description of the Spank-a-thon to appear as part of the room title (optional). You can include hashtags for keywords you want to be searchable', type:'str', minLength: 1,maxLength: 125, required: false, defaultValue: 'Spank-a-thon! Tip for your Favorite type of Spanks!'}, {name: 'spankNoticeInterval', label: '7B1. Time interval for displaying the Spank-a-thon notice and tip menu in the chat, in minutes. Decimals are ok as long as they are greater than 1. For example, 1.5 = "One minute and 30 second" intervals. Set to 0 to disable the Notice.',required: false,type: 'str',defaultValue: 3.8}, {name: 'spankIncludeGoals', label: '7B2. Include Goals in Notice -- Choose if you want to include a list of the remaining goals in the recurring Goal Notice. If set to "No" the notice will just contain a summary of the spank-a-thon and spank menu', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'spankSepChar', label: '7C. Choose your separator character to appear between menu items in the Spank Menu. You can also use a custom specific gif not in the list by entering one in the next setting below, which will override this setting', type: 'choice',choice1: 'Spank',choice2: 'Hearts',choice3: 'Glitter',choice4: 'Flowers',choice5: 'Bow',choice6: 'Hearts2',choice7: 'Smiley',choice8: 'Text Heart',choice9: 'Text Diamond',choice10: 'Text Star',choice11: 'Vertical Bar',choice12: 'Custom',defaultValue: 'Spank'}, {name: 'spankSepCharCustom', label: '7D. Choose your custom separator (optional) for the Spank Menu. It can be a gif or a string of text/special characters (&!&). If using a gif, make sure to start it with the colon (:) as it would be typed in the chat',type: 'str',required: false}, {name: 'spankGoalDescription1', label: 'Goal #1 Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Goal #1 Name'}, {name: 'spankGoalAmount1', label: 'Goal #1 Amount (tokens)', type: 'int',required: false, minValue: 1, maxValue: 100000, defaultValue: 500}, {name: 'spankGoalDescription2', label: 'Goal #2 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'spankGoalAmount2', label: 'Goal #2 Amount (tokens)', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'spankGoalDescription3', label: 'Goal #3 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'spankGoalAmount3', label: 'Goal #3 Amount (tokens)', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'spankGoalDescription4', label: 'Goal #4 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'spankGoalAmount4', label: 'Goal #4 Amount (tokens)', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'spankGoalDescription5', label: 'Goal #5 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'spankGoalAmount5', label: 'Goal #5 Amount (tokens)', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'spankMenuType1', label: '***** Tip Menu Spank Type 1 - Set the per spank price, and any multiple spank bundle prices that can be discounted when buying in bulk',type: 'str',required: false,defaultValue: 'Hand Spanks'}, {name: 'spankMenuType1Price1', label: 'Spank Type 1 Price per Spank',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType1Amount2', label: 'Spank Type 1 Bundle 1 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType1Price2', label: 'Spank Type 1 Bundle 1 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType1Amount3', label: 'Spank Type 1 Bundle 2 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType1Price3', label: 'Spank Type 1 Bundle 2 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType2', label: '***** Tip Menu Spank Type 2 - Set the per spank price, and any multiple spank bundle prices that can be discounted when buying in bulk',type: 'str',required: false,defaultValue: 'Paddle Spanks'}, {name: 'spankMenuType2Price1', label: 'Spank Type 2 Price per Spank',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType2Amount2', label: 'Spank Type 2 Bundle 1 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType2Price2', label: 'Spank Type 2 Bundle 1 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType2Amount3', label: 'Spank Type 2 Bundle 2 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType2Price3', label: 'Spank Type 2 Bundle 2 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType3', label: '***** Tip Menu Spank Type 3 - Set the per spank price, and any multiple spank bundle prices that can be discounted when buying in bulk',type: 'str',required: false}, {name: 'spankMenuType3Price1', label: 'Spank Type 3 Price per Spank',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType3Amount2', label: 'Spank Type 3 Bundle 1 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType3Price2', label: 'Spank Type 3 Bundle 1 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType3Amount3', label: 'Spank Type 3 Bundle 2 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType3Price3', label: 'Spank Type 3 Bundle 2 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType4', label: '***** Tip Menu Spank Type 4 - Set the per spank price, and any multiple spank bundle prices that can be discounted when buying in bulk',type: 'str',required: false}, {name: 'spankMenuType4Price1', label: 'Spank Type 4 Price per Spank',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType4Amount2', label: 'Spank Type 4 Bundle 1 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType4Price2', label: 'Spank Type 4 Bundle 1 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType4Amount3', label: 'Spank Type 4 Bundle 2 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType4Price3', label: 'Spank Type 4 Bundle 2 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType5', label: '***** Tip Menu Spank Type 5 - Set the per spank price, and any multiple spank bundle prices that can be discounted when buying in bulk',type: 'str',required: false}, {name: 'spankMenuType5Price1', label: 'Spank Type 5 Price per Spank',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType5Amount2', label: 'Spank Type 5 Bundle 1 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType5Price2', label: 'Spank Type 5 Bundle 1 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType5Amount3', label: 'Spank Type 5 Bundle 2 - Number of Spanks',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankMenuType5Price3', label: 'Spank Type 5 Bundle 2 - Price per Bundle',type: 'int',minValue: 0,maxValue: 99999,required: false}, {name: 'spankTextColor', label: '7F1. Text color for chat notices related to the Spank-a-thon feature',type: 'choice',choice1: textColorArray.dispname[0],choice2: textColorArray.dispname[1],choice3: textColorArray.dispname[2],choice4: textColorArray.dispname[3],choice5: textColorArray.dispname[4],choice6: textColorArray.dispname[5],choice7: textColorArray.dispname[6],choice8: textColorArray.dispname[7],choice9: textColorArray.dispname[8],choice10: textColorArray.dispname[9],choice11: textColorArray.dispname[10],choice12: textColorArray.dispname[11],choice13: textColorArray.dispname[12],choice14: textColorArray.dispname[13],choice15: textColorArray.dispname[14],choice16: textColorArray.dispname[15],choice17: textColorArray.dispname[16],choice18: textColorArray.dispname[17],choice19: textColorArray.dispname[18],choice20: textColorArray.dispname[19],choice21: textColorArray.dispname[20],choice22: textColorArray.dispname[21],choice23: textColorArray.dispname[22],choice24: textColorArray.dispname[23],choice25: textColorArray.dispname[24],choice26: textColorArray.dispname[25],choice27: textColorArray.dispname[26],choice28: textColorArray.dispname[27],choice29: textColorArray.dispname[28],choice30: textColorArray.dispname[29],choice31: textColorArray.dispname[30],choice32: textColorArray.dispname[31],choice33: 'Custom',defaultValue: textColorArray.dispname[9]}, {name: 'spankCustomTextColor', label: '7F2. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false}, {name: 'spankBgColor', label: '7F3. Background/Highlight color for chat notices related to the Spank-a-thon feature', type: 'choice', choice1: bgColorArray.dispname[0],choice2: bgColorArray.dispname[1],choice3: bgColorArray.dispname[2],choice4: bgColorArray.dispname[3],choice5: bgColorArray.dispname[4],choice6: bgColorArray.dispname[5],choice7: bgColorArray.dispname[6],choice8: bgColorArray.dispname[7],choice9: bgColorArray.dispname[8],choice10: bgColorArray.dispname[9],choice11: bgColorArray.dispname[10],choice12: bgColorArray.dispname[11],choice13: bgColorArray.dispname[12],choice14: bgColorArray.dispname[13],choice15: bgColorArray.dispname[14],choice16: bgColorArray.dispname[15],choice17: bgColorArray.dispname[16],choice18: bgColorArray.dispname[17],choice19: bgColorArray.dispname[18],choice20: bgColorArray.dispname[19],choice21: bgColorArray.dispname[20],choice22: bgColorArray.dispname[21],choice23: bgColorArray.dispname[22],choice24: bgColorArray.dispname[23],choice25: bgColorArray.dispname[24],choice26: bgColorArray.dispname[25],choice27: bgColorArray.dispname[26],choice28: bgColorArray.dispname[27],choice29: bgColorArray.dispname[28],choice30: bgColorArray.dispname[29],choice31: bgColorArray.dispname[30],choice32: 'Custom',defaultValue: bgColorArray.dispname[3]}, {name: 'spankCustomBgColor', label: '7F4. If you picked a custom text color in the previous setting, enter the hex color (6 character hex color codes including the # prefix, such as #FFFFFF):', type: 'str', minLength: 1, maxLength: 7, required: false} ]; // *********************************** Variables and Arrays ************************************** var initialize = 0; var chatMsgToggle = false; var groupTipToggle = false; var BC = cb.room_slug; var noFeatureText = ''; var finalGoalMet = false; var currentGoalMet = false; var noAppPanelText1 = ' '; var noAppPanelText2 = ' '; var noAppPanelText3 = ' '; var ticketAppPanelText3 = ' '; var panelText3Updated = false; var highestTip = 0; var highestTipper = 'None'; var mostRecentTip = 0; var mostRecentTipper = 'None'; var fontSize = 12; var ultraAppStartTime = 0; var bcText = ''; var VIPname = ''; var checkSpecial = /[!@#$%^&*()+\-=\[\]{};':"\\|,.<>\/?]+/; var wrapMaxLength = 80; var ticketemoji = '\uD83C\uDF9F\uFE0F'; var botName = 'Naty App: '; var borderCharSpacing = ''; var borderChar = cb.settings.separatorEmoji; var whichApp = ''; var backgroundImage = ''; var currentPanel = ''; var showStage = ''; var textColor = 'black'; var appNoticeColor = '#f4d599'; var appErrorColor = '#f4c1bc'; var ticketHolderBgColor = 'linear-gradient(to bottom, #ffffff 20%, #cdf2ff 100%)'; var panelPreviewOn = false; var savedPanelBackground = ''; var savedPanelFilename = ''; var savedBotPanel = false; var panelPreviewer = ''; var currentPanelCycleIndex = 0; var leftjust1 = 20; var leftjust2 = 20; var leftjust3 = 20; var topjust1 = 4; var topjust2 = 27; var topjust3 = 49; var themeWarning = false; var themeWarning2 = false; var themeWarning3 = false; var themeWarning4 = false; var themeWarning5 = false; var goalSubjectText = cb.settings.progressiveRoomSubjectSfx; var counterSubjectText = cb.settings.goalcounterRoomSubjectSfx; var ticketSubjectText = cb.settings.ticketRoomSubjectSfx; var goalraceSubjectText = cb.settings.goalraceSubjectText; var goalracePanelText = cb.settings.goalracePanelText; var spankSubjectText = cb.settings.spankSubjectText; var autoresetSubjectSfx = cb.settings.autoresetSubjectSfx; var genericRoomSubjectSfx = cb.settings.genericRoomSubjectSfx; var genericRoomSubjectPosn = cb.settings.genericRoomSubjectPosn; var genericRoomSubjectType = cb.settings.genericRoomSubjectType; var progressiveAutoNext = cb.settings.progressiveAutoNext; var minTippersToggle = cb.settings.minTippersToggle; var minTippersGoal = cb.settings.minTippersGoal; var tippersNeeded = cb.settings.minTippersGoal; var currentGoal = 1; var currentGoalTips = 0; var currentGoalTotal = 0; var currentSessionTotal = 0; var currentGoalRecycleCnt = 0; var currentGoalLevel = 1; var currentGoalCountAmt = 0; var currentGoalRecycleCount = 0; var currentGroupTipAmt = 0; var currentGoalDesc = ''; var currentAppTotalGoal = 0; var currentAppTotalCounter = 0; var currentAppTotalTicket = 0; var currentAppTotalGoalRace = 0; var currentAppTotalNone = 0; var currentAppTotalSpanks = 0; var currentAppTotalAutoreset = 0; var ticketNoticesTextColor = ''; var ticketNoticesBgColor = ''; var ticketTextColorFan = ''; var ticketBgColorFan = ''; var ticketTextColorVIP = ''; var ticketBgColorVIP = ''; var ticketTextColorMod = ''; var ticketBgColorMod = ''; var ticketTextColor = ''; var ticketBgColor = ''; var goalraceBgColor = ''; var goalraceTextColor = ''; var goalCountBgColor = ''; var goalCountTextColor = ''; var goalCountNoticesTxtColor = ''; var goalCountNoticesBgColor = ''; var progGoalBgColor = ''; var progGoalTextColor = ''; var progGoalNoticesTxtColor = ''; var progGoalNoticesBgColor = ''; var spankBgColor = ''; var spankTextColor = ''; var autoresetBgColor = ''; var autoresetTextColor = ''; var borderUniversalBottom = ''; var borderAllNotices = ''; var borderAllNoticesShort = ''; var borderTicketTop = ''; var borderTicketBottom = ''; var borderGoalsTop = ''; var borderGoalCountTop = ''; var borderSpankTop = ''; var borderGoalRaceTop = ''; var borderAutoresetTop = ''; var borderWelcomeTop = ''; var totalProgGoals = 0; var progGoalNoticeInt = 0; var goalCountNoticeInt = 0; var gcnums = 0; var totalSpanksTipped = 0; var totalSpanksCompleted = 0; var totalSpankGoals = 0; var spankCharacter = ''; var spankNoticeInt = 0; var goalRaceWinner = ''; var goalRaceNoticeInt = 0; var autoresetGoalAmount = cb.settings.autoresetGoalAmount; var autoresetEachGoal = cb.settings.autoresetEachGoal; var autoresetEndAfter = cb.settings.autoresetEndAfter; var autoresetFinalGoal = cb.settings.autoresetFinalGoal; var resetShowCount = true; var resetLastTip = 'No Tips Yet'; var autoresetNoticeInt = 0; var ticketStartMode = ''; var ticketModeAuto = ''; var ticketShowTotalTips = 0; var ticketShowTotalTicketsBought = 0; var ticketStartTime = 0; var ticketStopTime = 0; var ticketTimeAdded = 0; var ticketMinsRemain = 0; var ticketSecsRemain = 0; var ticketShowGoal = parseInt(cb.settings.ticketShowGoal); var ticketPrice = cb.settings.ticketShowPrice; var ticketShowEnded = false; var ticketSalesEnded = false; var ticketSalesOpen = false; var countTickets = 0; var ticketTimeAmt = cb.settings.ticketShowStartTimer; var ticketShowGoalTokens = cb.settings.ticketShowGoal; var ticketModeMessage = ''; var hiddenTime = 0; var ticketNoticeInt = 0; var ticketShowGoalTickets = 0; var ticketTimerStopping = false; var ticketDisplaySeconds = false; var ticketShowStartPrice = 0; var savedTicketSubjectText = ''; var afterNoticeInt = 90000; var ticketCountdownRunning = false; var savedProgGoals = false; var savedGoalCount = false; var savedGoalRace = false; var savedSpankGoals = false; var savedAutoresetGoals = false; var savedCurrentGoalTips = 0; var pgSave_finalGoalMet = false; var pgSave_currentGoalTips = 0; var pgSave_currentGoal = 0; var pgSave_currentGoalDesc = ''; var pgSave_currentGoalTotal = 0; var gcSave_finalGoalMet = false; var gcSave_currentGoalTips = 0; var gcSave_currentGoalDesc = ''; var gcSave_currentGoalTotal = 0; var gcSave_currentGoalCountAmt = 0; var gcSave_currentGoalLevel = 0; var tjSave_finalGoalMet = false; var tjSave_currentGoalTips = 0; var tjSave_currentGoal = 0; var tjSave_currentGoalDesc = ''; var tjSave_currentGoalTotal = 0; var tjSave_currentGoalLevel = 0; var tjSave_currentGoalRecycleTotal = 0; var tjSave_currentGoalRecycleCount = 0; var sqSave_finalGoalMet = false; var sqSave_currentGoalDesc = ''; var sqSave_currentGroupTipAmt = 0; var sqSave_currentGoalLevel = 0; var grSave_finalGoalMet = false; var grSave_currentGroupTipAmt = 0; var spSave_finalGoalMet = false; var spSave_currentGoalTips = 0; var spSave_currentGoal = 0; var spSave_currentGoalDesc = ''; var spSave_currentGoalTotal = 0; var arSave_finalGoalMet = false; var arSave_currentGoalTips = 0; var arSave_currentGoal = 0; var arSave_currentGoalDesc = ''; var arSave_currentGoalTotal = 0; var noticeOnlyBC = botName + 'Only broadcasters are able to use that command.'; var noticeOnlyBCMod1 = botName + 'Only broadcasters and moderators are able to use that command.'; var noticeOnlyBCMod2 = botName + 'Only broadcasters and "Standard/Advanced" moderator levels are able to use that command.'; var noticeOnlyBCMod3 = botName + 'Only broadcasters and the "Advanced" moderator level are able to use that command.'; var noticeOnlyBCMod3A = botName + 'Only broadcasters and moderators with configured "add" and "remove" authority are able to use that command.'; var noticeOnlyBCMod3P = botName + 'Only broadcasters and moderators with configured "price change" authority are able to use that command.'; var progressiveGoalDescription1 = cb.settings.progressiveGoalDescription1; var progressiveGoalDescription2 = cb.settings.progressiveGoalDescription2; var progressiveGoalDescription3 = cb.settings.progressiveGoalDescription3; var progressiveGoalDescription4 = cb.settings.progressiveGoalDescription4; var progressiveGoalDescription5 = cb.settings.progressiveGoalDescription5; var progressiveGoalDescription6 = cb.settings.progressiveGoalDescription6; var progressiveGoalDescription7 = cb.settings.progressiveGoalDescription7; var progressiveGoalDescription8 = cb.settings.progressiveGoalDescription8; var progressiveGoalDescription9 = cb.settings.progressiveGoalDescription9; var progressiveGoalDescription10 = cb.settings.progressiveGoalDescription10; var progressiveGoalAmount1 = cb.settings.progressiveGoalAmount1; var progressiveGoalAmount2 = cb.settings.progressiveGoalAmount2; var progressiveGoalAmount3 = cb.settings.progressiveGoalAmount3; var progressiveGoalAmount4 = cb.settings.progressiveGoalAmount4; var progressiveGoalAmount5 = cb.settings.progressiveGoalAmount5; var progressiveGoalAmount6 = cb.settings.progressiveGoalAmount6; var progressiveGoalAmount7 = cb.settings.progressiveGoalAmount7; var progressiveGoalAmount8 = cb.settings.progressiveGoalAmount8; var progressiveGoalAmount9 = cb.settings.progressiveGoalAmount9; var progressiveGoalAmount10 = cb.settings.progressiveGoalAmount10; var goalCounterDescription1 = cb.settings.goalcounterGoalDescription1; var goalCounterDescription2 = cb.settings.goalcounterGoalDescription2; var goalCounterDescription3 = cb.settings.goalcounterGoalDescription3; var goalCounterDescription4 = cb.settings.goalcounterGoalDescription4; var goalCounterDescription5 = cb.settings.goalcounterGoalDescription5; var goalCounterDescription6 = cb.settings.goalcounterGoalDescription6; var goalCounterDescription7 = cb.settings.goalcounterGoalDescription7; var goalCounterDescription8 = cb.settings.goalcounterGoalDescription8; var goalCounterDescription9 = cb.settings.goalcounterGoalDescription9; var goalCounterDescription10 = cb.settings.goalcounterGoalDescription10; var goalCounterAmount1 = cb.settings.goalcounterGoalAmount1; var goalCounterAmount2 = cb.settings.goalcounterGoalAmount2; var goalCounterAmount3 = cb.settings.goalcounterGoalAmount3; var goalCounterAmount4 = cb.settings.goalcounterGoalAmount4; var goalCounterAmount5 = cb.settings.goalcounterGoalAmount5; var goalCounterAmount6 = cb.settings.goalcounterGoalAmount6; var goalCounterAmount7 = cb.settings.goalcounterGoalAmount7; var goalCounterAmount8 = cb.settings.goalcounterGoalAmount8; var goalCounterAmount9 = cb.settings.goalcounterGoalAmount9; var goalCounterAmount10 = cb.settings.goalcounterGoalAmount10; var goalCounterAmount11 = cb.settings.goalcounterGoalAmount11; var goalCounterAmount12 = cb.settings.goalcounterGoalAmount12; var goalCounterAmount13 = cb.settings.goalcounterGoalAmount13; var goalCounterAmount14 = cb.settings.goalcounterGoalAmount14; var goalCounterAmount15 = cb.settings.goalcounterGoalAmount15; var goalraceDesc1 = cb.settings.goalraceDesc1; var goalraceDesc2 = cb.settings.goalraceDesc2; var goalraceAmount1 = cb.settings.goalraceAmount1; var goalraceAmount2 = cb.settings.goalraceAmount2; var currentGoalRace1Tips = 0; var currentGoalRace2Tips = 0; var spankGoalDescription1 = cb.settings.spankGoalDescription1; var spankGoalDescription2 = cb.settings.spankGoalDescription2; var spankGoalDescription3 = cb.settings.spankGoalDescription3; var spankGoalDescription4 = cb.settings.spankGoalDescription4; var spankGoalDescription5 = cb.settings.spankGoalDescription5; var spankGoalAmount1 = cb.settings.spankGoalAmount1; var spankGoalAmount2 = cb.settings.spankGoalAmount2; var spankGoalAmount3 = cb.settings.spankGoalAmount3; var spankGoalAmount4 = cb.settings.spankGoalAmount4; var spankGoalAmount5 = cb.settings.spankGoalAmount5; var spankMenuType1 = cb.settings.spankMenuType1; var spankMenuType2 = cb.settings.spankMenuType2; var spankMenuType3 = cb.settings.spankMenuType3; var spankMenuType4 = cb.settings.spankMenuType4; var spankMenuType5 = cb.settings.spankMenuType5; var spankMenuType1Amount1 = 1; var spankMenuType2Amount1 = 1; var spankMenuType3Amount1 = 1; var spankMenuType4Amount1 = 1; var spankMenuType5Amount1 = 1; var spankMenuType1Price1 = cb.settings.spankMenuType1Price1; var spankMenuType2Price1 = cb.settings.spankMenuType2Price1; var spankMenuType3Price1 = cb.settings.spankMenuType3Price1; var spankMenuType4Price1 = cb.settings.spankMenuType4Price1; var spankMenuType5Price1 = cb.settings.spankMenuType5Price1; var spankMenuType1Amount2 = cb.settings.spankMenuType1Amount2; var spankMenuType2Amount2 = cb.settings.spankMenuType2Amount2; var spankMenuType3Amount2 = cb.settings.spankMenuType3Amount2; var spankMenuType4Amount2 = cb.settings.spankMenuType4Amount2; var spankMenuType5Amount2 = cb.settings.spankMenuType5Amount2; var spankMenuType1Price2 = cb.settings.spankMenuType1Price2; var spankMenuType2Price2 = cb.settings.spankMenuType2Price2; var spankMenuType3Price2 = cb.settings.spankMenuType3Price2; var spankMenuType4Price2 = cb.settings.spankMenuType4Price2; var spankMenuType5Price2 = cb.settings.spankMenuType5Price2; var spankMenuType1Amount3 = cb.settings.spankMenuType1Amount3; var spankMenuType2Amount3 = cb.settings.spankMenuType2Amount3; var spankMenuType3Amount3 = cb.settings.spankMenuType3Amount3; var spankMenuType4Amount3 = cb.settings.spankMenuType4Amount3; var spankMenuType5Amount3 = cb.settings.spankMenuType5Amount3; var spankMenuType1Price3 = cb.settings.spankMenuType1Price3; var spankMenuType2Price3 = cb.settings.spankMenuType2Price3; var spankMenuType3Price3 = cb.settings.spankMenuType3Price3; var spankMenuType4Price3 = cb.settings.spankMenuType4Price3; var spankMenuType5Price3 = cb.settings.spankMenuType5Price3; // Arrays */ var fanClubInShow = []; var VIPListArray = []; var VIPsInShow = []; var moderatorList = {name: [], type: []}; var botModListArray = []; var modsInShow = []; var ticketShowViewerList = []; var ticketHolderList = []; //var raceTipNotesOn = []; var ninjaTipsOn = []; var tipCountArray = {name: [], amount: []}; var ticketCumulative = {name: [], amount: []}; var ticketShowExtraTickets = {name: [], count: []}; var progGoalArray = {desc: [], amt: [], recyc: []}; var goalCounterArray = {desc: [], amt: []}; var drawpanel = {panel: {}}; var raceUnclaimedVotes = {name: [], amount: []}; var spankGoalArray = {desc: [], amt: []}; var spankPricesArray = {typ: [], number: [], price: []}; var spankRequestsArray = {name: [], amt: [], type: []}; var spankTotalsArray = {typ: [], tipped: [], completed: []}; var noticeIntervals = {name: [], time: [], fieldname: []}; String.prototype.capitalize = function() { return this.charAt(0).toUpperCase() + this.slice(1); } // *********************************** Initialize ************************************** if (initialize == 0) { cb.sendNotice('Naty\'s App Version 1.0'); let intromessage = '\u26D4 Version 1.0 was released on Xxxxx XX, 2022 \u26D4'; intromessage += '\n \u2705 ' + wordWrap('Reminder: You can type "/uahelp cmd" to display the full command list, or use one of the group qualifiers to see more details within a group, such as "/uahelp goals"'); cb.sendNotice(intromessage, BC, appErrorColor); if (cb.settings.bctext) { bcText = cb.settings.bctext; } else { bcText = 'Nataly'; } // *** Initialize no-App Panel Display noAppPanelText1 = 'Welcome to ' + bcText + '\'s room'; // *** Initialize variables across all Apps once at beginning genericInit(); loadNoticeIntervals(); loadAllStaticColors(BC); loadAllFeatureColors(BC); noticeBorderChar(); borderTicketTop = new Array(37).join('-') + ' ' + ticketemoji + ' TICKET SHOW ' + ticketemoji + ' ' + new Array(37).join('-'); borderTicketBottom = '\n' + new Array(49).join('-') + ' \u25CF ' + new Array(49).join('-'); // *** Personalized names if (cb.settings.VIPname != '' && cb.settings.VIPname != null) { VIPname = cb.settings.VIPname; } else { VIPname = 'VIP List'; } // *** Load Array for Progressive Goals for (let loadindex = 1; loadindex <= 10; loadindex++) { var goaldesc = this["progressiveGoalDescription"+loadindex]; var goalamt = this["progressiveGoalAmount"+loadindex]; var goalrecyc = this["progressiveGoalRecycle"+loadindex]; if (goaldesc && goalamt > 0) { progGoalArray.desc.push(goaldesc); progGoalArray.amt.push(goalamt); progGoalArray.recyc.push(goalrecyc); } } // *** Load Array for Goal Counter for (let loadindex = 1; loadindex <= 10; loadindex++) { var goalcntdesc = this["goalCounterDescription"+loadindex]; var goalcntamt = this["goalCounterAmount"+loadindex]; if (goalcntdesc && goalcntamt > 0) { goalCounterArray.desc.push(goalcntdesc); goalCounterArray.amt.push(goalcntamt); } } // *** Auto-reset parms if (cb.settings.resetShowCount == 'No') { resetShowCount = false; } else { resetShowCount = true; } // *** Load Arrays for Spank-a-thon for (let loadindex = 1; loadindex <= 5; loadindex++) { var spankdesc = this["spankGoalDescription"+loadindex]; var spankamt = this["spankGoalAmount"+loadindex]; if(spankdesc != '' && spankdesc != null && spankamt != '' && spankamt != null) { spankGoalArray.desc.push(spankdesc); spankGoalArray.amt.push(spankamt); } } for (let j = 1; j <= 5; j++) { var spkmenutype = this["spankMenuType"+j]; if (spkmenutype) { spankTotalsArray.typ.push(spkmenutype); spankTotalsArray.tipped.push(0); spankTotalsArray.completed.push(0); for (let loadindex = 1; loadindex <= 3; loadindex++) { var spkmenuamt = this["spankMenuType"+j+"Amount"+loadindex]; var spkmenuprc = this["spankMenuType"+j+"Price"+loadindex]; if (spkmenuamt && spkmenuprc) { spankPricesArray.typ.push(spkmenutype); spankPricesArray.number.push(spkmenuamt); spankPricesArray.price.push(spkmenuprc); } else { spankPricesArray.typ.push(spkmenutype); spankPricesArray.number.push(0); spankPricesArray.price.push(0); } } } } // *** Init Feature switch (cb.settings.whichApp) { case 'Single/Progressive Goals': { if (validateFeature('goals')) { setAppFeature('goals', BC); } else { setAppFeature('none', BC); } break; } case 'Goal Counter': { if (validateFeature('goalcount')) { setAppFeature('goalcount', BC); } else { setAppFeature('none', BC); } break; } case 'UltraApp Ticket Show': { if (validateFeature('ticket')) { setAppFeature('ticket', BC); } else { setAppFeature('none', BC); } break; } case 'Goal Race': { if (validateFeature('goalrace')) { setAppFeature('goalrace', BC); } else { setAppFeature('none', BC); } break; } case 'Spank-a-thon': { if (validateFeature('spank')) { setAppFeature('spank', BC); } else { setAppFeature('none', BC); } break; } case 'Auto-Reset Goal': { if (validateFeature('autoreset')) { setAppFeature('autoreset', BC); } else { setAppFeature('none', BC); } break; } case 'None': { setAppFeature('none', BC); break; } default : { setAppFeature('none', BC); break; } } //*** Init VIP List if (cb.settings.VIPList) { var vipn = cb.settings.VIPList.toLowerCase(); var tempviparray = vipn.split(','); var initviptoadd = ''; for (var vipnidx = 0; vipnidx < tempviparray.length; vipnidx++) { initviptoadd = tempviparray[vipnidx].trim(); if (validateUserID(initviptoadd,'VIP List')) { VIPListArray.push(initviptoadd); } } } //*** Init App Mod List if (cb.settings.botModList) { var modn = cb.settings.botModList.toLowerCase(); var tempbotmodarray = modn.split(','); var initmodtoadd = ''; for (var modnidx = 0; modnidx < tempbotmodarray.length; modnidx++) { initmodtoadd = tempbotmodarray[modnidx].trim(); if (validateUserID(initmodtoadd,'App Moderator')) { addRmvMods(initmodtoadd,'botmod','a'); } } } //*** Initialize panel if (cb.settings.panelBackground != 'default - no image') { if (cbjs.arrayContains(backgroundArray.menu,cb.settings.panelBackground)) { var initbackground = backgroundArray.command[backgroundArray.menu.indexOf(cb.settings.panelBackground)]; customizePanelBackground(initbackground,BC,'init'); customizePanelText('',BC); } else { botPanel = false; cb.sendNotice('A panel background was chosen but it does not exist, using the default panel background.', BC, appNoticeColor); } } else { botPanel = false; } ultraAppStartTime = new Date(); addRmvMods(BC,'broadcaster','a'); initialize = 1; } // ** Functions ** function noticeBorderChar() { if (cb.settings.noticeSepStyle == 'Custom Emoji Below') { borderCharSpacing = 'emoji'; if (borderChar) { if (borderChar.includes(':') || borderChar.includes('\\')) { cb.sendNotice(botName + ' You have configured configured the notice border style as "Custom Emoji" but the value contains ":" or "\\" (CB gifs are not allowed). Defaulting to an emoji of Heavy Dashed Line " \u2796 ".', BC, appNoticeColor); borderChar = '\u2796'; } } else { borderChar = '\u2796'; cb.sendNotice(botName + ' You have configured configured the notice border style as "Custom Emoji" but an emoji is not defined. Defaulting to an emoji of Heavy Dashed Line " \u2796 ".', BC, appNoticeColor); } } else if (cb.settings.noticeSepStyle == 'Custom Unicode Character Below') { borderCharSpacing = 'unicode'; if (borderChar) { if (borderChar.includes(':')) { cb.sendNotice(botName + ' You have configured configured the notice border style as "Custom Unicode Character" but the value contains ":" (CB gifs are not allowed). Defaulting to a character of Heavy Dashed Line " \u268A ".', BC, appNoticeColor); borderChar = '\u268A'; } } else { borderChar = '\u268A'; cb.sendNotice(botName + ' You have configured configured the notice border style as "Custom Unicode Character" but a character is not defined. Defaulting to a character of Heavy Dashed Line " \u268A ".', BC, appNoticeColor); } } else if (cb.settings.noticeSepStyle == 'Heavy Dashed Line') { borderCharSpacing = 'unicode'; borderChar = '\u268A'; } else if (cb.settings.noticeSepStyle == 'Light Dashed Line') { borderCharSpacing = 'light'; borderChar = '-'; } else { borderCharSpacing = 'none'; } switch (borderCharSpacing) { case 'emoji': borderGoalsTop = noticeBorder('top','PROGRESSIVE GOALS',9,false,true) + '\n'; borderGoalCountTop = noticeBorder('top','GOAL COUNTER',8,false,true) + '\n'; borderGoalRaceTop = noticeBorder('top','GOAL RACE',8,false,true) + '\n'; borderSpankTop = noticeBorder('top','SPANK-a-THON',9,false,true) + '\n'; borderAutoresetTop = noticeBorder('top','AUTO-RESET GOALS',9,false,true) + '\n'; borderWelcomeTop = noticeBorder('top','WELCOME',10,false,true) + '\n'; borderUniversalBottom = '\n' + noticeBorder('bottom','',12,false,true); borderAllNotices = noticeBorder('bottom','',12,false,true); borderAllNoticesShort = noticeBorder('bottom','',8,false,true); break; case 'unicode': borderGoalsTop = noticeBorder('top','PROGRESSIVE GOALS',16,false,false); borderGoalCountTop = noticeBorder('top','GOAL COUNTER',18,false,false); borderGoalRaceTop = noticeBorder('top','GOAL RACE',19,false,false); borderSpankTop = noticeBorder('top','SPANK-a-THON',18,false,false); borderAutoresetTop = noticeBorder('top','AUTO-RESET GOALS',17,false,false); borderWelcomeTop = noticeBorder('top','WELCOME',19,true,false); borderUniversalBottom = noticeBorder('bottom','',22,false,false); borderAllNotices = noticeBorder('bottom','',22,false,false); borderAllNoticesShort = noticeBorder('bottom','',15,false,false); break; case 'light': borderGoalsTop = noticeBorder('top','PROGRESSIVE GOALS',35,true,false); borderGoalCountTop = noticeBorder('top','GOAL COUNTER',39,false,false); borderGoalRaceTop = noticeBorder('top','GOAL RACE',41,true,false); borderSpankTop = noticeBorder('top','SPANK-a-THON',39,false,false); borderAutoresetTop = noticeBorder('top','AUTO-RESET GOALS',36,true,false); borderWelcomeTop = noticeBorder('top','WELCOME',42,true,false); borderUniversalBottom = noticeBorder('bottom','',48,false,false); borderAllNoticesShort = noticeBorder('bottom','',32,false,false); borderUniversalBottom = noticeBorder('bottom','',48,false,false); borderAllNotices = noticeBorder('bottom','',48,false,false); borderAllNoticesShort = noticeBorder('bottom','',32,false,false); break; case 'none': borderGoalsTop = noticeBorder('top','PROGRESSIVE GOALS:',0,false,false); borderGoalCountTop = noticeBorder('top','GOAL COUNTER:',0,false,false); borderGoalRaceTop = noticeBorder('top','GOAL RACE:',0,false,false); borderSpankTop = noticeBorder('top','SPANK-a-THON:',0,false,false); borderAutoresetTop = noticeBorder('top','AUTO-RESET GOALS:',0,false,false); borderWelcomeTop = noticeBorder('top','WELCOME:',0,false,false); borderUniversalBottom = ''; borderAllNotices = ''; borderAllNoticesShort = ''; break; } } function noticeBorder(bordertopbtm,bordertext,bordernumchar,bordernumaddone,borderdispspace) { let outcharstring = ''; let halfcharstring = ''; let midchar = '\u25CF'; let dispnumber = 0; if (borderCharSpacing == 'none') { if (bordertopbtm == 'top') { outcharstring = bordertext; } } else { for (let charidx = 1; charidx <= bordernumchar; charidx++) { if (borderdispspace) { halfcharstring += borderChar + ' '; } else { halfcharstring += borderChar; } } if (bordertopbtm == 'top') { outcharstring += halfcharstring + ' ' + bordertext + ' ' + halfcharstring; if (bordernumaddone) { outcharstring += borderChar; } } else if (bordertopbtm == 'bottom') { if (borderdispspace) { outcharstring += halfcharstring + halfcharstring; } else { outcharstring += halfcharstring + ' ' + midchar + ' ' + halfcharstring; } } } return outcharstring; } //********** Check Next Line Function and Word Wrap ************** function checkNextLine(nlmessage) { var nlmessagearray = nlmessage.split(' '); var nllinearray = []; var templinearray = []; var lineidx = 0; var currlineidx = 0; var nlreplace = false; var nlsubfound = true; while (nlsubfound) { if (cbjs.arrayContains(nlmessagearray,'{n}')) { let nlmsgindex = nlmessagearray.indexOf('{n}'); templinearray = nlmessagearray.slice(currlineidx,nlmsgindex); nllinearray[lineidx] = wordWrap(cbjs.arrayJoin(templinearray,' ')); templinearray = nlmessagearray.slice(nlmsgindex+1); nllinearray[lineidx+1] = wordWrap(cbjs.arrayJoin(templinearray,' ')); lineidx++; currlineidx = nlmsgindex+1; nlreplace = true; } else { nlsubfound = false; } } if (nlreplace) { return cbjs.arrayJoin(nllinearray,'\n'); } else { return wordWrap(nlmessage); } } function wordWrap(wrapstring) { if (wrapMaxLength > 0) { let newlinechar = '\n'; let splitstring = ''; while (wrapstring.length > wrapMaxLength) { let spacefound = false; for (var wwidx = wrapMaxLength - 1; wwidx >= 0; wwidx--) { if (testWhite(wrapstring.charAt(wwidx))) { splitstring = splitstring + [wrapstring.slice(0, wwidx), newlinechar].join(''); wrapstring = wrapstring.slice(wwidx + 1); spacefound = true; break; } } if (!spacefound) { splitstring += [wrapstring.slice(0, wrapMaxLength), newlinechar].join(''); wrapstring = wrapstring.slice(wrapMaxLength); } } return splitstring + wrapstring; } else { return wrapstring; } } function testWhite(whitestring) { var white = new RegExp(/^\s$/); return white.test(whitestring.charAt(0)); } //********** Check Username Function ************** function checkUsername(ckusermessage,chkuser) { var responsemessagearray = ckusermessage.split(' '); var userreplace = false; if (cbjs.arrayContains(responsemessagearray,'{username}')) { let msgindex = responsemessagearray.indexOf('{username}'); responsemessagearray[msgindex] = chkuser; userreplace = true; } else if (cbjs.arrayContains(responsemessagearray,'{username},')) { let msgindex = responsemessagearray.indexOf('{username},'); responsemessagearray[msgindex] = chkuser + ','; userreplace = true; } else if (cbjs.arrayContains(responsemessagearray,'{username}!')) { let msgindex = responsemessagearray.indexOf('{username}!'); responsemessagearray[msgindex] = chkuser + '!'; userreplace = true; } else if (cbjs.arrayContains(responsemessagearray,'{username}:')) { let msgindex = responsemessagearray.indexOf('{username}:'); responsemessagearray[msgindex] = chkuser + ':'; userreplace = true; } else if (cbjs.arrayContains(responsemessagearray,'{username}.')) { let msgindex = responsemessagearray.indexOf('{username}.'); responsemessagearray[msgindex] = chkuser + '.'; userreplace = true; } if (userreplace) { return cbjs.arrayJoin(responsemessagearray,' '); } else { return ckusermessage; } } // Generic functions to set the color or separator characters function loadAllFeatureColors(loadfeatby) { progGoalColors(loadfeatby); goalCounterColors(loadfeatby); goalraceColors(loadfeatby); spankColors(loadfeatby); autoresetGoalColors(loadfeatby); ticketShowColors(loadfeatby); } function loadThemeColors(loadthemeby) { let tempthemebgcolor = setThemeBgColor(loadthemeby); let tempthemetextcolor = setThemeTextColor(loadthemeby); progGoalBgColor = tempthemebgcolor; progGoalTextColor = tempthemetextcolor; goalCountBgColor = tempthemebgcolor; goalCountTextColor = tempthemetextcolor; goalraceBgColor = tempthemebgcolor; goalraceTextColor = tempthemetextcolor; spankBgColor = tempthemebgcolor; spankTextColor = tempthemetextcolor; autoresetBgColor = tempthemebgcolor; autoresetTextColor = tempthemetextcolor; ticketBgColor = tempthemebgcolor; ticketTextColor = tempthemetextcolor; } function loadAllStaticColors() { progGoalNoticesTxtColor = setTextColor('Dark Red'); progGoalNoticesBgColor = setBgColor('Cream'); goalCountNoticesTxtColor = setTextColor('Dark Red'); goalCountNoticesBgColor = setBgColor('Cream'); ticketNoticesTextColor = setTextColor('Dark Red'); ticketNoticesBgColor = setBgColor('Cream'); ticketTextColorFan = setTextColor('Black'); ticketBgColorFan = setBgColor('Light Green'); ticketTextColorVIP = setTextColor('Black'); ticketBgColorVIP = setBgColor('Light Purple'); ticketTextColorMod = setTextColor('Black'); ticketBgColorMod = setBgColor('Light Red'); } function setTextColor(inputtextcolor,inputtextcolortype,inputtextcolorsendto) { var selectedtextcolor = '#FFFFFF'; if (cbjs.arrayContains(textColorArray.dispname,inputtextcolor)) { var txtclridx = textColorArray.dispname.indexOf(inputtextcolor); selectedtextcolor = textColorArray.colorID[txtclridx]; } else { if (/^#[0-9A-F]{6}$/i.test(inputtextcolor)) { selectedtextcolor = inputtextcolor; } else if (/^[0-9A-F]{6}$/i.test(inputtextcolor)) { selectedtextcolor = '#' + inputtextcolor; } else { cb.sendNotice(botName + ' ' + inputtextcolortype + ' - Error while setting the text color. It must be in a HEX code format. Using default value of black text.', inputtextcolorsendto, appNoticeColor); } } return selectedtextcolor; } function setBgColor(inputbgcolor,inputbgcolortype,inputbgcolorsendto) { var selectedbgcolor = '#FFFFFF'; if (cbjs.arrayContains(bgColorArray.dispname,inputbgcolor)) { var clridx = bgColorArray.dispname.indexOf(inputbgcolor); selectedbgcolor = bgColorArray.colorID[clridx]; } else { if (/^#[0-9A-F]{6}$/i.test(inputbgcolor)) { selectedbgcolor = inputbgcolor; } else if (/^[0-9A-F]{6}$/i.test(inputbgcolor)) { selectedbgcolor = '#' + inputbgcolor; } else { cb.sendNotice(botName + ' ' + inputbgcolortype + ' - Error while setting the background color. It must be in a HEX code format. Using default value of white/no background.', inputbgcolorsendto, appNoticeColor); } } if (selectedbgcolor != '#FFFFFF') { if (cb.settings.colorsGradientDirection == 'Linear, left to right') { selectedbgcolor = 'linear-gradient(to right, ' + selectedbgcolor + ', #FFFFFF)'; } else if (cb.settings.colorsGradientDirection == 'Linear, top to bottom') { selectedbgcolor = 'linear-gradient(to bottom, ' + selectedbgcolor + ', #FFFFFF)'; } else if (cb.settings.colorsGradientDirection == 'Linear, diagonally') { selectedbgcolor = 'linear-gradient(to right bottom, ' + selectedbgcolor + ', #FFFFFF)'; } } return selectedbgcolor; } function checkSepChar(char) { switch (char) { case 'Spank': return ':spankasss'; case 'Hearts': return ':heart2'; case 'Glitter': return ':pixelglitter'; case 'Flowers': return ':tinyflower2'; case 'Bow': return ':bluebow'; case 'Hearts2': return ':Hearts2'; case 'Smiley': return ':smile'; case 'Text Heart': return '\u2665'; case 'Text Diamond': return '\u2666'; case 'Text Star': return '\u2605'; case 'Vertical Bar': return '|'; default: return ':spankasss'; } } function leftJustify(textstring,line) { var textstringlength = textstring.length; if (fontSize == 11) { if (line == 1) { if (textstringlength < 4) { return 114; } else if (textstringlength < 6) { return 109; } else if (textstringlength < 8) { return 104; } else if (textstringlength < 10) { return 100; } else if (textstringlength < 12) { return 95; } else if (textstringlength < 14) { return 90; } else if (textstringlength < 16) { return 86; } else if (textstringlength < 18) { return 82; } else if (textstringlength < 20) { return 77; } else if (textstringlength < 22) { return 72; } else if (textstringlength < 24) { return 67; } else if (textstringlength < 26) { return 62; } else if (textstringlength < 28) { return 58; } else if (textstringlength < 30) { return 53; } else if (textstringlength < 32) { return 49; } else if (textstringlength < 34) { return 44; } else if (textstringlength < 37) { return 39; } else if (textstringlength < 40) { return 34; } else if (textstringlength < 42) { return 30; } else if (textstringlength < 44) { return 26; } else if (textstringlength < 47) { return 21; } else if (textstringlength < 49) { return 16; } else if (textstringlength < 51) { return 11; } else { return 7; } } else { if (textstringlength < 4) { return 114; } else if (textstringlength < 6) { return 110; } else if (textstringlength < 8) { return 105; } else if (textstringlength < 10) { return 100; } else if (textstringlength < 12) { return 97; } else if (textstringlength < 14) { return 93; } else if (textstringlength < 16) { return 88; } else if (textstringlength < 18) { return 83; } else if (textstringlength < 20) { return 79; } else if (textstringlength < 22) { return 74; } else if (textstringlength < 24) { return 71; } else if (textstringlength < 26) { return 66; } else if (textstringlength < 28) { return 61; } else if (textstringlength < 30) { return 57; } else if (textstringlength < 32) { return 53; } else if (textstringlength < 34) { return 48; } else if (textstringlength < 37) { return 44; } else if (textstringlength < 39) { return 39; } else if (textstringlength < 42) { return 34; } else if (textstringlength < 44) { return 29; } else if (textstringlength < 47) { return 24; } else if (textstringlength < 49) { return 19; } else if (textstringlength < 52) { return 14; } else { return 7; } } } else { if (line == 1) { if (textstringlength < 4) { return 113; } else if (textstringlength < 6) { return 108; } else if (textstringlength < 8) { return 103; } else if (textstringlength < 10) { return 99; } else if (textstringlength < 12) { return 94; } else if (textstringlength < 14) { return 89; } else if (textstringlength < 16) { return 84; } else if (textstringlength < 18) { return 80; } else if (textstringlength < 20) { return 75; } else if (textstringlength < 22) { return 70; } else if (textstringlength < 24) { return 65; } else if (textstringlength < 26) { return 60; } else if (textstringlength < 28) { return 55; } else if (textstringlength < 30) { return 50; } else if (textstringlength < 32) { return 46; } else if (textstringlength < 34) { return 41; } else if (textstringlength < 37) { return 36; } else if (textstringlength < 40) { return 31; } else if (textstringlength < 42) { return 26; } else if (textstringlength < 44) { return 22; } else if (textstringlength < 47) { return 17; } else if (textstringlength < 49) { return 12; } else if (textstringlength < 51) { return 7; } else { return 3; } } else { if (textstringlength < 4) { return 113; } else if (textstringlength < 6) { return 109; } else if (textstringlength < 8) { return 104; } else if (textstringlength < 10) { return 99; } else if (textstringlength < 12) { return 95; } else if (textstringlength < 14) { return 91; } else if (textstringlength < 16) { return 86; } else if (textstringlength < 18) { return 81; } else if (textstringlength < 20) { return 77; } else if (textstringlength < 22) { return 72; } else if (textstringlength < 24) { return 68; } else if (textstringlength < 26) { return 63; } else if (textstringlength < 28) { return 58; } else if (textstringlength < 30) { return 54; } else if (textstringlength < 32) { return 50; } else if (textstringlength < 34) { return 45; } else if (textstringlength < 37) { return 40; } else if (textstringlength < 39) { return 35; } else if (textstringlength < 42) { return 30; } else if (textstringlength < 44) { return 25; } else if (textstringlength < 47) { return 20; } else if (textstringlength < 49) { return 15; } else if (textstringlength < 52) { return 10; } else { return 3; } } } } //********** User ID list validations ************** function validateUserID(validateuser,validatetype) { if (validateuser.length < 3 || validateuser.includes(' ')) { cb.sendNotice('Cannot load ' + validatetype + ' entry ' + validateuser + ', it is improperly formatted (either contains a blank or is less than 3 characters).', BC, appNoticeColor); return false; } else if (checkSpecial.test(validateuser)) { cb.sendNotice('Cannot load ' + validatetype + ' entry ' + validateuser + ', it contains a special character.', BC, appNoticeColor); return false; } else { return true; } } //********** Build Moderator Array ************** function addRmvMods(armuser,armtype,armmode) { if (armmode == 'a') { if (!cbjs.arrayContains(moderatorList.name,armuser)) { moderatorList.name.push(armuser); moderatorList.type.push(armtype); } else { return; } } else if (armmode == 'r') { if (cbjs.arrayContains(moderatorList.name,armuser)) { let nameidx = moderatorList.name.indexOf(armuser); moderatorList.name.splice(nameidx,1); moderatorList.type.splice(nameidx,1); } else { return; } } else { return; } } function addRmvModsInShow(armisuser,armismode) { if (armismode == 'a') { if (cbjs.arrayContains(modsInShow,armisuser)) { return; } else { modsInShow.push(armisuser); } } else if (armismode == 'r') { if (cbjs.arrayContains(modsInShow,armisuser)) { cbjs.arrayRemove(modsInShow,armisuser); } else { return; } } } function addRmvFanClubInShow(arfcisuser,arfcismode) { if (arfcismode == 'a') { if (!cbjs.arrayContains(fanClubInShow,arfcisuser)) { fanClubInShow.push(arfcisuser); } else { return; } } else if (arfcismode == 'r') { if (cbjs.arrayContains(fanClubInShow,arfcisuser)) { cbjs.arrayRemove(fanClubInShow,arfcisuser); } else { return; } } } function addRmvVIP(arvuser,arvmode) { if (arvmode == 'a') { if (cbjs.arrayContains(VIPListArray,arvuser)) { return; } else { VIPListArray.push(arvuser); } } else if (arvmode == 'r') { if (cbjs.arrayContains(VIPListArray,arvuser)) { cbjs.arrayRemove(VIPListArray,arvuser); } else { return; } } } function addRmvVIPInShow(arvisuser,arvismode) { if (arvismode == 'a') { if (cbjs.arrayContains(VIPsInShow,arvisuser)) { return; } else { VIPsInShow.push(arvisuser); } } else if (arvismode == 'r') { if (cbjs.arrayContains(VIPsInShow,arvisuser)) { cbjs.arrayRemove(VIPsInShow,arvisuser); } else { return; } } } function genericInit() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoalCountAmt = 0; currentGoal = 1; currentGoalLevel = 1; currentGoalRecycleCount = 0; currentGroupTipAmt = 0; fontSize = 12; } function loadNoticeIntervals() { noticeIntervals.name[0] = 'goals'; noticeIntervals.fieldname[0] = 'progGoalNoticeInt'; updateIntervalArray('goals',cb.settings.progressiveGoalNoticeInterval,4.1); noticeIntervals.name[1] = 'goalcount'; noticeIntervals.fieldname[1] = 'goalCountNoticeInt'; updateIntervalArray('goalcount',cb.settings.goalcounterNoticeInterval,3.7); noticeIntervals.name[2] = 'goalrace'; noticeIntervals.fieldname[2] = 'goalRaceNoticeInt'; updateIntervalArray('goalrace',cb.settings.goalRaceNoticeInterval,3.5); noticeIntervals.name[3] = 'autoreset'; noticeIntervals.fieldname[3] = 'autoresetNoticeInt'; updateIntervalArray('autoreset',cb.settings.autoresetNoticeInterval,4.3); noticeIntervals.name[4] = 'ticket'; noticeIntervals.fieldname[4] = 'ticketNoticeInt'; updateIntervalArray('ticket',cb.settings.ticketNoticeInterval,1.7); noticeIntervals.name[5] = 'spank'; noticeIntervals.fieldname[5] = 'spankNoticeInt'; updateIntervalArray('spank',cb.settings.spankNoticeInterval,3.8); } function updateIntervalArray(updtinttype,updintint,updintdft) { if (!updintint) { updintint = updintdft; cb.sendNotice(botName + '"' + updtinttype + '" Chat Notice Interval is not configured. Using default value of ' + updintdft + ' minutes.', BC, appNoticeColor); } if (updintint > 0 && updintint < 1) { updintint = updintdft; cb.sendNotice(botName + '"' + updtinttype + '" Chat Notice Interval is too short, must be at least 1 minute. Using default value of ' + updintdft + ' minutes.', BC, appNoticeColor); } if (cbjs.arrayContains(noticeIntervals.name,updtinttype)) { let nameidx = noticeIntervals.name.indexOf(updtinttype); let actualint = parseInt(updintint*60000); this[noticeIntervals.fieldname[nameidx]] = actualint; noticeIntervals.time[nameidx] = actualint; } } //********** Tipper List ************** function findTipper(ftuser) { for (var i = 0; i < tipCountArray.name.length; i++) { if(tipCountArray.name[i] == ftuser) { break; } } return i; } //********** Set which Feature is Running ************** function setAppFeature(startapp, setby) { if (startapp == whichApp && startapp != 'none') { cb.sendNotice('The requested App feature is already running.', '', appNoticeColor, '', 'bold'); } else { switch (whichApp) { case 'goals': { setProgressiveGoalsToggle('off', setby); break; } case 'goalcount': { setGoalCounterToggle('off', setby); break; } case 'ticket': { setTicketShowToggle('off', setby); break; } case 'goalrace': { setGoalRaceToggle('off', setby); break; } case 'spank': { setSpankToggle('off', setby); break; } case 'autoreset': { setAutoresetToggle('off', setby); break; } } switch (startapp) { case 'goals': { setProgressiveGoalsToggle('on', setby); break; } case 'goalcount': { setGoalCounterToggle('on', setby); break; } case 'ticket': { setTicketShowToggle('on', setby); break; } case 'goalrace': { setGoalRaceToggle('on', setby); break; } case 'spank': { spankSepChar(); setSpankToggle('on', setby); break; } case 'autoreset': { setAutoresetToggle('on', setby); break; } case 'none': { whichApp = 'none'; changeRoomSubject(); break; } } cb.drawPanel(); } } //********** Reset current feature ************** function resetApp() { switch (whichApp) { case 'goals': { genericInit(); initProgGoal(); break; } case 'goalcount': { genericInit(); initGoalCounter(); break; } case 'goalrace': { genericInit(); initGoalRace(); break; } case 'spank': { genericInit(); initSpanks(); break; } case 'autoreset': { genericInit(); initAutoresetGoal(); break; } } cb.drawPanel(); } //********** Validate minimum required settings for each feature ************** function validateFeature(whichApp) { switch (whichApp) { case 'goals': { if (progGoalArray.desc.length <= 0) { cb.sendNotice(botName + 'The Feature of "Single/Progressive Goals" was chosen, but no goals have been defined. Please restart the UltraApp and define the goals you would like to use or change the app using the "/chgapp" command', BC, appNoticeColor); return false; } else { if (minTippersToggle == 'Yes' && minTippersGoal < 2) { cb.sendNotice(botName + 'The Feature of minimum number of tippers was enabled, but the number of tippers defined is too low. Please restart the UltraApp and set a valid minimum number of tippers (2 or greater) or change re-enable the setting using the "/mintippers (# of tippers)" command to re-enable this feature.', BC, appNoticeColor); minTippersToggle = 'No'; } return true; } } case 'goalcount': { if (goalCounterArray.desc.length <= 0) { cb.sendNotice(botName + 'The Feature of "Goal Counter" was chosen, but no goals have been defined. Please restart the UltraApp and define the goals you would like to use or change the app using the "/chgapp" command', BC, appNoticeColor); return false; } else { var allowgoals = true; let i = 1; while (goalCounterArray.amt[i] > 0) { if (goalCounterArray.amt[i] <= goalCounterArray.amt[i-1]) { allowgoals = false; } i++; } if (allowgoals) { return true; } else { cb.sendNotice(botName + 'The Feature of "Goal Counter" was chosen, but the "Number of goals" amounts are not listed in increasing value - each goal number setting amount be greater than the previous. Please restart the UltraApp and correct the goal amounts.', BC, appNoticeColor); return false; } } break; } case 'ticket': { if (ticketPrice <= 0) { cb.sendNotice(botName + 'Unable to start the Ticket Show feature, a ticket price is not set. You can either set the price now using the command "/ticketprice [amt]" where [amt] is in the price in tokens, or restart the UltraApp and set the price. The Ticket Show feature can be enabled after setting a price using the command "/chgapp ticket". ', BC, appNoticeColor); return false; } else { return true; } } case 'earlytkt': { if (ticketPrice <= 0) { cb.sendNotice(botName + 'Unable to start the Ticket sales, a ticket price is not set. You can either set the price now using the command "/ticketprice [amt]" where [amt] is in the price in tokens, or restart the UltraApp and set the price. Ticket sales can be enabled after setting a price using the command "/startsales". ', BC, appNoticeColor); return false; } else { return true; } } case 'goalrace': { if (goalraceDesc1 == '' || goalraceDesc1 == null || goalraceDesc2 == '' || goalraceDesc2 == null) { cb.sendNotice(botName + 'Unable to start the Goal Race feature, either the Goal 1 or Goal 2 Description is missing.', BC, appNoticeColor); return false; } else if (goalraceAmount1 == 0 || goalraceAmount2 == 0) { cb.sendNotice(botName + 'Unable to start the Goal Race feature, either the Goal 1 or Goal 2 Amount is zero.', BC, appNoticeColor); return false; } else { return true; } } case 'spank': { if (spankGoalDescription1 == '' || spankGoalDescription1 == null) { cb.sendNotice(botName + 'Unable to start the Spank-a-thon feature, Goal #1 Description is missing and at least one goal is required.', BC, appNoticeColor); return false; } else if (spankGoalAmount1 < 1 || spankGoalAmount1 == null) { cb.sendNotice(botName + 'Unable to start the Spank-a-thon feature, Goal #1 Tip Amount is missing and at least one goal is required.', BC, appNoticeColor); return false; } else if (spankPricesArray.typ[0] == '' || spankPricesArray.typ[0] == null) { cb.sendNotice(botName + 'Unable to start the Spank-a-thon feature, Spank Type 1 description is missing and at least one type is required.', BC, appNoticeColor); return false; } else if (spankPricesArray.price[0] < 1 || spankPricesArray.price[0] == null) { cb.sendNotice(botName + 'Unable to start the Spank-a-thon feature, Spank Type 1 price is missing and at least one type is required.', BC, appNoticeColor); return false; } else { var checkdup = true; var duprice = 0; for (var dupidx1 = 0; dupidx1 < spankPricesArray.price.length; dupidx1++) { for (var dupidx2 = dupidx1+1; dupidx2 < spankPricesArray.price.length; dupidx2++) { if (dupidx2 != dupidx1 && spankPricesArray.price[dupidx2] == spankPricesArray.price[dupidx1] && spankPricesArray.price[dupidx2] != 0) { duprice = spankPricesArray.price[dupidx2]; checkdup = false; break; } } } if (checkdup) { return true; } else { cb.sendNotice(botName + 'Unable to start the Spank-a-thon feature, there are duplicate entries with a price of ' + duprice + ' tokens.', BC, appNoticeColor); return false; } } } case 'autoreset': { if (autoresetGoalAmount <= 0) { cb.sendNotice(botName + 'The Feature of "Auto-Reset Goal" was chosen, but no individual goal amount has been defined. Please restart the UltraApp and define the goals you would like to use or change the app using the "/chgapp" command', BC, appNoticeColor); return false; } else { return true; } if (autoresetEachGoal <= 0) { cb.sendNotice(botName + 'The Feature of "Auto-Reset Goal" was chosen, but no individual goal amount has been defined. Please restart the UltraApp and define the goals you would like to use or change the app using the "/chgapp" command', BC, appNoticeColor); return false; } else { return true; } } case 'none': { return true; } } } //********** Set custom panel text and background ************** function customizePanelText(cptcolor,cptsendto) { if (!cptcolor && cb.settings.panelTextColor == 'Custom') { var textcolorchk = setTextColor(cb.settings.panelCustomTextColor,false); if (textcolorchk == 'default') { cb.sendNotice('Draw Panel Text Color - Error while setting the text color. It has to be in a HEX format.', cptsendto, appNoticeColor, ''); } else { textColor = textcolorchk; cb.drawPanel(); } } else if (cptcolor) { textcolorchk = setTextColor(cptcolor,false); if (textcolorchk == 'default') { cb.sendNotice('Draw Panel Text Color - Error while setting the text color. It has to be in a HEX format.', cptsendto, appNoticeColor, ''); } else { textColor = textcolorchk; cb.drawPanel(); } } else { textColor = setTextColor(cb.settings.panelTextColor,false); cb.drawPanel(); } } function customizePanelBackground(newbackground,sendto,custpanelmode) { botPanel = false; if (cbjs.arrayContains(backgroundArray.command,newbackground)) { var bgindex = backgroundArray.command.indexOf(newbackground); // ozzytester is the testbed user if (BC == 'ozzytester') { backgroundImage = backgroundArray.devfile[bgindex]; botPanel = true; currentPanel = newbackground; cb.drawPanel(); if (custpanelmode != 'init') { cb.sendNotice('You have updated the panel background to "' + newbackground + '".', sendto, appNoticeColor, ''); } } else { if (appVersion == 'Show1') { backgroundImage = backgroundArray.livefile[bgindex]; } else if (appVersion == 'Show2') { backgroundImage = backgroundArray.livefile2[bgindex]; } botPanel = true; currentPanel = newbackground; cb.drawPanel(); if (custpanelmode != 'init') { cb.sendNotice('You have updated the panel background to "' + newbackground + '".', sendto, appNoticeColor, ''); } } } else { cb.sendNotice('Invalid background name. The valid names are: \n' + cbjs.arrayJoin(backgroundArray.command, ', '), sendto, appNoticeColor, ''); } } function cyclePanels() { if (panelPreviewOn) { if (BC == 'ozzytester') { backgroundImage = backgroundArray.devfile[currentPanelCycleIndex]; } else { if (appVersion == 'Show1') { backgroundImage = backgroundArray.livefile[currentPanelCycleIndex]; } else if (appVersion == 'Show2') { backgroundImage = backgroundArray.livefile2[currentPanelCycleIndex]; } } currentPanel = backgroundArray.command[currentPanelCycleIndex]; if (currentPanel == 'custom') { cb.sendNotice('Skipping Panel #' + (currentPanelCycleIndex+1) + ': This is the placeholder for a customized room-specific panel.', panelPreviewer, appNoticeColor); } else { cb.drawPanel(); cb.sendNotice('Displaying Panel #' + (currentPanelCycleIndex+1) + ': panel ID is "' + currentPanel + '".', panelPreviewer, appNoticeColor); } botPanel = true; currentPanelCycleIndex ++; if (currentPanelCycleIndex >= backgroundArray.menu.length) { currentPanel = savedPanelBackground; backgroundImage = savedPanelFilename; botPanel = savedBotPanel; cb.drawPanel(); panelPreviewOn = false; cb.sendNotice('The panel background preview has finished, all panels displayed. It has now returned to the previously used panel.', panelPreviewer, appNoticeColor); } else { cb.setTimeout(cyclePanels, 10000); } } } // *********************************** Length of show ************************************** function clockTimeCal() { var timenow = new Date(); return timenow - ultraAppStartTime.getTime(); } function timeOnline() { var timeElapsed = clockTimeCal(); var clockMS = timeElapsed % 1000; var clockSeconds = ((timeElapsed - clockMS) % 60000); var clockMinutes = ((timeElapsed - clockSeconds - clockMS) % 3600000); var clockHours = (timeElapsed - clockMinutes - clockSeconds - clockMS); clockSeconds = clockSeconds / 1000; clockMinutes = clockMinutes / 60000; clockHours = clockHours / 3600000; if (clockHours > 0) { return clockHours + ' hour' + (clockHours > 1 ? 's' : '') + ' and ' + clockMinutes + ' minute' + (clockMinutes > 1 ? 's' : ''); } else if (clockMinutes > 0 && clockSeconds === 0) { return clockMinutes + ' minute' + (clockMinutes > 1 ? 's' : ''); } else if (clockMinutes > 0) { return clockMinutes + ' minute' + (clockMinutes > 1 ? 's' : '') + ' and ' + clockSeconds + ' second' + (clockSeconds > 1 ? 's' : ''); } else { return clockSeconds + ' second' + (clockSeconds > 1 ? 's' : ''); } } //******************** Goal Bar ************************** function goalBar(currentamt,totalamt) { var percentbar = 0; if (currentamt > 0 && totalamt > 0) { percentbar = Math.round(100*(currentamt/totalamt)); } var barstring = ''; var fullicon = '\u25C6'; var halficon = '\u25C8'; var emptyicon = '\u25C7'; var var1 = (percentbar - (percentbar % 10)) / 10; var var2 = (percentbar % 10) > 0 ? 1 : 0; var var3 = 10 - (var1 + var2); barstring += charRepeat(fullicon, var1); (var2 === 1 ? barstring += halficon : barstring += ''); barstring += charRepeat(emptyicon, var3) return barstring; } function charRepeat(repeatchar,repeatln) { var repeatstring = ''; for (var charindex = 1; charindex <= repeatln; charindex++) { repeatstring += repeatchar } return repeatstring; } //********** Record Tip and Track Goal Progress ************** function recordTip(tippedamount,tippedby,tippednote,tippedisfan,tippedisvip,tippedbydisp) { if (ticketSalesOpen) { checkFreeTickets(tippedby,cbjs.arrayContains(moderatorList.name,tippedby),tippedisfan,tippedisvip,'tip'); let chkin = false; if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (!ticketShowEnded && !cb.limitCam_userHasAccess(tippedby)) { if ((tippedisfan) && (!chkin)) { if (tippedamount >= cb.settings.ticketShowPriceFC) { addRmvTicket('addtip',tippedby,'fan',tippedamount,tippedbydisp); chkin = true; } else { checkCumulative(tippedby,tippedamount,cb.settings.ticketShowPriceFC,'fan','ticket',tippedbydisp); chkin = true; } } if ((tippedisvip) && (!chkin)) { if (tippedamount >= cb.settings.ticketShowPriceVIP) { addRmvTicket('addtip',tippedby,'vip',tippedamount,tippedbydisp); chkin = true; } else { checkCumulative(tippedby,tippedamount,cb.settings.ticketShowPriceVIP,'vip','ticket',tippedbydisp); chkin = true; } } if (!chkin) { if (tippedamount >= ticketPrice) { addRmvTicket('addtip',tippedby,'common',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketPrice,'common','ticket',tippedbydisp); } } } } } switch (whichApp) { case 'goals': { if (tippedby != 'bc') { currentAppTotalGoal += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { savedCurrentGoalTips = currentGoalTips; currentGoalTips -= tippedamount; if (currentGoalTips <= 0 || isNaN(currentGoalTips)) { if (progressiveAutoNext == 'Auto-start next') { exceedGoal(tippedamount); } else { if (!currentGoalMet) { goalComplete(); } } } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } cb.drawPanel(); break; } case 'goalcount': { let fgnum = currentGoalCountAmt + 1; gcnums = 0; if (tippedby != 'bc') { currentAppTotalCounter += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { savedCurrentGoalTips = currentGoalTips; currentGoalTips += tippedamount; if (currentGoalTips >= currentGoalTotal) { exceedGoal(tippedamount); } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } if (gcnums > 1) { cb.sendNotice('Goals #' + fgnum + ' through #' + (fgnum + gcnums - 1) + ' have been Completed!', '', goalCountBgColor, goalCountTextColor, 'bold'); } else if (gcnums == 1) { cb.sendNotice('Goal #' + fgnum + ' has been Completed!', '', goalCountBgColor, goalCountTextColor, 'bold'); } cb.drawPanel(); break; } case 'ticket': { if (tippedby != 'bc') { currentAppTotalTicket += tippedamount; currentSessionTotal += tippedamount; } /* moved to top, allow ticket sales when other *app* running if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (!ticketShowEnded && !ticketSalesEnded && !cb.limitCam_userHasAccess(tippedby)) { if (tippedisfan) { if (tippedamount >= cb.settings.ticketShowPriceFC) { addRmvTicket('addtip',tippedby,'fan',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,cb.settings.ticketShowPriceFC,'fan','ticket',tippedbydisp); } } else if (tippedisvip) { if (tippedamount >= cb.settings.ticketShowPriceVIP) { addRmvTicket('addtip',tippedby,'vip',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,cb.settings.ticketShowPriceVIP,'vip','ticket',tippedbydisp); } } else { if (tippedamount >= ticketPrice) { addRmvTicket('addtip',tippedby,'common',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketPrice,'common','ticket',tippedbydisp); } } } ticketShowGoalProgress(tippedamount); } */ if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (!ticketShowEnded && !ticketSalesEnded && !cb.limitCam_userHasAccess(tippedby)) { ticketShowGoalProgress(tippedamount); } } cb.drawPanel(); break; } case 'goalrace': { if (tippedby != 'bc') { currentAppTotalGoalRace += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { var originaltipamount = tippedamount; if (tippedisfan && cb.settings.goalraceBonusFC > 0) { tippedamount = parseInt(tippedamount * (1 + cb.settings.goalraceBonusFC/100)); } else if (tippedisvip && cb.settings.goalraceBonusVIP > 0) { tippedamount = parseInt(tippedamount * (1 + cb.settings.goalraceBonusVIP/100)); } let grd1L = goalraceDesc1.length; let grd2L = goalraceDesc2.length; if (tippednote.substring(0,grd1L) == goalraceDesc1) { currentGoalRace1Tips += tippedamount; if (currentGoalRace1Tips >= goalraceAmount1) { goalRaceWinner = goalraceDesc1; goalComplete(); } } else if (tippednote.substring(0,grd2L) == goalraceDesc2) { currentGoalRace2Tips += tippedamount; if (currentGoalRace2Tips >= goalraceAmount2) { goalRaceWinner = goalraceDesc2; goalComplete(); } } else { raceAddUnclaimed(tippedby,originaltipamount); } } cb.drawPanel(); break; } case 'spank': { checkSpankMenu(tippedamount,tippedby,tippedbydisp); if (tippedby != 'bc') { currentAppTotalSpanks += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { savedCurrentGoalTips = currentGoalTips; currentGoalTips += tippedamount; if (currentGoalTips >= currentGoalTotal) { exceedGoal(tippedamount); } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } cb.drawPanel(); break; } case 'autoreset': { if (tippedby != 'bc') { currentAppTotalAutoreset += tippedamount; currentSessionTotal += tippedamount; resetLastTip = tippedbydisp + ' (' + tippedamount + ')'; } if (!finalGoalMet) { savedCurrentGoalTips = currentGoalTips; currentGoalTips += tippedamount; if (currentGoalTips >= currentGoalTotal) { exceedGoal(tippedamount); } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } cb.drawPanel(); break; } case 'none': { if (tippedby != 'bc') { currentAppTotalNone += tippedamount; currentSessionTotal += tippedamount; } cb.drawPanel(); break; } } } function raceAddUnclaimed(tippedbyunc,origtipamount) { if (cb.settings.goalraceAllowClaim == 'Yes') { if (!cbjs.arrayContains(raceUnclaimedVotes.name, tippedbyunc)) { raceUnclaimedVotes.name.push(tippedbyunc); raceUnclaimedVotes.amount.push(origtipamount); } else { var raceaddidx = raceUnclaimedVotes.name.indexOf(tippedbyunc); raceUnclaimedVotes.amount[raceaddidx] += origtipamount; } cb.sendNotice(tippedbyunc + ', your tip was not recorded against a Goal Race choice, you can add your unclaimed tips to one of the goals with the command "/addrace1" for goal #1 or "/addrace2" for goal #2', tippedbyunc, goalraceBgColor, goalraceTextColor, 'bold'); } } function checkCumulative(tippedbycum,tippedamountcum,cumTktPrice,usertype,saletype,tippedbycumdisp) { if (cb.settings.ticketShowCumulative == 'Yes') { if (!cbjs.arrayContains(ticketCumulative.name, tippedbycum)) { ticketCumulative.name.push(tippedbycum); ticketCumulative.amount.push(tippedamountcum); } else { var chkcumidx = ticketCumulative.name.indexOf(tippedbycum); if (ticketCumulative.amount[chkcumidx] + tippedamountcum >= cumTktPrice) { if (saletype == 'ticket') { addRmvTicket('addtip',tippedbycum,usertype,tippedamountcum,tippedbycumdisp); } ticketCumulative.amount[chkcumidx] = 0; } else { ticketCumulative.amount[chkcumidx] += tippedamountcum; } } } } function exceedGoal(tippedamountexc) { var nextgif = ''; var excesstip = 0; switch (whichApp) { case 'ticket': { break; } case 'goals': { excesstip = tippedamountexc - savedCurrentGoalTips; goalComplete(); if (!finalGoalMet) { if (currentGoal == progGoalArray.desc.length && (minTippersToggle == 'Yes' && tippersNeeded > 0)) { changeRoomSubject(); } else { nextGoal(); currentGoalTips = currentGoalTotal - excesstip; if (currentGoalTips <= 0) { savedCurrentGoalTips = 0; exceedGoal(0 - currentGoalTips); } } } break; } case 'goalcount': { // cb.sendNotice('Goal #' + (currentGoalCountAmt + 1) + ' has been Completed!', '', goalCountBgColor, goalCountTextColor, 'bold'); gcnums++; excesstip = tippedamountexc - (currentGoalTotal - savedCurrentGoalTips); if ((currentGoalCountAmt + 1) >= goalCounterArray.amt[currentGoalLevel-1]) { goalComplete(); } if (!finalGoalMet) { nextGoal(); currentGoalTips = excesstip; if (currentGoalTips >= currentGoalTotal) { savedCurrentGoalTips = 0; exceedGoal(currentGoalTips); } } break; } case 'spank': { excesstip = tippedamountexc - (currentGoalTotal - savedCurrentGoalTips); goalComplete(); if (!finalGoalMet) { nextGoal(); currentGoalTips = excesstip; if (currentGoalTips >= currentGoalTotal) { savedCurrentGoalTips = 0; exceedGoal(currentGoalTips); } } break; } case 'autoreset': { excesstip = tippedamountexc - (currentGoalTotal - savedCurrentGoalTips); goalComplete(); if (!finalGoalMet) { nextGoal(); currentGoalTips = excesstip; if (currentGoalTips >= currentGoalTotal) { savedCurrentGoalTips = 0; exceedGoal(currentGoalTips); } } break; } } } function goalComplete() { switch (whichApp) { case 'ticket': { break; } case 'goals': { currentGoalMet = true; if (progGoalArray.desc.length == 1) { if (minTippersToggle == 'Yes' && tippersNeeded > 0) { cb.sendNotice(borderAllNoticesShort + '\n The Tokens Goal has been met... ' + tippersNeeded + ' additional tipper' + (tippersNeeded == 1 ? '' : 's') + ' needed to complete the goal!!! \n' + borderAllNoticesShort, '', progGoalBgColor, progGoalTextColor, 'bold'); currentGoalMet = false; } else { cb.sendNotice(borderAllNoticesShort + '\n :CGGoal15 The Goal has been met!! :CGGoal15 \n' + borderAllNoticesShort, '', progGoalBgColor, progGoalTextColor, 'bold'); updateFinalGoalMet(); } } else if (currentGoal < progGoalArray.desc.length || progressiveAutoNext == 'Select next goal from list') { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Goal #' + currentGoal + ' (' + progGoalArray.desc[currentGoal-1] + ') has been met!! :CGGoal15 \n' + borderAllNotices, '', progGoalBgColor, progGoalTextColor, 'bold'); if (progressiveAutoNext == 'Manually start next') { cb.sendNotice('Note to mods: The goal has been completed and per configuration the app is awaiting manual advance to the next goal using the "/next" command', '', appNoticeColor, '', '', 'red'); cb.sendNotice('The goal has been completed and per configuration the app is awaiting manual advance using the "/next" command', BC, appNoticeColor, '', ''); } else if (progressiveAutoNext == 'Select next goal from list') { cb.sendNotice('Note to mods: The current goal (#' + currentGoal + ') has been completed.\nYou can now choose the next goal from the below list by using the command /goal X, where X is the goal number. \nYou can select a new goal or re-use one that was already used. \nOr you can use the /next command to do the next goal in order, goal #' + (currentGoal+1) + '.' + listGoalsForSelect(), '', appNoticeColor, '', '', 'red'); cb.sendNotice('The current goal (#' + currentGoal + ') has been completed.\nYou can now choose your next goal from the below list by using the command /goal X, where X is the goal number. \nYou can select a new goal or re-use one you\'ve already used. \nOr you can use the /next command to do the next goal in order, goal #' + (currentGoal+1) + '.' + listGoalsForSelect(), BC, appNoticeColor, '', ''); } } else if (currentGoal == progGoalArray.desc.length) { if (minTippersToggle == 'Yes' && tippersNeeded > 0) { cb.sendNotice(borderAllNoticesShort + '\n All Token Goals have been met... ' + tippersNeeded + ' additional tipper' + (tippersNeeded == 1 ? '' : 's') + ' needed to complete the goal!!! \n' + borderAllNoticesShort, '', progGoalBgColor, progGoalTextColor, 'bold'); currentGoalMet = false; } else { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Final Goal (' + progGoalArray.desc[currentGoal-1] + ') has been met!! :CGGoal15 \n' + borderAllNotices, '', progGoalBgColor, progGoalTextColor, 'bold'); updateFinalGoalMet(); } } break; } case 'goalcount': { if (goalCounterArray.desc.length == 1) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Goal Counter Prize Level has been met!! :CGGoal15 \n' + borderAllNotices, '', goalCountBgColor, goalCountTextColor, 'bold'); } else if ((currentGoalLevel) < goalCounterArray.desc.length) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Goal Counter Prize Level #' + currentGoalLevel + ' (' + goalCounterArray.desc[currentGoalLevel-1] + ') has been met!! :CGGoal15 \n' + borderAllNotices, '', goalCountBgColor, goalCountTextColor, 'bold'); } else if ((currentGoalLevel) == goalCounterArray.desc.length) { cb.sendNotice(borderAllNoticesShort + '\n :CGGoal15 Final Prize Level (' + goalCounterArray.desc[currentGoalLevel-1] + ') has been met!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNoticesShort, '', goalCountBgColor, goalCountTextColor, 'bold'); updateFinalGoalMet(); } break; } case 'goalrace': { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Goal Race Winner : ' + goalRaceWinner + ' !! :CGGoal15 \n' + borderAllNotices, '', goalraceBgColor, goalraceTextColor, 'bold'); cb.sendNotice('The Goal Race has finished, you can use the command /restartrace to play the same race again (you can change the goal descriptions with the command /setgoal1 or /setgoal2), or change to a different App Feature.', '', appNoticeColor, '', 'bold'); updateFinalGoalMet(); break; } case 'spank': { if (spankGoalArray.desc.length == 1) { cb.sendNotice(borderAllNoticesShort + '\n :CGGoal15 The Spank Goal has been met!! :CGGoal15 \n' + borderAllNoticesShort, '', spankBgColor, spankTextColor, 'bold'); updateFinalGoalMet(); } else if ((currentGoal) < spankGoalArray.desc.length) { cb.sendNotice(borderAllNoticesShort + '\n :CGGoal15 Spank Goal #' + currentGoal + ' (' + spankGoalArray.desc[currentGoal-1] + ') has been met!! :CGGoal15 \n' + borderAllNoticesShort, '', spankBgColor, spankTextColor, 'bold'); } else if ((currentGoal) == spankGoalArray.desc.length) { cb.sendNotice(borderAllNoticesShort + '\n :CGGoal15 Final Spank Goal (' + spankGoalArray.desc[currentGoal-1] + ') has been met!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNoticesShort, '', spankBgColor, spankTextColor, 'bold'); updateFinalGoalMet(); } spankTotals(); break; } case 'autoreset': { if (autoresetEndAfter > 0 && currentGoal >= autoresetEndAfter) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Final Goal (' + autoresetFinalGoal + ') has been met!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, 'bold'); updateFinalGoalMet(); } else { if (resetShowCount) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Goal has been met ' + currentGoal + ' time' + (currentGoal == 1 ? '' : 's') + '!! :CGGoal15 \n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, 'bold'); } else { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Goal has been met!!! :CGGoal15 \n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, 'bold'); } } break; } } } function updateFinalGoalMet() { finalGoalMet = true; ticketCountdownRunning = false; if (cb.settings.autoStartTicket == 'Yes') { if (!ticketSalesOpen) { ticketCountdownRunning = true; cb.setTimeout(autostartTicketShow, 30000); cb.sendNotice('Ticket Sales will start automatically in 30 seconds.', '', appNoticeColor, '', 'bold'); cb.sendNotice(botName + 'Per configuration, the last goal is completed and ticket sales will be automatically started in 30 seconds.\nIf you do not want to start ticket sales now, before the 30 seconds are up, type: /cancelticket \nYou can start the show later when you are ready by typing: /prepticket or /chgapp ticket', BC, appNoticeColor); cb.sendNotice(botName + 'Per configuration, the last goal is completed and ticket sales will be automatically started in 30 seconds.\nIf the broadcaster does not want to start ticket sales now, before the 30 seconds are up, type: /cancelticket \nYou can start the show later when you are ready by typing: /prepticket or /chgapp ticket', BC, appNoticeColor, '', 'bold', 'red'); } else { setAppFeature('ticket', BC); } } else { changeRoomSubject(); } } function autostartTicketShow() { if (ticketCountdownRunning) { ticketCountdownRunning = false; setAppFeature('ticket', BC); } } function skipGoal() { switch (whichApp) { case 'goals': { goalComplete(); if (!finalGoalMet) { nextGoal(); } break; } case 'goalcount': { cb.sendNotice('Goal #' + (currentGoalCountAmt + 1) + ' has been Completed!', '', goalCountBgColor, goalCountTextColor, 'bold'); if ((currentGoalCountAmt + 1) >= goalCounterArray.amt[currentGoalLevel-1]) { goalComplete(); } if (!finalGoalMet) { nextGoal(); currentGoalTips = 0; } break; } case 'spank': { currentGoalTips = 0; goalComplete(); if (!finalGoalMet) { nextGoal(); } break; } case 'autoreset': { currentGoalTips = 0; goalComplete(); if (!finalGoalMet) { nextGoal(); } break; } } cb.drawPanel(); } function advanceGoalLevel() { var nextgifadv = ''; switch (whichApp) { case 'goalcount': { currentGoalCountAmt = goalCounterArray.amt[currentGoalLevel-1]; goalComplete(); currentGoalLevel++; currentGoalDesc = goalCounterArray.desc[currentGoalLevel-1]; if ((currentGoalLevel) <= goalCounterArray.desc.length) { changeRoomSubject(); } currentGoalTips = 0; break; } } cb.drawPanel(); } function getTopTipper() { var swapped, temp1, temp2; do { swapped = false; for (var i = 0; i < tipCountArray.amount.length ; i++) { if (tipCountArray.amount[i] < tipCountArray.amount[i + 1]) { temp1 = tipCountArray.amount[i]; temp2 = tipCountArray.name[i]; tipCountArray.amount[i] = tipCountArray.amount[i + 1]; tipCountArray.amount[i + 1] = temp1; tipCountArray.name[i] = tipCountArray.name[i + 1]; tipCountArray.name[i + 1] = temp2; swapped = true; } } } while (swapped); return tipCountArray.name[0]; } function nextGoal() { switch (whichApp) { case 'ticket': { break; } case 'goals': { currentGoal++; currentGoalDesc = progGoalArray.desc[currentGoal-1]; currentGoalTotal = progGoalArray.amt[currentGoal-1]; currentGoalTips = currentGoalTotal; currentGoalMet = false; changeRoomSubject(); break; } case 'goalcount': { currentGoalCountAmt++; if (currentGoalCountAmt >= goalCounterArray.amt[currentGoalLevel-1]) { currentGoalLevel++; currentGoalDesc = goalCounterArray.desc[currentGoalLevel-1]; changeRoomSubject(); } break; } case 'spank': { currentGoal++; currentGoalDesc = spankGoalArray.desc[currentGoal-1]; currentGoalTotal = spankGoalArray.amt[currentGoal-1]; changeRoomSubject(); break; } case 'autoreset': { currentGoal++; break; } } } function buildSubject(currenttext,addition) { if (currenttext != '') { return currenttext + ' -- ' + addition; } else { return addition; } } function changeRoomSubject() { var addsubject = ''; var newsubject = ''; var newtokens = 0; if (cb.settings.genericRoomSubjectRemGoal == 'Yes, at beginning') { if (whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'spank' || whichApp == 'autoreset') { if (whichApp == 'goals') { newtokens = (currentGoalTips > 0 ? currentGoalTips : 0); } else { newtokens = (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)); } newsubject += ' [' + newtokens + ' token' + (newtokens == 1 ? '' : 's') + ' to goal]'; } } if (genericRoomSubjectPosn == 'Beginning' && genericRoomSubjectSfx) { addsubject = genericRoomSubjectSfx; newsubject = buildSubject(newsubject,addsubject); } switch (whichApp) { case 'goals': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { if (progGoalArray.desc.length == 1) { if (currentGoalTips <= 0) { addsubject = 'Current Goal: ' + currentGoalDesc; if (minTippersToggle == 'Yes' && tippersNeeded > 0) { addsubject = addsubject + ', only ' + tippersNeeded + ' more new tippers needed for goal'; } } else { addsubject = 'Current Goal: ' + currentGoalDesc + ' once countdown reaches zero'; if (minTippersToggle == 'Yes' && tippersNeeded > 0) { addsubject = addsubject + ' and minimum number of tippers are met'; } } newsubject = buildSubject(newsubject,addsubject); } else { addsubject = 'Current Goal: ' + currentGoalDesc + ' once countdown reaches zero'; if (minTippersToggle == 'Yes' && tippersNeeded > 0) { addsubject = addsubject + ' and minimum number of tippers are met'; } newsubject = buildSubject(newsubject,addsubject); } if (genericRoomSubjectType == 'All') { if (progGoalArray.desc.length >= (currentGoal + 1)) { newsubject += ' -- Next Goal: ' + progGoalArray.desc[currentGoal]; } else if (progGoalArray.desc.length > 1) { newsubject += ' -- This is the Last Goal! '; } } } else { addsubject = 'All Goals Have Been Completed!!! '; newsubject = buildSubject(newsubject,addsubject); } } if (goalSubjectText) { newsubject = buildSubject(newsubject,goalSubjectText); } if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } if (cb.settings.genericRoomSubjectRemGoal == 'Yes, at end') { newtokens = (currentGoalTips > 0 ? currentGoalTips : 0); newsubject += ' [' + newtokens + ' token' + (newtokens == 1 ? '' : 's') + (tippersNeeded < 1 ? '' : ' and ' + tippersNeeded + ' tipper' + (tippersNeeded == 1 ? '' : 's') + ' needed') + ' to goal]'; } newsubject = (showStage == 'earlysales' ? checkEarlyTickets(newsubject) : newsubject); cb.changeRoomSubject(newsubject); break; } case 'goalcount': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { var startlevel = currentGoalLevel - 1; var numremgoals = goalCounterArray.desc.length - startlevel; var endlevel = startlevel + 3; if (endlevel > goalCounterArray.desc.length) { endlevel = goalCounterArray.desc.length; } if (genericRoomSubjectType == 'All') { if (numremgoals == 1) { addsubject = 'Final Prize Level at: '; newsubject = buildSubject(newsubject,addsubject); } else if (numremgoals == 2) { addsubject = 'Final 2 Prize Levels at: '; newsubject = buildSubject(newsubject,addsubject); } else if (numremgoals == 3) { addsubject = 'Final 3 Prize Levels at: '; newsubject = buildSubject(newsubject,addsubject); } else if (numremgoals > 3) { addsubject = 'Next 3 Prize Levels at: '; newsubject = buildSubject(newsubject,addsubject); } for (let lvlidx = startlevel; lvlidx < endlevel; lvlidx++) { if (goalCounterArray.desc[lvlidx] != '' && goalCounterArray.desc[lvlidx] != null) { newsubject += (lvlidx > startlevel ? ', ' : '') + goalCounterArray.amt[lvlidx] + (goalCounterArray.amt[lvlidx] > 1 ? ' goals (' : ' goal (') + goalCounterArray.desc[lvlidx] + ')'; } else { break; } } } else { if (numremgoals == 1) { addsubject = 'Final Prize Level at: '; newsubject = buildSubject(newsubject,addsubject); } else { addsubject = 'Next Prize Level at: '; newsubject = buildSubject(newsubject,addsubject); } newsubject += goalCounterArray.amt[startlevel] + (goalCounterArray.amt[startlevel] > 1 ? ' goals (' : ' goal (') + goalCounterArray.desc[startlevel] + ')'; } } else { addsubject = 'All Goals Have Been Completed!!! '; newsubject = buildSubject(newsubject,addsubject); } } if (counterSubjectText) { newsubject = buildSubject(newsubject,counterSubjectText); } if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } if (cb.settings.genericRoomSubjectRemGoal == 'Yes, at end') { newtokens = (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)); newsubject += ' [' + newtokens + ' token' + (newtokens == 1 ? '' : 's') + ' to goal]'; } newsubject = (showStage == 'earlysales' ? checkEarlyTickets(newsubject) : newsubject); cb.changeRoomSubject(newsubject); break; } case 'ticket': { if (cb.settings.ticketShowFanAppreciation == 'Yes') { if (showStage == 'ticketsales') { newsubject += 'FANCLUB APPRECIATION SHOW STARTING SOON! JOIN FANCLUB TO SEE! '; } else if (showStage == 'ticketshow' || showStage == 'showwarn' || showStage == 'showfinale') { newsubject += 'FANCLUB APPRECIATION SHOW IN PROGRESS! JOIN FANCLUB TO SEE! '; } else if (showStage == 'aftershow') { newsubject += 'FANCLUB APPRECIATION SHOW HAS ENDED. THANK YOU FANS! '; } else { newsubject += 'FANCLUB APPRECIATION SHOW '; } } else { if (showStage == 'ticketsales') { newsubject += 'TICKET SHOW SALES [' + ticketPrice + ' tokens]: '; } else if (showStage == 'ticketshow') { newsubject += 'TICKET SHOW IN PROGRESS [' + ticketPrice + ' tokens]: '; } else if (showStage == 'showwarn') { newsubject += 'TICKET SHOW ENDING SOON [' + ticketPrice + ' tokens]: '; } else if (showStage == 'showfinale') { newsubject += 'TICKET SHOW SALES ENDED -- DO NOT BUY A TICKET!: '; } else if (showStage == 'aftershow') { newsubject += 'TICKET SHOW HAS ENDED: '; } else { newsubject += 'TICKET SHOW '; } } newsubject += ticketSubjectText; if (genericRoomSubjectPosn == 'End') { newsubject += ' ' + genericRoomSubjectSfx; } cb.changeRoomSubject(newsubject); break; } case 'goalrace': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { addsubject = 'Goal Race! Tip to vote for [' + goalraceDesc1 + '] vs [' + goalraceDesc2 + ']'; newsubject = buildSubject(newsubject,addsubject); } else { addsubject = 'The Goal Race has been Completed! ' + goalRaceWinner + ' has won the race!!!'; newsubject = buildSubject(newsubject,addsubject); } } if (goalraceSubjectText) { newsubject = buildSubject(newsubject,goalraceSubjectText); } if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } newsubject = (showStage == 'earlysales' ? checkEarlyTickets(newsubject) : newsubject); cb.changeRoomSubject(newsubject); break; } case 'spank': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { addsubject = 'Current Goal: ' + currentGoalDesc + ' at ' + currentGoalTotal + ' tokens'; newsubject = buildSubject(newsubject,addsubject); if (genericRoomSubjectType == 'All') { if (spankGoalArray.desc.length >= (currentGoal + 1)) { addsubject = 'Next Goal: ' + spankGoalArray.desc[currentGoal]; newsubject = buildSubject(newsubject,addsubject); } else if (spankGoalArray.desc.length > 1) { addsubject = 'This is the Last Goal!'; newsubject = buildSubject(newsubject,addsubject); } } } else { addsubject = 'All Spank Goals Have Been Completed!!!'; newsubject = buildSubject(newsubject,addsubject); } } if (spankSubjectText) { newsubject = buildSubject(newsubject,spankSubjectText); } if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } if (cb.settings.genericRoomSubjectRemGoal == 'Yes, at end') { newtokens = (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)); newsubject += ' [' + newtokens + ' token' + (newtokens == 1 ? '' : 's') + ' to goal]'; } newsubject = (showStage == 'earlysales' ? checkEarlyTickets(newsubject) : newsubject); cb.changeRoomSubject(newsubject); break; } case 'autoreset': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { addsubject = 'At Goal: ' + currentGoalDesc + ' [every ' + currentGoalTotal + ' tokens]'; newsubject = buildSubject(newsubject,addsubject); if (genericRoomSubjectType == 'All') { if (autoresetEndAfter > 0) { addsubject = 'The goal will repeat ' + autoresetEndAfter + ' times'; newsubject = buildSubject(newsubject,addsubject); if (autoresetFinalGoal) { addsubject = 'Final Goal Prize: "' + autoresetFinalGoal + '"'; newsubject = buildSubject(newsubject,addsubject); } } } } else { addsubject = 'All Goals Have Been Completed!!! Final Goal Prize: ' + autoresetFinalGoal; newsubject = buildSubject(newsubject,addsubject); } } if (autoresetSubjectSfx) { newsubject = buildSubject(newsubject,autoresetSubjectSfx); } if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } if (cb.settings.genericRoomSubjectRemGoal == 'Yes, at end') { newtokens = (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)); newsubject += ' [' + newtokens + ' token' + (newtokens == 1 ? '' : 's') + ' to goal]'; } newsubject = (showStage == 'earlysales' ? checkEarlyTickets(newsubject) : newsubject); cb.changeRoomSubject(newsubject); break; } case 'none': { if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } if (newsubject == '' || newsubject == ' ' || newsubject == null) { newsubject = 'Welcome to ' + bcText + '\'s room'; } newsubject = (showStage == 'earlysales' ? checkEarlyTickets(newsubject) : newsubject); cb.changeRoomSubject(newsubject); break; } } } function listGoalsForSelect() { var listoutstring = ''; var listselindex = 0; while (progGoalArray.amt[listselindex] > 0 && progGoalArray.desc[listselindex] != null && progGoalArray.desc[listselindex] != '') { listoutstring += '\nGoal #' + (listselindex+1) + ' : ' + progGoalArray.desc[listselindex] + ' (' + progGoalArray.amt[listselindex] + ' tokens)'; listselindex++; } if (listselindex > 0) { return '\n' + borderAllNoticesShort + listoutstring + '\n' + borderAllNoticesShort; } else { return 'There are no goals configured'; } } function goalNotices(lggroup,lgreqby,listmode,lgcallmode) { var lgoutstring = ''; var listgoalssendto = ''; var listindex = 0; switch (whichApp) { case 'goals': { lgoutstring = borderGoalsTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nAll Goals have been completed!'; } else { lgoutstring += '\nYou can tip to help reach the current goal.'; lgoutstring += wordWrap('\nCurrent Goal: ' + currentGoalDesc); if (currentGoalTips > 0) { lgoutstring += wordWrap( ', once countdown reaches zero (' + (currentGoalTips < 0 ? 0 : currentGoalTips) + ' remaining)'); } if (progGoalArray.desc.length <= 1) { lgoutstring += '\nThere is a single goal for the show.'; } else { lgoutstring += '\nThere are ' + totalProgGoals + ' total goals for the show.'; if (progressiveAutoNext == 'Select next goal from list') { lgoutstring += '\nThe broadcaster is choosing the next goal after each goal finishes.'; lgoutstring += '\nTherefore the goals may not be completed in order.'; listmode = 'a'; } } if (minTippersGoal > 1) { if (tippersNeeded <= 0 || isNaN(tippersNeeded)) { lgoutstring += '\nThe minimum of tippers needed to reach goal has already been met!'; } else { lgoutstring += '\nThere is also a minimum ' + minTippersGoal + ' tippers needed to reach goal, only ' + tippersNeeded + ' additional tippers needed.'; } } } if (((cb.settings.progressiveIncludeGoals == 'Yes' && !finalGoalMet) || lgcallmode == 'cmd') && progGoalArray.desc.length > 0 && currentGoal > 0) { if (listmode == 'r') { listindex = (currentGoal-1); } lgoutstring += '\n' + (listmode == 'a' ? 'Full Goal List: ' : 'Remaining Goal List: '); while (progGoalArray.amt[listindex] > 0 && progGoalArray.desc[listindex] != null && progGoalArray.desc[listindex] != '') { lgoutstring += wordWrap('\nGoal #' + (listindex+1) + ' : ' + progGoalArray.desc[listindex] + ' (' + progGoalArray.amt[listindex] + ' tokens)'); listindex++; } } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, progGoalBgColor, progGoalTextColor, 'bold'); break; } case 'goalcount': { lgoutstring = borderGoalCountTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nAll Goals have been completed!'; } else { lgoutstring += '\nYou can tip to help reach the next goal level.'; lgoutstring += '\nThere are prizes at specific goal levels (' + (cbjs.arrayJoin(goalCounterArray.amt, 'g, ')) + 'g).'; } if (((cb.settings.goalcounterIncludeGoals == 'Yes' && !finalGoalMet) || lgcallmode == 'cmd') && goalCounterArray.desc.length > 0 && currentGoalLevel > 0) { if (listmode == 'r') { listindex = (currentGoalLevel-1); } lgoutstring += '\n' + (listmode == 'a' ? 'Full Goal Level List: ' : 'Remaining Goal Level List: '); while (goalCounterArray.amt[listindex] > 0 && goalCounterArray.desc[listindex] != null && goalCounterArray.desc[listindex] != '') { lgoutstring += '\nPrize Level #' + (listindex+1) + ' : ' + goalCounterArray.desc[listindex] + ' (' + goalCounterArray.amt[listindex] + ' goals)'; listindex++; } } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, goalCountBgColor, goalCountTextColor, 'bold'); break; } case 'goalrace': { lgoutstring = borderGoalRaceTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nRace Completed! Winning Goal: ' + goalRaceWinner; } else { lgoutstring += '\nIn the Goal Race, you can tip to vote for one of the goals.'; lgoutstring += '\nWhen tipping, cast your vote using the selection box in the tip window.'; lgoutstring += '\nGoal Progress:'; lgoutstring += '\nGoal #1 Progress (' + goalraceDesc1 + '): ' + (currentGoalRace1Tips > goalraceAmount1 ? goalraceAmount1 : currentGoalRace1Tips) + '/' + goalraceAmount1 + ' (' + Math.floor((currentGoalRace1Tips > goalraceAmount1 ? goalraceAmount1 : currentGoalRace1Tips)/goalraceAmount1*100) + '%)'; lgoutstring += '\nGoal #2 Progress (' + goalraceDesc2 + '): ' + (currentGoalRace2Tips > goalraceAmount2 ? goalraceAmount2 : currentGoalRace2Tips) + '/' + goalraceAmount2 + ' (' + Math.floor((currentGoalRace2Tips > goalraceAmount2 ? goalraceAmount2 : currentGoalRace2Tips)/goalraceAmount2*100) + '%)'; /* lgoutstring += '\nYou can switch between voting and sending tip notes by using commands:'; lgoutstring += '\n- To change your setting to allow tipnotes when tipping, type: /tipnoteon'; lgoutstring += '\n- To change your setting to allow voting when tipping, type: /tipnoteoff';*/ if (cb.settings.goalraceAllowClaim == 'Yes') { lgoutstring += '\nIf you don\'t choose goal when tipping, you can still claim the votes later.'; lgoutstring += '\nUse the commands "/addrace1" or "/addrace2" to assign your tips to a goal.'; } } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, goalraceBgColor, goalraceTextColor, 'bold'); break; } case 'spank': { lgoutstring = borderSpankTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nAll Spank-a-thon Goals have been completed!'; } else { lgoutstring += '\nThe "Spank-a-thon" is active, you can tip for the types of spanks listed below.'; lgoutstring += '\nThe tips will accumulate in the "spank bank" until the goal is reached.'; lgoutstring += '\nAt goal, the accumulated spanking begins (or at ' + bcText + '\'s discretion).'; lgoutstring += '\n' + spankMenu(); } if (((cb.settings.spankIncludeGoals == 'Yes' && !finalGoalMet) || lgcallmode == 'cmd') && spankGoalArray.desc.length > 0 && currentGoal > 0) { if (listmode == 'r') { listindex = (currentGoal-1); } lgoutstring += '\n' + (listmode == 'a' ? 'Full Goal List: ' : 'Remaining Goal List: '); while (spankGoalArray.amt[listindex] > 0 && spankGoalArray.desc[listindex] != null && spankGoalArray.desc[listindex] != '') { lgoutstring += '\nGoal #' + (listindex+1) + ' : ' + spankGoalArray.desc[listindex] + ' (' + spankGoalArray.amt[listindex] + ' tokens)'; listindex++; } } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, spankBgColor, spankTextColor, 'bold'); break; } case 'autoreset': { lgoutstring = borderAutoresetTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nAll Auto-reset Goals have been completed!'; } else { lgoutstring += '\nThe Auto-Reset Goal has a single repeating goal every ' + autoresetGoalAmount + ' tokens.'; lgoutstring += '\nGoal Description: ' + autoresetEachGoal; if (autoresetEndAfter > 0) { lgoutstring += '\nShow Ends after ' + autoresetEndAfter + ' goals.'; if (autoresetFinalGoal) { lgoutstring += '\nFinal Goal Prize: ' + autoresetFinalGoal; } } else { lgoutstring += '\nGoals will keep repeating, there is no goal count limit.'; } } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, autoresetBgColor, autoresetTextColor, 'bold'); break; } } } // *********************************** Progressive Goal Functions ************************************** function setProgressiveGoalsToggle(setpgoption, setpgby) { var setpgname = setpgby; if (setpgby == BC) { setpgname = bcText; } if (setpgoption == 'on') { whichApp = 'goals'; if (savedProgGoals) { restoreProgGoals(); } else { genericInit(); initProgGoal(); } changeRoomSubject(); initProgGoalsNoticeTimer(); if (progGoalArray.desc.length <= 1) { cb.sendNotice(borderAllNotices + '\n' + setpgname + ' has started the Progressive Goal Feature.\nThere is a single goal for the show' + (minTippersToggle == 'Yes' ? ' and ' + minTippersGoal + 'tippers needed. \n' : '. \n') + borderAllNotices, '', progGoalBgColor, progGoalTextColor, 'bold'); } else { cb.sendNotice(borderAllNotices + '\n' + setpgname + ' has started the Progressive Goal Feature.\nThere are ' + totalProgGoals + ' goals in the current show' + (minTippersToggle == 'Yes' ? ' and ' + minTippersGoal + ' tippers needed.\n' : '.\n') + borderAllNotices, '', progGoalBgColor, progGoalTextColor, 'bold'); } } else if (setpgoption == 'off') { saveProgGoals(); cb.sendNotice(borderAllNotices + '\n' + setpgname + ' has suspended the Progressive Goal Feature.\n' + borderAllNotices, '', progGoalBgColor, progGoalTextColor, 'bold'); } } function initProgGoal() { totalProgGoals = progGoalArray.desc.length; currentGoalDesc = progGoalArray.desc[currentGoal-1]; currentGoalTotal = progGoalArray.amt[currentGoal-1]; currentGoalTips = currentGoalTotal; if (genericRoomSubjectType == 'All' && progressiveAutoNext == 'Select next goal from list') { genericRoomSubjectType = 'All - Current goal only'; } } function saveProgGoals() { pgSave_finalGoalMet = finalGoalMet; pgSave_currentGoalTips = currentGoalTips; pgSave_currentGoal = currentGoal; pgSave_currentGoalDesc = currentGoalDesc; pgSave_currentGoalTotal = currentGoalTotal; savedProgGoals = true; } function restoreProgGoals() { finalGoalMet = pgSave_finalGoalMet; currentGoalTips = pgSave_currentGoalTips; currentGoal = pgSave_currentGoal; currentGoalDesc = pgSave_currentGoalDesc; currentGoalTotal = pgSave_currentGoalTotal; savedProgGoals = false; } function progGoalColors(pgcby) { if (cb.settings.progressiveGoalTextColor == 'Custom') { progGoalTextColor = setTextColor(cb.settings.progressiveGoalCustomTextColor,'Progressive Goals',pgcby); } else { progGoalTextColor = setTextColor(cb.settings.progressiveGoalTextColor,'Progressive Goals',pgcby); } if (cb.settings.progressiveGoalBgColor == 'Custom') { progGoalBgColor = setBgColor(cb.settings.progressiveGoalCustomBgColor,'Progressive Goals',pgcby); } else { progGoalBgColor = setBgColor(cb.settings.progressiveGoalBgColor,'Progressive Goals',pgcby); } } function initProgGoalsNoticeTimer() { if (progressiveAutoNext == 'Select next goal from list' && progGoalNoticeInt > 0) { cb.sendNotice(botName + 'Progressive Goal advance mode is set to "Select next goal from list", the recurring notice for remaining goals is being disabled.', BC, appNoticeColor); progGoalNoticeInt = 0; } if (progGoalNoticeInt > 0) { cb.setTimeout(progGoalsNotice, progGoalNoticeInt); } } function progGoalsNotice() { if (whichApp == 'goals' && !finalGoalMet) { goalNotices('timer','','r','timer'); cb.setTimeout(progGoalsNotice, progGoalNoticeInt); } } function restartGoal() { currentGoal--; finalGoalMet = false; currentGoalMet = false; nextGoal(); cb.drawPanel(); } // *********************************** Tip Goal Counter Functions ************************************** function setGoalCounterToggle(setgcoption, setgcby) { var setgcname = setgcby; if (setgcby == BC) { setgcname = bcText; } if (setgcoption == 'on') { whichApp = 'goalcount'; if (savedGoalCount) { restoreGoalCount(); } else { genericInit(); initGoalCounter(); } changeRoomSubject(); if (goalCountNoticeInt > 0) { cb.setTimeout(goalCountNotice, goalCountNoticeInt); } cb.sendNotice(borderAllNotices + '\n' + setgcname + ' has started the Tip Goal Counter Feature with\nprizes at specific goal levels (' + (cbjs.arrayJoin(goalCounterArray.amt, 'g, ')) + 'g).\n' + borderAllNotices, '', goalCountBgColor, goalCountTextColor, 'bold'); } else if (setgcoption == 'off') { saveGoalCount(); cb.sendNotice(borderAllNotices + '\n' + setgcname + ' has suspended the Tip Goal Counter Feature.\n' + borderAllNotices, '', goalCountBgColor, goalCountTextColor, 'bold'); } } function initGoalCounter() { currentGoalDesc = goalCounterArray.desc[currentGoalLevel-1]; currentGoalTotal = cb.settings.goalcounterPerGoalAmount; } function saveGoalCount() { gcSave_finalGoalMet = finalGoalMet; gcSave_currentGoalTips = currentGoalTips; gcSave_currentGoalDesc = currentGoalDesc; gcSave_currentGoalTotal = currentGoalTotal; gcSave_currentGoalCountAmt = currentGoalCountAmt; gcSave_currentGoalLevel = currentGoalLevel; savedGoalCount = true; } function restoreGoalCount() { finalGoalMet = gcSave_finalGoalMet; currentGoalTips = gcSave_currentGoalTips; currentGoalDesc = gcSave_currentGoalDesc; currentGoalTotal = gcSave_currentGoalTotal; currentGoalCountAmt = gcSave_currentGoalCountAmt; currentGoalLevel = gcSave_currentGoalLevel; savedGoalCount = false; } function goalCounterColors(gccby) { if (cb.settings.goalcounterTextColor == 'Custom') { goalCountTextColor = setTextColor(cb.settings.goalcounterCustomTextColor,'Goal Counter',gccby); } else { goalCountTextColor = setTextColor(cb.settings.goalcounterTextColor,'Goal Counter',gccby); } if (cb.settings.goalcounterBgColor == 'Custom') { goalCountBgColor = setBgColor(cb.settings.goalcounterCustomBgColor,'Goal Counter',gccby); } else { goalCountBgColor = setBgColor(cb.settings.goalcounterBgColor,'Goal Counter',gccby); } } function goalCountNotice() { if (whichApp == 'goalcount') { goalNotices('timer','','r','timer'); cb.setTimeout(goalCountNotice, goalCountNoticeInt); } } function updateGoalCount() { currentGoalDesc = goalCounterArray.desc[currentGoalLevel-1]; changeRoomSubject(); cb.drawPanel(); } function changeCountGoal(newgoal) { currentGoalTotal = newgoal; cb.drawPanel(); } // *********************************** Ultra App Ticket Show Functions ************************************** function setTicketShowToggle(setticketoption, setticketuser) { var setticketname = setticketuser; let tikchk = ticketSalesOpen; if (setticketuser == BC) { setticketname = bcText; } if (setticketoption == 'on') { initTicketShow(setticketuser); if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice(borderTicketTop + '\nToday\'s Ticket Show is a Fan Club Appreciation Show!\nOnly active Fan Club Members will be admitted to the show!\nPlease consider joining the Fan Club!' + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } else { if (!tikchk) { cb.sendNotice(borderTicketTop + '\n' + bcText + ' has started selling tickets for the show for ' + ticketPrice + ' tokens.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } } } else if (setticketoption == 'off') { if (cb.limitCam_isRunning()) { cb.limitCam_stop(); cb.sendNotice('The Ticket Show feature was suspended while the room was hidden.', setticketuser, appNoticeColor); hiddenTime = 0; ticketShowEnded = true; ticketSalesEnded = true; ticketSalesOpen = false; showStage = ''; } if (ticketMinsRemain > 0 || ticketSecsRemain > 0) { stopTicketTimer(); } cb.sendNotice('You have disabled the Ultra App Ticket Show feature. The ticket list and outstanding ticket list are still intact.', setticketuser, appNoticeColor); cb.sendNotice(borderAllNotices + '\n' + setticketname + ' has ended the UltraApp Ticket Show Feature.\n' + borderAllNotices, '', ticketBgColor, ticketTextColor, 'bold'); } } function initTicketShow(inittktsendto) { whichApp = 'ticket'; if (!ticketSalesOpen) { if (ticketHolderList.length > 0) { countTickets = ticketHolderList.length; } else { countTickets = 0; } ticketSalesOpen = true; ticketShowEnded = false; ticketSalesEnded = false; loadFreeTickets(); } showStage = 'ticketsales'; setTicketMode('init', inittktsendto); changeRoomSubject(); cb.setTimeout(ticketNoticeDisplay, ticketNoticeInt); cb.drawPanel(); } function ticketShowColors(tktcby) { if (cb.settings.ticketShowTextColor == 'Custom') { ticketTextColor = setTextColor(cb.settings.ticketShowCustomTextColor,'Ticket Show',tktcby); } else { ticketTextColor = setTextColor(cb.settings.ticketShowTextColor,'Ticket Show',tktcby); } if (cb.settings.ticketShowBgColor == 'Custom') { ticketBgColor = setBgColor(cb.settings.ticketShowCustomBgColor,'Ticket Show',tktcby); } else { ticketBgColor = setBgColor(cb.settings.ticketShowBgColor,'Ticket Show',tktcby); } } function ticketNoticeDisplay() { var tktmsg = ''; if (whichApp == 'ticket') { if (cb.settings.ticketShowFanAppreciation == 'Yes') { tktmsg += '\n**** Today\'s Ticket Show is a Fan Club Appreciation Show! ****'; tktmsg += '\nOnly configured Fan Club Members will be admitted to the show, please consider joining the Fan Club!'; } else { if (showStage == 'ticketsales') { tktmsg += '\n\u23E9 General Ticket Price: ' + ticketPrice + ' token' + (ticketPrice > 1 ? 's.' : '.'); tktmsg += '\n\u23E9 ' + wordWrap('Description: ' + ticketSubjectText); if (ticketStartMode == 'ticketgoal') { tktmsg += '\n\u23E9 Start Mode: Ticket Goal (Sold ' + ticketShowTotalTicketsBought + ' / ' + ticketShowGoalTickets + ' tickets)'; } else if (ticketStartMode == 'tokengoal') { tktmsg += '\n\u23E9 Start Mode: Token Goal (' + ticketShowTotalTips + ' / ' + ticketShowGoalTokens + ' tokens)'; } else if (ticketStartMode == 'timer') { tktmsg += '\n\u23E9 Start Mode: Timer (' + ((ticketSecsRemain > 0 || ticketMinsRemain > 0) ? ticketTimeLeft() : 'Timer not yet started') + ')'; } else { tktmsg += '\n\u23E9 Start Mode: Manual (Broadcaster or Moderator will start the show)'; } } else if (showStage == 'ticketshow') { tktmsg += '\n\u23E9 Ticket Price: ' + ticketPrice + ' token' + (ticketPrice > 1 ? 's.' : '.'); tktmsg += '\n\u23E9 Show has started, ticket sales still available'; tktmsg += '\n\u23E9 ' + wordWrap('Description: ' + ticketSubjectText); } else if (showStage == 'showwarn') { tktmsg += '\n\u23E9 Ticket Price: ' + ticketPrice + ' token' + (ticketPrice > 1 ? 's.' : '.'); tktmsg += '\n\u23E9 Ticket Sales available, but show is ending soon!'; tktmsg += '\n\u23E9 ' + wordWrap('Description: ' + ticketSubjectText); } else if (showStage == 'showfinale') { tktmsg += '\n\u23E9 ' + wordWrap('Description: ' + ticketSubjectText); tktmsg += '\n\u23E9 Ticket Sales have ended, do not buy a ticket!'; } else if (showStage == 'aftershow') { tktmsg += '\n\u23E9 ' + wordWrap('Show has ended! -- ' + cb.settings.ticketShowAfterNotice); } if (showStage != 'aftershow') { tktmsg += '\n\u23E9 Ticket Holders: ' + countTickets; if (ticketShowStartPrice > 0 && whichApp == 'ticket' && showStage == 'ticketsales') { tktmsg += '\n\u23E9 ' + wordWrap('Buy now! The ticket price will increase to ' + ticketShowStartPrice + ' tokens when the show is started.'); } if (cb.settings.ticketShowPriceFC > 1 && cb.settings.ticketShowFreeFC != 'Yes') { tktmsg += '\n\u23E9 Chaturbate Fanclub ticket price: ' + cb.settings.ticketShowPriceFC + ' token' + (cb.settings.ticketShowPriceFC > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeFC == 'Yes') { tktmsg += '\n\u23E9 Chaturbate Fanclub members get a free ticket!'; } if (cb.settings.ticketShowPriceVIP > 1 && cb.settings.ticketShowFreeVIP != 'Yes') { tktmsg += '\n\u23E9 ' + VIPname + ' ticket price: ' + cb.settings.ticketShowPriceVIP + ' token' + (cb.settings.ticketShowPriceVIP > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeVIP == 'Yes') { tktmsg += '\n\u23E9 ' + VIPname + ' members get a free ticket!'; } } } cb.sendNotice(borderTicketTop + tktmsg + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } else if (ticketSalesOpen) { tktmsg += '\n\u23E9 General Ticket Price: ' + ticketPrice + ' token' + (ticketPrice > 1 ? 's.' : '.'); tktmsg += '\n\u23E9 ' + bcText + ' will announce later in the stream when the show will start!'; tktmsg += '\n\u23E9 Ticket Holders: ' + countTickets; if (ticketShowStartPrice > 0 && whichApp == 'ticket' && showStage == 'ticketsales') { tktmsg += '\n\u23E9 ' + wordWrap('Buy now! The ticket price will increase to ' + ticketShowStartPrice + ' tokens when the show is started.'); } if (cb.settings.ticketShowPriceFC > 1 && cb.settings.ticketShowFreeFC != 'Yes') { tktmsg += '\n\u23E9 Chaturbate Fanclub ticket price: ' + cb.settings.ticketShowPriceFC + ' token' + (cb.settings.ticketShowPriceFC > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeFC == 'Yes') { tktmsg += '\n\u23E9 Chaturbate Fanclub members get a free ticket!'; } if (cb.settings.ticketShowPriceVIP > 1 && cb.settings.ticketShowFreeVIP != 'Yes') { tktmsg += '\n\u23E9 ' + VIPname + ' ticket price: ' + cb.settings.ticketShowPriceVIP + ' token' + (cb.settings.ticketShowPriceVIP > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeVIP == 'Yes') { tktmsg += '\n\u23E9 ' + VIPname + ' members get a free ticket!'; } cb.sendNotice(borderTicketTop + tktmsg + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } cb.setTimeout(ticketNoticeDisplay, ticketNoticeInt); } function setTicketPrice(amount,announce) { ticketPrice = parseInt(amount); if (announce == 'yes') { ticketPriceChangeNotice(ticketPrice); } } function ticketPriceChangeNotice(price) { cb.sendNotice(':ticket_red_small \u2749 \u2749 \u2749 :ticket_red_small \u2749 \u2749 \u2749 :ticket_red_small \n' + '\u21D2 . . . . Ticket Show Price Updated!!!. . . . . \u21D0 \n' + '\u21D2 . . . . Tickets are now ' + ticketPrice + ' tokens!!! . . . . . \u21D0 \n' + ':ticket_red_small \u2749 \u2749 \u2749 :ticket_red_small \u2749 \u2749 \u2749 :ticket_red_small' , "", ticketNoticesBgColor, ticketNoticesTextColor, "bold"); changeRoomSubject(); cb.drawPanel(); } function loadFreeTickets() { if (cb.settings.ticketShowFreeMods == 'Yes') { for (let i = 0; i < modsInShow.length; i++) { if (!cb.limitCam_userHasAccess(modsInShow[i])) { addRmvTicket('add',modsInShow[i],'mod',0,modsInShow[i]); } } } if (cb.settings.ticketShowFreeFC == 'Yes') { for (let i = 0; i < fanClubInShow.length; i++) { if (!cb.limitCam_userHasAccess(fanClubInShow[i])) { addRmvTicket('add',fanClubInShow[i],'fan',0,fanClubInShow[i]); } } } if (cb.settings.ticketShowFreeVIP == 'Yes') { for (let i = 0; i < VIPsInShow.length; i++) { if (!cb.limitCam_userHasAccess(VIPsInShow[i])) { addRmvTicket('add',VIPsInShow[i],'vip',0,VIPsInShow[i]); } } } } function checkFreeTickets(checkfreeuser,checkfreemod,checkfreefan,checkfreevip,checkmode) { if (!cb.limitCam_userHasAccess(checkfreeuser) && showStage != 'aftershow' && (ticketSalesOpen)) { if ((checkfreemod) && !cb.limitCam_userHasAccess(checkfreeuser)) { if (cb.settings.ticketShowFreeMods == 'Yes') { addRmvTicket('add',checkfreeuser,'mod',0,checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve been added to the show because you\'re a moderator! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (checkmode == 'enter') { cb.sendNotice(borderAllNotices + '\nThe Ticket Show has not been configured to give a free ticket to moderators. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } if ((checkfreefan) && !cb.limitCam_userHasAccess(checkfreeuser)) { if (cb.settings.ticketShowFreeFC == 'Yes') { addRmvTicket('add',checkfreeuser,'fan',0,checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the show because you\'re in the CB Fan Club! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (cb.settings.ticketShowPriceFC > 1 && checkmode == 'enter') { cb.sendNotice(borderAllNotices + '\nAs a CB Fan Club member, you can buy a ticket for ' + cb.settings.ticketShowPriceFC + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } if ((checkfreevip) && !cb.limitCam_userHasAccess(checkfreeuser)) { if (cb.settings.ticketShowFreeVIP == 'Yes') { addRmvTicket('add',checkfreeuser,'vip',0,checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the show because you\'re a ' + VIPname + ' member! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (cb.settings.ticketShowPriceVIP > 1 && checkmode == 'enter') { cb.sendNotice(borderAllNotices + '\nAs a ' + VIPname + ' member, you can buy a ticket for ' + cb.settings.ticketShowPriceVIP + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } } } function setTicketMode(settktmode,settktsendto) { var newticketmode = ''; var newticketauto = ''; if (settktmode === 'pre') { newticketmode = 'early'; } else if (settktmode === 'init') { if (cb.settings.ticketShowStartMode === 'Start Show Anytime') { newticketmode = 'manual'; } else if (cb.settings.ticketShowStartMode === 'Start Show after Timer') { newticketmode = 'timer'; } else if (cb.settings.ticketShowStartMode === 'Start Show after Ticket Goal') { newticketmode = 'ticketgoal'; } else if (cb.settings.ticketShowStartMode === 'Start Show after Token Goal') { newticketmode = 'tokengoal'; } if (cb.settings.ticketShowStartAuto === 'Start from Command') { newticketauto = 'bc'; } else if (cb.settings.ticketShowStartAuto === 'Automated Start') { newticketauto = 'auto'; } } else { newticketauto = ticketModeAuto; if (settktmode != '' && settktmode != null) { newticketmode = settktmode; } else { newticketmode = ticketStartMode; } } if (ticketStartMode === 'timer' && newticketmode != 'timer') { if (ticketMinsRemain > 0 || ticketSecsRemain > 0) { stopTicketTimer(settktsendto); } } if (newticketmode == 'early') { ticketStartMode = 'early'; ticketModeAuto = 'bc'; updtTicketModeMessage(); } else if (newticketmode == 'manual') { ticketStartMode = 'manual'; ticketModeAuto = 'bc'; updtTicketModeMessage(); } else if (newticketmode == 'timer') { if (cb.settings.ticketShowStartTimer >= 1) { ticketStartMode = 'timer'; ticketTimeAmt = cb.settings.ticketShowStartTimer; if (newticketauto == 'auto') { ticketAutoTimer(ticketTimeAmt); ticketModeAuto = 'auto'; updtTicketModeMessage(); } else { ticketAutoTimer(ticketTimeAmt); ticketModeAuto = 'bc'; updtTicketModeMessage(); } } else { ticketStartMode = 'manual'; ticketModeAuto = 'bc'; updtTicketModeMessage(); cb.sendNotice('Ticket Show start is set to manual mode. Mode was requested for automatic timer, but the timer was not set on the start page. You can restart the bot and update the missing time, or continue and start the show manually at the end of a timer or at a time of your choosing.', settktsendto, appNoticeColor); } } else if (newticketmode == 'tokengoal') { if (cb.settings.ticketShowGoal >= 1) { ticketStartMode = 'tokengoal'; ticketShowGoalTokens = cb.settings.ticketShowGoal; if (newticketauto == 'auto') { ticketModeAuto = 'auto'; updtTicketModeMessage(); } else { ticketModeAuto = 'bc'; updtTicketModeMessage(); } } else { ticketStartMode = 'manual'; ticketModeAuto = 'bc'; updtTicketModeMessage(); cb.sendNotice('Ticket Show start is set to manual mode. Mode was requested for automatic goal start, but the goal was not set on the start page. You can restart the bot and update the missing goal, update the goal using the command "/chgticketgoal [amt]" and then change the mode using "/chgticketmode goal", or continue and start the show manually at the end of a timer or at a time of your choosing.', settktsendto, appNoticeColor); } } else if (newticketmode == 'ticketgoal') { if (cb.settings.ticketShowGoal >= 1) { ticketStartMode = 'ticketgoal'; ticketShowGoalTickets = cb.settings.ticketShowGoal; if (newticketauto == 'auto') { ticketModeAuto = 'auto'; updtTicketModeMessage(); } else { ticketModeAuto = 'bc'; updtTicketModeMessage(); } } else { ticketStartMode = 'manual'; ticketModeAuto = 'bc'; updtTicketModeMessage(); cb.sendNotice('Ticket Show start is set to manual mode. Mode was requested for automatic goal start, but the goal was not set on the start page. You can restart the bot and update the missing goal, update the goal using the command "/chgticketgoal [amt]" and then change the mode using "/chgticketmode goal", or continue and start the show manually at the end of a timer or at a time of your choosing.', settktsendto, appNoticeColor); } } } function setTicketAuto(automode) { if (automode === 'bc') { ticketModeAuto = 'bc'; updtTicketModeMessage(); } else if (automode === 'auto') { ticketModeAuto = 'auto'; updtTicketModeMessage(); } } function updtTicketModeMessage() { if (ticketStartMode == 'early') { ticketModeMessage = 'Ticket show will be later in the show. \n' + bcText + ' will announce later in the stream when the ticket show will start.'; } else if (ticketStartMode == 'manual') { ticketModeMessage = 'Ticket Show start mode is set to "Manual"\nThe show will be started by ' + bcText + ' or a moderator.'; } else if (ticketStartMode == 'timer') { if (ticketModeAuto == 'auto') { ticketModeMessage = 'Ticket Show start mode is set to "Automatic timer" with a duration of ' + ticketTimeAmt + ' minutes.\nThe Ticket Show will start automatically when the timer runs out.'; } else if (ticketModeAuto == 'bc') { ticketModeMessage = 'Ticket Show start mode is set to "Broadcaster managed timer". \n' + bcText + ' will set a timer and start the show when the timer runs out.'; } } else if (ticketStartMode == 'tokengoal') { if (ticketModeAuto == 'auto') { ticketModeMessage = 'Ticket Show start mode is set to "Automatic at token goal." \nThere will be a short 2 minute countdown started once the goal is met for total tip amount (' + ticketShowGoalTokens + ' tokens), and then the show will start automatically.'; } else if (ticketModeAuto == 'bc') { ticketModeMessage = 'Ticket Show start mode is set to "Broadcaster managed token goal". \n' + bcText + ' will start the show once the goal is met for the total tips (' + ticketShowGoalTokens + ' tokens).'; } } else if (ticketStartMode == 'ticketgoal') { if (ticketModeAuto == 'auto') { ticketModeMessage = 'Ticket Show start mode is set to "Automatic at ticket goal." \nThere will be a short 2 minute countdown started once the goal is met for tickets sold (' + ticketShowGoalTickets + ' tickets), and then the show will start automatically.'; } else if (ticketModeAuto == 'bc') { ticketModeMessage = 'Ticket Show start mode is set to "Broadcaster managed ticket goal". \n' + bcText + ' will start the show once the goal is met for tickets sold (' + ticketShowGoalTickets + ' tickets).'; } } } function startTicketShowTimer(startnumtimer) { ticketMinsRemain = startnumtimer; ticketStartMode = 'timer'; ticketAutoTimer(ticketMinsRemain); cb.drawPanel(); } function ticketAutoTimer(addtime) { if (ticketModeAuto == 'auto') { if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice(borderTicketTop + '\nAn automatic timer was started for the countdown to the Fan Appreciation show.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } else { cb.sendNotice(borderTicketTop + '\nAn automatic timer was started, ticket show price will be ' + ticketPrice + ' tokens.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } } else if (ticketModeAuto == 'bc') { cb.sendNotice(borderTicketTop + '\nA timer was started to provide an countdown to approximate showtime. \n ' + bcText + ' will start the show after the timer runs out.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } ticketMinsRemain = addtime; ticketSecsRemain = 0; ticketStartTime = new Date(); ticketStopTime = new Date(ticketStartTime.getTime() + ticketMinsRemain * 60000); ticketTimerStopping = false; ticketTimerMin(); } function ticketTimerMin() { if (!ticketTimerStopping && ticketStartMode == 'timer' && whichApp == 'ticket') { switch (ticketMinsRemain) { case 60: case 45: case 30: case 25: case 20: case 15: case 10: case 9: case 8: case 7: case 6: case 5: case 4: case 3: case 2: if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + ticketMinsRemain + ' minutes left until the Fan Appreciation Show! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', 'bold'); } else { cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + ticketMinsRemain + ' minutes left to buy a ticket for ' + ticketPrice + ' tokens before the show starts! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', 'bold'); } break; case 1: if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice('\u23f1 \u23f1 \u23f1 There is 1 minute left until the Fan Appreciation Show! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', "bold"); } else { cb.sendNotice('\u23f1 \u23f1 \u23f1 There is 1 minute left to buy a ticket for ' + ticketPrice + ' tokens before the show starts! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', 'bold'); } break; } cb.drawPanel(); ticketMinsRemain--; if (ticketMinsRemain > 0) { ticketDisplaySeconds = false; } else { ticketDisplaySeconds = true; } ticketSecsRemain = 60; ticketTimerSec(); } } function ticketTimerSec() { if (!ticketTimerStopping && ticketStartMode === 'timer' && whichApp === 'ticket') { if (ticketDisplaySeconds) { switch (ticketSecsRemain) { case 30: case 10: case 5: case 4: case 3: case 2: cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + ticketSecsRemain + ' seconds left! \u23f1 \u23f1 \u23f1', '', appErrorColor, '', 'bold'); break; case 1: cb.sendNotice('\u23f1 \u23f1 \u23f1 There is 1 second left! \u23f1 \u23f1 \u23f1', '', appErrorColor, '', 'bold'); break; } cb.drawPanel(); } if (ticketSecsRemain < 1) { if (ticketMinsRemain >= 1) { ticketTimerMin(); } else { if (ticketStartMode = 'timer' && ticketModeAuto === 'auto') { cb.sendNotice('\u23f0 \u23f0 \u23f0 Time is up! Ticket Show is starting! \u23f0 \u23f0 \u23f0', '', ticketNoticesBgColor, ticketNoticesTextColor, 'bold'); startTicketShow(bcText); } else { cb.sendNotice('\u23f0 \u23f0 \u23f0 Time is up! ' + bcText + ' will be starting the show! \u23f0 \u23f0 \u23f0', '', ticketNoticesBgColor, ticketNoticesTextColor, 'bold'); } } } else { ticketSecsRemain--; cb.setTimeout(ticketTimerSec, 1000); } } } function ticketAddTime(tickettimetoadd, tatuser) { ticketStopTime = new Date(ticketStopTime.getTime() + tickettimetoadd * 60000); var timetoaddabs = Math.abs(tickettimetoadd); if (tickettimetoadd > 0) { cb.sendNotice(tatuser + ' has added ' + tickettimetoadd + ' minute' + (tickettimetoadd === 1 || tickettimetoadd === -1 ? '' : 's') + ' to the timer.',BC,'','', 'bold'); cb.sendNotice(tickettimetoadd + ' minute' + (tickettimetoadd === 1 || tickettimetoadd === -1 ? ' has' : 's have') + ' been added to the timer. Now ' + ticketTimeLeft(),'',appNoticeColor,'', 'bold'); } else { cb.sendNotice(tatuser + ' has subtracted ' + timetoaddabs + ' minute' + (timetoaddabs === 1 || timetoaddabs === -1 ? '' : 's') + ' from the timer.',BC,'','', 'bold'); cb.sendNotice(timetoaddabs + ' minute' + (timetoaddabs === 1 || timetoaddabs === -1 ? ' has' : 's have') + ' been subtracted from the timer. Now ' + ticketTimeLeft(),'',appNoticeColor,'', 'bold'); } ticketMinsRemain = ticketMinsRemain + tickettimetoadd; if (ticketMinsRemain > 0) { ticketDisplaySeconds = false; } else { ticketDisplaySeconds = true; } cb.drawPanel(); return; } function ticketTimeCal() { ticketStartTime = new Date(); return ticketStopTime - ticketStartTime.getTime(); } function stopTicketTimer(mod) { ticketStopTime = new Date(); ticketTimerStopping = true; ticketMinsRemain = 0; ticketSecsRemain = 0; if(mod != null && mod != '') { cb.sendNotice(mod + ' has stopped the ticket show timer.','',appNoticeColor,'', 'bold'); } cb.drawPanel(); } function ticketTimeLeft() { var ticketTimeLeft = ticketTimeCal(); var ticketMS = ticketTimeLeft % 1000; var ticketSeconds = ((ticketTimeLeft - ticketMS) % 60000); var ticketMinutes = ((ticketTimeLeft - ticketSeconds - ticketMS) % 3600000); var ticketHours = (ticketTimeLeft - ticketMinutes - ticketSeconds - ticketMS); ticketSeconds = ticketSeconds / 1000; ticketMinutes = ticketMinutes / 60000; ticketHours = ticketHours / 3600000; if (ticketHours > 0) { return ticketHours + ' hour' + (ticketHours > 1 ? 's' : '') + ' and ' + ticketMinutes + ' min' + (ticketMinutes > 1 ? 's' : '') + ' left on the show timer.'; } else if (ticketMinutes > 0 && ticketSeconds === 0) { return ticketMinutes + ' min' + (ticketMinutes > 1 ? 's' : '') + ' left on the show timer.'; } else if (ticketMinutes > 0) { return ticketMinutes + ' min' + (ticketMinutes > 1 ? 's' : '') + ' and ' + ticketSeconds + ' sec' + (ticketSeconds > 1 ? 's' : '') + ' left on the show timer.'; } else { return ticketSeconds + ' sec' + (ticketSeconds > 1 ? 's' : '') + ' left on the show timer.'; } } function ticketTimeLeftPanel() { var ticketTimeLeft = ticketTimeCal(); var ticketMS = ticketTimeLeft % 1000; var ticketSeconds = ((ticketTimeLeft - ticketMS) % 60000); var ticketMinutes = ((ticketTimeLeft - ticketSeconds - ticketMS) % 3600000); var ticketHours = (ticketTimeLeft - ticketMinutes - ticketSeconds - ticketMS); ticketSeconds = ticketSeconds / 1000; ticketMinutes = ticketMinutes / 60000; if (ticketMinutes > 0) { return 'Less than ' + (ticketMinutes+1) + ' min rem'; } else { return (ticketSeconds) + ' sec rem'; } } function addRmvTicket(addrmvtktmode,addrmvtktuser,addrmvtktusertype,addrmvtkttipamt,addrmvtktdispuser) { switch (addrmvtktmode) { case 'add': { cb.limitCam_addUsers([addrmvtktuser]); addViewer(addrmvtktuser); addTicketHolder(addrmvtktuser); if (addrmvtktusertype == 'fan') { cb.sendNotice('CB Fan Club member ' + addrmvtktdispuser + ' has been added to the ticket show list.', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvtktusertype == 'vip') { cb.sendNotice(VIPname + ' member ' + addrmvtktdispuser + ' has been added to the ticket show list.', '', ticketBgColorVIP, ticketTextColorVIP, 'bold'); } else if (addrmvtktusertype == 'mod') { cb.sendNotice('Moderator ' + addrmvtktdispuser + ' has been added to the ticket show list.', '', ticketBgColorMod, ticketTextColorMod, 'bold'); } else { cb.sendNotice(addrmvtktdispuser + ' has been added to the ticket show list.', '', ticketHolderBgColor, '', 'bold'); } ticketShowGoalTickets++; cb.drawPanel(); break; } case 'rmv': { cb.limitCam_removeUsers([addrmvtktuser]); removeTicketHolder(addrmvtktuser); removeViewer(addrmvtktuser); cb.sendNotice(addrmvtktdispuser + ' has been removed from the ticket show list.', '', ticketHolderBgColor, '', 'bold'); cb.drawPanel(); break; } case 'addtip': { cb.limitCam_addUsers([addrmvtktuser]); addViewer(addrmvtktuser); addTicketHolder(addrmvtktuser); ticketShowTotalTicketsBought++; ticketShowGoalTickets++; if (addrmvtktdispuser == 'Anonymous User') { cb.sendNotice(ticketemoji + 'Welcome! You have bought a ticket to the show!', '', ticketHolderBgColor, '', 'bold'); } else { if (addrmvtktusertype == 'fan') { cb.sendNotice(ticketemoji + ' Welcome CB Fan Club member ' + addrmvtktdispuser + ', you have bought a ticket to the show!', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvtktusertype == 'vip') { cb.sendNotice(ticketemoji + ' Welcome ' + VIPname + ' member ' + addrmvtktdispuser + ', you have bought a ticket to the show!', '', ticketBgColorVIP, ticketTextColorVIP, 'bold'); } else if (addrmvtktusertype == 'mod') { cb.sendNotice(ticketemoji + ' Welcome ' + addrmvtktdispuser + ', you have bought a ticket to the show!', '', ticketHolderBgColor, '', 'bold'); } else { cb.sendNotice(ticketemoji + ' Welcome ' + addrmvtktdispuser + ', you have bought a ticket to the show!', '', ticketHolderBgColor, '', 'bold'); } } break; } } } function addViewer(addedviewer) { if (!cbjs.arrayContains(ticketShowViewerList,addedviewer)) { ticketShowViewerList.push(addedviewer); } } function removeViewer(removedviewer) { if (cbjs.arrayContains(ticketShowViewerList,removedviewer)) { var rmvvindex = ticketShowViewerList.indexOf(removedviewer); ticketShowViewerList.splice(rmvvindex,1); } } function addTicketHolder(addedtktholder) { if (!cbjs.arrayContains(ticketHolderList,addedtktholder)) { ticketHolderList.push(addedtktholder); countTickets ++; } } function removeTicketHolder(rmvtktholder) { if (cbjs.arrayContains(ticketHolderList,rmvtktholder)) { var rmvvindexh = ticketHolderList.indexOf(rmvtktholder); ticketHolderList.splice(rmvvindexh,1); if (countTickets > 0) { countTickets --; } } } function ticketShowGoalProgress(tippedamountprogress) { ticketShowTotalTips = ticketShowTotalTips + tippedamountprogress; if (ticketStartMode == 'tokengoal' && ticketShowTotalTips >= ticketShowGoalTokens) { cb.sendNotice(borderTicketTop + '\n :siren1 \u25C8 \u25C8 \u25C8 The ticket show Tip goal has been met!! \u25C8 \u25C8 \u25C8 :siren1 \n \u25C8 \u25C8 \u25C8 Starting a 2 minute timer for automatic start of show! \u25C8 \u25C8 \u25C8 ' + borderTicketBottom, '', ticketNoticesBgColor, ticketNoticesTextColor, 'bold'); ticketStartMode = 'timer'; ticketAutoTimer(2); } else if (ticketStartMode == 'ticketgoal' && ticketShowTotalTicketsBought >= ticketShowGoalTickets) { cb.sendNotice(borderTicketTop + '\n :siren1 \u25C8 \u25C8 The ticket show Ticket goal has been met!! \u25C8 \u25C8 :siren1 \n \u25C8 \u25C8 \u25C8 Starting a 2 minute timer for automatic start of show! \u25C8 \u25C8 \u25C8 ' + borderTicketBottom, '', ticketNoticesBgColor, ticketNoticesTextColor, 'bold'); ticketStartMode = 'timer'; ticketAutoTimer(2); } } function startTicketShow(startedby) { if (ticketShowStartPrice > 0) { ticketPrice = ticketShowStartPrice; ticketPriceChangeNotice(ticketShowStartPrice); } cb.sendNotice(borderTicketTop + '\n :siren1 :siren1 :siren1 ' + startedby + ' has started the show! :siren1 :siren1 :siren1' + borderTicketBottom,'',ticketBgColor, ticketNoticesTextColor, 'bold'); if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.limitCam_start('Ultra App Hidden Show\n\nFan Club Appreciation Show in progress. Join the Fanclub to unlock the screen!'); } else { cb.limitCam_start('Ultra App Ticket Show\n\nHidden Cam show in progress \nCheck room notices for ticket price'); } showStage = 'ticketshow'; changeRoomSubject(); if (ticketMinsRemain > 0 || ticketSecsRemain > 0) { stopTicketTimer(); } hiddenTime = Date.now(); cb.drawPanel(); } function restartTicketShow(startedby) { cb.sendNotice(borderTicketTop + '\n :siren1 \u25C8 \u25C8 \u25C8 ' + startedby + ' has restarted the ticket show! \u25C8 \u25C8 \u25C8 :siren1 \n \u25C8 \u25C8 \u25C8 All current ticket holders still have access. \u25C8 \u25C8 \u25C8 \n \u25C8 \u25C8 \u25C8 Ticket sales are open, price is ' + ticketPrice + ' tokens. \u25C8 \u25C8 \u25C8 ' + borderTicketBottom,'',ticketBgColor, ticketNoticesTextColor, 'bold'); if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.limitCam_start('Ultra App Ticket Show\n\nFan Club Appreciation Show in progress. Join the Fanclub to unlock the screen!'); } else { cb.limitCam_start('Ultra App Ticket Show\n\nHidden Cam show in progress \nCheck room notices for ticket price'); } if (showStage == 'aftershow') { ticketSubjectText = savedTicketSubjectText; } showStage = 'ticketshow'; changeRoomSubject(); ticketShowEnded = false; ticketSalesEnded = false; if (ticketMinsRemain > 0 || ticketSecsRemain > 0) { stopTicketTimer(); } hiddenTime = Date.now(); cb.drawPanel(); } function restartTicketSales(startedby) { if (showStage == 'aftershow') { cb.sendNotice(borderTicketTop + '\n :siren1 ' + startedby + ' has restarted ticket sales! :siren1 \nAll current ticket holders still have access. \nTicket price is ' + ticketPrice + ' tokens. \n' + borderTicketBottom,'',ticketBgColor, ticketNoticesTextColor, 'bold'); showStage = 'ticketsales'; ticketSubjectText = savedTicketSubjectText; ticketShowEnded = false; } else if (showStage == 'showfinale') { cb.sendNotice(borderTicketTop + '\n :siren1 ' + startedby + ' has restarted ticket sales! :siren1 \nTicket price is ' + ticketPrice + ' tokens. ' + borderTicketBottom,'',ticketBgColor, ticketNoticesTextColor, 'bold'); showStage = 'ticketshow'; } ticketSalesEnded = false; changeRoomSubject(); cb.drawPanel(); } function warnShowEnding(warnby) { showStage = 'showwarn'; if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice(borderTicketTop + '\n \u26A0\uFE0F :siren1 ' + warnby + ' has indicated the Fan Appreciation Show is nearly finished :siren1 \u26A0\uFE0F ' + borderTicketBottom,'',ticketBgColor,ticketNoticesTextColor,'bold'); } else { if (cb.settings.ticketShowReducePriceWarn > 0) { ticketPrice = ticketPrice - cb.settings.ticketShowReducePriceWarn; cb.sendNotice(borderTicketTop + '\n :siren1 ' + warnby + ' has indicated the show is nearly finished. :siren1 \n \u26A0\uFE0F It is not recommended to buy a ticket, but sales are still open. \u26A0\uFE0F \n \u26A0\uFE0F The Ticket Price has been reduced to ' + ticketPrice + ' tokens! \u26A0\uFE0F ' + borderTicketBottom,'',ticketBgColor,ticketNoticesTextColor,'bold'); } else { cb.sendNotice(borderTicketTop + '\n :siren1 ' + warnby + ' has indicated the show is nearly finished. :siren1 \n \u26A0\uFE0F It is not recommended to buy a ticket, but sales are still open. \u26A0\uFE0F ' + borderTicketBottom,'',ticketBgColor,ticketNoticesTextColor,'bold'); } } changeRoomSubject(); cb.drawPanel(); } function stopTicketSales(salesstoppedby) { if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice(borderTicketTop + '\n \u26A0\uFE0F :siren1 ' + salesstoppedby + ' has indicated the Fan Appreciation Show is nearly finished! :siren1 ' + borderTicketBottom,'',ticketBgColor,ticketNoticesTextColor,'bold'); } else { cb.sendNotice(borderTicketTop + '\n \u26A0\uFE0F :siren1 \u26A0\uFE0F ' + salesstoppedby + ' has ended ticket sales for the show! \u26A0\uFE0F :siren1 \u26A0\uFE0F ' + borderTicketBottom,'',ticketBgColor,ticketNoticesTextColor,'bold'); } showStage = 'showfinale'; changeRoomSubject(); ticketSalesEnded = true; ticketSalesOpen = false; cb.drawPanel(); setAfterNoticeTimeout(); } function stopTicketShow(stoppedby) { cb.sendNotice(borderTicketTop + '\n \u26A0\uFE0F :siren1 \u26A0\uFE0F ' + stoppedby + ' has ended the show! \u26A0\uFE0F :siren1 \u26A0\uFE0F \n Ticket Show length ' + ((Date.now() - hiddenTime)/60000).toFixed(2) + ' minutes.\nThe broadcast is returning to Public Chat.' + borderTicketBottom,'',ticketBgColor,ticketNoticesTextColor,'bold'); cb.limitCam_stop(); hiddenTime = 0; ticketShowEnded = true; ticketSalesEnded = true; ticketSalesOpen = false; showStage = 'aftershow'; savedTicketSubjectText = ticketSubjectText; ticketSubjectText = cb.settings.ticketShowAfterNotice; changeRoomSubject(); cb.drawPanel(); } function setAfterNoticeTimeout() { cb.setTimeout(afterNoticeTimer, afterNoticeInt); } function afterNoticeTimer() { if (showStage == 'showfinale') { cb.sendNotice(' :siren1 Ticket Sales Ended! Do not tip to buy a ticket! :siren1 ', '', ticketBgColor, ticketNoticesTextColor, 'bold'); setAfterNoticeTimeout(); } } function sendPublicNotice (pnmessage, user, type) { if (pnmessage != null) { if (pnmessage != '' || pnmessage != ' ' || pnmessage != '\u00a0') { switch (type) { case 'div': cb.sendNotice(borderAllNotices + '\n\u25ba ' + pnmessage.capitalize() + '\n' + borderAllNotices, '', '', ticketNoticesTextColor, 'bold'); break; case 'divh': cb.sendNotice(borderAllNotices + '\n\u25ba ' + pnmessage.capitalize() + '\n' + borderAllNotices, '', ticketBgColor,ticketNoticesTextColor, 'bold'); break; case 'h': cb.sendNotice("\u25ba " + pnmessage.capitalize(), '', ticketBgColor,ticketNoticesTextColor, 'bold'); break; case '': cb.sendNotice('\u25ba ' + pnmessage.capitalize(), '', '', ticketNoticesTextColor, 'bold'); break; } } else { cb.sendNotice('You cannot send a blank message. Type a message and try again.', user, appNoticeColor); } } else { cb.sendNotice('You cannot send a blank message. Type a message and try again.', user, appNoticeColor); } } function prepTicketShow(startedby,prepnumtimer) { if (ticketPrice <= 0) { cb.sendNotice('Unable to start the Ultra App Ticket Show feature, a ticket price is not defined. The command "/ticketprice [price]" can be used to set a price.', startedby, appNoticeColor); } else { setAppFeature('ticket', startedby); } if (cb.settings.prepTicketStartTimer == 'Yes' && ticketStartMode == 'manual') { if (prepnumtimer > 0) { startTicketShowTimer(prepnumtimer); } else if (cb.settings.ticketShowStartTimer > 0) { startTicketShowTimer(cb.settings.ticketShowStartTimer); } else { cb.sendNotice('Unable to start the ticket show countdown timer, a value was not provided on the /prepticket command and no "Default Timer" duration is defined on the launch page. You can manually start a timer with the "/ticketstarttimer xx" command (no quotes).', startedby, appNoticeColor); } } } function checkEarlyTickets(subject) { subject = 'TICKET SALES OPEN [' + ticketPrice + ' tokens]: ' + subject; return subject; } // *********************************** Goal Race ************************************** function setGoalRaceToggle(setgroption, setgrsetby) { var setgrname = setgrsetby; if (setgrsetby == BC) { setgrname = bcText; } if (setgroption == 'on') { whichApp = 'goalrace'; if (savedGoalRace) { restoreGoalRace(); } else { genericInit(); initGoalRace(); } changeRoomSubject(); if (goalRaceNoticeInt > 0) { cb.setTimeout(goalRaceNotice, goalRaceNoticeInt); } cb.sendNotice(borderAllNotices + '\n' + setgrname + ' has started the Goal Race feature.\nSelect which goal to vote for when you tip!\n' + borderAllNotices, '', goalraceBgColor, goalraceTextColor, 'bold'); } else if (setgroption == 'off') { saveGoalRace(); cb.sendNotice(borderAllNotices + '\n' + setgrname + ' has suspended the Goal Race feature.\n' + borderAllNotices, '', goalraceBgColor, goalraceTextColor, 'bold'); } } function initGoalRace() { tipOptions = true; fontSize = 11; } function saveGoalRace() { grSave_finalGoalMet = finalGoalMet; grSave_currentGroupTipAmt = currentGroupTipAmt; savedGoalRace = true; } function restoreGoalRace() { finalGoalMet = grSave_finalGoalMet; currentGroupTipAmt = grSave_currentGroupTipAmt; savedGoalRace = false; } function goalraceColors(grcby) { if (cb.settings.goalraceTextColor == 'Custom') { goalraceTextColor = setTextColor(cb.settings.goalraceCustomTextColor,'Goal Race',grcby); } else { goalraceTextColor = setTextColor(cb.settings.goalraceTextColor,'Goal Race',grcby); } if (cb.settings.goalraceBgColor == 'Custom') { goalraceBgColor = setBgColor(cb.settings.goalraceCustomBgColor,'Goal Race',grcby); } else { goalraceBgColor = setBgColor(cb.settings.goalraceBgColor,'Goal Race',grcby); } } function goalRaceNotice() { if (whichApp == 'goalrace') { goalNotices('timer','','r','timer'); cb.setTimeout(goalRaceNotice, goalRaceNoticeInt); } } function restartRace() { currentGoalRace1Tips = 0; currentGoalRace2Tips = 0; finalGoalMet = false; cb.drawPanel(); } // *********************************** Spank-a-thon Functions ************************************** function setSpankToggle(setspkoption, setspkby) { var setspkname = setspkby; if (setspkby == BC) { setspkname = bcText; } if (setspkoption == 'on') { whichApp = 'spank'; if (savedSpankGoals) { restoreSpankGoals(); } else { genericInit(); initSpanks(); } changeRoomSubject(); cb.sendNotice(borderAllNotices + '\n' + setspkname + ' has started the Spank-a-thon Feature.\nUse the Spanks Tip Menu to tip for a type of spank (/spankmenu).\nSpanking commences at each goal or at ' + bcText + '\'s discretion.\n' + borderAllNotices, '', spankBgColor, spankTextColor, 'bold'); } else if (setspkoption == 'off') { saveSpankGoals(); cb.sendNotice(borderAllNotices + '\n' + setspkname + ' has suspended the Spank-a-thon Feature.\n' + borderAllNotices, '', spankBgColor, spankTextColor, 'bold'); } } function initSpanks() { totalSpankGoals = spankGoalArray.desc.length; currentGoalDesc = spankGoalArray.desc[currentGoal-1]; currentGoalTotal = spankGoalArray.amt[currentGoal-1]; if (spankNoticeInt > 0) { cb.setTimeout(spanksNotice, spankNoticeInt); } } function saveSpankGoals() { spSave_finalGoalMet = finalGoalMet; spSave_currentGoalTips = currentGoalTips; spSave_currentGoal = currentGoal; spSave_currentGoalDesc = currentGoalDesc; spSave_currentGoalTotal = currentGoalTotal; savedSpankGoals = true; } function restoreSpankGoals() { finalGoalMet = spSave_finalGoalMet; currentGoalTips = spSave_currentGoalTips; currentGoal = spSave_currentGoal; currentGoalDesc = spSave_currentGoalDesc; currentGoalTotal = spSave_currentGoalTotal; savedSpankGoals = false; } function spankSepChar() { if (cb.settings.spankSepChar == 'Custom') { if (cb.settings.spankSepCharCustom) { spankCharacter = cb.settings.spankSepCharCustom; } else { spankCharacter = ':spankasss'; } } else { spankCharacter = checkSepChar(cb.settings.spankSepChar) } spankCharacter += ' '; } function spankColors(spkcby) { if (cb.settings.spankTextColor == 'Custom') { spankTextColor = setTextColor(cb.settings.spankCustomTextColor,'Spank-a-thon',spkcby); } else { spankTextColor = setTextColor(cb.settings.spankTextColor,'Spank-a-thon',spkcby); } if (cb.settings.spankGoalBgColor == 'Custom') { spankBgColor = setBgColor(cb.settings.spankCustomBgColor,'Spank-a-thon',spkcby); } else { spankBgColor = setBgColor(cb.settings.spankBgColor,'Spank-a-thon',spkcby); } } function spanksNotice() { if (whichApp == 'spank') { goalNotices('timer','','r','timer'); } cb.setTimeout(spanksNotice, spankNoticeInt); } function updateSpankGoal() { currentGoalDesc = progGoalArray.desc[currentGoal-1]; currentGoalTotal = progGoalArray.amt[currentGoal-1]; changeRoomSubject(); cb.drawPanel(); } function checkSpankMenu(tippedamountspank,tippedbyspank,tippedbydispspank) { for (let smindex = 0; smindex < spankPricesArray.typ.length; smindex++) { if (tippedamountspank == spankPricesArray.price[smindex]) { spankRequestsArray.name.push(tippedbyspank); spankRequestsArray.amt.push(spankPricesArray.number[smindex]); spankRequestsArray.type.push(spankPricesArray.typ[smindex]); var sptotalsindex = spankTotalsArray.typ.indexOf(spankPricesArray.typ[smindex]) spankTotalsArray.tipped[sptotalsindex] += spankPricesArray.number[smindex]; totalSpanksTipped += spankPricesArray.number[smindex]; if (spankPricesArray.number[smindex] == 1) { cb.sendNotice(tippedbydispspank + ' tipped for ' + spankPricesArray.typ[smindex] + '.', '', spankBgColor, spankTextColor, 'bold'); } else { cb.sendNotice(tippedbydispspank + ' tipped for ' + spankPricesArray.typ[smindex] + ' (bundle of ' + spankPricesArray.number[smindex] + ').', '', spankBgColor, spankTextColor, 'bold'); } } } } function spankTips() { var tipsmessage = 'Spank Tips:'; if (spankRequestsArray.name.length > 0) { for (let stindex = 0; stindex < spankRequestsArray.name.length; stindex++) { if (spankRequestsArray.name[stindex]) { tipsmessage += '\n' + spankRequestsArray.name[stindex] + ' tipped for (' + spankRequestsArray.amt[stindex] + ') ' + spankRequestsArray.type[stindex]; } } } else { tipsmessage += '\nNo Spank Tips Yet'; } cb.sendNotice(tipsmessage, '', spankBgColor, spankTextColor, 'bold'); } function spanksCompleted(scindex,number) { spankTotalsArray.completed[scindex] += number; totalSpanksCompleted += number; cb.sendNotice(number + (number == 1 ? ' spank has' : ' spanks have') + ' just been completed for ' + spankTotalsArray.typ[scindex] + ' (' + spankTotalsArray.completed[scindex] + ' completed so far, ' + (spankTotalsArray.tipped[scindex] - spankTotalsArray.completed[scindex]) + ' remaining).', '', spankBgColor, spankTextColor, 'bold'); cb.drawPanel(); } function spankTotals() { var sptotalsmessage = 'Spank Totals:'; for (let totindex = 0; totindex < spankTotalsArray.typ.length; totindex++) { if (spankTotalsArray.typ[totindex]) { sptotalsmessage += '\n' + spankTotalsArray.typ[totindex] + ' (' + spankTotalsArray.tipped[totindex] + ' spanks tipped for / ' + spankTotalsArray.completed[totindex] + ' completed)'; } } cb.sendNotice(sptotalsmessage, '', spankBgColor, spankTextColor, 'bold'); } function spankMenu() { var spmenumessage = 'Spank Menu:'; var savetype = ''; for (let m = 0; m < spankPricesArray.typ.length; m++) { if (spankPricesArray.typ[m]) { if (spankPricesArray.typ[m] != savetype) { spmenumessage += '\n' + spankPricesArray.typ[m] + ' :: '; savetype = spankPricesArray.typ[m]; } if (spankPricesArray.price[m] > 0 && spankPricesArray.number[m] > 0) { spmenumessage += spankCharacter + '(' + spankPricesArray.price[m] + ') for ' + spankPricesArray.number[m] + (spankPricesArray.number[m] > 1 ? ' spanks ' : ' spank '); } } } return spmenumessage; } // *********************************** Auto-Reset Goal Functions ************************************** function setAutoresetToggle(setaroption, setarby) { var setarname = setarby; if (setarby == BC) { setarname = bcText; } if (setaroption == 'on') { whichApp = 'autoreset'; if (savedAutoresetGoals) { restoreAutoresetGoals(); } else { genericInit(); initAutoresetGoal(); if (autoresetNoticeInt > 0) { cb.setTimeout(autoresetNotice,autoresetNoticeInt); } } changeRoomSubject(); cb.sendNotice(borderAllNotices + '\n' + setarname + ' has started the Auto-Reset Goal Feature.\nThe same goal will continually repeat every ' + autoresetGoalAmount + ' tokens.\n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, 'bold'); } else if (setaroption == 'off') { saveAutoresetGoals(); cb.sendNotice(borderAllNotices + '\n' + setarname + ' has suspended the Auto-Reset Goal Goal Feature.\n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, 'bold'); } } function initAutoresetGoal() { currentGoalDesc = autoresetEachGoal; currentGoalTotal = autoresetGoalAmount; } function saveAutoresetGoals() { arSave_finalGoalMet = finalGoalMet; arSave_currentGoalTips = currentGoalTips; arSave_currentGoal = currentGoal; arSave_currentGoalDesc = currentGoalDesc; arSave_currentGoalTotal = currentGoalTotal; savedAutoresetGoals = true; } function restoreAutoresetGoals() { finalGoalMet = arSave_finalGoalMet; currentGoalTips = arSave_currentGoalTips; currentGoal = arSave_currentGoal; currentGoalDesc = arSave_currentGoalDesc; currentGoalTotal = arSave_currentGoalTotal; savedAutoresetGoals = false; } function autoresetGoalColors(arcby) { if (cb.settings.autoresetTextColor == 'Custom') { autoresetTextColor = setTextColor(cb.settings.autoresetCustomTextColor,'Autoreset Goals',arcby); } else { autoresetTextColor = setTextColor(cb.settings.autoresetTextColor,'Autoreset Goals',arcby); } if (cb.settings.autoresetBgColor == 'Custom') { autoresetBgColor = setBgColor(cb.settings.autoresetCustomBgColor,'Autoreset Goals',arcby); } else { autoresetBgColor = setBgColor(cb.settings.autoresetBgColor,'Autoreset Goals',arcby); } } function autoresetNotice() { if (whichApp == 'autoreset') { goalNotices('timer','','r','timer'); cb.setTimeout(autoresetNotice, autoresetNoticeInt); } } function updateAutoresetGoal() { currentGoalDesc = autoresetEachGoal; currentGoalTotal = autoresetGoalAmount; changeRoomSubject(); cb.drawPanel(); } // *********************************** About Function ************************************** function displayAbout(sendtoabout) { let aboutmessage = 'Dorothy\'s UltraApp'; aboutmessage += '\n \u2705 About the App:'; aboutmessage += '\n \u2022 ' + wordWrap('Version 3.4 was released on April 11, 2021, and was written by CB users chelsea2950 and butter_my_toast.'); aboutmessage += '\n \u2022 ' + wordWrap('We like to visit rooms to check on people using the app so feel free to say hello if you see us!'); aboutmessage += '\n \u2022 ' + wordWrap('Comments, suggestions, requests, and bug reports can be made by either tweeting @thechelsea2950, or by posting comments on bot help page.'); aboutmessage += '\n \u2022 ' + wordWrap('The purpose of this Ultra/All in one App is to serve as a single tool that broadcasters can use for most of the common goal types and ticket show features.'); aboutmessage += '\n \u2022 ' + wordWrap('You can type "/uahelp" to display the command list summary, and use one of the group qualifiers to see more details within a group, such as "/uahelp ticket"'); aboutmessage += '\n \u2022 ' + wordWrap('The app description page includes extensive details on App features, moderator trust levels, commands and recent release notes:'); aboutmessage += '\n https://chaturbate.com/apps/app_details/dorothys-ultraapp/ '; aboutmessage += '\n\n \u2705 Highlights of fixes and improvements for version 3.4:'; aboutmessage += '\n \u2022 ' + wordWrap('Updated the notice border settings and spacing to make them a consistent length'); aboutmessage += '\n \u2022 ' + wordWrap('Updated to use the "Shades of Blue" theme as the default color scheme for the the app'); aboutmessage += '\n \u2022 ' + wordWrap('Added line wrap to more text areas as needed - welcome, help, about, goal descriptions, etc'); aboutmessage += '\n \u2022 ' + wordWrap('Added /uachgint command to change the intervals for the recurring notices that are used as part of each goal'); aboutmessage += '\n \u2022 ' + wordWrap('Added /chgtheme (and /uachgtheme) commands to allow for update to the color theme used during the show'); aboutmessage += '\n \u2022 ' + wordWrap('Added /uaseethemes command to display a sampling in the chat of all available Color Themes that you can choose from'); aboutmessage += '\n \u2022 ' + wordWrap('Added /seepanels command to cycle the draw panel through all available backgrounds on a 10 second rotation. As each new panel is displayed, a message is sent to the requester with the panel background name. Only one person (moderator or broadcaster) can request this at a time, and it can be canceled with either the command /stoppanel or /freezepanel. The "stop" will return to the panel in use before starting, and the "freeze" will remain on the one displayed at the time of the command.'); aboutmessage += '\n \u2022 ' + wordWrap('Added /setbrdsep (and /uasetbrdsep) commands to allow for updating the Notice Border emoji or character'); cb.sendNotice(aboutmessage, sendtoabout, appNoticeColor); } // *********************************** Help Function ************************************** function helpCommon(comhelpreqby) { let helptxtmsg = '\u23E9 Dorothys Ultra App - Help Menu'; helptxtmsg += '\n \u2705 == General =='; helptxtmsg += '\n \u2022 ' + wordWrap('/ninjatipon, /ninjatipoff: These commands let you toggle whether your tips are counting toward the goals. They are not valid when using the ticket show. You can also type the text --NOGOAL-- in your tip note instead of using the commands.'); helptxtmsg += '\n \u2705 == Goal Race =='; helptxtmsg += '\n \u2022 ' + wordWrap('/tipnoteon, /tipnoteoff: This command lets you toggle whether you are sending tip notes to ' + bcText + ' or casting votes for the goal.'); helptxtmsg += '\n \u2705 == Spank-a-thon =='; helptxtmsg += '\n \u2022 ' + wordWrap('/spanktotals: Display the current totals for spanks tipped for and administered per spank type.'); helptxtmsg += '\n \u2022 ' + wordWrap('/spankmenu: Display the spank menu on demand, note it also shows in the chat notice periodically.'); helptxtmsg += '\n \u2022 /spanktips: Display the history of the spank tips.'; helptxtmsg += '\n \u2705 == Ticket Show =='; helptxtmsg += '\n \u2022 ' + wordWrap('/tickets: Display the list of users that have bought or been given a ticket. If the parameter of "alpha" is added, the list is displayed alphabetically. Note that viewers can be added back to the show using the "/add" command and pasting the list that is shown from the "/tickets" command.'); helptxtmsg += '\n \u2022 ' + wordWrap('/tickettimeleft: Display the time left on the ticket show countdown for either automatic or manual starting mode.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showtime: Display a message showing how long the current show has been hidden.'); cb.sendNotice(helptxtmsg, comhelpreqby, appNoticeColor); } function helpModBC(helpoption,helpreqby) { var validhelpcmd = false; if (helpoption == null) { helpoption = ''; } if (helpoption == '' && whichApp != 'none') { helpoption = whichApp; } switch (helpoption) { case 'cmds': case 'cmd': case '': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothys Ultra App - Help Menu for Broadcasters and Moderators'; helptxtmsg += '\n \u2705 ' + wordWrap('An abbreviated summary of the available commands is listed below.'); helptxtmsg += '\n \u2705 ' + wordWrap('You can also type /uahelp [submenu], where [submenu] is one of the following choices, to display a submenu with more detailed information.'); helptxtmsg += '\n \u2705 ' + wordWrap('For example: Type "/uahelp goals" (no quotes) to see the submenu for the Progressive Goal Show related commands.'); helptxtmsg += '\n\n \u2705 SUBMENUS:'; helptxtmsg += '\n \u2022 ' + wordWrap('"all" : (/chgapp, /stats, /addtips, /listgoals (/lg), /listremgoals (/lrg),/setgentext, /gentextposn, /ninjatipon, /ninjatipoff, /uachgtheme, /uachgint)'); helptxtmsg += '\n \u2022 ' + wordWrap('"goals" : (/restartgoal, /setgoal1, /setgoal2, /setgoal3...(thru /setgoal20), /rmvgoal, /setgoaltext, /next, /skip, /resetapp)'); helptxtmsg += '\n \u2022 ' + wordWrap('"goalcount" : (/setcount1, /setcount2, /setcount3...(thru /setcount10), /rmvcount, /setcounttext, /chgcountgoal, /skip, /skiplevel, /resetapp)'); helptxtmsg += '\n \u2022 ' + wordWrap('"spank" : (/setspankgoal1, /setspankgoal2,...(thru /setspankgoal5), /rmvspankgoal, /setspanktext, /skip, /spanked, /spankmenu, /spanktips, /spanktotals, /spankall, /resetapp)'); helptxtmsg += '\n \u2022 "autoreset" : (/setautogoal, /setautofinal, /setautotext, /resetapp, /skip)'; helptxtmsg += '\n \u2022 "ticket" : (see full command list in submenu "ticketshow")'; helptxtmsg += '\n \u2022 "about" : See a summary of the app current version and release notes'; cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'about': { validhelpcmd = true; displayAbout(helpreqby); break; } case 'general': case 'gen': case 'all': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - General Commands'; helptxtmsg += '\n \u2022 ' + wordWrap('These commands are used across all app features. The primary command noted below is the /chgapp command, which lets you switch between each of the 5 apps that are built into the UltraApp so far. You can switch at any time, but you if you change in the middle of a goal, you cancel any open goals in that app when you do. Total tipping and time online stats are also kept and can be accessed anytime.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgapp [newapp] : switch from one app to another, or turn off the current app. The values used for [newapp] are "goals", "goalcount", and "ticket" to go along with the 3 types of shows, or the value of "none" to turn off the current app feature and not start a new one immediately.'); helptxtmsg += '\n \u2022 ' + wordWrap('/stats : Display a listing of your time online (with the app running), total tips, and tips broken down by app.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addtips [tokens] : Within the goal apps (Progressive Goal, Goal Count, and Tip Jar), this can be used to simulate users having tipped and advance the token count within a goal. Indicate the number of tokens you are adding as the [tokens] parameter, and note the value can be negative. You can add more than the current goal, but you cannot subtract less than has been tipped in the current goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/listgoals (also /lg) : List the current setup of all goals for the App feature that is active. You can add the "all" parameter to display to the entire chat ("/lg all" for example).'); helptxtmsg += '\n \u2022 ' + wordWrap('/listremgoals (also /lrg) : List the current setup of the remaining goals for the App feature that is active. You can add the "all" parameter to display to the entire chat ("/lrg all" for example).'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgpanelbg [imagename]: Change the background of the drawpanel to one of the valid images. Images are updated regularly, so you can see the current list by entering this command with no parameter and the error message will show the current valid choices. Note that you can also see the images by going to the "Source Code" tab for the app and clicking the link for "App Images".'); helptxtmsg += '\n \u2022 ' + wordWrap('/paneltextcolor [newcolor]: Change the color of the text in the drawpanel to either a hex code (#0000ff) or the exact text of one of the color choices from the menu ("Dark Green", "Dark Red", etc). You can lookup hex codes for any color on a site such as Color-hexa: https://www.colorhexa.com/ '); helptxtmsg += '\n \u2022 ' + wordWrap('/paneltext1 [newtext] (also /paneltext2, /paneltext3): When there is no App running, change the text displayed in the draw panel, separate commands for lines 1-3, max of 50 characters each. If an app is running but completed, can change to no app running with "/chgapp none".'); helptxtmsg += '\n \u2022 ' + wordWrap('/seepanels: Rotate through a 10 second preview of each of the available panel backgrounds so you can find one you like. See the /stoppanel and /freezepanel commands that accompany this feature.'); helptxtmsg += '\n \u2022 ' + wordWrap('/stoppanels: Stop the panel background preview and reset the background to what it was before starting.'); helptxtmsg += '\n \u2022 ' + wordWrap('/freezepanels: Stop the panel background preview and keep it on the current preview panel being displayed.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setgentext [newtext]: Update the general text that is shown across all app features.'); helptxtmsg += '\n \u2022 ' + wordWrap('/gentextposn [0 or 1]: Update the default setting for whether the General Text shown across all App features is shown at the beginning or the end of the room title.'); helptxtmsg += '\n \u2022 ' + wordWrap('/ninjatipon, /ninjatipoff: These commands let any viewer toggle whether their tips are counting toward the goals. The commands are not valid when using the ticket show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/uachgint [notice type] [new interval]: Change one of the recurring notice intervals. This command requires two parameters, to define the notice type, and to define the new interval (in minutes), in the format "/uachgint [notice type] [new interval]". You can enter the command with no parameters to see the valid notice types. The [new interval] is the new display interval in minutes. An example of a valid command would be "/uachgint goals 3.2" to update the Progressive Goals Notice interval to 3.2 minutes.'); helptxtmsg += '\n \u2022 ' + wordWrap('/emojis: Display a sample listing of emojis that can be used in the chat, in app and bot messaging, roomtitles, etc.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setbrdsep [emoji or character]: (mods/bc only) Updates the emoji or text character used for the border of the recurring notices for each type of goal feature. if you do not want to use an emoji, you can type in a characters of text like "-" or "*" (no quotes). It is not recommended to change from emoji to text or text to emoji during the show as the spacing has already been defined by the separator type'); helptxtmsg += '\n \u2022 ' + wordWrap('/uasetbrdsep [emoji or character]: (mods/bc only) Same command as /setbrdsep above, but only updates for the UltraApp (the /setbrdsep command will update across all Dorothy Apps currently running)'); helptxtmsg += '\n \u2022 ' + wordWrap('/addfan [user]: (bc only) From within the show, adds a user to the External Fan Club list where [user] is the person you want to add. Normally these users are entered in the Fan Club List field on the launch page, but the command can be used if there is a new fan during the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvfan [user]: (bc only) Removes a user from the External Fan Club List where [user] is the person you want to remove.'); helptxtmsg += '\n \u2022 ' + wordWrap('/fanlist : (mods/bc only) Displays the list of users currently in the External Fan Club list.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addvip [user]: (bc only) From within the show, adds a user to the VIP list where [user] is the person you want to add. Normally these users are entered in the VIP List field on the launch page, but the command can be used if there is a new VIP during the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvvip [user]: (bc only) Removes a user from the VIP List where [user] is the person you want to remove.'); helptxtmsg += '\n \u2022 ' + wordWrap('/viplist : (mods/bc only) Displays the list of users currently in the VIP list.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addmod [username]: (bc only) Add users to the UltraApp Moderator List.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvmod [username]: (bc only) Remove users from the UltraApp Moderator List.'); helptxtmsg += '\n \u2022 ' + wordWrap('/modlist: (mods/bc only) Displays the list of users currently in the UltraApp Moderator list (list will also include broadcaster and CB mods).'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'goals': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Progressive Goals'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Progressive Goals specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2022 ' + wordWrap('For the progressive goal show, you can setup a single goal, or multiple goals that will be progressed through in sequence as users tip. There is a configuration flag that defines if the app automatically moves from one goal to the next, or requires the use of the /next command to advance after the goal is met. If set to automatic advance, tips that exceed the goal will carry over to the next goal. If set to advance by command, the tips do not roll over.'); helptxtmsg += '\n \u2022 ' + wordWrap('By default, the room subject will show the current goal amount and description, the next goal description and a configurable block of text that you can use to describe what happens at the last goal or after the goals, or even just put searchable hashtags to draw more people into the show. This room subject text can be edited using the "/setgoaltext" command as noted below.'); helptxtmsg += '\n \u2022 ' + wordWrap('Any changes you make are not stored permanently, they are only stored within the current session. They will be kept if you switch between app features, but not if you deactivate the app.'); helptxtmsg += '\n \u2022 ' + wordWrap('Also, you must keep the goals in sequence, and cannot add an entry that would leave an empty level. For example, if goal levels 1-3 are filled in, you can\'t add a level 5, you must add a level 4 first.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/restartgoal : If you\'d like to repeat a goal (common if you have a single goal you\'re recycling), you can use this command at any time to reset the tip count on the current goal to 0 (even if already complete)'); helptxtmsg += '\n \u2022 ' + wordWrap('/setgoal1, /setgoal2, /setgoal3...(thru /setgoal20) [goal] [description] : These are the commands that let you edit the goals for your show. Both the [goal] and [description] parameters must be entered every time and will update both values. Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n ' + wordWrap('--- The 1-20 designation as part of the command identifies which entry you are modifying.'); helptxtmsg += '\n ' + wordWrap('--- The [goal] parameter is the new value you are setting for the goal amount. Even if you are not changing the goal (only changing the description, you must still enter the existing value for the goal.'); helptxtmsg += '\n ' + wordWrap('--- The [description] Parameter is the new value you are setting for the goal description. Even if you are not changing the description (only changing the goal amount), you must still enter the existing value for the description.'); helptxtmsg += '\n ' + wordWrap('--- An example of the syntax for this command would be "/setgoal4 400 Blow job", which would set goal 4 to be a Blow Job once you reach 400 tokens.'); helptxtmsg += '\n ' + wordWrap('--- Note that you can\'t make updates to the current goal or past goal, only future goals.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvgoal [level] : Remove the goal entry for goal level of [level] (amount and description are both removed). Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setgoaltext [newsubject] : Update the text that is shown in the Room Subject between the current goal and the next goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/goal [goal] : Used to manually select the next [goal] once the current goal is complete, when auto advance is turned off. You can choose a new goal or one that was already completed.'); helptxtmsg += '\n \u2022 ' + wordWrap('/next : Used to advance to the next goal once the current goal is complete, when auto advance is turned off.'); helptxtmsg += '\n \u2022 ' + wordWrap('/skip : Advance to the next goal regardless of the status of the current goal.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'autoreset': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Auto-reset Goal'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Autoreset Goals specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2022 ' + wordWrap('For the Auto-Reset Goal show, you define a single goal that will be recycled repeatedly each time it is hit. Tips that exceed the current goal will carry over and be applied to the next goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('You can also configure if there is and end to the show after a certain number of goals, or if it just keeps going, and if there is a final goal prize after all goals are complete.'); helptxtmsg += '\n \u2022 ' + wordWrap('By default, the room subject will show the current goal amount and description, the final goal count and grand prize (if used), and a configurable block of text that you can use to describe the show, and even put searchable hashtags to draw more people into the show. This room subject text can be edited using the "/setautotext" command as noted below.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/setautogoal [amount] [description]: This command allows you to edit the goal amount and goal description for your show (or just goal amount). Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setautofinal [amount] [description]: This command allows you to edit the final number of goals for End of Show / Grand Prize, and the description of that prize or what happens at the end of the show. You can also just change the number of goals without changing the description). Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setautotext [description]: Update the configurable text that describes the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/resetapp: Reset the App to the very beginning to start a new show, starting over at goal 1.'); helptxtmsg += '\n \u2022 ' + wordWrap('/skip: Advance to the next goal regardless of the status of the current goal. Since all auto-reset goals are the same, this wouldn\'t likely be used much unless trying to advance closer to the end of show goal.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'goalcount': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Goal Counter'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Goal Counter specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2022 ' + wordWrap('For the Goal Counter show, you define the smaller goal amount that will be tipped repeatedly, and then define prizes that will occur at a certain number of goals reached. For example, you may define that each goal will be 99 tokens, and then at 5 goals you take off your shirt, 10 goals take off your pants, etc. If there is a final/end goal, set it up as the highest of the goal level thresholds in the configuration list, the app will stop counting once the highest goal count is met.'); helptxtmsg += '\n \u2022 ' + wordWrap('By default, the room subject will show the remaining goals levels and their descriptions/prizes, plus a configurable block of text that you can use to describe what happens at the last goal or after the goals, or even just put searchable hashtags to draw more people into the show. This room subject text can be edited using the "/setcounttext" command as noted below.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/setcount1, /setcount2, /setcount3...(thru /setcount15) [goal] [description] : These are the commands that let you edit the goal levels for your show. Both the [goal] and [description] parameters must be entered every time and will update both values. Note that you can use these commands to add or edit an entry, however they cannot be used to delete an entry (use the /rmvcount).'); helptxtmsg += '\n ' + wordWrap('--- The 1-15 designation as part of the command identifies which Goal Level entry you are adding or modifying.'); helptxtmsg += '\n ' + wordWrap('--- The [goal] parameter is the new value you are setting for the goal level (such as "10" for doing a prize at the 10th goal). Even if you are not changing the goal level (only changing the description, you must still enter the existing value for the goal level.'); helptxtmsg += '\n ' + wordWrap('--- The [description] Parameter is the new value you are setting for the goal level description. Even if you are not changing the description (only changing the goal level), you must still enter the existing value for the description.'); helptxtmsg += '\n ' + wordWrap('--- An example of the syntax for this command would be "/setcount5 20 Blow job", which would set goal level 5 to be a Blow Job, to be done after you\'ve hit the individual goals 20 times.'); helptxtmsg += '\n ' + wordWrap('--- Note that you can\'t make updates to the current goal level or past goal levels, only future goal levels.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvcount [level] : Remove the goal from goal counter level of [level] (amount and description are both removed). Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setcounttext [newsubject] : Update the text that is shown in the Room Subject after the listing of the remaining goal levels.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgcountgoal [amt] : Change the amount that is being used for the individual goal levels (99 in the above example) to a new value of [amt]. If changing the value after the tipping has already started, it cannot be set to a value that is less than the tips received against the current goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/skip : Advance to the next individual goal regardless of the status of the current goal. Note this will only do one individual goal at a time, you can use the command /skiplevel described below to skip past the current goal level threshold.'); helptxtmsg += '\n \u2022 ' + wordWrap('/skiplevel : Advance to the point the next goal level is complete, regardless of the status of the current goal. If there are no more goal level prizes, this will skip to the end of the show, so check the goal list (/lg) to be sure you know how what levels remaining have goals. As an example, if you are on goal 7, and there are prizes every 5 goals, it will skip ahead to having completed goal 10.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'ticket': case 'ticketshow': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Ticket Show'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Ticket Show specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2705 KEY COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/tickets : (all users) Display the list of users that have bought or been given a ticket. If the parameter of "alpha" is added, the list is displayed alphabetically. Note that viewers can be added back to the show using the "/add" command and pasting the list that is shown from the /tickets command.'); helptxtmsg += '\n \u2022 ' + wordWrap('/add or (/addticket) [user]: (bc only, moderator when granted privileges) Manually add a user to the ticket show list. Can be a specific user or a list of users separated by a comma.'); helptxtmsg += '\n \u2022 ' + wordWrap('/startshow: (mods/bc only) Start the ticket show when not set to automatic start. Once started, the show will only be visible to ticket holders. Hint: start the show when you are in a good position for the preview pic to be frozen that will attract more ticket buyers.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showwarn (or /showover): (mods/bc only) Display a warning that the show will be ending soon and ticket purchases are allowed but not recommended. If configured, this can also end the positions menu, and reduce the ticket price.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showend (or /stopsales): (mods/bc only) Suspend ticket sales, no more automatic ticket purchases can be made. Recommended to always do this once you are less than a few minutes from the end of the show so people do not buy at the last second and are disappointed by getting a short show. If configured, this can also end the positions menu, and reduce the ticket price.'); helptxtmsg += '\n \u2022 ' + wordWrap('/stopshow: (mods/bc only) End the hidden show and return to a public broadcast.'); helptxtmsg += '\n \u2022 ' + wordWrap('/ticketprice (or /ctprice, or /chgticketprice) [newprice]: (mods/bc only) Update the ticket price to the [newprice].'); helptxtmsg += '\n \u2022 ' + wordWrap('/starttimer X: (mods/bc only) Use a number in place of "X" to start a X minute timer for the ticket show when in "timer" mode with the start of the show to be triggered by the /startshow command. The timer will count down but not automatically start the show unless configured to do so.'); helptxtmsg += '\n\n \u2705 ADDITIONAL COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/rmv (or /rmvticket or /del or /delticket) [user]: (mods/bc only) Manually remove a specific user from the ticket show list, only used for one user at a time.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addtime (or /ticketaddtime, or /addtickettime) [time]: (mods/bc only) Add [time] minutes to the timer for either automatic or manual drawing mode. The [time] value can be a negative number to subtract time, but cannot be greater than the remaining time.'); helptxtmsg += '\n \u2022 ' + wordWrap('/stoptimer (or /ticketstoptimer, or /stoptickettimer): (mods/bc only) Stop the ticket show timer for either automatic or manual drawing mode.'); helptxtmsg += '\n \u2022 ' + wordWrap('/newticketshow: (mods/bc only) Completely refresh the ticket show to start a brand new show. This will remove all the ticket holders from the list, and re-initialize all settings using the configuration from the launch page.'); helptxtmsg += '\n \u2022 ' + wordWrap('/restartshow: (mods/bc only) Go back into hidden cam mode if the show was accidentally ended too soon, or the broadcaster wanted to go back to public to sell more tickets. The ticket holder list and all settings are kept intact.'); helptxtmsg += '\n \u2022 ' + wordWrap('/restartsales: (mods/bc only) Restart ticket sales either during the show (after /showend was used), or after you\'ve ended the show to go back to the ticket sales stage. The ticket holder list, ticket price and show description are kept intact.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showstartprice [newprice]: (mods/bc only) Update the increased price that can be used at the start of the show to a new value of [newprice].'); helptxtmsg += '\n \u2022 ' + wordWrap('/tickettimeleft : (mods/bc only) Display the time left on the ticket show countdown for either automatic or manual starting mode.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showtime : (all users) Display a message showing how long the current show has been hidden.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgticketmode (or /chgtktmode) [manual/timer/ticketgoal/tokengoal]: (mods/bc only) Switch between the modes being used to determine when to start the ticket show. If switching from a timer show to a non-timer show, the timer will be ended. Ticket count and Tip Count are being tracked regardless of mode, so switching to a "goal" mode should not require starting progress at 0.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgticketauto (or /chgtktauto) [auto/bc]: (mods/bc only) Switch between the modes being used to define if the show starts automatically when a goal is reached or timer expires, or if the broadcaster or mods still control the start of the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/ticketsubject (or /ctsubject) [newsubject]: (mods/bc only) Change the room description/subject/title to a new value. This keeps the standard ticket show formatting and only changes the configurable part of the subject.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addlbtop [X]: (bc/mods - Fembot command) Add the top [X] number of tippers for the current session to the ticket show. Moderators may only use this if allowed per configuration. Note this command is actually being executed in the Fembot, so the Ultra Fembot must be running to make use of it. The /add command is executed, which will add them to the show if the user executing them has authority to the /add command.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addlbamt [X]: (bc/mods - Fembot command) Add tippers who have tipped at least [X] number of tokens for the current session to the ticket show. Moderators may only use this if allowed per configuration. Note this command is actually being executed in the Fembot, so the Ultra Fembot must be running to make use of it. The /add command is executed, which will add them to the show if the user executing them has authority to the /add command.'); helptxtmsg += '\n \u2022 ' + wordWrap('/cancelticket: (mods/bc only) Stop the automatic countdown to ticket sales that will start after the last goal if setting "1A2" is enabled.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'goalrace': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Goal Race'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Goal Race specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2022 ' + wordWrap('For the Goal Race, you define the two goal amounts and descriptions, and viewers tip toward each goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('When this app is active and a user tips, the tip panel will show a choice of the two goals to tip toward, they can choose one of the two goals, or not make a selection.'); helptxtmsg += '\n \u2022 ' + wordWrap('The user can also toggle on and off a personal setting that allows them to either send votes or regular tip notes.'); helptxtmsg += '\n \u2022 ' + wordWrap('The goals, goal descriptions, room subject text, and panel subject text are configurable on the launch page and also through the commands below, so you don\'t have to restart the app to make changes.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/restartrace: Lets you restart the goal race (reset tip amounts to 0) with the same settings.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setrace1, /setrace2 [amount] [description] : These are the commands that let you edit the goal amounts and goal descriptions for your show. Both the [amount] and [description] parameters must be entered every time and will update both values.'); helptxtmsg += '\n ' + wordWrap('--- The "1" or "2" designation as part of the command identifies which Goal Choice entry you are modifying.'); helptxtmsg += '\n ' + wordWrap('--- The [amount] parameter is the new value you are setting for the goal amount (such as "500" for a goal amount). Even if you are not changing the goal amount (only changing the description, you must still enter the existing value for the goal amount as both will be updated.'); helptxtmsg += '\n ' + wordWrap('--- The [description] parameter is the new value you are setting for the goal description (such as "Bra Off"). Even if you are not changing the description (only changing the goal amount), you must still enter the existing value for the description.'); helptxtmsg += '\n ' + wordWrap('--- An example of the syntax for this command would be "/setrace2 500 Bra Off", which would set goal #2 to be the choice of "Bra Off" (which may compete against "Panties Off, for example).'); helptxtmsg += '\n ' + wordWrap('--- Note that you can\'t make updates to the goal amount that would be less than what is already tipped for that goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addrace1, /addrace2: If you have tipped during the Race and did not specify a goal at the time, these tips are accumulated and can be "claimed" for either goal 1 or goal 2 using one of these two commands. The accrued amount cannot be split, the whole amount will go toward the goal selected.'); helptxtmsg += '\n \u2022 ' + wordWrap('/tipnoteon, /tipnoteoff: This command lets you toggle whether you are sending tip notes to the broadcaster or casting votes for the goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setracetext [newsubject] : Update the text that is shown in the Room Subject after the listing of the race goals.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setracepaneltext [newsubject] : Update the text that is shown in the First Line of the Draw Panel.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'spank': case 'spankathon': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Spank-a-thon'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Spank-thon specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2022 ' + wordWrap('The Spank-a-thon is a combination of Tip Menu and Goals. The broadcaster can configure up to 5 types of spankings to be administered (such as hand spanks, paddle spanks, riding crop, etc). For each type, a price is defined per spank, and there is configuration available for 2 more "bundle" prices as well. The types and prices can also be set using commands.'); helptxtmsg += '\n \u2022 ' + wordWrap('Goals can also be setup to set a point at which the spanks will be done. Only one goal is required and up to 5 goals can be configured.'); helptxtmsg += '\n \u2022 ' + wordWrap('There is a setting to define if all tips count toward the goal or if only spank tips count.'); helptxtmsg += '\n \u2022 ' + wordWrap('When spanks are completed, the /spanked command can be used to mark them off from the "to do" list.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/setspanktype1, /setspanktype2, ...(thru /setspanktype5) [description]: These commands let you create or edit each of the 5 available spank types, such as "hand spanks", "paddle spanks", etc. The types must be defined before bundle quantities and prices can be set with the below command. Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setspankXpY [bundle amount] [price]: For each of the 5 types (once a type is defined), you can define up to 3 bundle amounts and prices.'); helptxtmsg += '\n ' + wordWrap('--- The "X" in the command is the spank type (1-5), and the "Y" is the price level (1-3).'); helptxtmsg += '\n ' + wordWrap('--- The first parameter is how many spanks in the bundle. For example, you may set price level 1 as the price per spank, so the number would be 1. For price level 2, you may be pricing a bundle of 10, and for price level 3, you may be pricing a bundle of 50.'); helptxtmsg += '\n ' + wordWrap('--- The second parameter is the correspondnig price for each.'); helptxtmsg += '\n ' + wordWrap('--- Therefore, in the example "/setspank2p3 50 499", you would be defining that for spank type 2, price level 3, the price is 499 for a bundle of 50.'); helptxtmsg += '\n ' + wordWrap('--- Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setspankgoal1, /setspankgoal2, ...(thru /setspankgoal5) [goal] [description]: These are the commands that let you edit the spank goals for your show. Both the [goal] and [description] parameters must be entered every time and will update both values. Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n ' + wordWrap('--- The 1-5 designation as part of the command identifies which entry you are modifying.'); helptxtmsg += '\n ' + wordWrap('--- The [goal] parameter is the new value you are setting for the goal amount. Even if you are not changing the goal (only changing the description), you must still enter the existing value for the goal.'); helptxtmsg += '\n ' + wordWrap('--- The [description] Parameter is the new value you are setting for the goal description. Even if you are not changing the description (only changing the goal amount), you must still enter the existing value for the description.'); helptxtmsg += '\n ' + wordWrap('--- An example of the syntax for this command would be "/setspankgoal2 500 Administer Spanks Round 2", which would set goal 2 to be the described text once you reach 500 tokens.'); helptxtmsg += '\n ' + wordWrap('--- Note that you can\'t make updates to the current goal or past goal, only future goals.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvspankgoal [level]: Remove the goal entry for goal level of [level] (amount and description are both removed). Note this is also only a temporary change made within the session, it does not permanently update the launch page config.'); helptxtmsg += '\n \u2022 ' + wordWrap('/setspanktext [newsubject]: Update the text that is shown in the Room Subject between the current goal and the next goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/spanktotals: Display the current totals for spanks tipped for and administered per spank type.'); helptxtmsg += '\n \u2022 ' + wordWrap('/spankmenu: Display the spank menu on demand, note it also shows in the chat notice periodically.'); helptxtmsg += '\n \u2022 ' + wordWrap('/spanktips: Display the history of the spank tips.'); helptxtmsg += '\n \u2022 ' + wordWrap('/spanked X Y: (bc/mods with authority) Update the number of completed spanks for the spank type X, adding Y spanks to the number of spanks completed (subtracting from the spanks remaining).'); helptxtmsg += '\n \u2022 ' + wordWrap('/spankall: (bc/mods with authority) Mark all outstanding spanks as complete, rather than the one at a time that is done with the /spanked command.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } } if (!validhelpcmd) { cb.sendNotice(botName + helpoption + ' is not a valid subsection of the help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.',helpreqby,appNoticeColor); } } // ******************************* Upon user entry of a Message ************************************** cb.onMessage(function (msg) { var rawmsg = msg.m; var msgarray = rawmsg.split(' '); var msguser = msg.user; var msgismod = msg.is_mod; var msgisfan = msg.in_fanclub; var msgisvip = cbjs.arrayContains(VIPListArray,msguser); var msgisbc = (msguser === BC); var command = msgarray[0]; var commandvar1 = parseInt(msgarray[1]); var commandvar2 = parseInt(msgarray[2]); var listregexp = /[,\s]+/; var ismodlvlmsg1 = false; var ismodlvlmsg2 = false; var ismodlvlmsg3 = false; if (msgismod) { addRmvMods(msguser,'cbmod','a'); } else { if (cbjs.arrayContains(moderatorList.name,msguser)) { var nameidx = moderatorList.name.indexOf(msguser); if (moderatorList.type[nameidx] == 'botmod') { msgismod = true; } } } if (msgismod) { ismodlvlmsg1 = true; ismodlvlmsg2 = true; ismodlvlmsg3 = true; } if (msgismod) { addRmvModsInShow(msguser,'a'); } if (msgisfan) { addRmvFanClubInShow(msguser,'a'); } if (msgisvip) { addRmvVIPInShow(msguser,'a'); } if (msgismod || msgisfan || msgisvip) { if (whichApp == 'ticket') { checkFreeTickets(msguser,msgismod,msgisfan,msgisvip,'msg'); } } if (msgarray[0].charAt(0) == '/') { var recognizedcmd = false; msg['X-Spam'] = true; var ntc = null; for (var n1 = 1; n1 < msgarray.length; n1++) { if (n1 == 1) ntc = msgarray[n1]; else ntc += ' ' + msgarray[n1]; } var ntc2 = null; for (var n2 = 2; n2 < msgarray.length; n2++) { if (n2 == 2) ntc2 = msgarray[n2]; else ntc2 += ' ' + msgarray[n2]; } var cmdval = null; for (var n3 = 1; n3 < msgarray.length; n3++) { if (n3 == 1) cmdval = msgarray[n3]; else cmdval += ' ' + msgarray[n3]; } switch (command) { //********* General Ultra App Commands case '/chgapp': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var newapp = msgarray[1].toLowerCase(); if (newapp != 'ticket' && newapp != 'goals' && newapp != 'goalcount' && newapp != 'goalrace' && newapp != 'spank' && newapp != 'autoreset' && newapp != 'none') { cb.sendNotice(botName + 'The value entered for the app to use is invalid, please try again using a value of "ticket", "goals", "autoreset", "goalcount", "goalrace", "spank", or "none".', msguser, appNoticeColor); } else { if (validateFeature(newapp)) { if (newapp === whichApp) { cb.sendNotice(botName + 'The value entered for the new mode is the same as the existing mode, command ignored.', msguser, appNoticeColor); } else { setAppFeature(newapp, msguser); } } } } else { cb.sendNotice(botName + 'This command requires a parameter to define to the new app feature that is to be used, in the format "/chgapp [appname]", where [appname] is one of the defined app features ("ticket", "goals", "autoreset", "goalcount", "goalrace", "spank", or "none").', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/chgpanelbg': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var newbg = msgarray[1].toLowerCase(); customizePanelBackground(newbg,msguser,'cmd'); if (panelPreviewOn) { panelPreviewOn = false; cb.sendNotice('You have stopped the panel background preview.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The /chgpanelbg command requires the entry of a parameter following the command, such as "/chgpanelbg lavalamp". The valid formats are: \n' + cbjs.arrayJoin(backgroundArray.command, ', '), msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/paneltextcolor': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var pnltxtclr = rawmsg.substring(16).trim(); if (pnltxtclr) { customizePanelText(pnltxtclr,msguser); } else { cb.sendNotice(botName + 'The /paneltextcolor command requires the entry of a parameter following the command, which represents either the color name or the color hex code, such as "/paneltextcolor Blue" or "/paneltextcolor #0000ff".' , msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/seepanel': case '/seepanels': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (panelPreviewOn) { cb.sendNotice('A panel preview has already been started.', msguser, appNoticeColor); } else { cb.sendNotice('Beginning the Panel Background Preview\nEvery 10 seconds, the panel background will change.\nEach time it changes, you will receive a message with the name of the current panel.\nTo stop the preview, and return to your previous panel background, type: /stoppanel \nTo stop the preview, and stay on the currently previewed panel background, type: /freezepanel \nTo update the panel manually, use the /chgpanel command (a successful update will also stop the preview)', msguser, appNoticeColor); savedPanelBackground = currentPanel; savedPanelFilename = backgroundImage; savedBotPanel = botPanel; currentPanelCycleIndex = 0; panelPreviewOn = true; panelPreviewer = msguser; cyclePanels(); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/stoppanel': case '/stoppanels': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (panelPreviewOn) { currentPanel = savedPanelBackground; backgroundImage = savedPanelFilename; botPanel = savedBotPanel; cb.drawPanel(); panelPreviewOn = false; cb.sendNotice('You have stopped the panel background preview and returned to the previously used panel.\nPlease wait at least 10 seconds before using the /seepanels command again.', msguser, appNoticeColor); } else { cb.sendNotice('The panel preview is not currently active.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/freezepanel': case '/freezepanels': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (panelPreviewOn) { panelPreviewOn = false; botPanel = true; cb.sendNotice('You have stopped the panel background preview and kept the current preview panel.\nPlease wait at least 10 seconds before using the /seepanels command again.', msguser, appNoticeColor); } else { cb.sendNotice('The panel preview is not currently active.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/next': { recognizedcmd = true; if (whichApp == 'goals') { if (msgisbc || ismodlvlmsg2) { if (finalGoalMet) { cb.sendNotice(botName + 'Command cannot be used once all goals have been completed.', msguser, appNoticeColor); } else if (currentGoalTips > 0) { cb.sendNotice(botName + 'Command cannot be used during goal, it should be used only to manually advance a goal once complete if the auto-advance is off. The /skip command can be used to bypass the rest of a goal.', msguser, appNoticeColor); } else if (cb.settings.progressiveAutoNext == 'Select next goal from list' && (currentGoal+1) > progGoalArray.desc.length) { cb.sendNotice(botName + 'Already at the highest goal in the list, please select a specific goal with the /goal command, or add another goal.', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'You have manually advanced to the next goal.', msguser, appNoticeColor); nextGoal(); cb.drawPanel(); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/next" command is for use with the Progressive Goals, which is not currently running.', msguser, appNoticeColor); } break; } case '/skip': { recognizedcmd = true; if (whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'spank' || whichApp == 'autoreset') { if (msgisbc || ismodlvlmsg2) { if (finalGoalMet) { cb.sendNotice(botName + 'Command cannot be used once all goals, cycles or sequences have been completed.', msguser, appNoticeColor); } else { skipGoal(); cb.sendNotice(botName + 'You have skipped the remainder of the current goal, cycle, or sequence and advanced to the next.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/skip" command is for use with the Progressive Goals, Auto-Reset Goal, or Goal Counter apps (current app is ' + whichApp + ').', msguser, appNoticeColor); } break; } case '/skiplevel': { recognizedcmd = true; if (whichApp == 'goalcount') { if (msgisbc || ismodlvlmsg2) { if (finalGoalMet) { cb.sendNotice(botName + 'Command cannot be used once all goals, cycles or sequences have been completed.', msguser, appNoticeColor); } else if (whichApp == 'goalcount' && (currentGoalLevel) > goalCounterArray.amt.length) { cb.sendNotice(botName + 'No further Goal Counter Prize Levels to skip.', msguser, appNoticeColor); } else { advanceGoalLevel(); cb.sendNotice(botName + 'You have skipped the remainder of the current Prize Level and advanced to the next Prize Level.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/skiplevel" command is for use with the Goal Counter app (current app is ' + whichApp + ').', msguser, appNoticeColor); } break; } case '/addtips': { recognizedcmd = true; if (whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'goalrace' || whichApp == 'spank' || whichApp == 'autoreset') { if (msgisbc || ismodlvlmsg2) { if (commandvar1 == 0 || isNaN(commandvar1)) { if (whichApp == 'goalrace') { cb.sendNotice(botName + 'The first parameter is the tip amount to be added to the current tip count, and must be a positive or negative number other than 0. For example, use "/addtips 100 1" to simulate a user having tipped 100 tokens for goal choice 1.',msguser,appNoticeColor); } else { cb.sendNotice(botName + 'The parameter is the tip amount to be added to the current tip count, and must be a positive or negative number other than 0. For example, use "/addtips 100" to simulate a user having tipped 100 tokens.',msguser,appNoticeColor); } } else if (finalGoalMet) { cb.sendNotice(botName + 'Command cannot be used once all goals, cycles or sequences have been completed.', msguser, appNoticeColor); } else if (whichApp == 'goalrace' && commandvar2 != 1 && commandvar2 != 2) { cb.sendNotice(botName + 'For Goal Race, the second parameter must be a "1" or a "2" to specify which goal the amout is added. Example syntax to add 25 tokens to Goal Choice 2 is "/addtips 25 2".', msguser, appNoticeColor); } else { var addtipnote = ''; if (whichApp == 'goalrace' && commandvar2 == 1) { addtipnote = goalraceDesc1; } else if (whichApp == 'goalrace' && commandvar2 == 2) { addtipnote = goalraceDesc2; } recordTip(commandvar1,'bc',addtipnote,msgisfan,msgisvip,'broadcaster'); if (commandvar1 > 0) { cb.sendNotice(botName + 'You have added ' + commandvar1 + ' tokens.', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'You have subtracted ' + Math.abs(commandvar1) + ' tokens.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/addtips" command is only for use with the Tip Jar, Progressive Goals, Auto-Reset Goal, Goal Race, Spank-a-thon, or Goal Counter (current app is ' + whichApp + ').', msguser, appNoticeColor); } break; } case '/stats': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { cb.sendNotice('Current Session Stats:' + '\n \u21D2 UltraApp Time Online................ ' + timeOnline() + '\n \u21D2 Total Tips................................. ' + currentSessionTotal + ' tokens ($' + Number(currentSessionTotal*.05).toFixed(2) + ' @ 5 cents per token)' + (currentAppTotalGoal > 0 ? '\n \u21D2 \u21D2 \u21D2 Progressive Goal Total....... ' + currentAppTotalGoal + ' tokens' : '') + (currentAppTotalAutoreset > 0 ? '\n \u21D2 \u21D2 \u21D2 Auto-Reset Goal Total........ ' + currentAppTotalAutoreset + ' tokens' : '') + (currentAppTotalCounter > 0 ? '\n \u21D2 \u21D2 \u21D2 Goal Counter Total............ ' + currentAppTotalCounter + ' tokens' : '') + (currentAppTotalGoalRace > 0 ? '\n \u21D2 \u21D2 \u21D2 Goal Race Total................. ' + currentAppTotalGoalRace + ' tokens' : '') + (currentAppTotalSpanks > 0 ? '\n \u21D2 \u21D2 \u21D2 Spank-a-thon Total........... ' + currentAppTotalSpanks + ' tokens' : '') + (currentAppTotalTicket > 0 ? '\n \u21D2 \u21D2 \u21D2 Ticket Show Total............. ' + currentAppTotalTicket + ' tokens' : '') + (countTickets > 0 ? '\n \u21D2 \u21D2 \u21D2 \u21D2 Total Tickets Sold.............. ' + countTickets + ' tickets' : '') + (currentAppTotalNone > 0 ? '\n \u21D2 \u21D2 \u21D2 No App Running Total....... ' + currentAppTotalNone + ' tokens' : ''), msguser, appNoticeColor, '', 'bold'); } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/lg': case '/listgoals': { recognizedcmd = true; if (whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'spank' || whichApp == 'autoreset' || whichApp == 'goalrace') { if (msgisbc || ismodlvlmsg1) { if (!msgarray[1] || msgarray[1] == 'all') { goalNotices(msgarray[1],msguser,'a','cmd'); } else { cb.sendNotice(botName + 'Invalid parameter provided, the parameter should be left blank to send the list to only the requester, or can be set to "all" to send to the entire room.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/listgoals" command is for use with the Progressive Goals, Auto-Reset Goal, Goal Count, Spank-a-thon, and Goal Race apps.', msguser, appNoticeColor); } break; } case '/lrg': case '/listremgoals': { recognizedcmd = true; if (whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'spank' || whichApp == 'autoreset' || whichApp == 'goalrace') { if (msgisbc || ismodlvlmsg1) { if (!msgarray[1] || msgarray[1] == 'all') { goalNotices(msgarray[1],msguser,'r','cmd'); } else { cb.sendNotice(botName + 'Invalid parameter provided, the parameter should be left blank to send the list to only the requester, or can be set to "all" to send to the entire room.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/listremgoals" command is for use with the Progressive Goals, Auto-Reset Goal, Goal Count, Spank-a-thon, and Goal Race apps.', msguser, appNoticeColor); } break; } case '/resetapp': { recognizedcmd = true; if (whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'autoreset') { if (msgisbc || ismodlvlmsg3) { resetApp(); cb.sendNotice(botName + 'You have reset the App and started the goal show again at the first goal.', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/resetapp" command is for use with the Progressive Goals, Auto-Reset Goal, and Goal Count apps.', msguser, appNoticeColor); } break; } case '/paneltext1': case '/paneltext2': case '/paneltext3': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var linenum = parseInt(msgarray[0].substring(10)); var paneltext = rawmsg.substring(12).trim(); if (!paneltext) { cb.sendNotice(botName + 'No value was specified for the new panel text line ' + linenum + '.', msguser, appNoticeColor); } else if (whichApp == 'none') { if (linenum == 1) { noAppPanelText1 = paneltext; cb.drawPanel(); cb.sendNotice(botName + 'Panel Line 1 Text has been updated.', msguser, appNoticeColor); } else if (linenum == 2) { noAppPanelText2 = paneltext; cb.drawPanel(); cb.sendNotice(botName + 'Panel Line 2 Text has been updated.', msguser, appNoticeColor); } else if (linenum == 3) { noAppPanelText3 = paneltext; panelText3Updated = true; cb.drawPanel(); cb.sendNotice(botName + 'Panel Line 3 Text has been updated.', msguser, appNoticeColor); } cb.drawPanel(); } else if (whichApp == 'ticket' && linenum == 3 && showStage == 'aftershow') { if (linenum == 3) { ticketAppPanelText3 = paneltext; cb.drawPanel(); cb.sendNotice(botName + 'Panel Line 3 Text has been updated.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/paneltextX" command is only for use when no app feature is running, or for line 3 when in the "after show" portion of the Ticket Show Feature. For example, at the end of the show, you may turn off all the features and simply put a text message in the panel. Message can either be set on launch page or with this command.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/setgentext': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { genericRoomSubjectSfx = rawmsg.substring(12).trim() if (genericRoomSubjectSfx != '' && genericRoomSubjectSfx != null) { changeRoomSubject(); } else { cb.sendNotice(botName + 'No value was specified for the new generic room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/gentextposn': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { if (msgarray[1]) { if (msgarray[1] != '0' && msgarray[1] != '1') { cb.sendNotice(botName + 'Invalid parameter value, valid values are "0" for the beginning or "1" for the end of the room title.', msguser, appNoticeColor); } else { if (msgarray[1] == '0') { if (genericRoomSubjectPosn == 'Beginning') { cb.sendNotice(botName + 'The General Text position is already set to "Beginning".', msguser, appNoticeColor); } else { genericRoomSubjectPosn = 'Beginning'; cb.sendNotice(botName + 'The General Text Position has been updated to "Beginning".', msguser, appNoticeColor); changeRoomSubject(); } } else if (msgarray[1] == '1') { if (genericRoomSubjectPosn == 'End') { cb.sendNotice(botName + 'The General Text position is already set to "End".', msguser, appNoticeColor); } else { genericRoomSubjectPosn = 'End'; cb.sendNotice(botName + 'The General Text Position has been updated to "End".', msguser, appNoticeColor); changeRoomSubject(); } } } } else { cb.sendNotice(botName + 'No parameter was specified for the new position, valid values are "0" for the beginning or "1" for the end of the room title.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/uasetbrdsep': case '/setbrdsep': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (cb.settings.noticeSepStyle == 'Heavy Dashed Line' || cb.settings.noticeSepStyle == 'Light Dashed Line' || cb.settings.noticeSepStyle == 'No Border') { cb.sendNotice(botName + ' The Notice Border character can only be updated if the bot is configured for a custom emoji or custom unicode character.', msguser, appNoticeColor); } else if (!msgarray[1]) { cb.sendNotice(botName + ' The parameter for the new Notice Border character was not specified.', msguser, appNoticeColor); } else { borderChar = msgarray[1]; noticeBorderChar(); cb.sendNotice(botName + ' Notice Border character has been updated to " ' + borderChar + ' ".\nNote this does not apply to notices for the ticket show.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/ninjatipon': case '/ninjatip': { recognizedcmd = true; if (whichApp == 'ticket') { cb.sendNotice(botName + 'This command cannot be used during a ticket show.', msguser, appNoticeColor); } else { if (cbjs.arrayContains(ninjaTipsOn, msguser)) { cb.sendNotice(botName + 'You have already enabled Ninja Tipping for yourself during this show. Note you can turn this off with the command /ninjatipoff.', msguser, appNoticeColor); } else { ninjaTipsOn.push(msguser); cb.sendNotice(botName + 'You have enabled Ninja Tipping for yourself during this show, you will now be able to select the --NOGOAL-- tip note so yor tips do not count toward goal. You can switch back to regular tipping using the command /ninjatipoff.', msguser, appNoticeColor); } } break; } case '/ninjatipoff': { recognizedcmd = true; if (cbjs.arrayContains(ninjaTipsOn, msguser)) { var ntidx = ninjaTipsOn.indexOf(msguser); ninjaTipsOn.splice(ntidx,1); cb.sendNotice(botName + 'You have disabled Ninja Tipping for yourself, your tips will now count toward goals.', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'You have already disabled (or never enabled) Ninja Tipping for yourself.', msguser, appNoticeColor); } break; } case '/uachgint': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { let newtimer = parseFloat(msgarray[2]); if (msgarray[1]) { let intervaltochange = msgarray[1].toLowerCase(); if (!cbjs.arrayContains(noticeIntervals.name,intervaltochange)) { cb.sendNotice('The value entered for the notice type to change is invalid, please try again. The valid types are: \n' + cbjs.arrayJoin(noticeIntervals.name, ', '), msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice('This command requires a parameter to define the new interval (in minutes), in the format "/uachgint [notice type] [new interval]", where [new interval] is the new display interval in minutes. An example of a valid command would be "/uachgint goals 3.2" to update the Goals Notice interval to 3.2 minutes.', msguser, appNoticeColor); } else if (isNaN(newtimer)) { cb.sendNotice('The second parameter requires a numeric value to define the new interval (in minutes), in the format "/uachgint [notice type] [new interval]", where [new interval] is the new display interval in minutes. An example of a valid command would be "/uachgint goals 3.2" to update the Goals Notice interval to 3.2 minutes.', msguser, appNoticeColor); } else if (newtimer > 0 && newtimer < 1) { cb.sendNotice('The new value for the timer interval must be "0" or a number greater than or equal to "1" (such as 1, 3, 4.5, etc). Setting to zero will disable the Notice.', msguser, appNoticeColor); } else { updateIntervalArray(intervaltochange,newtimer,0); cb.sendNotice('The "' + intervaltochange + '" notice timer has been updated to a new interval of ' + newtimer + ' minutes. This will take effect after the next display of the notice.', msguser, appNoticeColor); } } else { cb.sendNotice('This command requires a parameter to define the notice timer that is to be updated.\n The command format is "/uachgint [notice type] [new interval]", where [notice type] is one of the values from this list: \n' + cbjs.arrayJoin(noticeIntervals.name, ', ') + '\nAn example of a valid command would be "/uachgint goals 3.2" to update the Goals Notice interval to 3.2 minutes.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/emoji': case '/emojis': { if (msgisbc || ismodlvlmsg1) { if (cb.settings.fembotRunning == 'No') { let emojimsg = wordWrap('The following is a sample listing of some emojis and unicode characters that can be copied and pasted into the App launch page values for emoji borders or used in other text strings such as a welcome message, goal name, or room title. Note that the room title will not allow some of the more complex emojis so those are separated out in the list.'); emojimsg += '\nRandom 1: \uD83D\uDC45 \uD83D\uDC44 \uD83D\uDC41\uFE0F \uD83D\uDCAA \uD83D\uDC59 \uD83D\uDC60 \uD83D\uDC5C \uD83D\uDCA5 \uD83D\uDCA6 \uD83D\uDECF\uFE0F \uD83E\uDDFB \uD83D\uDEBD \uD83D\uDCFD\uFE0F \uD83D\uDCA1 \uD83D\uDCFA \uD83D\uDD12 \uD83C\uDF0E \u2705 \u274C \u2796 \u2755 \u2754 \u260E\uFE0F'; emojimsg += '\nRandom 2: \uD83D\uDC51 \uD83D\uDD0B \uD83D\uDDA5\uFE0F \uD83C\uDF7D\uFE0F \uD83C\uDF7A \uD83C\uDF78 \uD83E\uDDCA \u2615 \uD83D\uDD95 \uD83D\uDC4F \uD83D\uDE4F \uD83D\uDCEA \uD83D\uDCE7 \u2709\uFE0F \uD83D\uDCB2 \uD83D\uDCB0 \uD83C\uDFB5 \uD83E\uDD41 \uD83C\uDFB8 \uD83C\uDF34 \uD83C\uDF35'; emojimsg += '\nRandom 3: \u2601\uFE0F \uD83D\uDD25 \u26A1 \uD83C\uDF29\uFE0F \uD83C\uDF19 \u2600\uFE0F \u2744\uFE0F \u2B50 \u23F0 \u231B \u231A \uD83D\uDD5B \uD83D\uDEE1\uFE0F \u2694\uFE0F \uD83D\uDDE1\uFE0F \uD83C\uDFF9 \uD83D\uDEA7 \uD83D\uDED1 \u2693'; emojimsg += '\nAnimal Faces: \uD83D\uDE38 \uD83D\uDC3B \uD83D\uDC2E \uD83D\uDC36 \uD83E\uDD81 \uD83D\uDC35 \uD83D\uDC3C \uD83D\uDC37 \uD83E\uDD8A'; emojimsg += '\nAnimals: \uD83D\uDC12 \uD83D\uDC18 \uD83D\uDC04 \uD83E\uDD84 \uD83E\uDD98 \uD83D\uDC1F \uD83D\uDC19 \uD83D\uDC09 \uD83D\uDC22 \uD83E\uDD9E \uD83E\uDD8B \uD83D\uDD77\uFE0F \uD83D\uDD78\uFE0F \uD83D\uDC1E'; emojimsg += '\nFlowers: \uD83C\uDF3C \uD83C\uDF38 \uD83C\uDFF5\uFE0F \uD83C\uDF37 \uD83C\uDF3B'; emojimsg += '\nFood: \uD83C\uDF69 \uD83C\uDF4C \uD83C\uDF52 \uD83C\uDF51 \uD83C\uDF49 \uD83C\uDF53 \uD83E\uDD68 \uD83C\uDF54 \uD83C\uDF2D \uD83C\uDF55 \uD83C\uDF2E \uD83E\uDDC1 \uD83E\uDD55 \uD83C\uDF46 \uD83C\uDF44'; emojimsg += '\nFaces: \uD83D\uDE32 \uD83D\uDE22 \uD83E\uDD21 \uD83D\uDC7D \uD83E\uDD16 \uD83D\uDCA9 \uD83D\uDE0D \uD83D\uDC7F \uD83D\uDC80 \uD83D\uDE44 \uD83D\uDE10 \uD83D\uDE42 \uD83D\uDE07 \uD83D\uDE43'; emojimsg += '\nHearts: \u2764\uFE0F \uD83E\uDDE1 \uD83D\uDC9B \uD83D\uDC9A \uD83D\uDC99 \uD83D\uDC9C \uD83E\uDD0D \uD83D\uDDA4 \uD83D\uDC94'; emojimsg += '\nAwards: \uD83E\uDD47 \uD83E\uDD48 \uD83E\uDD49 \uD83C\uDFC6'; emojimsg += '\nSymbols: \u25AA\uFE0F \u25FE \u25AB\uFE0F \u25FD \uD83D\uDD39 \uD83D\uDD38 \u26D4 \u2622\uFE0F \uD83D\uDEAB \u26A0\uFE0F'; emojimsg += '\nNumbers: 1\uFE0F\u20E3 2\uFE0F\u20E3 3\uFE0F\u20E3 4\uFE0F\u20E3 5\uFE0F\u20E3 6\uFE0F\u20E3 7\uFE0F\u20E3 8\uFE0F\u20E3 9\uFE0F\u20E3 \uD83D\uDD1F'; emojimsg += '\nSports and Gaming: \u265F\uFE0F \u2663\uFE0F \u2665\uFE0F \u2660\uFE0F \uD83C\uDFB0 \uD83C\uDFB1 \uD83D\uDD79\uFE0F \uD83C\uDFAE \u26BE \uD83C\uDFC8 \u26BD'; emojimsg += '\nSeasonal: \u2601\uFE0F \uD83C\uDF84 \u26C4 \uD83C\uDF81 \uD83C\uDF80 \uD83C\uDF85 \uD83C\uDF83 \uD83E\uDD87 \u2618\uFE0F \uD83C\uDF40'; emojimsg += '\nUnicode characters and the following emojis can be used in your room title:'; emojimsg += '\nSample unicode characters: \u2103 \u2109 \u21D0 \u21D1 \u21D2 \u21D3 \u21D4 \u2655 \u265A \u2776 \u2777 \u2778 \u2764 \u2756 \u2749 \u272D \u2740 \u27A4 \uA5BB \uA564 \uA66C \uFFFD'; emojimsg += '\nRoom Title Set 1: \u231B \u2744\uFE0F \u2B50 \u2600\uFE0F \u2764\uFE0F \u26F5 \u26EA \u26A1 \u231A \u26D4 \u2694\uFE0F \u26A0\uFE0F \u26C4 \u2693 \u2618\uFE0F'; emojimsg += '\nRoom Title Set 2: \u2663\uFE0F \u2665\uFE0F \u2660\uFE0F \u25AA\uFE0F \u25FE \u25AB\uFE0F \u25FD \u2615 \u2709\uFE0F 1\uFE0F\u20E3 2\uFE0F\u20E3 3\uFE0F\u20E3 4\uFE0F\u20E3 5\uFE0F\u20E3 6\uFE0F\u20E3 7\uFE0F\u20E3 8\uFE0F\u20E3 9\uFE0F\u20E3'; cb.sendNotice(emojimsg, msguser); } else { cb.sendNotice(botName + 'Since setting "3K" indicates the Fembot is also running, the emojis list is not displayed by this App.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/help': { recognizedcmd = true; let helpcmdtext = 'The Dorothy Apps and Bots each have their own help command, please use one of the following:'; helpcmdtext += '\n/uahelp - Dorothy\'s UltraApp (currently running)'; cb.sendNotice(helpcmdtext, msguser, appNoticeColor); break; } //********* Progressive Goal Commands case '/restartgoal': { recognizedcmd = true; if (whichApp == 'goals') { if (msgisbc || ismodlvlmsg2) { restartGoal(); cb.sendNotice('You have restarted the current goal.', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/restartgoal" command is for use with the Progressive Goals app, which is not running.', msguser, appNoticeColor); } break; } case '/setgoal1': case '/setgoal2': case '/setgoal3': case '/setgoal4': case '/setgoal5': case '/setgoal6': case '/setgoal7': case '/setgoal8': case '/setgoal9': case '/setgoal10': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var goalnum = parseInt(msgarray[0].substring(8)); if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice('The first parameter is the new amount for goal ' + goalnum + ' and has be be a number greater than 0. For example, "/setgoal' + goalnum + ' 300 Shirt Off" to set goal ' + goalnum + ' to Shirt off at 300 tokens.', msguser, appNoticeColor); } else if (goalnum == currentGoal && commandvar1 <= (currentGoalTotal - currentGoalTips)) { cb.sendNotice('The current goal cannot be updated to an amount less than what has already been tipped for this goal, or would complete the goal. The "/restartgoal" command can be used to clear the current goal tip totals, but this is not recommended.', msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice('The second parameter is the description of goal ' + goalnum + ' (can be multiple words). For example, "/setgoal' + goalnum + ' 300 Shirt Off" to set goal ' + goalnum + ' to Shirt off at 300 tokens.', msguser, appNoticeColor); } else if (goalnum > 1 && !progGoalArray.amt[goalnum-2] >= 1) { cb.sendNotice('You cannot skip goal levels. If setting a goal for level ' + goalnum + ', then level ' + (goalnum-1) + ' must already have a goal defined.', msguser, appNoticeColor); } else { var goallabel = ''; for (let m6 = 2; m6 < msgarray.length; m6++) { if (m6 == 2) { goallabel = msgarray[m6]; } else { goallabel += ' ' + msgarray[m6]; } } progGoalArray.amt[goalnum-1] = commandvar1; progGoalArray.desc[goalnum-1] = goallabel; if (goalnum == currentGoal) { currentGoalDesc = goallabel; var savcurrenttotal = currentGoalTotal; currentGoalTotal = commandvar1; } if (finalGoalMet == true && goalnum == (currentGoal + 1)) { finalGoalMet = false; currentGoalTips = currentGoalTotal; nextGoal(); } else { changeRoomSubject(); } cb.drawPanel(); cb.sendNotice('Goal ' + goalnum + ' was added/updated to the amount of ' + commandvar1 + ' and a description of "' + goallabel + '".', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Progressive Goal #' + goalnum + ' to the goal amount of ' + commandvar1 + ' and a description of "' + goallabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' added/updated Progressive Goal #' + goalnum + ' to the goal amount of ' + commandvar1 + ' and a description of "' + goallabel + '".', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/goal': { recognizedcmd = true; if (whichApp == 'goals') { if (progressiveAutoNext == 'Select next goal from list') { if (msgisbc || ismodlvlmsg2) { var dogoalnum = msgarray[1]; var dogoalmax = progGoalArray.desc.length; if (!msgarray[1]) { cb.sendNotice('For this command, a parameter is required for the goal selection, and must be a number from 1 to ' + dogoalmax + '.', msguser, appNoticeColor); } else if (isNaN(dogoalnum) || dogoalnum < 1 || dogoalnum > dogoalmax) { cb.sendNotice('The parameter is the goal selection, and must be a number from 1 to ' + dogoalmax + '.', msguser, appNoticeColor); } else if (currentGoalTips != currentGoalTotal && currentGoalTips > 0) { cb.sendNotice('Command cannot be used during goal, it should be used only to choose the next goal once the current goal is finished. \nThe /skip command can be used to bypass the rest of the current goal.', msguser, appNoticeColor); } else { currentGoal = dogoalnum-1; nextGoal(); cb.drawPanel(); cb.sendNotice('You have started goal #' + currentGoal + ' "' + currentGoalDesc + '".', msguser, appNoticeColor); if (msguser != BC) { cb.sendNotice(msguser + ' has started goal #' + currentGoal + ' "' + currentGoalDesc + '".', BC, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/goal" command is for use when the goal advance mode is set to "Select next goal from list", and that mode is not enabled.', msguser, appNoticeColor); } } else { cb.sendNotice('The "/goal" command is for use with the Progressive Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/rmvgoal': { recognizedcmd = true; if (whichApp == 'goals') { if (msgisbc || ismodlvlmsg2) { var rmvgoalnum = msgarray[1]; if (isNaN(rmvgoalnum) || rmvgoalnum < 1 || rmvgoalnum > 20) { cb.sendNotice('The first parameter is the goal level being removed, and must be a number from 1 to 20 to indicate the goal level number that should be removed. For example, "/rmvgoal 3" will remove the goal and description info for level 3.', msguser, appNoticeColor); } else if (rmvgoalnum > progGoalArray.amt.length) { cb.sendNotice('There is no goal entry at level ' + rmvgoalnum + ', there are currently only entries up to goal level ' + progGoalArray.amt.length + '.', msguser, appNoticeColor); } else { progGoalArray.amt.splice((rmvgoalnum-1),1); progGoalArray.desc.splice((rmvgoalnum-1),1); currentGoalDesc = progGoalArray.desc[currentGoal-1]; currentGoalTotal = progGoalArray.amt[currentGoal-1]; changeRoomSubject(); cb.drawPanel(); cb.sendNotice('Progressive Goal #' + rmvgoalnum + ' was removed from the goal list', msguser, appNoticeColor); cb.sendNotice(msguser + ' removed Progressive Goal #' + rmvgoalnum + ' from the goal list', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' removed Progressive Goal #' + rmvgoalnum + ' from the goal list', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/rmvgoal" command is for use with the Progressive Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setgoaltext': { recognizedcmd = true; if (whichApp == 'goals') { if (msgisbc || ismodlvlmsg1) { goalSubjectText = rawmsg.substring(13).trim() if (goalSubjectText != '' && goalSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new Goal Show room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setgoaltxt" command is for use with the Progressive Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } //********* Auto-Reset Goal Commands case '/setautogoal': { recognizedcmd = true; if (whichApp == 'autoreset') { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var argoalnum = parseInt(msgarray[1]); if (argoalnum <= 0 || isNaN(commandvar1)) { cb.sendNotice('The first parameter is the new Auto-Reset goal amount and must be be a number greater than 0. For example, use "/setautogoal 300 Edging show" to set the recurring goal to Edging Show every 300 tokens, or use "/setautogoal 300" to change only the goal amount to 300, but leave the description the same as it already is.', msguser, appNoticeColor); } else if (argoalnum < currentGoalTips) { cb.sendNotice('The goal amount cannot be updated to an amount less than what has already been tipped for the current cycle.', msguser, appNoticeColor); } else { var autolabel = ''; if (msgarray[2] != '' && msgarray[2] != null) { for (let m6 = 2; m6 < msgarray.length; m6++) { if (m6 == 2) { autolabel = msgarray[m6]; } else { autolabel += ' ' + msgarray[m6]; } } autoresetGoalAmount = argoalnum; autoresetEachGoal = autolabel; updateAutoresetGoal(); cb.sendNotice(msguser + ' updated the Auto-Reset goal amount to ' + argoalnum + ' tokens, and a description of "' + autolabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' updated the Auto-Reset goal amount to ' + argoalnum + ' tokens, and a description of "' + autolabel + '".', BC, appNoticeColor); } else { autoresetGoalAmount = argoalnum; updateAutoresetGoal(); cb.sendNotice(msguser + ' updated the Auto-Reset goal amount to ' + argoalnum + ' tokens.', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' updated the Auto-Reset goal amount to ' + argoalnum + ' tokens.', BC, appNoticeColor); } } } else { cb.sendNotice('No parameter was specified for the goal amount. You can either change the goal amount (/setautogoal [amt]), or change both the amount and goal description (/setautogoal [amt] [description]), but the goal amount is required.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setautogoal" command is for use with the Auto-Reset Goal app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setautofinal': { recognizedcmd = true; if (whichApp == 'autoreset') { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var fgoalnum = parseInt(msgarray[1]); if (fgoalnum <= 0 || isNaN(commandvar1)) { cb.sendNotice('The first parameter is the new Auto-Reset Final Goal Count amount and must be be a number greater than 0. For example, use "/setautofinal 10 Cum Show" to set the Final Goal to a Cum Show after 10 reset goals have been completed, or use "/setautofinal 10" to change only the final goal count to 10, but leave the description the same as it already is.', msguser, appNoticeColor); } else if (fgoalnum < currentGoal) { cb.sendNotice('The final goal count cannot be updated to an amount less than the number of the current goal (' + currentGoal + ').', msguser, appNoticeColor); } else { if (msgarray[2] != '' && msgarray[2] != null) { var aflabel = ''; for (let m7 = 2; m7 < msgarray.length; m7++) { if (m7 == 2) { aflabel = msgarray[m7]; } else { aflabel += ' ' + msgarray[m7]; } } autoresetEndAfter = fgoalnum; autoresetFinalGoal = aflabel; updateAutoresetGoal(); cb.sendNotice(msguser + ' updated the Auto-Reset total goal count for End of Show to ' + fgoalnum + ' goals, and a Show End description of "' + aflabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' updated the Auto-Reset total goal count for End of Show to ' + fgoalnum + ' goals, and a Show End description of "' + aflabel + '".', BC, appNoticeColor); } else { autoresetEndAfter = fgoalnum; updateAutoresetGoal(); cb.sendNotice(msguser + ' updated the Auto-Reset total goal count for End of Show to ' + fgoalnum + ' goals.', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' updated the Auto-Reset total goal count for End of Show to ' + fgoalnum + ' goals.', BC, appNoticeColor); } } } else { cb.sendNotice('No parameter was specified for the goal amount. You can either change the goal amount (/setautofinal [amt]), or change both the amount and goal description (/setautofinal [amt] [description]), but the goal amount is required.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setautofinal" command is for use with the Auto-Reset Goal app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setautotext': { recognizedcmd = true; if (whichApp == 'autoreset') { if (msgisbc || ismodlvlmsg1) { autoresetSubjectSfx = rawmsg.substring(13).trim() if (autoresetSubjectSfx) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new Auto-Reset Goal room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setautotext" command is for use with the Auto-Reset Goal app, and that feature is not running.', msguser, appNoticeColor); } break; } //********* Goal Counter Commands case '/setcount1': case '/setcount2': case '/setcount3': case '/setcount4': case '/setcount5': case '/setcount6': case '/setcount7': case '/setcount8': case '/setcount9': case '/setcount10': case '/setcount11': case '/setcount12': case '/setcount13': case '/setcount14': case '/setcount15': { recognizedcmd = true; if (whichApp == 'goalcount') { if (msgisbc || ismodlvlmsg2) { var cntgoalnum = parseInt(msgarray[0].substring(9)); if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice('The first parameter is the new goal count for goal level ' + cntgoalnum + ' and has be be a number greater than 0. For example, "/setcount' + cntgoalnum + ' 20 Shirt Off" to set goal level ' + cntgoalnum + ' as Shirt off at 20 goals.', msguser, appNoticeColor); } else if (cntgoalnum <= currentGoal) { cb.sendNotice('You cannot update the attributes of the current goal level or a goal level that has already been passed in the goal sequence.', msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice('The second parameter is the description of prize at goal level ' + cntgoalnum + ' (can be multiple words). For example, "/setcount' + cntgoalnum + ' 20 Shirt Off" to set goal level ' + cntgoalnum + ' as Shirt off at 20 goals.', msguser, appNoticeColor); } else if (cntgoalnum > 1 && !goalCounterArray.amt[cntgoalnum-2] >= 1) { cb.sendNotice('You cannot skip goal levels. If setting a value for level ' + cntgoalnum + ', then level ' + (cntgoalnum-1) + ' must already have a goal value defined.', msguser, appNoticeColor); } else if (cntgoalnum > 1 && commandvar1 <= goalCounterArray.amt[cntgoalnum-2]) { cb.sendNotice('You cannot update the value for a goal level to be less than or equal to the value for the previous level (level ' + (cntgoalnum-1) + ' has a value of ' + goalCounterArray.amt[cntgoalnum-2] + ', so you must use a value greater than ' + goalCounterArray.amt[cntgoalnum-2] + ' for level ' + cntgoalnum + ').', msguser, appNoticeColor); } else if (cntgoalnum < 15 && commandvar1 >= goalCounterArray.amt[cntgoalnum] && goalCounterArray.amt[cntgoalnum] > 0) { cb.sendNotice('You cannot update the value for a goal level to be greater than or equal to the value for the next level (level ' + (cntgoalnum+1) + ' has a value of ' + goalCounterArray.amt[cntgoalnum] + ', so you must use a value less than ' + goalCounterArray.amt[cntgoalnum] + ' for level ' + cntgoalnum + ').', msguser, appNoticeColor); } else { var cntlabel = ''; for (let i = 2; i < msgarray.length; i++) { if (i === 2) { cntlabel = msgarray[i]; } else { cntlabel += " " + msgarray[i]; } } goalCounterArray.amt[cntgoalnum-1] = commandvar1; goalCounterArray.desc[cntgoalnum-1] = cntlabel; updateGoalCount(); cb.sendNotice('Goal Counter Level ' + cntgoalnum + ' was updated to the goal count of ' + commandvar1 + ' and a description of "' + cntlabel + '".', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Goal Counter Level #' + cntgoalnum + ' to the goal count of ' + commandvar1 + ' and a description of "' + cntlabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' added/updated Goal Counter Level #' + cntgoalnum + ' to the goal count of ' + commandvar1 + ' and a description of "' + cntlabel + '".', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setcountX" command is for use with the Tip Goal Counter App, and that feature is not running.', msguser, appNoticeColor); } break; } case '/rmvcount': { recognizedcmd = true; if (whichApp == 'goalcount') { if (msgisbc || ismodlvlmsg2) { var rmvcntgoalnum = msgarray[1]; if (isNaN(rmvcntgoalnum) || rmvcntgoalnum < 1 || rmvcntgoalnum > 15) { cb.sendNotice('The first parameter is the goal level being removed, and must be a number from 1 to 15 to indicate the goal level number that should be removed. For example, "/rmvcount 3" will remove the goal and description info for level 3.', msguser, appNoticeColor); } else if (rmvcntgoalnum > goalCounterArray.amt.length) { cb.sendNotice('There is no goal entry at level ' + rmvcntgoalnum + ', there are currently only entries up to goal level ' + goalCounterArray.amt.length + '.', msguser, appNoticeColor); } else { goalCounterArray.amt.splice((rmvcntgoalnum-1),1); goalCounterArray.desc.splice((rmvcntgoalnum-1),1); updateGoalCount(); cb.sendNotice('Goal Counter Level #' + rmvcntgoalnum + ' was removed from the goal list', msguser, appNoticeColor); cb.sendNotice(msguser + ' removed Goal Counter Level #' + rmvcntgoalnum + ' from the goal list', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' removed Goal Counter Level #' + rmvcntgoalnum + ' from the goal list', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/rmvcount" command is for use with the Tip Goal Counter App, and that feature is not running.', msguser, appNoticeColor); } break; } case '/chgcountgoal': { recognizedcmd = true; if (whichApp == 'goalcount') { if (msgisbc || ismodlvlmsg2) { if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice('The parameter is the new Tip Goal Counter individual goal amount and has be be a number greater than 0. For example, use "/chgcountgoal 99" to set each goal to 99 tokens.', msguser, appNoticeColor); } else if (commandvar1 <= currentGoalTips){ cb.sendNotice('Cannot change the goal amount to be less then or equal to the tip count for the current goal (' + currentGoalTips + ').', msguser, appNoticeColor); } else { changeCountGoal(commandvar1); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/chgcountgoal" command is for use with the Tip Goal Counter App, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setcounttext': { recognizedcmd = true; if (whichApp == 'goalcount') { if (msgisbc || ismodlvlmsg1) { counterSubjectText = rawmsg.substring(14).trim(); if (counterSubjectText != '' && counterSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new Goal Counter room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setcounttxt" command is for use with the Tip Goal Counter App, and that feature is not running.', msguser, appNoticeColor); } break; } //********* Goal Race Commands case '/restartrace': { recognizedcmd = true; if (whichApp == 'goalrace') { if (msgisbc || ismodlvlmsg2) { restartRace(); cb.sendNotice('You have restarted the goal race with the same goal choices. You can change the goal choices with the command "/setrace1" or "/setrace2".', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/restartrace" command is for use with the Goal Race app, which is not running.', msguser, appNoticeColor); } break; } case '/setrace1': case '/setrace2': { recognizedcmd = true; if (whichApp == 'goalrace') { if (msgisbc || ismodlvlmsg2) { var racenum = parseInt(msgarray[0].substring(8)); if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice('The first parameter is the new amount for goal ' + racenum + ' and has be be a number greater than 0. For example, "/setrace' + racenum + ' 300 Shirt Off" to set goal ' + racenum + ' to Shirt off at 300 tokens.', msguser, appNoticeColor); } else if (racenum == 1 && commandvar1 < currentGoalRace1Tips) { cb.sendNotice('The goal 1 amount cannot be updated to a value less than what has already been tipped for this goal. The "/restartrace" command can be used to clear the current race totals, but this is not recommended.', msguser, appNoticeColor); } else if (racenum == 2 && commandvar1 < currentGoalRace2Tips) { cb.sendNotice('The goal 2 amount cannot be updated to a value less than what has already been tipped for this goal. The "/restartrace" command can be used to clear the current race totals, but this is not recommended.', msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice('The second parameter is the description of goal ' + racenum + ' (can be multiple words). For example, "/setrace' + racenum + ' 300 Shirt Off" to set goal ' + racenum + ' to Shirt off at 300 tokens.', msguser, appNoticeColor); } else { var racelabel = ''; for (let m4 = 2; m4 < msgarray.length; m4++) { if (m4 == 2) { racelabel = msgarray[m4]; } else { racelabel += " " + msgarray[m4]; } } if (racenum == 1) { goalraceAmount1 = commandvar1; goalraceDesc1 = racelabel; } else if (racenum == 2) { goalraceAmount2 = commandvar1; goalraceDesc2 = racelabel; } changeRoomSubject(); cb.drawPanel(); cb.sendNotice('Goal ' + racenum + ' was added/updated to the amount of ' + commandvar1 + ' and a description of "' + racelabel + '".', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Goal #' + racenum + ' to the goal amount of ' + commandvar1 + ' and a description of "' + racelabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' added/updated Goal #' + racenum + ' to the goal amount of ' + commandvar1 + ' and a description of "' + racelabel + '".', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setraceX" command is for use with the Goal Race app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/addrace1': case '/addrace2': { recognizedcmd = true; if (whichApp == 'goalrace') { var addracenum = parseInt(msgarray[0].substring(8)); if (!cbjs.arrayContains(raceUnclaimedVotes.name, msguser)) { cb.sendNotice('Sorry, you do not have any unclaimed tips.', msguser, appNoticeColor); } else { var aridx = raceUnclaimedVotes.name.indexOf(msguser); var unclaimedtip = raceUnclaimedVotes.amount[aridx]; if (unclaimedtip > 0) { var artipnote = ''; if (addracenum == 1) { artipnote = goalraceDesc1; } else if (addracenum == 2) { artipnote = goalraceDesc2; } recordTip(unclaimedtip,'bc',artipnote,msgisfan,msgisvip,'broadcaster'); raceUnclaimedVotes.amount[aridx] = 0; cb.sendNotice(msguser + ' has claimed previous tips and voted ' + unclaimedtip + ' tokens to goal #' + addracenum + '.', '', goalraceBgColor, goalraceTextColor, 'bold'); cb.drawPanel(); } else { cb.sendNotice('Sorry, you do not have any unclaimed tips.', msguser, appNoticeColor); } } } else { cb.sendNotice('The "/addrace" commands are for use with the Goal Race app, and that feature is not running.', msguser, appNoticeColor); } break; } /* case '/tipnoteon': { recognizedcmd = true; if (whichApp == 'goalrace') { if (cbjs.arrayContains(raceTipNotesOn, msguser)) { cb.sendNotice('Sorry, you have already enabled Tip Notes for yourself during the Goal Race.', msguser, appNoticeColor); } else { raceTipNotesOn.push(msguser); cb.sendNotice('You have enabled Tip Notes for yourself during the Goal Race, you will now be able to enter tip notes rather than vote for the goals. You can switch back to voting for goals using the command /tipnoteoff.', msguser, appNoticeColor); } } else { cb.sendNotice('The "/tipnote" commands are for use with the Goal Race app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/tipnoteoff': { recognizedcmd = true; if (whichApp == 'goalrace') { if (cbjs.arrayContains(raceTipNotesOn, msguser)) { var noteoffidx = raceTipNotesOn.indexOf(msguser); raceTipNotesOn.splice(noteoffidx,1); cb.sendNotice('You have disabled Tip Notes for yourself during the Goal Race, you will now be able to vote for the goals. You can switch back to entering tip notes using the command /tipnoteon.', msguser, appNoticeColor); } else { cb.sendNotice('Sorry, you have already disabled (or never enabled) Tip Notes for yourself during the Goal Race.', msguser, appNoticeColor); } } else { cb.sendNotice('The "/tipnote" commands are for use with the Goal Race app, and that feature is not running.', msguser, appNoticeColor); } break; } */ case '/setracetext': { recognizedcmd = true; if (whichApp == 'goalrace') { if (msgisbc || ismodlvlmsg1) { goalraceSubjectText = rawmsg.substring(13).trim() if (goalraceSubjectText != '' && goalraceSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new Goal Race room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setracetxt" command is for use with the Goal Race app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setracepaneltext': { recognizedcmd = true; if (whichApp == 'goalrace') { if (msgisbc || ismodlvlmsg1) { goalracePanelText = rawmsg.substring(18).trim() if (goalracePanelText != '' && goalracePanelText != null) { cb.drawPanel(); } else { cb.sendNotice('No value was specified for the new panel text.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setracepaneltext" command is for use with the Goal Race app, and that feature is not running.', msguser, appNoticeColor); } break; } //********* Ultra App Hidden Ticket Show Commands case '/tickets': { recognizedcmd = true; if (ticketSalesOpen) { // if (whichApp == 'ticket') { if (ticketHolderList.length > 0) { if (msgarray[1] == 'a' || msgarray[1] == 'A' || msgarray[1] == 'alpha') { var sortedticketlist = ticketHolderList.slice(); sortedticketlist.sort(); cb.sendNotice(botName + 'Alphabetic listing of users currently with access to the Hidden Ticket Show (' + countTickets + ' ticket holders) :\n' + cbjs.arrayJoin(sortedticketlist, ', ') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'Users currently with access to the Hidden Ticket Show, in order of when added (' + countTickets + ' ticket holders). Use the "alpha" parameter for a sorted list : \n' + cbjs.arrayJoin(ticketHolderList, ', ') + '\nEnd of List', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No ticket holders yet.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show feature has not yet been started.', msguser, appNoticeColor); } break; } case '/backup': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (whichApp == 'ticket') { if (ticketHolderList.length > 0) { cb.sendNotice('Copy and paste the below command into the chat to backup the current ticket show list to the Fembot:\n/backupfb ' + cbjs.arrayJoin(ticketHolderList, ', ')); } else { cb.sendNotice('No ticket buyers yet.', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp is not selling tickets for a show.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/prepticket': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var prepnumtimer = parseInt(msgarray[1]) if (isNaN(prepnumtimer)) { cb.sendNotice('The value entered for the show countdown timer is not numeric, please try again.', msguser, appNoticeColor); break; } else if (prepnumtimer < 1 || prepnumtimer > 120) { cb.sendNotice('The value entered for the show countdown timer is outside allowable values from 1 to 120 min, please try again.', msguser, appNoticeColor); } else { prepTicketShow(msguser,prepnumtimer); cb.sendNotice('UltraApp Ticket show prep has been completed.', msguser, appNoticeColor); } } else { prepTicketShow(msguser,0); cb.sendNotice('UltraApp Ticket show prep has been completed.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/ctprice': case '/chgticketprice': case '/ticketprice': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsChgPrice === 'Yes')) { if (msgarray[1]) { var numprice = parseInt(msgarray[1]) if (isNaN(numprice)) { cb.sendNotice(botName + 'The value entered for the ticket price is not numeric, please try again.', msguser, appNoticeColor); break; } else if (numprice < 1 || numprice > 99999) { cb.sendNotice(botName + 'The value entered for the ticket price is outside allowable values from 1 to 99999, please try again.', msguser, appNoticeColor); } else { var ticketannounce = 'no'; if (ticketSalesOpen) { // if (whichApp == 'ticket') { cb.sendNotice(botName + 'The Ticket Show price has been updated to ' + numprice + ' tokens. You can view the ticket list with the command "/tickets".', msguser, appNoticeColor); ticketannounce = 'yes'; } else { cb.sendNotice(botName + 'The Ticket Show price has been updated to ' + numprice + ' tokens, however the UltraApp ticket feature is not yet active.', msguser, appNoticeColor); } setTicketPrice(numprice,ticketannounce); } } else { cb.sendNotice(botName + 'No parameter was specified for the new ticket price.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3P, msguser, appNoticeColor); } break; } case '/showstartprice': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsChgPrice === 'Yes')) { if (whichApp == 'ticket' && showStage != 'ticketsales') { cb.sendNotice(botName + 'The show start ticket price can only be updated while another app feature is running or prior to show start.', msguser, appNoticeColor); } else { if (msgarray[1]) { var numnewprice = parseInt(msgarray[1]) if (isNaN(numnewprice)) { cb.sendNotice(botName + 'The value entered for the ticket show start price is not numeric, please try again.', msguser, appNoticeColor); break; } else if (numnewprice < 1 || numnewprice > 99999) { cb.sendNotice(botName + 'The value entered for the ticket show start price is outside allowable values from 1 to 99999, please try again.', msguser, appNoticeColor); } else if (numnewprice <= ticketPrice) { cb.sendNotice(botName + 'The value entered for the ticket show start price is less than or equal to the current ticket price; it must be greater than the ticket price, please try again.', msguser, appNoticeColor); } else { ticketShowStartPrice = numnewprice; cb.sendNotice(botName + 'The Ticket Show Start price has been updated to ' + numnewprice + ' tokens. The ticket price will be updated to this amount when the show starts.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No parameter was specified for the new ticket show start price.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod3P, msguser, appNoticeColor); } break; } case '/starttickettimer': case '/ticketstarttimer': case '/starttimer': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg1) { if (msgarray[1]) { var numtimer = parseInt(msgarray[1]) if(isNaN(numtimer)) { cb.sendNotice('The value entered for the minutes to use for the timer is not numeric, please try again.', msguser, appNoticeColor); break; } else if (numtimer < 1 || numtimer > 60) { cb.sendNotice('The value entered for the minutes to use for the ticket show timer is outside allowable values from 1 to 60 minutes, please try again.', msguser, appNoticeColor); } else { startTicketShowTimer(numtimer); } } else { cb.sendNotice('No parameter was specified for the number of minutes to use for the timer.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/addtickettime': case '/ticketaddtime': case '/addtime': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg1) { if (ticketMinsRemain > 0 || ticketSecsRemain > 0) { if (msgarray[1]) { var addnumtimer = parseInt(msgarray[1]); if (isNaN(addnumtimer)) { cb.sendNotice('The value entered for the minutes to add to the ticket show start timer is not numeric, please try again.', msguser, appNoticeColor); } else if (addnumtimer < -60 || addnumtimer > 60) { cb.sendNotice('The value entered for the minutes to add to the ticket show start timer is outside allowable values from -60 to 60, please try again. Negative numbers can be used to subtract time.', msguser, appNoticeColor); } else if (addnumtimer == 0) { cb.sendNotice('Cannot add zero time.', msguser, appNoticeColor); } else if (addnumtimer < 0 && Math.abs(addnumtimer) > ticketMinsRemain) { cb.sendNotice('The value entered for the minutes to subtract is greater than the remaining time on the timer, please try again. Negative numbers can be used to subtract time.', msguser, appNoticeColor); } else if (addnumtimer > 0 && (Math.abs(addnumtimer) + ticketMinsRemain) > 60) { cb.sendNotice('The value entered for the minutes to add would exceed the maximum countdown time of 60 minutes when added to the current time left.', msguser, appNoticeColor); } else { ticketAddTime(addnumtimer, msguser); } } else { cb.sendNotice('No parameter was specified for the number of minutes to add to the timer.', msguser, appNoticeColor); } } else { cb.sendNotice('A timer has not been started yet for the ticket show countdown.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/ticketstoptime': case '/ticketstoptimer': case '/stoptimer': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg1) { if (ticketMinsRemain > 0 || ticketSecsRemain > 0) { stopTicketTimer(msguser); } else { cb.sendNotice('A ticket timer is not currently running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/add': case '/addticket': { recognizedcmd = true; if (ticketSalesOpen) { // if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (cmdval != null) { var cmdvalsplit = cmdval.split(listregexp); if (cmdvalsplit.length > 1) { cb.sendNotice('Adding multiple users to the ticket show list.', msguser, appNoticeColor); for (var i = 0; i < cmdvalsplit.length; i++) { if (cmdvalsplit[i]) { var nametoadd = cmdvalsplit[i].toLowerCase(); if (!cb.limitCam_userHasAccess(nametoadd)) { addRmvTicket('add',nametoadd,'',0,nametoadd); cb.sendNotice('Added ' + nametoadd + ' to the ticket show list.', msguser); cb.sendNotice(msguser + ' has added you to the ticket show list.', nametoadd, appNoticeColor); } else { cb.sendNotice(nametoadd + ' is already on the ticket show list. Skipping.', msguser); } } else { cb.sendNotice('Skipping null entry.', msguser); } } cb.sendNotice('All users were added and notified.', msguser, appNoticeColor) cb.sendNotice(msguser + ' has added multiple users to the ticket show list.\n' + 'Users added: ' + cbjs.arrayJoin(cmdvalsplit, ', '), '', appNoticeColor, '', 'normal', 'red'); } else { var nametoadd2 = msgarray[1].toLowerCase(); if (!cb.limitCam_userHasAccess(nametoadd2)) { addRmvTicket('add',nametoadd2,'',0,nametoadd2); } else { cb.sendNotice('Note: User ' + nametoadd2 + ' is already in the Ticket Show List.', msguser, appNoticeColor); } } } else { if (!cb.limitCam_userHasAccess(msguser)) { addRmvTicket('add',msguser,'',0,nametoadd2); } else { cb.sendNotice('Note: User ' + msguser + ' is already in the Ticket Show List.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice('The ticket show feature is not yet active in the UltraApp.', msguser, appNoticeColor); } break; } case '/rmv': case '/del': case '/delticket': case '/rmvticket': { recognizedcmd = true; if (ticketSalesOpen) { // if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (msgarray[1]) { var nametormv = msgarray[1].toLowerCase(); if (cb.limitCam_userHasAccess(nametormv)) { addRmvTicket('rmv',nametormv,'',0,nametormv); } else { cb.sendNotice('User is not in the UltraApp Ticket Show List.', msguser, appNoticeColor); } } else { cb.sendNotice('No user was specified for the /rmv command.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice('The ticket show feature is not yet active in the UltraApp.', msguser, appNoticeColor); } break; } case '/chgtktmode': case '/chgticketmode': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var chgtktmode = msgarray[1].toLowerCase(); if (chgtktmode != 'manual' && chgtktmode != 'timer' && chgtktmode != 'ticketgoal' && chgtktmode != 'tokengoal') { cb.sendNotice('The value entered for the new mode is not valid, please try again using a value of "manual", "timer", "ticketgoal", or "tokengoal".', msguser, appNoticeColor); } else { if (chgtktmode == ticketStartMode) { cb.sendNotice('The value entered for the new mode is the same as the existing mode, command ignored.', msguser, appNoticeColor); } else { setTicketMode(chgtktmode,msguser); cb.sendNotice(ticketModeMessage,'', ticketBgColor,ticketTextColor,'bold'); cb.drawPanel(); } } } else { cb.sendNotice('No parameter was specified for the new ticket show start mode. Valid values are "manual", "timer", "ticketgoal", or "tokengoal".', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/chgtktauto': case '/chgticketauto': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var chgticketautovar = msgarray[1].toLowerCase(); if (chgticketautovar != 'bc' && chgticketautovar != 'auto') { cb.sendNotice('The value entered for the new mode is not valid, please try again using a value of "bc" or "auto".', msguser, appNoticeColor); } else if (chgticketautovar === 'auto' && ticketStartMode === 'manual') { cb.sendNotice('The mode cannot be changed to "auto" unless there is a goal or timer being used to define when the show will start. Show is currently to be started at broadcaster discretion.', msguser, appNoticeColor); } else if (chgticketautovar === ticketModeAuto) { cb.sendNotice('The value entered for the new mode is the same as the existing mode, command ignored.', msguser, appNoticeColor); } else { setTicketAuto(chgticketautovar); cb.sendNotice(ticketModeMessage, '', ticketBgColor, ticketTextColor, 'bold'); cb.drawPanel(); } } else { cb.sendNotice('No parameter was specified for the new autostart mode. Valid values are "bc" or "auto" (broadcaster command or automatic start).', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/tickettimeleft': { recognizedcmd = true; if (whichApp == 'ticket') { if (ticketMinsRemain >= 1 || ticketSecsRemain >= 1) { cb.sendNotice(ticketTimeLeft(), '', appNoticeColor, '', 'bold'); } else { cb.sendNotice('A Hidden Ticket Show timer is not running.', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/showtime': { recognizedcmd = true; if (whichApp == 'ticket') { if (cb.limitCam_isRunning()) { cb.sendNotice(' \u25B7 \u25B7 \u25B7 Hidden Ticket Show in progress for ' + ((Date.now() - hiddenTime)/60000).toFixed(1) + ' minutes. \u25C1 \u25C1 \u25C1 ', msguser, ticketBgColor,ticketTextColor,'bold'); } else { cb.sendNotice('The ticket show is not running, it has not yet started or already finished.', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/startsales': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if ((whichApp != 'ticket') && (!ticketSalesOpen)) { if (validateFeature('earlytkt')) { if (ticketHolderList.length > 0) { countTickets = ticketHolderList.length; } else { countTickets = 0; } setTicketMode('pre', msguser); ticketSalesOpen = true; showStage = 'earlysales'; changeRoomSubject(); ticketShowEnded = false; ticketSalesEnded = false; loadFreeTickets(); cb.setTimeout(ticketNoticeDisplay, ticketNoticeInt); cb.drawPanel(); cb.sendNotice(borderTicketTop + '\n' + bcText + ' has started selling tickets for the show for ' + ticketPrice + ' tokens.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, 'bold'); } } else { cb.sendNotice('Ticket Sales are already open!', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/startshow': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var enteredby = msguser; if (msgisbc) { enteredby = bcText; } if (showStage == 'earlysales') { setAppFeature('ticket', enteredby); } if (whichApp == 'ticket') { if (!cb.limitCam_isRunning() && ticketShowEnded == false) { startTicketShow(enteredby); } else if (!cb.limitCam_isRunning() && ticketShowEnded == true) { cb.sendNotice('The Hidden Cam show was already started and stopped, please use the command "/restartshow" to resume the hidden show.', msguser, appNoticeColor); } else { cb.sendNotice('The Hidden Cam show is already underway.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show feature is not running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/showover': case '/showwarn': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var soenteredby = msguser; if (msgisbc) { soenteredby = bcText; } if (whichApp == 'ticket') { if (cb.limitCam_isRunning()) { if (showStage == 'ticketshow') { warnShowEnding(soenteredby); } else if (showStage == 'showwarn') { cb.sendNotice('The /showwarn or /showover command has already been used. To suspend ticket sales, use the /showend command. To end the show and return to a public broadcast, use the /stopshow command.', msguser, appNoticeColor); } else if (showStage == 'showfinale') { cb.sendNotice('The show has already progressed past the point this command should be used, ticket sales have already been suspended. To end the show and return to a public broadcast, use the /stopshow command.', msguser, appNoticeColor); } } else { cb.sendNotice('The Hidden Cam show has not been started or has already ended.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show feature is not running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/showend': case '/stopsales': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var seenteredby = msguser; if (msgisbc) { seenteredby = bcText; } if ((ticketSalesOpen) && whichApp != 'ticket') { ticketSalesOpen = false; showStage = ''; changeRoomSubject(); cb.drawPanel(); cb.sendNotice('Early ticket sales have been suspended. You can resume sales without losing the current ticket list with command "/startsales" or by switching the active app to the ticket show with "/chgapp ticket". If you wish to reset the ticket list before restarting sales use command "/newticketshow".', msguser, appNoticeColor); } else if (whichApp == 'ticket') { if (cb.limitCam_isRunning()) { if (showStage == 'showfinale') { cb.sendNotice('Ticket Sales have already been suspended.', msguser, appNoticeColor); } else { stopTicketSales(seenteredby); } } else { cb.sendNotice('The Hidden Cam show has not been started or has already ended.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show feature is not running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/stopshow': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var ssenteredby = msguser; if (msgisbc) { ssenteredby = bcText; } if (whichApp == 'ticket') { if (cb.limitCam_isRunning()) { stopTicketShow(ssenteredby); } else { cb.sendNotice('The Hidden Cam show has not been started or has already ended.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show feature is not running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/ticketsubject': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg1) { ticketSubjectText = rawmsg.substring(15).trim(); if (ticketSubjectText != '' && ticketSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/ctsubject': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg1) { ticketSubjectText = rawmsg.substring(11).trim(); if (ticketSubjectText != '' && ticketSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/newticketshow': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg2) { if (!cb.limitCam_isRunning()) { if (ticketHolderList.length > 0) { cb.sendNotice('In case the ticket show was reset by mistake, the previous ticket list is displayed below so they can be added back to the show using the "/add" command : ', BC, appNoticeColor); cb.sendNotice(cbjs.arrayJoin(ticketHolderList, ', '), BC); cb.sendNotice('End of List', BC, appNoticeColor); if (ismodlvlmsg2) { cb.sendNotice('In case the ticket show was reset by mistake, the previous ticket list is displayed below so they can be added back to the show using the "/add" command : ', msguser, appNoticeColor); cb.sendNotice(cbjs.arrayJoin(ticketHolderList, ', '), msguser); cb.sendNotice('End of List', msguser, appNoticeColor); } } else { cb.sendNotice('No ticket buyers in the previous ticket list.', msguser, appNoticeColor); } initTicketShow(msguser,ticketPrice); cb.limitCam_removeAllUsers(); ticketHolderList.length = 0; while (ticketShowViewerList.length > 0) ticketShowViewerList.pop(); } else { cb.sendNotice('A Hidden Cam show is still running. The /newticketshow command can be used to start a brand new ticket show. The ticket list is cleared but the outstanding ticket list and changes list remains intact.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/restartshow': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg2) { if (!cb.limitCam_isRunning()) { var rsstartedby = msguser; if (msgisbc) { rsstartedby = bcText; } restartTicketShow(rsstartedby); } else { cb.sendNotice('A Hidden Cam show is already running. The /restartshow command can be used to resume a hidden show that was ended prematurely or accidentally. All settings remain the same and the ticket list stays intact.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/restartsales': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg2) { if (ticketSalesEnded == true) { restartTicketSales(msguser); } else { cb.sendNotice('Ticket Sales are already enabled.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/chgticketshow': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if ((whichApp == 'ticket') && (msgarray[1] == 'fembot' || msgarray[1] == 'Fembot')) { cb.sendNotice('*** Warning *** You have enabled the ticket show in the Fembot and the UltraApp simultaneously. Only one app or bot should have the ticket show enabled. Running a ticket show in both will cause the user access to the ticket show to fail.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/cancelticket': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (ticketCountdownRunning) { ticketCountdownRunning = false; cb.sendNotice('The automatic start of ticket sales has been canceled.', '', appNoticeColor, '', 'bold'); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } //********* Spank-a-thon Commands case '/setspanktype1': case '/setspanktype2': case '/setspanktype3': case '/setspanktype4': case '/setspanktype5': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var typenum = parseInt(msgarray[0].substring(13)); if (!msgarray[1]) { cb.sendNotice(botName + 'The parameter is the "spank type" ' + typenum + ' description (can be multiple words). For example, "/setspanktype' + typenum + ' Hand Spanks".', msguser, appNoticeColor); } else if (typenum > 1 && typenum > (spankPricesArray.typ.length + 1)) { cb.sendNotice(botName + 'You cannot skip spank type levels. If adding type ' + typenum + ', then type ' + (typenum-1) + ' must already have been set up.', msguser, appNoticeColor); } else { var spklabel = ''; for (let m5 = 1; m5 < msgarray.length; m5++) { if (m5 == 1) { spklabel = msgarray[m5]; } else { spklabel += " " + msgarray[m5]; } } if (typenum > spankTotalsArray.typ.length) { spankTotalsArray.typ.push(spklabel); spankTotalsArray.tipped.push(0); spankTotalsArray.completed.push(0); } else { spankTotalsArray.typ[typenum-1] = spklabel; } if (typenum > spankPricesArray.typ.length) { for (let loadindex = 1; loadindex <= 3; loadindex++) { spankPricesArray.typ.push(spklabel); spankPricesArray.number.push(0); spankPricesArray.price.push(0); } } else { spankPricesArray.typ[(typenum-1)*3] = spklabel; spankPricesArray.typ[(typenum-1)*3+1] = spklabel; spankPricesArray.typ[(typenum-1)*3+2] = spklabel; } cb.sendNotice(botName + 'Spank-a-thon Type ' + typenum + ' was added/updated with a description of "' + spklabel + '".', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Spank-a-thon Type ' + typenum + ' with a description of "' + spklabel + '".', '', appNoticeColor, '', '', 'red'); if (msguser != BC) { cb.sendNotice(msguser + ' added/updated Spank-a-thon Type ' + typenum + ' with a description of "' + spklabel + '".', BC, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/setspank1p1': case '/setspank1p2': case '/setspank1p3': case '/setspank2p1': case '/setspank2p2': case '/setspank2p3': case '/setspank3p1': case '/setspank3p2': case '/setspank3p3': case '/setspank4p1': case '/setspank4p2': case '/setspank4p3': case '/setspank5p1': case '/setspank5p2': case '/setspank5p3': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg2) { var sstypenum = parseInt(msgarray[0].substring(9)); var sspricenum = parseInt(msgarray[0].substring(11)); if ((sstypenum*3) > spankPricesArray.typ.length) { cb.sendNotice(botName + 'Spank type ' + sstypenum + ' has not yet been defined, please use "/setspanktype' + sstypenum + ' to define the spank type before setting the spank prices.', msguser, appNoticeColor); } else if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice(botName + 'The first parameter is the number of spanks in a bundle for spank type ' + sstypenum + ', price level ' + sspricenum + ', and has be be a number greater than 0. For example, "/setspank' + sstypenum + 'p' + sspricenum + ' 10 99 to set spank type ' + sstypenum + ', price level ' + sspricenum + ' to be 10 spanks for 99 tokens. When setting level 1, it is typically the per spank price, so the number fo spanks would be 1, however this is not required.', msguser, appNoticeColor); } else if (commandvar2 <= 0 || isNaN(commandvar1)) { cb.sendNotice(botName + 'The second parameter is the tip amount for spank type ' + sstypenum + ', price level ' + sspricenum + ', and has be be a number greater than 0. For example, "/setspank' + sstypenum + 'p' + sspricenum + ' 10 99 to set spank type ' + sstypenum + ', price level ' + sspricenum + ' to be 10 spanks for 99 tokens.', msguser, appNoticeColor); } else { spankPricesArray.number[(sstypenum-1)*3+(sspricenum-1)] = commandvar1; spankPricesArray.price[(sstypenum-1)*3+(sspricenum-1)] = commandvar2; cb.sendNotice(botName + 'Spank-a-thon Spank Type ' + sstypenum + ', price level ' + sspricenum + ' was added/updated with the number of spanks as ' + commandvar1 + ' for a price of ' + commandvar2 + '.', msguser, appNoticeColor); cb.sendNotice(botName + msguser + ' added/updated Spank-a-thon Spank Type ' + sstypenum + ', price level ' + sspricenum + ' with the number of spanks as ' + commandvar1 + ' for a price of ' + commandvar2 + '.', '', appNoticeColor, '', '', 'red'); if (msguser != BC) { cb.sendNotice(botName + msguser + ' added/updated Spank-a-thon Spank Type ' + sstypenum + ', price level ' + sspricenum + ' with the number of spanks as ' + commandvar1 + ' for a price of ' + commandvar2 + '.', BC, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The command for setting spank prices is for use with the Spank-a-thon app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setspankgoal1': case '/setspankgoal2': case '/setspankgoal3': case '/setspankgoal4': case '/setspankgoal5': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg2) { var spkgoalnum = parseInt(msgarray[0].substring(13)); if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice(botName + 'The first parameter is the new amount for goal ' + spkgoalnum + ' and has be be a number greater than 0. For example, "/setspankgoal' + spkgoalnum + ' 300 Spanking Round 1" to set goal ' + spkgoalnum + ' to the first round of spanking at 300 tokens.', msguser, appNoticeColor); } else if (spkgoalnum == currentGoal && commandvar1 < currentGoalTips) { cb.sendNotice(botName + 'The current goal cannot be updated to an amount less than what has already been tipped for this goal.', msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice(botName + 'The second parameter is the description of goal ' + spkgoalnum + ' (can be multiple words). For example, "/setgoal' + spkgoalnum + ' 300 Spanking Round 1" to set goal ' + spkgoalnum + ' to the first round of spanking at 300 tokens.', msguser, appNoticeColor); } else if (spkgoalnum > 1 && !spankGoalArray.amt[spkgoalnum-2] >= 1) { cb.sendNotice(botName + 'You cannot skip goal levels. If setting a goal for level ' + spkgoalnum + ', then level ' + (spkgoalnum-1) + ' must already have a goal defined.', msguser, appNoticeColor); } else { var ssglabel = ''; for (let m5 = 2; m5 < msgarray.length; m5++) { if (m5 == 2) { ssglabel = msgarray[m5]; } else { ssglabel += " " + msgarray[m5]; } } spankGoalArray.amt[spkgoalnum-1] = commandvar1; spankGoalArray.desc[spkgoalnum-1] = ssglabel; updateSpankGoal(); cb.sendNotice(botName + 'Spank-a-thon Goal ' + spkgoalnum + ' was added/updated to the amount of ' + commandvar1 + ' and a description of "' + ssglabel + '".', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Spank-a-thon Goal #' + spkgoalnum + ' to the goal amount of ' + commandvar1 + ' and a description of "' + ssglabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' added/updated Spank-a-thon Goal #' + spkgoalnum + ' to the goal amount of ' + commandvar1 + ' and a description of "' + ssglabel + '".', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/setspankgoalX" command is for use with the Spank-a-thon app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/rmvspankgoal': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg2) { var rmvspkgoalnum = msgarray[1]; if (isNaN(rmvspkgoalnum) || rmvspkgoalnum < 1 || rmvspkgoalnum > 20) { cb.sendNotice(botName + 'The first parameter is the goal level being removed, and must be a number from 1 to 5 to indicate the goal level number that should be removed. For example, "/rmvspankgoal 3" will remove the goal and description info for level 3.', msguser, appNoticeColor); } else if (rmvspkgoalnum > spankGoalArray.amt.length) { cb.sendNotice(botName + 'There is no goal entry at level ' + rmvspkgoalnum + ', there are currently only entries up to goal level ' + progGoalArray.amt.length + '.', msguser, appNoticeColor); } else { spankGoalArray.amt.splice((rmvspkgoalnum-1),1); spankGoalArray.desc.splice((rmvspkgoalnum-1),1); updateSpankGoal(); cb.sendNotice(botName + 'Spank-a-thon Goal #' + rmvspkgoalnum + ' was removed from the goal list', msguser, appNoticeColor); cb.sendNotice(msguser + ' removed Spank-a-thon Goal #' + rmvspkgoalnum + ' from the goal list', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' removed Spank-a-thon Goal #' + rmvspkgoalnum + ' from the goal list', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/rmvspankgoal" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setspanktext': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg1) { spankSubjectText = rawmsg.substring(14).trim() if (spankSubjectText != '' && spankSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice(botName + 'No value was specified for the new room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/setspanktext" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/spankmenu': { recognizedcmd = true; if (whichApp == 'spank') { cb.sendNotice(spankMenu(), '', spankBgColor, spankTextColor, 'bold'); } else { cb.sendNotice(botName + 'The "/spankmenu" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/spanktips': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg1) { spankTips(); } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/spanktips" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/spanktotals': { recognizedcmd = true; if (whichApp == 'spank') { spankTotals(); } else { cb.sendNotice(botName + 'The "/spanktotals" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/spanked': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg1) { var spanktype = parseInt(msgarray[1]); var spanknumber = parseInt(msgarray[2]); if (isNaN(spanktype) || spanktype < 1 || spanktype > 5) { cb.sendNotice(botName + 'The first parameter is the Spank Type being counted, and must be a number from 1 to 5. For example, "/spanked 3 20" will add 20 spanks to the total completed for spank type 3.', msguser, appNoticeColor); } else if (isNaN(spanknumber) || spanktype < 1) { cb.sendNotice(botName + 'The second parameter is the Number of Spanks being counted as complete, and must be a number greater than zero. For example, "/spanked 3 20" will add 20 spanks to the total completed for spank type 3.', msguser, appNoticeColor); } else if (spanktype > spankTotalsArray.typ.length) { cb.sendNotice(botName + 'There is no Spank Type configured for type ' + spanktype + ', there are currently only entries configured up to type ' + spankTotalsArray.typ.length + '.', msguser, appNoticeColor); } else { var completedindex = spanktype - 1; if (spankTotalsArray.tipped[completedindex] < spanknumber) { cb.sendNotice(botName + 'The number of spanks entered (' + spanknumber + ') is greater than the number tipped for so far (' + spankTotalsArray.tipped[completedindex] + ') for level ' + spanktype + '.', msguser, appNoticeColor); } else { spanksCompleted(completedindex,spanknumber); } } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/spanked" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/spankall': { recognizedcmd = true; if (whichApp == 'spank') { if (msgisbc || ismodlvlmsg1) { var spanksdone = false; for (let totindex = 0; totindex < spankTotalsArray.tipped.length; totindex++) { var spankstoadd = spankTotalsArray.tipped[totindex] - spankTotalsArray.completed[totindex]; if (spankstoadd > 0) { spanksCompleted(totindex,spankstoadd); spanksdone = true; } } if (spanksdone) { cb.sendNotice(botName + 'All incomplete spank types were marked as complete.', msguser, appNoticeColor); spankTotals(); } else { cb.sendNotice(botName + 'There were no incomplete spank types found, no updates made. You can use the command /spanktotals to see the current number of spanks tipped for and completed.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/spankall" command is for use with the Spank-a-thon Goals app, and that feature is not running.', msguser, appNoticeColor); } break; } //******** VIP Commands *********** case '/addvip': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (cmdval != null) { var vipcmdvalsplit = cmdval.split(listregexp); if (vipcmdvalsplit.length > 1) { var vipaddnotice = botName + 'Adding multiple users to the UltraApp ' + VIPname + '.'; vipaddnotice += '\nNote this is only applied during this session, permanent ' + VIPname + ' changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.'; for (var vipi = 0; vipi < vipcmdvalsplit.length; vipi++) { if (vipcmdvalsplit[vipi] != '') { var viptoadd = vipcmdvalsplit[vipi].toLowerCase(); if (!cbjs.arrayContains(VIPListArray, viptoadd)) { addRmvVIP(viptoadd,'a'); vipaddnotice += '\nAdded ' + viptoadd + ' to the UltraApp ' + VIPname + '.'; cb.sendNotice(botName + msguser + ' has added you to the UltraApp ' + VIPname + '.', viptoadd, appNoticeColor); } else { vipaddnotice += '\n' + viptoadd + ' is already on the UltraApp ' + VIPname + '. Skipping.'; } } } vipaddnotice += '\nAll users were added and notified.'; cb.sendNotice(vipaddnotice, msguser, appNoticeColor); cb.sendNotice(msguser + ' has added multiple users to the UltraApp ' + VIPname + '.\n' + 'Users added: ' + cbjs.arrayJoin(vipcmdvalsplit, ', '), '', appNoticeColor, '', 'normal', 'red'); } else { var viptoadd2 = msgarray[1].toLowerCase(); if (cbjs.arrayContains(VIPListArray,viptoadd2)) { cb.sendNotice(botName + viptoadd2 + ' is already on the ' + VIPname + '.', msguser, appNoticeColor); } else { addRmvVIP(viptoadd2,'a'); cb.sendNotice(botName + 'You have added ' + viptoadd2 + ' to the ' + VIPname + '.\nNote this is only applied during this session, permanent ' + VIPname + ' changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.', msguser, appNoticeColor); cb.sendNotice(botName + 'Congratulations! You have been added to the ' + VIPname + '!', viptoadd2, appNoticeColor); } } } else { cb.sendNotice(botName + 'You did not specify the username you want to add to the ' + VIPname + '.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/rmvvip': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (msgarray[1]) { var viptormv = msgarray[1].toLowerCase(); if (cbjs.arrayContains(VIPListArray,viptormv)) { addRmvVIP(viptormv,'r'); cb.sendNotice(botName + 'You have removed ' + viptormv + ' from the ' + VIPname + '.\nNote this is only applied during this session, permanent ' + VIPname + ' changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.', msguser, appNoticeColor); } else { cb.sendNotice(botName + viptormv + ' is not on the ' + VIPname + '.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'You did not specify the username you want to remove from the ' + VIPname + '.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/viplist': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { cb.sendNotice(botName + 'Users currently on the ' + VIPname + ': ' + VIPListArray.length + '\n' + (VIPListArray.length > 0 == true ? cbjs.arrayJoin(VIPListArray, ', ') : 'No users.') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } //******** Moderator List Commands *********** case '/addmod': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (cmdval != null) { var modcmdvalsplit = cmdval.split(listregexp); if (modcmdvalsplit.length > 1) { var modaddnotice = 'Adding multiple users to the UltraApp Moderator list.'; modaddnotice += '\nNote this is only applied during this session, permanent Moderator list changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.'; for (var modi = 0; modi < modcmdvalsplit.length; modi++) { if (modcmdvalsplit[modi] != '') { var modtoadd = modcmdvalsplit[modi].toLowerCase(); if (!cbjs.arrayContains(moderatorList.name, modtoadd)) { addRmvMods(modtoadd,'botmod','a'); modaddnotice += '\nAdded ' + modtoadd + ' to the UltraApp Moderator list.'; cb.sendNotice(msguser + ' has added you to the UltraApp Moderator list. You now have all moderator privileges, you can view the full command list with the "/uahelp" command.', modtoadd, appNoticeColor); } else { modaddnotice += '\n' + modtoadd + ' is already on the UltraApp Moderator list. Skipping.'; } } } modaddnotice += '\nAll users were added and notified.'; cb.sendNotice(modaddnotice, msguser, appNoticeColor); cb.sendNotice(msguser + ' has added multiple users to the UltraApp Moderator list.\n' + 'Users added: ' + cbjs.arrayJoin(modcmdvalsplit, ', '), '', appNoticeColor, '', 'normal', 'red'); } else { var modtoadd2 = msgarray[1].toLowerCase(); if (!cbjs.arrayContains(moderatorList.name,modtoadd2)) { addRmvMods(modtoadd2,'botmod','a'); cb.sendNotice(botName + 'You have added ' + modtoadd2 + ' to the moderator list.', msguser, appNoticeColor); cb.sendNotice('Note this is only applied during this session, permanent moderator list changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.', msguser, appNoticeColor); cb.sendNotice(msguser + ' has added you to the UltraApp moderator list. You now have all moderator privileges, you can view the full command list with the "/uahelp" command.', modtoadd2, appNoticeColor); } else { cb.sendNotice(botName + modtoadd2 + ' is already on the UltraApp moderator list.', msguser, appNoticeColor); } } } else { cb.sendNotice(botName + 'You did not specify the username you want to add to the moderator list.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/rmvmod': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (msgarray[1]) { var modtormv = msgarray[1].toLowerCase(); if (cbjs.arrayContains(moderatorList.name,modtormv)) { addRmvMods(modtormv,'botmod','r'); cb.sendNotice(botName + 'You have removed ' + modtormv + ' from the Moderator list.', msguser, appNoticeColor); } else { cb.sendNotice(botName + modtormv + ' is not in the Moderator list.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'A parameter is required for this command to specify the user to remove from the Moderator list, such as "/rmvmod username".', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/modlist': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { var mlmessage = botName; mlmessage += 'Users currently on the UltraApp Moderator List: '; if (moderatorList.name.length > 0) { for (var modli = 0; modli < moderatorList.name.length; modli++) { mlmessage += '\n' + (modli+1) + '. ' + moderatorList.name[modli] + ' (' + moderatorList.type[modli] + ')'; } } else { mlmessage += '\nNo moderators currently assigned.'; } mlmessage += '\nEnd of List'; cb.sendNotice(mlmessage, msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } //********* Help Menu case '/uahelp': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { helpModBC(msgarray[1],msguser); } else { helpCommon(msguser); } break; } case '/about': { recognizedcmd = true; displayAbout(msguser); break; } //********* End of Expected commands } if (msgarray[0] == '/chscoreboard' || msgarray[0] == '/chs' || msgarray[0] == '/chfixscore' || msgarray[0] == '/chlength' || msgarray[0] == '/chclearlist' || msgarray[0] == '/chskip' || msgarray[0] == '/chwin' || msgarray[0] == '/chend' || msgarray[0] == '/chstoptimer' || msgarray[0] == '/chfree' || msgarray[0] == '/chword' || msgarray[0] == '/chprice' || msgarray[0] == '/chplay' || msgarray[0] == '/chh' || msgarray[0] == '/chq' || msgarray[0] == '/chlist' || msgarray[0] == '/chi' || msgarray[0] == '/charades' || msgarray[0] == '/randomfree' || msgarray[0] == '/random' || msgarray[0] == '/ri' || msgarray[0] == '/rw' || msgarray[0] == '/badfree' || msgarray[0] == '/badlibs' || msgarray[0] == '/bli' || msgarray[0] == '/blc' || msgarray[0] == '/badprice' || msgarray[0] == '/blw' || msgarray[0] == '/addwho' || msgarray[0] == '/addwhat' || msgarray[0] == '/addwhere' || msgarray[0] == '/addhowlong' || msgarray[0] == '/uapresale' || msgarray[0] == '/uapresales' || msgarray[0] == '/backup' || msgarray[0] == '/ki' || msgarray[0] == '/gbhelp' || msgarray[0] == '/whs' || msgarray[0] == '/rri' || msgarray[0] == '/whi' || msgarray[0] == '/di' || msgarray[0] == '/setusercolor' || msgarray[0] == '/setusernn' || msgarray[0] == '/setusericon' || msgarray[0] == '/pt' || msgarray[0] == '/gbstats' || msgarray[0] == '/wi' || msgarray[0] == '/wf' || msgarray[0] == '/warfree' || msgarray[0] == '/pi' || msgarray[0] == '/pf' || msgarray[0] == '/pressfree' || msgarray[0] == '/pw' || msgarray[0] == '/presswinners' || msgarray[0] == '/ww' || msgarray[0] == '/warwinners' || msgarray[0] == '/cpd' || msgarray[0] == '/chgprizedesc' || msgarray[0] == '/addprize' || msgarray[0] == '/cpp' || msgarray[0] == '/changeprizeprice' || msgarray[0] == '/mpl' || msgarray[0] == '/masterprizelist' || msgarray[0] == '/ap' || msgarray[0] == '/pq' || msgarray[0] == '/pressq' || msgarray[0] == '/pressqueue' || msgarray[0] == '/stoppress' || msgarray[0] == '/press' || msgarray[0] == '/pp' || msgarray[0] == '/pressprizes' || msgarray[0] == '/war' || msgarray[0] == '/waragain' || msgarray[0] == '/stopwar' || msgarray[0] == '/warprizes' || msgarray[0] == '/wl' || msgarray[0] == '/warlist' || msgarray[0] == '/wd' || msgarray[0] == '/wardraw' || msgarray[0] == '/wp' || msgarray[0] == '/kb' || msgarray[0] == '/freekeno' || msgarray[0] == '/wargame' || msgarray[0] == '/keno' || msgarray[0] == '/kp' || msgarray[0] == '/kpbc' || msgarray[0] == '/rrprice' || msgarray[0] == '/chambers' || msgarray[0] == '/rrp' || msgarray[0] == '/rr' || msgarray[0] == '/rrs' || msgarray[0] == '/shooters' || msgarray[0] == '/wheelspins' || msgarray[0] == '/wheelprice' || msgarray[0] == '/freespin' || msgarray[0] == '/freeroll' || msgarray[0] == '/wheel' || msgarray[0] == '/wheelprizes' || msgarray[0] == '/dice' || msgarray[0] == '/diceprizes' || msgarray[0] == '/diceprice' || msgarray[0] == '/usegraylock' || msgarray[0] == '/chggraytime' || msgarray[0] == '/fbhelp' || msgarray[0] == '/expps' || msgarray[0] == '/pslist' || msgarray[0] == '/subject' || msgarray[0] == '/check' || msgarray[0] == '/pass' || msgarray[0] == '/plist' || msgarray[0] == '/plistw' || msgarray[0] == '/email' || msgarray[0] == '/newshow' || msgarray[0] == '/useraffle' || msgarray[0] == '/uselushmenu' || msgarray[0] == '/pm' || msgarray[0] == '/reply' || msgarray[0] == '/bc' || msgarray[0] == '/tm' || msgarray[0] == '/tbm' || msgarray[0] == '/silencelevel' || msgarray[0] == '/graphiclevel' || msgarray[0] == '/sl' || msgarray[0] == '/gl' || msgarray[0] == '/ninja' || msgarray[0] == '/unninja' || msgarray[0] == '/ninjalist' || msgarray[0] == '/silence' || msgarray[0] == '/unsilence' || msgarray[0] == '/silencelist' || msgarray[0] == '/tipmenu' || msgarray[0] == '/tipmenurequests' || msgarray[0] == '/tmr' || msgarray[0] == '/tmrb' || msgarray[0] == '/tipmenuadd' || msgarray[0] == '/tipmenurmv' || msgarray[0] == '/usemenu' || msgarray[0] == '/posmenu' || msgarray[0] == '/posmenurequests' || msgarray[0] == '/posmenuadd' || msgarray[0] == '/posmenurmv' || msgarray[0] == '/useposmenu' || msgarray[0] == '/startclock' || msgarray[0] == '/addtoclock' || msgarray[0] == '/timeleft' || msgarray[0] == '/stopclock' || msgarray[0] == '/cn' || msgarray[0] == '/cnh' || msgarray[0] == '/cnd' || msgarray[0] == '/cndh' || msgarray[0] == '/usenotifier' || msgarray[0] == '/chgmsg1' || msgarray[0] == '/chgmsg2' || msgarray[0] == '/chgmsg3' || msgarray[0] == '/chgmsg4' || msgarray[0] == '/chgmsg5' || msgarray[0] == '/dspmsg' || msgarray[0] == '/leaders' || msgarray[0] == '/useleaderboard' || msgarray[0] == '/usetipcount' || msgarray[0] == '/tippers' || msgarray[0] == '/poll' || msgarray[0] == '/usepoll' || msgarray[0] == '/endpoll' || msgarray[0] == '/restartpoll' || msgarray[0] == '/addvote' || msgarray[0] == '/polloptadd' || msgarray[0] == '/polloptrmv' || msgarray[0] == '/pollstarttimer' || msgarray[0] == '/polladdtime' || msgarray[0] == '/pollstoptimer' || msgarray[0] == '/pl' || msgarray[0] == '/polllead' || msgarray[0] == '/addnice' || msgarray[0] == '/rmvnice' || msgarray[0] == '/nicelist' || msgarray[0] == '/exportvip' || msgarray[0] == '/exportfans' || msgarray[0] == '/addword' || msgarray[0] == '/rmvword' || msgarray[0] == '/wordlist' || msgarray[0] == '/newsubject' || msgarray[0] == '/dumpsettings' || msgarray[0] == '/checkcolor' || msgarray[0] == '/dsptlist' || msgarray[0] == '/usetlist' || msgarray[0] == '/exptlist' || msgarray[0] == '/addlbtop' || msgarray[0] == '/addlbamt' || msgarray[0] == '/useticketshow' || msgarray[0] == '/exppresale' || msgarray[0] == '/addpresale' || msgarray[0] == '/rmvpresale' || msgarray[0] == '/chgpresalemode' || msgarray[0] == '/lushmenu' || msgarray[0] == '/uselushmenu' || msgarray[0] == '/chgtoy' || msgarray[0] == '/uselush' || msgarray[0] == '/usedomi' || msgarray[0] == '/usenora' || msgarray[0] == '/useferri' || msgarray[0] == '/media' || msgarray[0] == '/medialist' || msgarray[0] == '/usemedia' || msgarray[0] == '/userules' || msgarray[0] == '/rules' || msgarray[0] == '/ruleslist' || msgarray[0] == '/prizes' || msgarray[0] == '/usedice' || msgarray[0] == '/chgdiceprice' || msgarray[0] == '/dicerolls' || msgarray[0] == '/usealltime' || msgarray[0] == '/top10' || msgarray[0] == '/alltime' || msgarray[0] == '/startprivate' || msgarray[0] == '/stopprivate' || msgarray[0] == '/useraffle' || msgarray[0] == '/entries' || msgarray[0] == '/raffletickets' || msgarray[0] == '/raffleentries' || msgarray[0] == '/previousentries' || msgarray[0] == '/resetraffle' || msgarray[0] == '/clearraffle' || msgarray[0] == '/addraffletkt' || msgarray[0] == '/rmvraffletkt' || msgarray[0] == '/addraffleprize' || msgarray[0] == '/rmvraffleprize' || msgarray[0] == '/raffleprizes' || msgarray[0] == '/setraffleprice' || msgarray[0] == '/raffledrawing' || msgarray[0] == '/rafflestarttimer' || msgarray[0] == '/startraffletimer' || msgarray[0] == '/raffleaddtime' || msgarray[0] == '/addraffletime' || msgarray[0] == '/rafflestoptimer' || msgarray[0] == '/stopraffletimer' || msgarray[0] == '/raffletimeleft' || msgarray[0] == '/textpoll' || msgarray[0] == '/usetextpoll' || msgarray[0] == '/endtextpoll' || msgarray[0] == '/restarttextpoll' || msgarray[0] == '/resettextpoll' || msgarray[0] == '/addtextvote' || msgarray[0] == '/textoptadd' || msgarray[0] == '/textoptrmv' || msgarray[0] == '/textstarttimer' || msgarray[0] == '/textaddtime' || msgarray[0] == '/textstoptimer' || msgarray[0] == '/textlead' || msgarray[0] == '/textchgtitle' || msgarray[0] == '/texttimeleft' || msgarray[0] == '/setbrdsep' || msgarray[0] == '/clear' || msgarray[0] == '/dspmsg' || msgarray[0] == '/chgmsg' || msgarray[0] == '/chgrafflemode') { recognizedcmd = true; } if (cb.settings.fembotRunning != 'Yes') { if (!recognizedcmd) { cb.sendNotice(msgarray[0] + ' is not a recognized UltraApp command.\nType "/uahelp" to see a full list of the available UltraApp commands.', msguser, appNoticeColor); } } } // Highlight background for Ticket show if (whichApp == 'ticket') { if (cb.limitCam_userHasAccess(msguser) && showStage != 'aftershow') { msg.background = ticketHolderBgColor; if (cb.settings.ticketshowEnableIcon == 'Yes') { msg.m = ticketemoji + ' ' + msg.m; } } } return msg; }); // *********************************** Tip Options ********************************** cb.tipOptions(function(user) { var tipoptuser = user.user; if (cbjs.arrayContains(ninjaTipsOn, tipoptuser)) { return {options:[{label: '--NOGOAL-- Do not count tips toward goal'}, {label: 'Regular tipping'}], label: 'Choose if you would like your tips to count toward goal :'}; } else { if (whichApp == 'goalrace') { // if (cbjs.arrayContains(raceTipNotesOn, tipoptuser)) { // return; // } else { return {options:[{label: goalraceDesc1}, {label: goalraceDesc2}], label: 'Select a Goal to Vote for :'}; // } } else { return; } } }); // **************************** Actions on user entering ***************************** cb.onEnter(function(user) { var enteruser = user.user; var enterismod = user.is_mod; var enterisbc = (enteruser === BC); var enterisfan = user.in_fanclub; var enterisvip = cbjs.arrayContains(VIPListArray,enteruser); var enterisnotgray = user.has_tokens; if (enterismod) { addRmvMods(enteruser,'cbmod','a'); } else { if (cbjs.arrayContains(moderatorList.name,enteruser)) { var modnameidx = moderatorList.name.indexOf(enteruser); if (moderatorList.type[modnameidx] == 'botmod') { enterismod = true; } } } if (enterismod) { addRmvModsInShow(enteruser,'a'); } if (enterisfan) { addRmvFanClubInShow(enteruser,'a'); } if (enterisvip) { addRmvVIPInShow(enteruser,'a'); } if (whichApp == 'ticket') { if (cb.limitCam_userHasAccess(enteruser)) { addViewer(enteruser); cb.drawPanel(); } } // **** General Entry Message if (cb.settings.enableEntryMessage == 'Yes' && !enterisbc) { var genentrymsg = false; var entrymessage = ''; if (cb.settings.entryMessage) { entrymessage = borderWelcomeTop + '\n' + checkNextLine(checkUsername(cb.settings.entryMessage,enteruser)) + '\n' + borderUniversalBottom; genentrymsg = true; } switch (whichApp) { case 'goals': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, progGoalBgColor, progGoalTextColor, 'bold'); } goalNotices(enteruser,enteruser,'r','entry'); break; } case 'goalcount': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, goalCountBgColor, goalCountTextColor, 'bold'); } goalNotices(enteruser,enteruser,'r','entry'); break; } case 'goalrace': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, goalraceBgColor, goalraceTextColor, 'bold'); } goalNotices(enteruser,enteruser,'r','entry'); break; } case 'spank': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, spankBgColor, spankTextColor, 'bold'); } goalNotices(enteruser,enteruser,'r','entry'); break; } case 'autoreset': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, autoresetBgColor, autoresetTextColor, 'bold'); } goalNotices(enteruser,enteruser,'r','entry'); break; } case 'ticket': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, ticketBgColor, ticketTextColor, 'bold'); } break; } case 'none': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, '', '', 'bold'); } break; } default : { break; } } } // **** Ticket Show entry functions and messages if (!enterisbc && whichApp == 'ticket') { if (enterismod || enterisfan || enterisvip) { checkFreeTickets(enteruser,enterismod,enterisfan,enterisvip,'enter'); } if (whichApp == 'ticket') { var enterticketmessage = ''; if (cb.limitCam_isRunning()) { if (!cb.limitCam_userHasAccess(enteruser)) { if (cb.settings.ticketShowFanAppreciation == 'Yes') { enterticketmessage += '\nToday\'s Ticket Show is a Fan Appreciation Show!'; enterticketmessage += '\nOnly configured Fan Club Members will be admitted to the show!'; enterticketmessage += '\nPlease consider joining the Fan Club!'; } else { enterticketmessage += '\nHidden Ticket Show in progress for ' + ((Date.now() - hiddenTime)/60000).toFixed(1) + ' minutes.'; if (showStage == 'ticketshow') { enterticketmessage += '\nTicket sales still available, ticket price is ' + ticketPrice + ' tokens.'; } else if (showStage == 'showwarn') { enterticketmessage += '\n ' + bcText + ' has indicated the show is nearly over.'; enterticketmessage += '\nBuying a ticket is not recommended, but you may still buy one for ' + ticketPrice + ' tokens.'; } else if (showStage == 'showfinale') { enterticketmessage += '\n ' + bcText + ' has indicated the show is nearly over.'; enterticketmessage += '\nTicket Sales have been suspended, you can no longer buy a ticket.'; enterticketmessage += '\n' + bcText + ' may be returning to public chat shortly.'; } } } else { enterticketmessage += '\nWelcome, you already have a ticket, enjoy the show!'; } cb.sendNotice(borderTicketTop + enterticketmessage + borderTicketBottom, enteruser, ticketBgColor, ticketTextColor, 'bold'); } else { if (showStage == 'ticketsales') { if (cb.settings.ticketShowFanAppreciation == 'Yes') { if (cb.limitCam_userHasAccess(enteruser)) { enterticketmessage += '\nWelcome! You have a ticket to the Fan Appreciation show and the show has not yet started.'; } else { enterticketmessage += '\nToday\'s Ticket Show is a Fan Appreciation Show!'; enterticketmessage += '\nOnly Fan Club Members will be admitted to the show!'; enterticketmessage += '\nPlease consider joining the Fan Club!'; } } else { if (cb.limitCam_userHasAccess(enteruser)) { enterticketmessage += '\nWelcome! You have a ticket, and the show has not yet started.'; } else { enterticketmessage += '\nTicket Show sales are active and the show has not yet started.'; enterticketmessage += '\nThe ticket price is ' + ticketPrice + ' tokens.'; if (ticketShowStartPrice > 0) { enterticketmessage += '\nThe ticket price will increase to ' + ticketShowStartPrice + ' tokens when the show is started.'; enterticketmessage += '\nGet your ticket before the price goes up!'; } } } cb.sendNotice(borderTicketTop + enterticketmessage + borderTicketBottom, enteruser, ticketBgColor, ticketTextColor, 'bold'); } else if (showStage == 'aftershow' || ticketShowEnded == true) { enterticketmessage += '\nSorry, the Ticket Show is over.'; cb.sendNotice(borderTicketTop + enterticketmessage + borderTicketBottom, enteruser, ticketBgColor, ticketTextColor, 'bold'); } } } } }); // *********************************** Actions upon leaving ************************************** cb.onLeave(function(user) { var leaveuser = user.user; if (leaveuser != BC) { if (whichApp == 'ticket') { removeViewer(leaveuser); } } }); // *********************************** Actions upon Draw Panel ************************************** cb.onDrawPanel(function (user) { var panel = {}; if (botPanel) { panel.template = 'image_template'; panel.row1_label = "row1_label"; panel.row1_value = "row1_value"; panel.row2_label = "row2_label"; panel.row2_value = "row2_value"; panel.row3_label = "row3_label"; panel.row3_value = "row3_value"; } else { panel.template = '3_rows_11_21_31'; } if (whichApp == 'ticket') { if (cb.settings.ticketShowFanAppreciation == 'Yes') { panel.row1_value = 'Fan Appreciation Show'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Only Fan Club Admitted'; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Join the Fanclub Today to See the Show!'; leftjust3 = leftJustify(panel.row3_value,3); } else if (showStage == 'ticketsales') { if (ticketStartMode == 'ticketgoal') { panel.row1_value = 'Start Mode: Ticket Goal \u25FE ' + ticketShowTotalTicketsBought + ' / ' + ticketShowGoalTickets + ' tickets'; leftjust1 = leftJustify(panel.row1_value+'..',1); panel.row2_value = 'Ticket Holders: ' + countTickets + ' \u25FE Viewers: ' + ticketShowViewerList.length; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Ticket Price: ' + ticketPrice + ' tokens ' + (cb.settings.showTotals == 'Yes' ? (' \u25FE Total Tips: ' + currentAppTotalTicket) : ''); leftjust3 = leftJustify(panel.row3_value,3); } else if (ticketStartMode == 'tokengoal') { panel.row1_value = 'Start Mode: Token Goal \u25FE ' + ticketShowTotalTips + ' / ' + ticketShowGoalTokens + ' tokens'; leftjust1 = leftJustify(panel.row1_value+'...',1); panel.row2_value = 'Ticket Holders: ' + countTickets + ' \u25FE Viewers: ' + ticketShowViewerList.length; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Ticket Price: ' + ticketPrice + ' tokens ' + (cb.settings.showTotals == 'Yes' ? (' \u25FE Total Tips: ' + currentAppTotalTicket) : ''); leftjust3 = leftJustify(panel.row3_value,3); } else if (ticketStartMode == 'timer') { panel.row1_value = 'Start Mode: Timer (' + ((ticketSecsRemain > 0 || ticketMinsRemain > 0) ? ticketTimeLeftPanel() : 'Timer not yet started') + ')'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Ticket Holders: ' + countTickets + ' \u25FE Viewers: ' + ticketShowViewerList.length; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Ticket Price: ' + ticketPrice + ' tokens ' + (cb.settings.showTotals == 'Yes' ? (' \u25FE Total Tips: ' + currentAppTotalTicket) : ''); leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = 'Start Mode: Manual'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Ticket Holders: ' + countTickets; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Ticket Price: ' + ticketPrice + ' tokens ' + (cb.settings.showTotals == 'Yes' ? (' \u25FE Total Tips: ' + currentAppTotalTicket) : ''); leftjust3 = leftJustify(panel.row3_value,3); } } else if (showStage == 'showfinale') { panel.row1_value = 'Ticket Holders: ' + countTickets; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Viewers: ' + ticketShowViewerList.length; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Show ending soon, no more ticket sales!'; leftjust3 = leftJustify(panel.row3_value,3); } else if (showStage == 'aftershow') { panel.row1_value = 'Ticket Show is Over!'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Thank you so much everyone for joining!'; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = ticketAppPanelText3; leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = 'Ticket Holders: ' + countTickets; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Viewers: ' + ticketShowViewerList.length; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Ticket Price: ' + ticketPrice + ' tokens ' + (cb.settings.showTotals == 'Yes' ? (' \u25FE Total Tips: ' + currentAppTotalTicket) : ''); leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'goals') { if (!finalGoalMet) { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; } else if (minTippersToggle == 'Yes' && tippersNeeded > 0) { panel.row1_value = ((currentGoalTips <= 0 || isNaN(currentGoalTips)) ? '\u25FE Token Goal Reached!' : 'Tokens left for Goal: ' + currentGoalTips) ; } else { panel.row1_value = ((currentGoalTips <= 0 || isNaN(currentGoalTips)) ? '\u25FE Goal Reached!' : 'Tokens left for Goal: ' + currentGoalTips) ; } leftjust1 = leftJustify(panel.row1_value,1); if (ticketSalesOpen) { panel.row2_value = ((currentGoalTips <= 0 || isNaN(currentGoalTips)) ? (minTippersToggle == 'Yes' && tippersNeeded > 0 ? 'Progress: ' + tippersNeeded + ' new tipper' + (tippersNeeded > 1 ? 's' : '') + ' needed for goal!' : '\u25FE Token Goal Reached!') : 'Tokens left for Goal: ' + currentGoalTips); } else if (minTippersToggle == 'Yes' && tippersNeeded > 0 && (currentGoalTips <= 0 || isNaN(currentGoalTips))) { panel.row2_value = 'Progress: ' + tippersNeeded + ' new tipper' + (tippersNeeded > 1 ? 's' : '') + ' needed for goal!'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row2_value = 'Progress: ' + goalBar((currentGoalTips <= 0 ? currentGoalTotal : (currentGoalTotal-currentGoalTips)),currentGoalTotal) + ' (' + Math.floor((currentGoalTips <= 0 ? currentGoalTotal : (currentGoalTotal-currentGoalTips))/currentGoalTotal*100) + ' %)'; leftjust2 = 42; } if (minTippersToggle == 'Yes' && tippersNeeded > 0 && currentGoalTips > 0) { panel.row3_value = tippersNeeded + ' new tipper' + (tippersNeeded > 1 ? 's' : '') + ' needed'; } else if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Total Goal Show Tips: ' + currentAppTotalGoal; } else { panel.row3_value = 'At Goal: ' + currentGoalDesc; } leftjust3 = leftJustify(panel.row3_value,3); } else { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'All Goals Completed!'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'All Goals Completed!'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar(100,100) + ' (100 %)'; leftjust2 = 42; } if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Total Goal Show Tips: ' + currentAppTotalGoal; } else { panel.row3_value = 'Thanks to all tippers!'; } leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'autoreset') { if (!finalGoalMet) { if (resetShowCount) { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Current Goal (#' + (currentGoal) + ') : ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'Current Goal (#' + (currentGoal) + ') : ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar((currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips),currentGoalTotal) + ' (' + Math.floor((currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)/currentGoalTotal*100) + ' %)'; leftjust2 = 42; } if (autoresetEndAfter > 0) { panel.row3_value = 'Goals Completed: ' + (currentGoal-1) + ' / ' + autoresetEndAfter; } else { panel.row3_value = 'Goals Completed: ' + (currentGoal-1); } leftjust3 = leftJustify(panel.row3_value,3); } else { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Current Goal: ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'Current Goal: ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar((currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips),currentGoalTotal) + ' (' + Math.floor((currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)/currentGoalTotal*100) + ' %)'; leftjust2 = 42; } panel.row3_value = 'Last Tipper: ' + resetLastTip; leftjust3 = leftJustify(panel.row3_value,3); } } else { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'All Goals Completed!'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'All Goals Completed!'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar(100,100) + ' (100 %)'; leftjust2 = 42; } if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Total Auto-Reset Goal Tips: ' + currentAppTotalAutoreset; leftjust3 = leftJustify(panel.row3_value,3); } else if (ticketSalesOpen) { panel.row3_value = 'Progress: ' + goalBar(100,100) + ' (100 %)'; leftjust3 = 42; } else { panel.row3_value = ''; leftjust3 = leftJustify(panel.row3_value,3); } } } else if (whichApp == 'goalcount') { if (!finalGoalMet) { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Current Goal (#' + (currentGoalCountAmt + 1) + '): ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'Current Goal (#' + (currentGoalCountAmt + 1) + '): ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar((currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips),currentGoalTotal) + ' (' + Math.floor((currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)/currentGoalTotal*100) + ' %)'; leftjust2 = 42; } panel.row3_value = 'Completed: ' + currentGoalCountAmt + ' \u25FE Next Prize Level: ' + goalCounterArray.amt[currentGoalLevel-1]; leftjust3 = leftJustify(panel.row3_value,3); } else { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'All Goals Completed!'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'All Goals Completed!'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar(100,100) + ' (100 %)'; leftjust2 = 42; } if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Total Goal Count Tips: ' + currentAppTotalCounter; } else { panel.row3_value = ' '; } leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'goalrace') { if (!finalGoalMet) { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); } else { panel.row1_value = 'Goal Race! ' + goalracePanelText; leftjust1 = leftJustify(panel.row1_value,1); } panel.row2_value = goalraceDesc1.substring(0,12) + ': ' + goalBar((currentGoalRace1Tips > goalraceAmount1 ? goalraceAmount1 : currentGoalRace1Tips),goalraceAmount1) + ' (' + Math.floor((currentGoalRace1Tips > goalraceAmount1 ? goalraceAmount1 : currentGoalRace1Tips)/goalraceAmount1*100) + '%) \u25FE ' + (currentGoalRace1Tips > goalraceAmount1 ? goalraceAmount1 : currentGoalRace1Tips) + '/' + goalraceAmount1; leftjust2 = 15; panel.row3_value = goalraceDesc2.substring(0,12) + ': ' + goalBar((currentGoalRace2Tips > goalraceAmount2 ? goalraceAmount2 : currentGoalRace2Tips),goalraceAmount2) + ' (' + Math.floor((currentGoalRace2Tips > goalraceAmount2 ? goalraceAmount2 : currentGoalRace2Tips)/goalraceAmount2*100) + '%) \u25FE ' + (currentGoalRace2Tips > goalraceAmount2 ? goalraceAmount2 : currentGoalRace2Tips) + '/' + goalraceAmount2; leftjust3 = 15; } else { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); if (cb.settings.showTotals == 'Yes') { panel.row2_value = 'Race Over! Winning Goal: ' + goalRaceWinner; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Total Goal Race Tips: ' + currentAppTotalGoalRace; leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row2_value = 'Goal Race Completed!'; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Winning Goal: ' + goalRaceWinner; leftjust3 = leftJustify(panel.row3_value,2); } } else { panel.row1_value = 'Goal Race Completed!'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Winning Goal: ' + goalRaceWinner; leftjust2 = leftJustify(panel.row2_value,2); if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Total Goal Race Tips: ' + currentAppTotalGoalRace; } else { panel.row3_value = ' '; } leftjust3 = leftJustify(panel.row3_value,3); } } } else if (whichApp == 'spank') { if (ticketSalesOpen) { panel.row1_value = 'Ticket Price: ' + ticketPrice + ' tkns. Show later...'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Total Banked / Spanked: ' + totalSpanksTipped + ' / ' + totalSpanksCompleted + ' (' + (totalSpanksTipped-totalSpanksCompleted) + ' left)'; leftjust2 = leftJustify(panel.row2_value,2); } else { panel.row1_value = 'Total Spanks Banked: ' + totalSpanksTipped; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Total Spanks Spanked: ' + totalSpanksCompleted + ' (' + (totalSpanksTipped-totalSpanksCompleted) + ' left)'; leftjust2 = leftJustify(panel.row2_value,2); } panel.row3_value = 'Current Goal (#' + (currentGoal) + ') : ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'; leftjust3 = leftJustify(panel.row3_value,3); } else if (whichApp == 'none') { if (cb.settings.noAppRunningDisplay == 'Welcome to my room') { panel.row1_value = noAppPanelText1; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = noAppPanelText2; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = noAppPanelText3; leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = 'Highest Tip: ' + highestTipper + ' (' + highestTip + ')'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Most Recent Tip: ' + mostRecentTipper + ' (' + mostRecentTip + ')'; leftjust2 = leftJustify(panel.row2_value,2); if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Total Show Tips: ' + currentSessionTotal; } else { panel.row3_value = 'Number of Unique Tippers: ' + tipCountArray.name.length; } leftjust3 = leftJustify(panel.row3_value,3); } } panel.layers = [ {'type': 'image', 'fileID': backgroundImage}, { 'type': 'text', 'text': panel.row1_value, 'top': topjust1, 'left': leftjust1, 'font-size': fontSize, 'font-weight': 'bold', 'color': textColor, }, { 'type': 'text', 'text': panel.row2_value, 'top': topjust2, 'left': leftjust2, 'font-size': fontSize, 'color': textColor, }, { 'type': 'text', 'text': panel.row3_value, 'top': topjust3, 'left': leftjust3, 'font-size': fontSize, 'color': textColor, }, ] return panel; }); // *********************************** Actions upon tipping ************************************** cb.onTip (function (tip) { var tipamount = Number.parseInt(tip.amount, 10); var tipnote = tip.message; var tipfromuser = tip.from_user var tipisfan = tip.from_user_in_fanclub; var tipisvip = cbjs.arrayContains(VIPListArray,tipfromuser); var tipisanon = tip.is_anon_tip; var tipfromdisplayuser = tipfromuser; // ***** Tip Count Array if (tipisanon) { tipfromdisplayuser = 'Anonymous User'; } else { if (!cbjs.arrayContains(tipCountArray.name, tipfromuser)) { tipCountArray.name.push(tipfromuser); tipCountArray.amount.push(tipamount); } else { tipCountArray.amount[findTipper(tipfromuser)] += tipamount; } } if (tipamount >= highestTip) { highestTip = tipamount; highestTipper = tipfromdisplayuser; } mostRecentTip = tipamount; mostRecentTipper = tipfromdisplayuser; // ***** Record tip for goal progress and track total stats var counttip = true; var tipnotemessagearray = []; tipnotemessagearray = tipnote.split(' '); if (minTippersToggle == 'Yes') { tippersNeeded = minTippersGoal - tipCountArray.name.length; if (isNaN(tippersNeeded)) { tippersNeeded = 0; } } if (whichApp != 'ticket' && cbjs.arrayContains(tipnotemessagearray,'--NOGOAL--')) { counttip = false; } if (counttip) { recordTip(tipamount,tipfromuser,tipnote,tipisfan,tipisvip,tipfromdisplayuser); } else { cb.sendNotice(botName + tipfromuser + ' tipped with Ninja Tipping enabled, their tip was not counted toward any goals.', BC, appNoticeColor); cb.sendNotice(botName + tipfromuser + ' tipped with Ninja Tipping enabled, their tip was not counted toward any goals.', BC, appNoticeColor, '', '', 'red'); } });
© Copyright Chaturbate 2011- 2024. All Rights Reserved.