Apps Home
|
Create an App
RO crew
Author:
rach_oneill
Description
Source Code
Launch App
Current Users
Created by:
Rach_Oneill
// ==UserScript== // @name Dorothy's UltraApp // @version 3.7 // @description UltraApp // ==/UserScript== // Author: butter_my_toast // Created 01/27/2019 //** 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', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75' ]; 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', 'Water Droplets', 'Light Blue Splash', 'Pool Surface', 'Turquoise Tiles', 'Shifting Hexagons', 'Topography', 'Gray & White Floral', 'Tropical Leaves', 'Animated Red Waves', 'Animated Rainbow Waves', 'Pink & Purple Pattern', 'Big Leaves', 'Animated Gray Liquid', 'Animated Light Streaks' ], 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', 'waterdroplets', 'bluesplash', 'pool', 'turqoise_tiles', 'hexagons', 'topography', 'gw_floral', 'tropical_leaves', 'red_waves', 'Rainbow Waves', 'pp_pattern', 'big_leaves', 'gray_liquid', 'light_streaks' ], 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', 'add', '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', '850a2496-df57-4491-8764-db9f5ed27ef2', '7d65f794-12dc-44d7-8bcc-d833766a0c1b', 'fa2e4a49-39ff-45e4-be13-2c3bc661ac1e', '1776f45b-26b4-4d30-ae38-17630810a502', 'cb6b2dfc-1c9a-480c-a57a-0a5e9b2b5c3d', 'f7f7f4e5-6fc4-42c2-9398-7d9a0b9d3f52', '2f273904-153e-4627-b7c5-12a1f8956cee', '9e500db8-5821-4b17-8b35-1866c66689a1', '1ddefc54-fee4-4b88-bd50-afcf230eba28', '2e8aff83-ecc4-4f25-b011-db708c6e3da5', '30a34fd7-e5a2-4a54-b00b-0620ed3d14c7', '1107d858-2e04-4e21-9cd7-03d517e5e950', '4a20f71d-dfd6-46bd-96df-241a24f08af0', 'a83ec613-fa57-4155-9d26-dfec582a1926' ], 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', '56d78fcd-95dd-4a8b-a60e-426297962587', 'edb3e69d-f9e1-47bc-81f4-27dae3cdca79', '37ac13d6-f2d6-40f1-b58f-0386caa33502', '1f759390-6cb2-4f13-84c9-6a772fa7886c', '61977041-8295-482f-8a19-12147b50f578', '70625b79-d838-4060-a91a-f36fff1cae73', '83dd021e-45a7-4589-9327-22f9ce00188b', '8a425e38-5ba9-440e-87f3-11f0aec82d71', 'a9d6beba-eaaa-4bfa-b305-df7eace6f2a6', 'aaa81355-c136-427f-847c-0646ba91e113', 'aae3bddc-f1b8-485f-8848-70eb3b3365bc', 'd0cc92ec-26c5-46ff-b0e8-751ded5425f9', 'f91a5c06-89fb-4430-a88c-b5ecae474666', '06933d7c-446b-41b4-a0a8-f2808ef38029' ] }; var backgroundArrayCustom = { menu: ['Dorothy - Testbed', 'Liv and Drew', 'Batmansuperhero', 'xoxo Caroline', 'Taylor Love', 'Fawn Moon', 'BB Milfshake', 'Tessa Rae', 'Sarai Sublime', 'Mystic Bunny', 'Top1Asian', 'Big Boobs McGee'], command: ['dorothy', 'livanddrew', 'batmansuperhero', 'xoxo_caroline', 'taylor_love_303', 'fawnmoonx', 'bigbananamilfshake', 'tessa_rae', 'sarai_sublime', 'mystic_bunny', 'top1asian', 'bigboobsmcgee2021'], devfile: ['96e9f3c5-0db4-47f8-ba31-2f06a5eaa006', 'add', 'add', '2eb0c7d7-2582-479c-b675-1c87de9f2dff', 'add', 'add', 'add', 'add', 'add', 'add', 'add', 'add'], livefile: ['NA', '51864982-fa61-466f-9de6-367461031eb9', '357665d8-b76a-4fa0-a49c-ac85c33982ad', '0a14c989-3b29-4007-97b6-80b79e4cf202', '17a3e463-d713-4d49-95be-e93fc150fb5d', 'a301ad63-f6a7-410a-bd1b-bf16cbc38346', 'ab87a6c0-9ce9-47e7-beff-36f76eb792f8', '1f9b1f7a-7502-479e-bb31-ad2ca1e2bb6c', 'c97ba657-c019-471e-ac31-a884524603aa', '6254dd58-72c8-4f32-aab1-64fb8f8b6fe3', '45ad03c1-ea35-49d2-ba10-bd4799cb1c3e', '5bf034a8-a52a-4460-b5fa-7ea2dfcd2a91'], livefile2: ['NA', '3dfe588d-6b3b-4059-96b2-c5b1da9ecb9b', 'bfdb4283-7b9f-4046-ab29-1191b5190e08', '5daf2aae-83c2-48db-b547-4cc9f730bd43', '373aa015-a9d3-4923-a888-72b757afe9c0', 'b5679c01-e4de-4ad0-91f1-4dc70173f185', '2cfc5fc2-8e1e-420f-bae9-15fa7a09cd73', '7b66525f-960f-467b-9f4f-ae2dc0c3bf59', 'c780981d-7cf4-45a6-854b-7ec88b179f93', '3a6ec6e4-d88e-40e8-885b-097b49fedebf', '2a36f9a9-0a61-4b31-929d-ecf8527dbcd1', 'd21fe148-1091-4a74-a8ad-a8b3cecf3740'] }; 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' ], name: ['white', 'black', 'darkgrey', 'darkred', 'darkorange', 'darkgreen', 'darkaqua', 'darkblue', 'darkpurple', 'darkpink', 'darkgold', 'darkteal', 'darkbrown', 'darkbronze', 'darkperiwinkle', 'darkfuschia', 'darklime', 'darkplum' ], colorID: ['#FFFFFF', '#000000', '#737373', '#cc0000', '#e77400', '#006600', '#006767', '#0629AC', '#3d003d', '#FF6680', '#998100', '#003f1f', '#582c00', '#a56728', '#155bd7', '#d6155c', '#6b790c', '#7f13bf' ]}; 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'], name: ['white', 'lightyellow', 'lightblue', 'lightpink', 'lightred', 'lightgreen', 'lightpurple', 'lightorange', 'lightgrey', 'lightaqua', 'lightteal', 'cream', 'lightbronze', 'lightperiwinkle', 'lightfuschia', 'lightlime', 'lightplum' ], colorID: ['#FFFFFF', '#ffff94', '#d1eaee', '#FFE6EA', '#ff9a9a', '#94e594', '#f2cdff', '#ffd9b3', '#e6e6e6', '#adeaea', '#d7fbee', '#f9f6ed', '#ebccad', '#d7e4fb', '#fbd7e4', '#ecf6a7', '#e3c0f9' ]}; cb.settings_choices = [ {name: 'intro', label: '******************* INTRODUCTION ********************* Latest Update: March 19, 2022 (version 3.7) ********************************************************* Welcome ' + cb.room_slug + ' to Dorothy\'s Ultra App, if this is your first time using the App, the main thing you are choosing is which feature to start with, and then setting up the defaults for that section. Since Apps control the draw panel below the video window, only one app feature can be active at a time. Other features can be set up in future shows or turned on during the show by you or your moderator. Moderators are given significant privileges by this App, so please make sure you assign moderators you can trust. We have defined default settings using prices that we find to be common, but please update these per your preference, the defaults are only suggestions! Also, you can see the full list of commands for the App by typing "/uahelp" in the chat (no quotes), and then also see more detailed help by section. It is expected that you would be using an Ultrabot such as Dorothy\'s Ultra Fembot alongside this for features such as Chat Control and Tipper Count/Leader features. Please enjoy using the Ultra App and feel free to say hello if you see us around, or DM us on twitter @thechelsea2950 if you have questions. Thank you! - butter_my_toast and chelsea2950', type: 'choice',required: false}, // *** General Settings {name: 'general', label: '---------------------------------------------------------------------------------- PART 1: GENERAL SETUP ---------------------------------------------------------------------------------- Please make a choice below for which App feature you will start the show with. For each feature, there is a Config section below where you will define the App startup defaults, and some settings can be changed on the fly during the show. See App description page for more details on each goal type', type: 'choice',required: false}, {name: 'whichApp', label: '1A. Which Goal Type / Show Type would you like to begin the show with?', type: 'choice', choice1: 'Single/Progressive Goals', choice2: 'Goal Counter', choice3: 'Asc/Desc Tip Sequence Goals', choice4: 'Tip Jar', choice5: 'UltraApp Ticket Show', choice6: 'Goal Race', choice7: 'Spank-a-thon', choice8: 'Peep Show', choice9: 'Auto-Reset Goal', choice10: 'None - Display Tip Stats', defaultValue: 'Single/Progressive Goals'}, {name: 'autoStartTicket', label: '1A2. Automatically start Ticket Show sales after the last goal is completed? If enabled, a 30 second countdown will start after finishing the last goal. Note: If you change your mind, you can cancel the start of ticket sales during that period with the command /cancelticket and start sales later', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'enableEntryMessage', label: '1B. Announcement? -- Display a notification to users when they enter? If enabled, it will display both the general welcome text below in setting "1C" (if configured), and a separate goal type specific announcement that describes how that goal works. If enabled and "1C" is left blank, only the goal specific note will be displayed (ideal if your welcome message is configured in a separate bot, such as Dorothy\'s Ultra Fembot or Easy Fembot)', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'entryMessage', label: '1C. Announcement Text -- Note if you are also using Dorothys Ultra Fembot, only set a welcome message in one of them, otherwise users get two messages. The substitution identifiers {username} and {n} can be used within your welcome text to have the username of the person joining the room show in the welcome message, or make a line break. Just make sure there is a space on either side of the identifier so that it is recognized. Leave this field blank if you\'ve configured a general welcome message in another bot', type: 'str',required: false, minLength: 1, maxLength: 1000, defaultValue: 'Welcome {username} to my room! Please see the goal notices for details on the type of show for today.'}, {name: 'modLevel', label: '1D. Moderator Trust Level -- This will control how much authority and access is granted to moderators. This applies to CB mods and bot mods, and the same moderator level is applied to all moderators (not user specific). There are 3 levels - Basic, Standard, and Advanced. "Standard" covers most of what mods are normally able to do and will be the default. Basic gives less access which is ideal when you are newer and may not have trusted mods yet. "Advanced" gives them mostly the same command usage abilities as a broadcaster (not CB functions like banning users), with the exception of editing or viewing the Private Blocked Word List, and seeing User Notes. See the bot description page for a full listing of the Access for each mod level, or type "/uahelp modlevels" in the chat', type: 'choice', choice1: 'Basic', choice2: 'Standard', choice3: 'Advanced', defaultValue: 'Standard'}, {name: 'botModList', label: '1E. App Mods -- Enter the names of any users you would like to grant moderator privileges without assigning Chaturbate moderator status. Users in this group are granted the same permissions as the red-named moderators assigned through CB. They can be entered here or added during the show with the "/addmod" command', type: 'str', minLength: 1, maxLength: 1000, defaultValue: '', required: false}, {name: 'extFanList', label: '1F1. External FanClub 1 List -- Enter the names of any External Fan Club 1 members 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: 'EFCname', label: '1F2. External FanClub 1 Name -- Enter the Display Name of the External Fan Club 1 if you have a personalized one. Default is "External Fan Club" if you do not enter one.', type: 'str', minLength: 1, maxLength: 50, required: false}, {name: 'extFanList2', label: '1G1. External FanClub 2 List -- Enter the names of any External Fan Club 2 members 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: 'EFCname2', label: '1G2. External FanClub 2 Name -- Enter the Display Name of the External Fan Club 2 if you have a personalized one. Default is "External Fan Club 2" if you do not enter one.', type: 'str', minLength: 1, maxLength: 50, required: false}, {name: 'VIPList', label: '1H. 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: '1J. 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}, {name: 'bctext', label: '1K. Broadcaster Display Name -- Replace the general term "broadcaster" and in some cases the room name with the name of your choosing (the name you would like to be called, couples name, etc) wherever it appears in chat notices. Leave blank to just use "The Broadcaster".', type: 'str',required: false, minLength: 1, maxLength: 50}, {name: 'maxTipToGoal', label: '1L. Max tip applied to goals -- Tips over this amount should not be counted toward goals, a comment to this affect is added to the recurring notice for the goal type. Does not apply to Ticket Show and Peep Show types. Default to zero to not use this setting and allow all tips to apply to goals', type: 'int', defaultValue: 0, minValue: 0, maxValue: 999999}, {name: 'showTotals', label: '1M. Display Total Tips -- Show the total tips so far for the current app in the draw panel?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'fembotRunning', label: '1N. Suppress command errors in App -- Are you using Dorothy\'s Fembot alongside the Ultra App? If not, you should be! :) This setting is used to suppress error messages from the UltraApp for invalid commands as it is expected the Fembot will handle errors (so you don\'t get the same error twice)', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'genericRoomSubjectType', label: '1P1. 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 1P2, 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 1P2 and the individual app features.', type: 'choice', choice1: 'All', choice2: 'All - Current goal only', choice3: 'User-entered text only', defaultValue: 'All'}, {name: 'genericRoomSubjectSfx', label: '1P2. 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: '1P3. 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: '1P4. 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, but can be enabled if you want this to show in the title. Not applicable to ticket show, peep show, tip sequence and goal race features. You can put the count at the very beginning or very end of the room title. Note that if you put it at the beginning, it will show in the preview of your room on the thumbnails page, which you may or may not want to have shown', type: 'choice', choice1: 'No', choice2: 'Yes, at beginning', choice3: 'Yes, at end', defaultValue: 'No'}, {name: 'panelBackground', label: '1R. 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], choice64: backgroundArray.menu[62], choice65: backgroundArray.menu[63], choice66: backgroundArray.menu[64], defaultValue: backgroundArray.menu[0]}, {name: 'panelTextColor', label: '1S. 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: '1T. 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: '1U. 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: 'colorTheme', label: '1V. Use a color theme in all recurring notice text backgrounds? -- This will trigger all background highlighting in the different recurring notices to follow the same color theme. You can set this to the same value as in other Dorothy Apps and Bots to make all the notices match. If choosing "none" (default), the colors defined in each individual App feature section are used. If choosing one of the existing themes or a custom theme, the individual color settings from each section will be overridden and the theme colors will be used for all recurring notices (does not include user chat or bot responses and warnings). When choosing "custom", enter your chosen colors below in settings "1W1-4". Note that if a specific theme is used, it will always show a diagonal gradient, and setting "1X" below is ignored. More color themes will be added, send us your requests!', type: 'choice', choice1: 'None', choice2: 'Custom', choice3: 'Placeholder - do not use', choice4: themeArray.name[0], choice5: themeArray.name[1], choice6: themeArray.name[2], choice7: themeArray.name[3], choice8: themeArray.name[4], choice9: themeArray.name[5], choice10: themeArray.name[6], choice11: themeArray.name[7], choice12: themeArray.name[8], choice13: themeArray.name[9], choice14: themeArray.name[10], choice15: themeArray.name[11], choice16: themeArray.name[12], choice17: themeArray.name[13], defaultValue: 'None'}, {name: 'colorThemeCustBg1', label: '1W1. Custom Color 1 - If you picked "custom" color theme above in setting 0B, enter the hex color code for background color 1 ("#" prefix plus the 6 character hex color codes, such #ffffff)',type: 'str',minLength: 1,maxLength: 7,required: false}, {name: 'colorThemeCustBg2', label: '1W2. Custom Color 2 - If you picked "custom" color theme above in setting 0B, enter the hex color code for background color 2, it will default to white (#ffffff) if none is chosen as two colors are required',type: 'str',minLength: 1,maxLength: 7,required: false}, {name: 'colorThemeCustBg3', label: '1W3. Custom Color 3 - If you picked "custom" color theme above in setting 0B, enter the hex color code for background color 3 (optional)',type: 'str',minLength: 1,maxLength: 7,required: false}, {name: 'colorThemeCustText', label: '1W4. Custom Text Color - If you picked "custom" color theme above in setting 0B, enter the hex color code for text - it will default to white if none is chosen', type: 'str',minLength: 1,maxLength: 7,required: false}, {name: 'colorsGradientDirection', label: '1X. Use a color gradient (gradual fade from one color to another) in all recurring notice backgrounds? -- This will trigger all background highlighting in the different recurring notices to follow a similar gradient pattern. You can also choose the direction of the fade - left right, top to bottom, or diagonal. Note that the gradient setting only affects the shading pattern, the actual colors used are defined either in the individual bot feature sections or overriden above in setting "1V" or "1W1-4". Also note that if a pre-configured color theme is used above, the gradient direction is unique to the theme and this gradient setting will be ignored', type: 'choice', choice1: 'No', choice2: 'Linear, left to right', choice3: 'Linear, top to bottom', choice4: 'Linear, diagonally', defaultValue: 'Linear, diagonally'}, {name: 'noticeSepStyle', label: '1Y1. 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: '1Y2. 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". You can use the command /setbrdsep to update the emoji separator during your show and play around with them to find one that has the color and spacing you are looking for.', type: 'str',minLength: 0,maxLength: 2,required: false}, {name: 'textWrapLength', label: '1Z. Set the number of characters at which to wrap a long line of text to the next line. The same setting is used in several places where longer text could be used. Default value is 80 as that typically fits the width of the border for recurring notices, and must be at least 25 or set to zero to disable automatic line wrapping. Values from 1-24 will default to 25', type: 'int',minValue: 0,maxValue: 120,defaultValue: 80}, {name: 'boldText', label: '1Z2. Display all recurring notices in bold or plain text?', type: 'choice', choice1: 'Bold Text', choice2: 'Plain Text', defaultValue: 'Bold Text'}, // *** 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 (with a dash in front) 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: 'Sex Show at Final Goal #couple #goals'}, {name: 'progressiveAutoNext', label: '2B1. 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: 'progressiveRestartGoals', label: '2B2. Restart Goal List when Complete -- Choose if you want to restart the goal list from the beginning when you finish the last goal. Note this setting will be ignored if setting "2B1" above is set to "Select next goal from list" or setting "1A2" above is set to start ticket sales after the last goal.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'progressiveUpDown', label: '2C. Count Up or Count Down -- Choose if you want to count up from zero to each goal, or count down from the goal amount to zero', type: 'choice', choice1: 'Count Up To Goal', choice2: 'Count Down To Zero', defaultValue: 'Count Up To Goal'}, {name: 'progressiveGoalNoticeInterval', label: '2D1. Notice Interval -- Time interval for displaying the Goals Notice in the chat, in minutes. Can also define in setting 2D2 below if the notice includes the remaining goals list. 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: '2D2. 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: 'progressiveGoalDescription1', label: 'Goal #1 Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Goal #1 Name'}, {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: 500}, {name: 'progressiveGoalDescription2', label: 'Goal #2 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount2', label: 'Goal #2 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription3', label: 'Goal #3 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount3', label: 'Goal #3 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {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: 'progressiveGoalDescription11', label: 'Goal #11 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount11', label: 'Goal #11 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription12', label: 'Goal #12 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount12', label: 'Goal #12 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription13', label: 'Goal #13 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount13', label: 'Goal #13 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription14', label: 'Goal #14 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount14', label: 'Goal #14 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription15', label: 'Goal #15 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount15', label: 'Goal #15 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription16', label: 'Goal #16 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount16', label: 'Goal #16 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription17', label: 'Goal #17 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount17', label: 'Goal #17 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription18', label: 'Goal #18 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount18', label: 'Goal #18 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription19', label: 'Goal #19 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount19', label: 'Goal #19 amount', type: 'int',required: false, minValue: -99999, maxValue: 99999}, {name: 'progressiveGoalDescription20', label: 'Goal #20 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'progressiveGoalAmount20', label: 'Goal #20 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 (will be overridden if a Color Theme is selected in stting "1V")',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: '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 (will be overridden if a Color Theme is selected in stting "1V")', 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: '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: 'Sex Show at Final Goal #couple #goals'}, {name: 'goalcounterPerGoalAmount', label: '3B. Individual goal amount', type: 'int',required: false, minValue: 1, maxValue: 10000, defaultValue: 99}, {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: 'Prize #1 Name'}, {name: 'goalcounterGoalAmount1', label: 'Prize Threshold #1 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000, defaultValue: 5}, {name: 'goalcounterGoalDescription2', label: 'Prize Description #2', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount2', label: 'Prize Threshold #2 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription3', label: 'Prize Description #3', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount3', label: 'Prize Threshold #3 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {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: 'goalcounterGoalDescription11', label: 'Prize Description #11', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount11', label: 'Prize Threshold #11 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription12', label: 'Prize Description #12', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount12', label: 'Prize Threshold #12 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription13', label: 'Prize Description #13', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount13', label: 'Prize Threshold #13 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription14', label: 'Prize Description #14', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount14', label: 'Prize Threshold #14 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterGoalDescription15', label: 'Prize Description #15', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'goalcounterGoalAmount15', label: 'Prize Threshold #15 - Cumulative Total Number of Goals', type: 'int',required: false, minValue: 1, maxValue: 1000}, {name: 'goalcounterTextColor', label: '3E1. Notice Text Color -- Text color for chat notices related to the Goal Counter Feature (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'goalcounterCustomTextColor', label: '3E2. 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: '3E3. Notice Background Color -- Background/Highlight color for chat notices related to the Goal Counter feature (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[2]}, {name: 'goalcounterCustomBgColor', label: '3E4. 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}, // *** Ascending/Descending Tip Sequence Goals {name: 'tipsequence', label: '---------------------------------------------------------------------------------- PART 4: TIP SEQUENCE GOALS ---------------------------------------------------------------------------------- With this Feature, users tip the next amount in a sequence that counts up or down through your configured range, toward a Final Prize at the end of the range. Intermediate prizes can be performed at intervals, such as (if counting up) top off at 20, bottoms off at 30, etc., or you can just have a single goal at the end. The room title will automatically show the thresholds you configure for prizes. For reference, here are the total number of tips for several common sequence ranges: from 1-10=55, 1-20=210, 1-30=465, 1-40=820, 1-50=1275, 1-60=1830, 1-70=2485, 1-80=3240, 1-90=4095, 1-100=5050', type: 'choice',required: false}, {name: 'tipsequenceRoomSubjectSfx', label: '4A. Title? -- Description of the show or end goal to appear as part of the room title (optional). Enter any text or hashtags you would like appended to the list of individual sequence thresholds that will be shown in the room title. The end goal can either be stated here or in a sequence goal entry below for the last sequence to be used', type: 'str',required: false, minLength: 1, maxLength: 125, defaultValue: 'Help us hit the goal! #couple #goals'}, {name: 'tipsequenceDirection', label: '4B. Use Ascending sequence (count up) or Descending sequence (count down). Typically counting down makes the tipping go faster near the end', type: 'choice', choice1: 'Ascending', choice2: 'Descending', defaultValue: 'Ascending'}, {name: 'tipsequenceLowNumber', label: '4C. Set the value used for the low end of your sequence range, which will be the start of the count when counting up, or end when counting down (this is typically 1 for ascending sequence)', type: 'int',required: false, minValue: 1, maxValue: 200, defaultValue: 1}, {name: 'tipsequenceHighNumber', label: '4D. Set the value used for the high end of your sequence range, which will be the end of the count when counting up, or start when counting down (this is typically a value that makes the total equate to what you want to make by the final goal - see reference table above)', type: 'int',required: false, minValue: 1, maxValue: 200, defaultValue: 50}, {name: 'tipsequenceGroupTips', label: '4E. Allow Group Tipping of the next number, where any tip counts toward the next value (faster), or only count tips of at least the next sequence number amount toward the goal (slower)', type: 'choice', choice1: 'Group Tipping', choice2: 'Exact Amount or Greater', defaultValue: 'Group Tipping'}, {name: 'tipsequenceChatMsg', label: '4F. Show a message in the chat each time a sequence number is tipped or surpassed?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'tipsequenceShowTotal', label: '4G. Show the total goal for the sequence range in the draw panel?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'tipsequenceNoticeInterval', label: '4H1. Time interval for displaying the recurring Tip Sequence 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.3}, {name: 'tipsequenceIncludeGoals', label: '4H2. 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 Tip Sequence feature', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'tipsequenceGoalDescription1', label: 'Prize #1 - Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Prize #1 Name'}, {name: 'tipsequenceGoalAmount1', label: 'Prize #1 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200, defaultValue: 20}, {name: 'tipsequenceGoalDescription2', label: 'Prize #2 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount2', label: 'Prize #2 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription3', label: 'Prize #3 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount3', label: 'Prize #3 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription4', label: 'Prize #4 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount4', label: 'Prize #4 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription5', label: 'Prize #5 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount5', label: 'Prize #5 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription6', label: 'Prize #6 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount6', label: 'Prize #6 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription7', label: 'Prize #7 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount7', label: 'Prize #7 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription8', label: 'Prize #8 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount8', label: 'Prize #8 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription9', label: 'Prize #9 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount9', label: 'Prize #9 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceGoalDescription10', label: 'Prize #10 - Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipsequenceGoalAmount10', label: 'Prize #10 at Tip Sequence of', type: 'int',required: false, minValue: 1, maxValue: 200}, {name: 'tipsequenceTextColor', label: '4K1. Text color for chat notices related to the Tip Sequence Feature (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[9]}, {name: 'tipsequenceCustomTextColor', label: '4K2. 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: 'tipsequenceBgColor', label: '4K3. Background/Highlight color for chat notices related to the Tip Sequence feature (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[3]}, {name: 'tipsequenceCustomBgColor', label: '4K4. 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}, // *** Tip Jar {name: 'tipjar', label: '---------------------------------------------------------------------------------- PART 5: TIP JAR ---------------------------------------------------------------------------------- The Tip Jar is used to perform a particular action once goal is reached, and keep doing it until the tip jar empties, so be sure to use it for prizes that you are willing to do for that long, and make sense to use as an ongoing goal (stay naked, oral, sex, etc). Set the goal(s) and drain rate, tokens do not start to drain until the goal is hit. Can be operated with a single goal or multiple goals similar to Progressive Goal show. Set the Recycle Count to "0" to only use each goal once, or set it to the number of times you will continue to do that prize if the jar is refilled again. Only the goals that are filled in will be used, blank out the goals and amounts you do not wish to use.', type: 'choice',required: false}, {name: 'tipjarRoomSubjectSfx', label: '5A. 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. The actual goal prize descriptions are defined below at each level', type: 'str',required: false, minLength: 1, maxLength: 125, defaultValue: 'Welcome to our tip jar show! #tipjar #couples #sex.'}, {name: 'tipjarWhenEmpty', label: '5B. When Empty? - Once the tip jar is empty, should the app allow users to continue tipping to fill it up again, or end the current goal and start the next goal/recycle of the current goal? If allowing continued tipping, use the /skip command when ready to advance', type: 'choice', choice1: 'Tip to continue current goal', choice2: 'Advance to Next Goal/Cycle', defaultValue: 'Tip to continue current goal'}, {name: 'tipjarStartDrainRate', label: '5C. Drain Rate for how fast tokens are removed from the jar. Default is 1 token/sec but initial setting can be changed here, and can also be changed during the show using the "/faster" and "/slower" commands', type: 'choice', choice1: '5 tokens per second (fastest)', choice2: '4 tokens per second', choice3: '3 tokens per second', choice4: '2 tokens per second', choice5: '1 token per second', choice6: '1 token every 2 seconds', choice7: '1 token every 3 seconds', choice8: '1 token every 4 seconds', choice9: '1 token every 5 seconds', choice10: '1 token every 10 seconds (slowest)', defaultValue: '1 token per second'}, {name: 'tipjarNoticeInterval', label: '5D1. Time interval for displaying the recurring Tip Jar 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.5}, {name: 'tipjarIncludeGoals', label: '5D2. 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 Tip Jar feature', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'tipjarDescription1', label: 'Goal #1 Description', type: 'str',required: false, minLength: 1, maxLength: 100, defaultValue: 'Goal #1 Name.'}, {name: 'tipjarAmount1', label: 'Goal #1 amount', type: 'int',required: false, minValue: 1, maxValue: 100000, defaultValue: 500}, {name: 'tipjarRecycle1', label: 'Goal #1 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription2', label: 'Goal #2 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount2', label: 'Goal #2 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle2', label: 'Goal #2 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription3', label: 'Goal #3 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount3', label: 'Goal #3 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle3', label: 'Goal #3 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription4', label: 'Goal #4 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount4', label: 'Goal #4 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle4', label: 'Goal #4 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription5', label: 'Goal #5 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount5', label: 'Goal #5 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle5', label: 'Goal #5 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription6', label: 'Goal #6 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount6', label: 'Goal #6 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle6', label: 'Goal #6 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription7', label: 'Goal #7 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount7', label: 'Goal #7 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle7', label: 'Goal #7 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription8', label: 'Goal #8 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount8', label: 'Goal #8 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle8', label: 'Goal #8 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription9', label: 'Goal #9 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount9', label: 'Goal #9 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle9', label: 'Goal #9 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarDescription10', label: 'Goal #10 Description', type: 'str',required: false, minLength: 1, maxLength: 100}, {name: 'tipjarAmount10', label: 'Goal #10 amount', type: 'int',required: false, minValue: 1, maxValue: 100000}, {name: 'tipjarRecycle10', label: 'Goal #10 Recycle Count', type: 'int',required: false, minValue: 0, maxValue: 10, defaultValue: 0}, {name: 'tipjarTextColor', label: '5F1. Text color for chat notices related to the Tip Jar Feature (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[8]}, {name: 'tipjarCustomTextColor', label: '5F2. 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: 'tipjarBgColor', label: '5F3. Background/Highlight color for chat notices related to the Tip Jar feature (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[6]}, {name: 'tipjarCustomBgColor', label: '5F4. 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: 'Hidden Sex Show! Token Poll for cumshot!'}, {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: 69,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: 'ticketShowStartAuto', label: '6F. 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: 'ticketShowGoal', label: '6G. 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: '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: 'ticketShowFreeEFC', label: '6M1. Give a free ticket to External Fanclub 1 members? Even if the External FanClub 1 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: 'ticketShowFreeEFC2', label: '6M2. Give a free ticket to External Fanclub 2 members? Even if the External FanClub 2 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: 'ticketShowFreeVIP', label: '6N. 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: '6P. 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: 99999,defaultValue: 35,required: false}, {name: 'ticketShowPriceEFC', label: '6Q1. Discounted Price for a ticket to the show for External Fan Club 1 members. If no price is set, the External Fan Club 1 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: 99999,required: false}, {name: 'ticketShowPriceEFC2', label: '6Q2. Discounted Price for a ticket to the show for External Fan Club 2 members. If no price is set, the External Fan Club 2 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: 99999,required: false}, {name: 'ticketShowPriceVIP', label: '6R. 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: 99999,required: false}, {name: 'ticketShowModsAdd', label: '6S. 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: '6T. 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: '6U. 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 us on twitter @yourusername.'}, {name: 'ticketShowReducePriceWarn', label: '6V. 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, and it will apply to all ticket prices (including fans clubs and VIPs)', type: 'int',minValue: 0,maxValue: 300,defaultValue: 0,required: false}, {name: 'ticketShowEnableOT', label: '6W. Use OT List? -- Enable Outstanding Ticket feature so users can save their ticket for use in a later show. Note that the broadcaster must still record the user names and enter them in the OTS list below. Can also be used for granting free tickets to viewers for a future show in place of a refund if there was a problem with a show, or user bought at the last second and missed the show.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'ticketShowOTList', label: '6X. OT List -- If the Outstanding Ticket feature is enabled above, enter the names of any viewers you would like to grant a ticket to a future show. The user will be notified when they enter that they have a free ticket, and if they choose to use it, the broadcaster is notified so they can remove them from the list before next show. The format should be a comma separated list (user1,user2,user3)', type: 'str', minLength: 1, maxLength: 1000, defaultValue: '', required: false}, {name: 'ticketShowAllowGift', label: '6Y. Gifting Allowed? -- Allow users to buy additional tickets they can use as gifts to other users', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'ticketShowPreviewLength', label: '6Z. If a free preview will be allowed for people joining the room after the show starts, define the length of the preview period. Set to "No Preview" to disable, or select a length from the list.',type: 'choice', choice1: 'No Preview', choice2: '10 seconds',choice3: '20 seconds', choice4: '30 seconds', choice5: '1 minute', choice6: '2 minutes', choice7: '3 minutes',choice8: '4 minutes',choice9: '5 minutes',defaultValue: 'No Preview'}, {name: 'ticketShowPreviewGrays', label: '6AA. Allow users without tokens to view the free preview?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'No'}, {name: 'ticketshowEnableIcon', label: '6AB. 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: '6AC. 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: '6AD. Text color for chat notices related to the Ticket Show (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'ticketShowCustomTextColor', label: '6AE. 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: '6AF. Background/Highlight color for chat notices related to the Ticket Show (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[9]}, {name: 'ticketShowCustomBgColor', label: '6AG. 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: 'ticketShowEnablePresales', label: '6AH. When using pre-sales, will the pre-sale be performed in the UltraApp or Fembot? It is always recommended to perform pre-sales in the UltraApp when the ticket show is in the UltraApp', type: 'choice', choice1: 'UltraApp', choice2: 'Fembot', defaultValue: 'UltraApp'}, {name: 'ticketShowPresalesMode', label: '6AJ. Ticket show pre-sales mode to use at the start of the broadcast? Note you can also start selling pre-sale tickets later during the broadcast using the "/startpresale" command if the above setting indicates UltraApp will be used for pre-sales. You can also change the mode during the show using the command "/chgpresalemode [mode]". There are three modes available, with further settings below for the automated modes', type: 'choice', choice1: 'No Pre-sales', choice2: 'Yes, Mode 1: Increment Manually', choice3: 'Yes, Mode 2: Increment on X Tickets Sold',choice4: 'Yes, Mode 3: Increment on Automatic Timer',defaultValue: 'No Pre-sales'}, {name: 'ticketShowPresaleCountIncrement', label: '6AK. For Presales Mode 2: If setting an automatic increment per number of tickets sold, indicate the number of tickets to be sold in each increment here. After this number are sold, price will increase by the amount specified in the later setting.',type: 'int',minValue: 1,maxValue: 50,required: false,defaultValue: 10}, {name: 'ticketShowPresaleTimedIncrement', label: '6AL. For Presales Mode 3: If setting an automatic timer increment, this is the number of minutes for the timer. Timer will recycle and continue incrementing until limit is reached, or next increment would exceed the planned ticket show price. After each cycle of the timer expires, the price will automatically increase by the amount specified in the below setting.',type: 'int',minValue: 1,maxValue: 50,required: false,defaultValue: 15}, {name: 'ticketShowPresaleIncreasePerIncrement',label: '6AM. If setting an automatic increment by timer or number of tickets sold, indicate the increment in ticket price for each occurrence. This means every [X] minutes (defined in previous setting), the price will go up by the number of tokens defined here, or if selling by count, once all tickets have been sold at the current price, the price will increment by this amount. Note that the price will never increment above the defined ticket price above.',type: 'int',minValue: 1,maxValue: 100,required: false,defaultValue: 10}, {name: 'ticketShowPresaleMaxIncrements', label: '6AN. When using mode 2 or mode 3 for an automatic increment, this is the maximum number of increments before the presale stops increments. Once it reaches this point (or next increment would exceed the planned ticket price), the price stops incrementing and pre-sales will continue at the same price until they are ended.',type: 'int',minValue: 1,maxValue: 10,required: false,defaultValue: 3}, {name: 'ticketShowPresalePrice', label: '6AP. If the ticket show pre-sales are enabled at the start of the show, use this price as the initial pre-sale price. Otherwise, price can be updated during show with /presaleprice [p]. Note that as soon as pre-sales are enabled, this ticket price will be used if defined, so leave it blank here if it will be defined during the show.',type: 'int',minValue: 1,maxValue: 1000,required: false,defaultValue: 45}, {name: 'ticketShowPresaleNoticeInterval', label: '6AQ. Display Interval for generic Ticket Show Pre-sales notice (in minutes). This notice just alerts the room that a pre-sale is active, regardless of mode',type: 'int',minValue: 1,maxValue: 15,required: false,defaultValue: 2}, {name: 'prepTicketStartTimer', label: '6AR. Start a Countdown timer for the ticketshow when /prepticket is used? Note this will only be allowed if the Ticket Show start mode near the top of this section is set to manual start ("Start Show Anytime"). If set to "Yes", and no timer is specified on the /prepticket command, the "Default Timer" setting above is used', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, // *** Goal Race {name: 'goalrace', label: '---------------------------------------------------------------------------------- PART 7: GOAL RACE ---------------------------------------------------------------------------------- With this Feature, the broadcaster defines 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: '7A. 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: '7B. 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: '7C. Goal Description #1', type: 'str',required: false, minLength: 1, maxLength: 20, defaultValue: 'Oral for him'}, {name: 'goalraceAmount1', label: '7D. Goal Amount #1', type: 'int',required: false, minValue: 1, maxValue: 10000, defaultValue: 500}, {name: 'goalraceDesc2', label: '7E. Goal Description #2', type: 'str',required: false, minLength: 1, maxLength: 20, defaultValue: 'Oral for her'}, {name: 'goalraceAmount2', label: '7F. Goal Amount #2', type: 'int',required: false, minValue: 1, maxValue: 10000, defaultValue: 500}, {name: 'goalraceAllowClaim', label: '7G. Allow users to "claim" their unspecified votes later in the goal race.', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'goalraceBonusFC', label: '7H. 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: 'goalraceBonusEFC', label: '7J1. Percent bonus for External Fan Club 1 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: 'goalraceBonusEFC2', label: '7J2. Percent bonus for External Fan Club 2 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: '7K. 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: '7L. 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: '7N1. Text color for chat notices related to the Goal Race feature (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'goalraceCustomTextColor', label: '7N2. 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: '7N3. Background/Highlight color for chat notices related to the Goal Race feature (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[2]}, {name: 'goalraceCustomBgColor', label: '7N4. 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 8: 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: '8A. 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 Show! Tip for your Favorite type of Spank to be performed at goal!'}, {name: 'spankNoticeInterval', label: '8B1. 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: '8B2. 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: '8C. 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: '8D. 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: 'Spank Type 1'}, {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}, {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: '8F1. Text color for chat notices related to the Spank-a-thon feature (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[9]}, {name: 'spankCustomTextColor', label: '8F2. 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: '8F3. Background/Highlight color for chat notices related to the Spank-a-thon feature (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[3]}, {name: 'spankCustomBgColor', label: '8F4. 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}, // *** Peep Show {name: 'peepshow', label: '---------------------------------------------------------------------------------- PART 9: PEEP SHOWS ---------------------------------------------------------------------------------- The "Peep Show" is a hidden show format that requires viewers to pay as they go and buy time in the show. The broadcaster sets a "per minute" price, and then viewers can tip to buy time at that rate. Prices should ideally be a multiple of 6 as the Peep Show will check hidden show status every 10 seconds, and access is being bought in 10 second chunks. As a guideline, if doing a show that you may charge 60 tokens for in ticket format that would last 10 minutes, charging 6 tokens per minute would equate to the same amount. This does assume someone stayed for the whole show, however, so going a little higher is also fine. If the price were set at 10 tokens/min, and a user tips 17, they would be buying 1.7 minutes or 102 seconds. Since the access check is made every 10 seconds, they would get 11 "chunks" of 10 second viewing time (still 2 seconds left after the 10th check).', type: 'choice',required: false}, {name: 'peepshowSubjectText', label: '9A. Title? -- Description of the Peep 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: 100, required: false, defaultValue: 'Hidden Sex Show!'}, {name: 'peepshowAfterNotice', label: '9B. 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 us on twitter @yourusername.'}, {name: 'peepshowPrice', label: '9C. Peep Show Price per Minute? -- The Peep Show feature cannot be enabled without a "per minute" price defined.', type: 'int',minValue: 6,maxValue: 9999,defaultValue: 12,required: false}, {name: 'peepshowStartAuto', label: '9D. AutoStart? -- Do you want the show to start automtically from the end of a timer, or upon user command. Note that the timer is started automatically once sales are started if a time is specified below, otherwise the timer can be started by command with either mode. You can use a timer in either mode, and this seeting defines if the show starts automatically when the timer ends. Usually the show is started manually by the broadcaster (or moderator).', type: 'choice', choice1: 'Manual Start from Command', choice2: 'Automated Start from Timer', defaultValue: 'Manual Start from Command'}, {name: 'peepshowStartTimer', label: '9E. Default Time to Start -- If a value is set here and show is defined to start automatically at end of timer, this value is used for the countdown timer. Timer will start immediately upon starting the broadcast, or when the Peep Show feature is turned on later. For manual timer control, start the show in "Manual Start from Command" mode, and use "/peeptimer" to do a countdown', type: 'int',minValue: 0,maxValue: 120,defaultValue: 0,required: false}, {name: 'peepshowLengthOfShow', label: '9F. Peep Show Length (in minutes)? -- The Peep Show feature cannot be enabled without an estimated length of show configured. With the Peep Show feature, since viewers are buying time in the show in increments, it is VERY important to be as accurate as possible when defining the show length. You should try to avoid using the Peep Show for types of shows that could end earlier than expected. This could be used to do a show where you will do an activity for a specified amount of time, such as "Finger myself for 10 minutes" or "Have sex for 15 minutes". If people prepay for a 15 minute show and you stop afte 5 minutes, it will likely lead to down votes. Note that you can change the length of the show using the command "/peeplength xx", where xx is the new show length in minutes.', type: 'int',minValue: 1,maxValue: 300,defaultValue: 15,required: false}, {name: 'peepshowAutoEnd', label: '9G. Auto End? -- Automatically end the show after the configured Show Length?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowFreeFC', label: '9H. Allow free access to CB Fanclub members?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowFreeMods', label: '9J. Allow free access to moderators?', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowFreeEFC', label: '9K1. Allow free access to External Fanclub 1 members? Even if the External FanClub 1 list has been setup in the Fembot, it must also be duplicated here in the UltraApp (see General Setup section above) to provide Peep Show privileges', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowFreeEFC2', label: '9K2. Allow free access to External Fanclub 2 members? Even if the External FanClub 2 list has been setup in the Fembot, it must also be duplicated here in the UltraApp (see General Setup section above) to provide Peep Show privileges', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowFreeVIP', label: '9L. Allow free access to VIP List members? Even if the VIP list has been setup in the Fembot, it must also be duplicated here in the UltraApp (see General Setup section above) to provide Peep Show privileges', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowModsAdd', label: '9M. Allow moderators to use the "/peepadd" command? This allows them to add users to the show for free', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowModsChgPrice', label: '9N. Allow moderators to use the "/peepprice" command? This allows them to change the per minute rate for watching the show', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes'}, {name: 'peepshowTextColor', label: '9P1. Text color for chat notices related to the Peep Show (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[7]}, {name: 'peepshowCustomTextColor', label: '9P2. 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: 'peepshowBgColor', label: '9P3. Background/Highlight color for chat notices related to the Peep Show (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[9]}, {name: 'peepshowCustomBgColor', label: '9P4. 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 10: 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: '10A. 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: '10B. 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: 500}, {name: 'autoresetEachGoal', label: '10C. 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: 'Sex Tease'}, {name: 'autoresetEndAfter', label: '10D. 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: 10,required: false}, {name: 'autoresetFinalGoal', label: '10E. 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: 'Sex Show'}, {name: 'resetShowCount', label: '10F. 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: '10G. 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: 4.2}, {name: 'autoresetTextColor', label: '10H1. Text color for chat notices related to the Auto-Reset Goal feature (will be overridden if a Color Theme is selected in stting "1V")',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: 'Custom',defaultValue: textColorArray.dispname[5]}, {name: 'autoresetCustomTextColor', label: '10H2. 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: '10H3. Background/Highlight color for chat notices related to the Auto-Reset Goal feature (will be overridden if a Color Theme is selected in stting "1V")', 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: 'Custom',defaultValue: bgColorArray.dispname[1]}, {name: 'autoresetCustomBgColor', label: '10H4. 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 EFCname = ''; var EFCname2 = ''; var VIPname = ''; var checkSpecial = /[!@#$%^&*()+\-=\[\]{};':"\\|,.<>\/?]+/; var wrapMaxLength = cb.settings.textWrapLength; var ticketEmoji = '\uD83C\uDF9F\uFE0F'; var botName = 'UltraApp: '; var borderCharSpacing = ''; var borderChar = cb.settings.separatorEmoji; var whichApp = ''; var modLevel = cb.settings.modLevel; var backgroundImage = ''; var currentPanel = ''; var colorTheme = cb.settings.colorTheme; var maxTipToGoal = cb.settings.maxTipToGoal; var boldTextLiteral = ''; var textColor = 'black'; var appNoticeColor = '#f4d599'; var appErrorColor = '#f4c1bc'; var ticketHolderBgColor = 'linear-gradient(to bottom, #ffffff 20%, #cdf2ff 100%)'; var peepshowViewerBgColor = 'linear-gradient(to bottom, #ffffff 20%, #f2e8fb 100%)'; var freePreviewerBgColor = '#fff6e7' 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 goalSubjectText = cb.settings.progressiveRoomSubjectSfx; var counterSubjectText = cb.settings.goalcounterRoomSubjectSfx; var sequenceSubjectText = cb.settings.tipsequenceRoomSubjectSfx; var tipjarSubjectText = cb.settings.tipjarRoomSubjectSfx; 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 progressiveRestartGoals = cb.settings.progressiveRestartGoals; 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 goalsReset = false; var currentAppTotalGoal = 0; var currentAppTotalCounter = 0; var currentAppTotalSequence = 0; var currentAppTotalTipJar = 0; var currentAppTotalTicket = 0; var currentAppTotalGoalRace = 0; var currentAppTotalPeep = 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 peepNoticesTxtColor = ''; var peepNoticesBgColor = ''; var peepTextColorFan = ''; var peepBgColorFan = ''; var peepTextColorVIP = ''; var peepBgColorVIP = ''; var peepTextColorMod = ''; var peepBgColorMod = ''; var peepTextColor = ''; var peepBgColor = ''; var goalraceBgColor = ''; var goalraceTextColor = ''; var goalCountBgColor = ''; var goalCountTextColor = ''; var goalCountNoticesTxtColor = ''; var goalCountNoticesBgColor = ''; var tipsequenceBgColor = ''; var tipsequenceTextColor = ''; var sequenceNoticesTxtColor = ''; var sequenceNoticesBgColor = ''; var progGoalBgColor = ''; var progGoalTextColor = ''; var progGoalNoticesTxtColor = ''; var progGoalNoticesBgColor = ''; var spankBgColor = ''; var spankTextColor = ''; var autoresetBgColor = ''; var autoresetTextColor = ''; var tipjarNoticesBgColor = ''; var tipjarNoticesTxtColor = ''; var tipjarBgColor = ''; var tipjarTextColor = ''; var borderUniversalBottom = ''; var borderAllNotices = ''; var borderAllNoticesShort = ''; var borderTicketTop = ''; var borderTicketBottom = ''; var borderPresaleTop = ''; var borderPresaleBottom = ''; var borderGoalsTop = ''; var borderGoalCountTop = ''; var borderSequenceTop = ''; var borderTipJarTop = ''; var borderSpankTop = ''; var borderGoalRaceTop = ''; var borderAutoresetTop = ''; var borderWelcomeTop = ''; var totalProgGoals = 0; var progGoalNoticeInt = 4.1; var goalCountNoticeInt = 3.7; var tipJarNoticeInt = 3.7; var tipSeqNoticeInt = 3.3; var goalRaceNoticeInt = 3.5; var spankNoticeInt = 3.8; var autoresetNoticeInt = 4.3; var ticketNoticeInt = 1.7; var presaleNoticeInt = 3.1; var tipJarRunning = false; var tipOptions = false; var lastTipJarEmptyNotice = 0; var skipWarned = false; var tipJarPaused = false; var tipJarCurrentTokens = 0; var totalTipJarLevels = 0; var currentGoalRecycleTotal = 0; var jarEmptyRate = 0; var jarEmptyTokens = 0; var drainLevel = 0; var tipSequenceDirection = ''; var endSequence = 0; var nextSequence = 0; var sequenceTotalTipsGoal = 0; var currentGoalSequence = 0; var lowSequence = cb.settings.tipsequenceLowNumber; var highSequence = cb.settings.tipsequenceHighNumber; var totalSpanksTipped = 0; var totalSpanksCompleted = 0; var totalSpankGoals = 0; var spankCharacter = ''; var goalRaceWinner = ''; 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 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 ticketShowAllowGift = cb.settings.ticketShowAllowGift; var ticketShowOtToggle = false; var ticketPrice = cb.settings.ticketShowPrice; var ticketShowEnded = false; var ticketSalesEnded = false; var countTickets = 0; var countPresaleTickets = 0; var countTicketsFromPreview = 0; var ticketTimeAmt = cb.settings.ticketShowStartTimer; var ticketShowGoalTokens = cb.settings.ticketShowGoal; var ticketModeMessage = ''; var hiddenTime = 0; var ticketShowGoalTickets = 0; var ticketTimerStopping = false; var ticketDisplaySeconds = false; var ticketShowStartPrice = 0; var freePreviewLength = 0; var savedTicketSubjectText = ''; var afterNoticeInt = 90000; var freePreviewLengthText = ''; var ticketCountdownRunning = false; var tktNoticeRunning = false; var ticketShowPriceFC = cb.settings.ticketShowPriceFC; var ticketShowPriceEFC = cb.settings.ticketShowPriceEFC; var ticketShowPriceEFC2 = cb.settings.ticketShowPriceEFC2; var ticketShowPriceVIP = cb.settings.ticketShowPriceVIP; var presalePrice = cb.settings.ticketShowPresalePrice; var presaleTimeAmt = cb.settings.ticketShowPresaleTimedIncrement; var presaleIncAmt = cb.settings.ticketShowPresaleIncreasePerIncrement; var presaleCountAmt = cb.settings.ticketShowPresaleCountIncrement; var nextPresalePrice = 0; var countPresaleSold = 0; var presaleMode = ''; var presaleModeText = ''; var presalesToggle = false; var presaleStartTime = 0; var presaleStopTime = 0; var presaleTimeAdded = 0; var presaleMinsRemain = 0; var presaleSecsRemain = 0; var presaleSkipNotice = false; var presaleIncrements = 0; var stopIncrement = false; var ticketsLeft = 0; var enablePresales = false; var presaleTimerStopping = false; var presaleDisplaySeconds = false; var peepshowStartMode = cb.settings.peepshowStartMode; var peepshowModeAuto = cb.settings.peepshowModeAuto; var peepshowPrice = cb.settings.peepshowPrice; var peepshowSubjectText = cb.settings.peepshowSubjectText; var peepshowLengthOfShow = cb.settings.peepshowLengthOfShow; var peepshowAutoEnd = cb.settings.peepshowAutoEnd; var peepshowTotalTips = 0; var peepshowStartStartTime = 0; var peepshowStartStopTime = 0; var peepshowStartMinsRemain = 0; var peepshowStartSecsRemain = 0; var monitorMinsRem = 0; var monitorMins = 0; var monitorSecs = 0; var monitorCalls = 0; var peepshowEnded = false; var peepshowSalesEnded = false; var peepshowTimerStopping = false; var peepDisplaySeconds = false; var peepShowEndTime = 0; var peepshowStartHiddenTime = 0; var showStage = ''; var minimumPeepShowTip = 0; var peepshowModeMessage = ''; var purchasedTimeRemaining = 0; var numberPeepViewers = 0; var savedpeepshowSubjectText = ''; var peepNoticeInt = 90000; var savedProgGoals = false; var savedGoalCount = false; var savedGoalRace = false; var savedGoalSequence = false; var savedSpankGoals = false; var savedTipJar = 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 progressiveGoalDescription11 = cb.settings.progressiveGoalDescription11; var progressiveGoalDescription12 = cb.settings.progressiveGoalDescription12; var progressiveGoalDescription13 = cb.settings.progressiveGoalDescription13; var progressiveGoalDescription14 = cb.settings.progressiveGoalDescription14; var progressiveGoalDescription15 = cb.settings.progressiveGoalDescription15; var progressiveGoalDescription16 = cb.settings.progressiveGoalDescription16; var progressiveGoalDescription17 = cb.settings.progressiveGoalDescription17; var progressiveGoalDescription18 = cb.settings.progressiveGoalDescription18; var progressiveGoalDescription19 = cb.settings.progressiveGoalDescription19; var progressiveGoalDescription20 = cb.settings.progressiveGoalDescription20; 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 progressiveGoalAmount11 = cb.settings.progressiveGoalAmount11; var progressiveGoalAmount12 = cb.settings.progressiveGoalAmount12; var progressiveGoalAmount13 = cb.settings.progressiveGoalAmount13; var progressiveGoalAmount14 = cb.settings.progressiveGoalAmount14; var progressiveGoalAmount15 = cb.settings.progressiveGoalAmount15; var progressiveGoalAmount16 = cb.settings.progressiveGoalAmount16; var progressiveGoalAmount17 = cb.settings.progressiveGoalAmount17; var progressiveGoalAmount18 = cb.settings.progressiveGoalAmount18; var progressiveGoalAmount19 = cb.settings.progressiveGoalAmount19; var progressiveGoalAmount20 = cb.settings.progressiveGoalAmount20; 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 goalCounterDescription11 = cb.settings.goalcounterGoalDescription11; var goalCounterDescription12 = cb.settings.goalcounterGoalDescription12; var goalCounterDescription13 = cb.settings.goalcounterGoalDescription13; var goalCounterDescription14 = cb.settings.goalcounterGoalDescription14; var goalCounterDescription15 = cb.settings.goalcounterGoalDescription15; 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 sequenceDescription1 = cb.settings.tipsequenceGoalDescription1; var sequenceDescription2 = cb.settings.tipsequenceGoalDescription2; var sequenceDescription3 = cb.settings.tipsequenceGoalDescription3; var sequenceDescription4 = cb.settings.tipsequenceGoalDescription4; var sequenceDescription5 = cb.settings.tipsequenceGoalDescription5; var sequenceDescription6 = cb.settings.tipsequenceGoalDescription6; var sequenceDescription7 = cb.settings.tipsequenceGoalDescription7; var sequenceDescription8 = cb.settings.tipsequenceGoalDescription8; var sequenceDescription9 = cb.settings.tipsequenceGoalDescription9; var sequenceDescription10 = cb.settings.tipsequenceGoalDescription10; var sequenceAmount1 = cb.settings.tipsequenceGoalAmount1; var sequenceAmount2 = cb.settings.tipsequenceGoalAmount2; var sequenceAmount3 = cb.settings.tipsequenceGoalAmount3; var sequenceAmount4 = cb.settings.tipsequenceGoalAmount4; var sequenceAmount5 = cb.settings.tipsequenceGoalAmount5; var sequenceAmount6 = cb.settings.tipsequenceGoalAmount6; var sequenceAmount7 = cb.settings.tipsequenceGoalAmount7; var sequenceAmount8 = cb.settings.tipsequenceGoalAmount8; var sequenceAmount9 = cb.settings.tipsequenceGoalAmount9; var sequenceAmount10 = cb.settings.tipsequenceGoalAmount10; var tipjarDescription1 = cb.settings.tipjarDescription1; var tipjarDescription2 = cb.settings.tipjarDescription2; var tipjarDescription3 = cb.settings.tipjarDescription3; var tipjarDescription4 = cb.settings.tipjarDescription4; var tipjarDescription5 = cb.settings.tipjarDescription5; var tipjarDescription6 = cb.settings.tipjarDescription6; var tipjarDescription7 = cb.settings.tipjarDescription7; var tipjarDescription8 = cb.settings.tipjarDescription8; var tipjarDescription9 = cb.settings.tipjarDescription9; var tipjarDescription10 = cb.settings.tipjarDescription10; var tipjarAmount1 = cb.settings.tipjarAmount1; var tipjarAmount2 = cb.settings.tipjarAmount2; var tipjarAmount3 = cb.settings.tipjarAmount3; var tipjarAmount4 = cb.settings.tipjarAmount4; var tipjarAmount5 = cb.settings.tipjarAmount5; var tipjarAmount6 = cb.settings.tipjarAmount6; var tipjarAmount7 = cb.settings.tipjarAmount7; var tipjarAmount8 = cb.settings.tipjarAmount8; var tipjarAmount9 = cb.settings.tipjarAmount9; var tipjarAmount10 = cb.settings.tipjarAmount10; var tipjarRecycle1 = cb.settings.tipjarRecycle1; var tipjarRecycle2 = cb.settings.tipjarRecycle2; var tipjarRecycle3 = cb.settings.tipjarRecycle3; var tipjarRecycle4 = cb.settings.tipjarRecycle4; var tipjarRecycle5 = cb.settings.tipjarRecycle5; var tipjarRecycle6 = cb.settings.tipjarRecycle6; var tipjarRecycle7 = cb.settings.tipjarRecycle7; var tipjarRecycle8 = cb.settings.tipjarRecycle8; var tipjarRecycle9 = cb.settings.tipjarRecycle9; var tipjarRecycle10 = cb.settings.tipjarRecycle10; 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 extFanListArray = []; var extFanList2Array = []; var extFansInShow = []; var extFansInShow2 = []; var moderatorList = {name: [], type: []}; var botModListArray = []; var modsInShow = []; var ticketShowViewerList = []; var ticketShowPreViewerList = []; var ticketHolderList = []; var raceTipNotesOn = []; var ninjaTipsOn = []; var tipCountArray = {name: [], amount: []}; var ticketCumulative = {name: [], amount: []}; var outstandingTicketArray = []; var otChangesArray = {name: [], type: []}; var ticketShowExtraTickets = {name: [], count: []}; var progGoalArray = {desc: [], amt: [], recyc: []}; var sequenceArray = {desc: [], amt: []}; var goalCounterArray = {desc: [], amt: []}; var tipjarGoalArray = {desc: [], amt: [], recyc: []}; var drawpanel = {panel: {}}; var raceUnclaimedVotes = {name: [], amount: []}; var peepshowViewers = []; var peepshowFreeViewers = []; var peepshowPurchasers = []; var peepshowPurchasedTime = [0]; var freePreviewUserArray = []; var freePreviewTimeArray = [0]; var spankGoalArray = {desc: [], amt: []}; var spankPricesArray = {typ: [], number: [], price: []}; var spankRequestsArray = {name: [], amt: [], type: []}; var spankTotalsArray = {typ: [], tipped: [], completed: []}; var noticeIntervals = {name: [], time: [], fieldname: [], settingID: []}; String.prototype.capitalize = function() { return this.charAt(0).toUpperCase() + this.slice(1); } // *********************************** Initialize ************************************** if (initialize == 0) { cb.sendNotice(' :DorothysUltraApp v3.7 '); let intromessage = '\u26D4 Version 3.7 was released on March 19, 2022 \u26D4'; intromessage += '\n \u2705 Dorothy\'s ultraApp was written by CB users chelsea2950 and butter_my_toast.'; intromessage += '\n \u2705 ' + wordWrap('Please use the command "/about" to see the highlights for the current version and recent previous releases, or you can read the full list on the bot description page:'); intromessage += '\n \u2705 https://www.freesexcam.ca/apps/app_details/dorothys-ultraapp/ '; intromessage += '\n \u2705 ' + wordWrap('Be sure to check out the fan club menus, color themes, gradient color patterns, notice border patterns and more in versions 3.0-3.4. They are a great way to further personalize your room!'); 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 = 'The Broadcaster'; } // *** Initialize no-App Panel Display noAppPanelText1 = 'Welcome to ' + bcText + '\'s room'; // *** Initialize variables across all Apps once at beginning loadNoticeIntervals(); loadAllStaticColors(BC); if (colorTheme == 'None') { loadAllFeatureColors(BC); } else { loadThemeColors(BC); } if (cb.settings.boldText == 'Bold Text') { boldTextLiteral = 'bold'; } noticeBorderChar(); borderTicketTop = new Array(37).join('-') + ' ' + ticketEmoji + ' TICKET SHOW ' + ticketEmoji + ' ' + new Array(38).join('-'); borderTicketBottom = '\n' + new Array(49).join('-') + ' \u25CF ' + new Array(49).join('-'); borderPresaleTop = new Array(40).join('-') + ' ' + ticketEmoji + ' PRE-SALES ' + ticketEmoji + ' ' + new Array(40).join('-'); borderPresaleBottom = '\n' + new Array(49).join('-') + ' \u25CF ' + new Array(49).join('-'); // *** Personalized names if (cb.settings.EFCname != '' && cb.settings.EFCname != null) { EFCname = cb.settings.EFCname; } else { EFCname = 'External Fan Club'; } if (cb.settings.EFCname2 != '' && cb.settings.EFCname2 != null) { EFCname2 = cb.settings.EFCname2; } else { EFCname2 = 'External Fan Club 2'; } 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 <= 20; loadindex++) { let goaldesc = this["progressiveGoalDescription"+loadindex]; let goalamt = this["progressiveGoalAmount"+loadindex]; let goalrecyc = this["progressiveGoalRecycle"+loadindex]; if (goaldesc && goalamt > 0) { progGoalArray.desc.push(goaldesc); progGoalArray.amt.push(goalamt); progGoalArray.recyc.push(goalrecyc); } } totalProgGoals = progGoalArray.desc.length; // *** Load Array for Goal Counter for (let loadindex = 1; loadindex <= 15; loadindex++) { let goalcntdesc = this["goalCounterDescription"+loadindex]; let goalcntamt = this["goalCounterAmount"+loadindex]; if (goalcntdesc && goalcntamt > 0) { goalCounterArray.desc.push(goalcntdesc); goalCounterArray.amt.push(goalcntamt); } } // *** Load Array for Tip Seqeunce for (let loadindex = 1; loadindex <= 10; loadindex++) { let seqdesc = this["sequenceDescription"+loadindex]; let seqamt = this["sequenceAmount"+loadindex]; if (seqdesc && seqamt > 0) { sequenceArray.desc.push(seqdesc); sequenceArray.amt.push(seqamt); } } // *** Load Array for Tip Jar for (let loadindex = 1; loadindex <= 10; loadindex++) { let jardesc = this["tipjarDescription"+loadindex]; let jaramt = this["tipjarAmount"+loadindex]; let jarrecyc = this["tipjarRecycle"+loadindex]; if (jardesc && jaramt > 0) { tipjarGoalArray.desc.push(jardesc); tipjarGoalArray.amt.push(jaramt); tipjarGoalArray.recyc.push(jarrecyc); } } // *** Init Sequence switch (cb.settings.tipsequenceDirection) { case 'Ascending': { tipSequenceDirection = 'up'; break; } case 'Descending': { tipSequenceDirection = 'down'; break; } } // *** Auto-reset parms if (cb.settings.resetShowCount == 'No') { resetShowCount = false; } else { resetShowCount = true; } // *** Init Tip Jar Drain Rate switch (cb.settings.tipjarStartDrainRate) { case '5 tokens per second (fastest)': { setDrainRate(1); break; } case '4 tokens per second': { setDrainRate(2); break; } case '3 tokens per second': { setDrainRate(3); break; } case '2 tokens per second': { setDrainRate(4); break; } case '1 token per second': { setDrainRate(5); break; } case '1 token every 2 seconds': { setDrainRate(6); break; } case '1 token every 3 seconds': { setDrainRate(7); break; } case '1 token every 4 seconds': { setDrainRate(8); break; } case '1 token every 5 seconds': { setDrainRate(9); break; } case '1 token every 10 seconds (slowest)': { setDrainRate(10); break; } } // *** Init Ticket Show Related Settings freePreviewLengthText = cb.settings.ticketShowPreviewLength; switch (freePreviewLengthText) { case 'No Preview': { freePreviewLength = 0; break; } case '10 seconds': { freePreviewLength = 10; break; } case '20 seconds': { freePreviewLength = 20; break; } case '30 seconds': { freePreviewLength = 30; break; } case '1 minute': { freePreviewLength = 60; break; } case '2 minutes': { freePreviewLength = 120; break; } case '3 minutes': { freePreviewLength = 180; break; } case '4 minutes': { freePreviewLength = 240; break; } case '5 minutes': { freePreviewLength = 300; break; } } // *** 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 'Asc/Desc Tip Sequence Goals': { if (validateFeature('sequence')) { setAppFeature('sequence', BC); } else { setAppFeature('none', BC); } break; } case 'Tip Jar': { if (validateFeature('tipjar')) { setAppFeature('tipjar', 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 'Peep Show': { if (validateFeature('peep')) { setAppFeature('peep', BC); } else { setAppFeature('none', BC); } break; } case 'Auto-Reset Goal': { if (validateFeature('autoreset')) { setAppFeature('autoreset', BC); } else { setAppFeature('none', BC); } break; } case 'None - Display Tip Stats': { setAppFeature('none', BC); break; } default : { setAppFeature('none', BC); break; } } //*** Init Presales for Ticket Show if (cb.settings.ticketShowEnablePresales == 'UltraApp' && cb.settings.ticketShowPresalesMode != 'No Pre-sales') { if (whichApp == 'ticket') { cb.sendNotice('Note: Cannot enable ticket show pre-sales, the Hidden Ticket Show feature was started.', BC, appNoticeColor); enablePresales = false; } else if (whichApp == 'peep' ) { cb.sendNotice('Note: Cannot enable ticket show pre-sales, the Peep Show feature has been started.', BC, appNoticeColor); enablePresales = false; } else { enablePresales = true; } } else { enablePresales = false; } if (enablePresales) { if (cb.settings.ticketShowPresalesMode != 'No Pre-sales') { if (cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice('Unable to start pre-sales since the Ticket Show feature is configured for a Fan Appreciation Show.', BC, appNoticeColor); } else { if (cb.settings.ticketShowPresalePrice > 0) { if (cb.settings.ticketShowPresalePrice > ticketPrice && ticketPrice > 0) { cb.sendNotice('Unable to start pre-sales, the pre-sales price (' + cb.settings.ticketShowPresalePrice + ' tokens) is greater than the planned ticket show price (' + cb.settings.ticketShowPrice + ' tokens).\n Use the command "/presaleprice xx", where xx is in the initial price, to update and then start pre-sales with the command "/startpresale".', BC, appNoticeColor); } else if (ticketPrice <= 0) { cb.sendNotice('Unable to start pre-sales, the Ticket Price for the actual show is not yet defined. You can use the command "/ticketprice [amt]" to update the price, where [amt] is the planned price. The price can be changed later but is required now for pre-sales.', BC, appNoticeColor); } else { setPresalesToggle('on', BC, cb.settings.ticketShowPresalePrice); } } else { cb.sendNotice('Unable to start pre-sales, a pre-sale ticket price must be set first using the command "/presaleprice [p]" where [p] is in the initial price. Pre-sales can then be started using the command "/startpresale". ', BC, appNoticeColor); } } } } if (cb.settings.ticketShowStartPrice > 0) { if (cb.settings.ticketShowStartPrice > ticketPrice) { ticketShowStartPrice = cb.settings.ticketShowStartPrice; } else { cb.sendNotice('Unable to set new price for start of Ticket Show as it is not greater than the initial price. Ticket Show Start price change is disabled. You can use the command "/showstartprice xx" to update the price to a value greater than the initial price of ' + ticketPrice + '.', BC, appNoticeColor); ticketShowStartPrice = 0; } } else { ticketShowStartPrice = 0; } //*** Init OT List for Ticket Show if (cb.settings.ticketShowEnableOT == 'Yes' && cb.settings.ticketShowFanAppreciation != 'Yes') { setTicketShowOtToggle('on',BC) } if (cb.settings.ticketShowOTList) { let otn = cb.settings.ticketShowOTList.toLowerCase(); let tempotlarray = otn.split(','); let initotltoadd = ''; for (let otlidx = 0; otlidx < tempotlarray.length; otlidx++) { initotltoadd = tempotlarray[otlidx].trim(); if (validateUserID(initotltoadd,'Outstanding Ticket List')) { if (cbjs.arrayContains(outstandingTicketArray,initotltoadd)) { cb.sendNotice(botName + 'Skipping duplicate entry for user "' + initotltoadd + '" in the Outstanding Ticket List.', BC, appNoticeColor); } else { outstandingTicketArray.push(initotltoadd); } } } } //*** Init External Fan Club 1 List if (cb.settings.extFanList) { let efcn = cb.settings.extFanList.toLowerCase(); let tempefcarray = efcn.split(','); let initefctoadd = ''; for (let efcnidx = 0; efcnidx < tempefcarray.length; efcnidx++) { initefctoadd = tempefcarray[efcnidx].trim(); if (validateUserID(initefctoadd,'External Fan Club 1 List')) { if (cbjs.arrayContains(extFanListArray,initefctoadd)) { cb.sendNotice(botName + 'Skipping duplicate entry for user "' + initefctoadd + '" in the External Fanclub List.', BC, appNoticeColor); } else { extFanListArray.push(initefctoadd); } } } } //*** Init External Fan Club 2 List if (cb.settings.extFanList2) { let efc2n = cb.settings.extFanList2.toLowerCase(); let tempefc2array = efc2n.split(','); let initefc2toadd = ''; for (let efc2nidx = 0; efc2nidx < tempefc2array.length; efc2nidx++) { initefc2toadd = tempefc2array[efc2nidx].trim(); if (validateUserID(initefc2toadd,'External Fan Club 2 List')) { if (cbjs.arrayContains(extFanList2Array,initefc2toadd)) { cb.sendNotice(botName + 'Skipping duplicate entry for user "' + initefc2toadd + '" in the External Fanclub 2 List.', BC, appNoticeColor); } else { extFanList2Array.push(initefc2toadd); } } } } //*** Init VIP List if (cb.settings.VIPList) { let vipn = cb.settings.VIPList.toLowerCase(); let tempviparray = vipn.split(','); let initviptoadd = ''; for (let vipnidx = 0; vipnidx < tempviparray.length; vipnidx++) { initviptoadd = tempviparray[vipnidx].trim(); if (validateUserID(initviptoadd,'VIP List')) { if (cbjs.arrayContains(VIPListArray,initviptoadd)) { cb.sendNotice(botName + 'Skipping duplicate entry for user "' + initviptoadd + '" in the VIP List.', BC, appNoticeColor); } else { VIPListArray.push(initviptoadd); } } } } //*** Init App Mod List if (cb.settings.botModList) { let modn = cb.settings.botModList.toLowerCase(); let tempbotmodarray = modn.split(','); let initmodtoadd = ''; for (let 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)) { let 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'; borderSequenceTop = noticeBorder('top','TIP SEQUENCE GOALS',9,false,true) + '\n'; borderTipJarTop = noticeBorder('top','TIP JAR GOALS',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); borderSequenceTop = noticeBorder('top','TIP SEQUENCE GOALS',16,false,false); borderTipJarTop = noticeBorder('top','TIP JAR GOALS',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); borderSequenceTop = noticeBorder('top','TIP SEQUENCE GOALS',35,true,false); borderTipJarTop = noticeBorder('top','TIP JAR GOALS',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); 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); borderSequenceTop = noticeBorder('top','TIP SEQUENCE GOALS:',0,true,false); borderTipJarTop = noticeBorder('top','TIP JAR GOALS:',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); sequenceColors(loadfeatby); tipJarColors(loadfeatby); goalraceColors(loadfeatby); spankColors(loadfeatby); autoresetGoalColors(loadfeatby); ticketShowColors(loadfeatby); peepshowColors(loadfeatby); } function loadThemeColors(loadthemeby) { let tempthemebgcolor = setThemeBgColor(loadthemeby); let tempthemetextcolor = setThemeTextColor(loadthemeby); progGoalBgColor = tempthemebgcolor; progGoalTextColor = tempthemetextcolor; goalCountBgColor = tempthemebgcolor; goalCountTextColor = tempthemetextcolor; tipsequenceBgColor = tempthemebgcolor; tipsequenceTextColor = tempthemetextcolor; goalraceBgColor = tempthemebgcolor; goalraceTextColor = tempthemetextcolor; spankBgColor = tempthemebgcolor; spankTextColor = tempthemetextcolor; autoresetBgColor = tempthemebgcolor; autoresetTextColor = tempthemetextcolor; tipjarBgColor = tempthemebgcolor; tipjarTextColor = tempthemetextcolor; ticketBgColor = tempthemebgcolor; ticketTextColor = tempthemetextcolor; peepBgColor = tempthemebgcolor; peepTextColor = tempthemetextcolor; } function loadAllStaticColors() { progGoalNoticesTxtColor = setTextColor('Dark Red'); progGoalNoticesBgColor = setBgColor('Cream'); goalCountNoticesTxtColor = setTextColor('Dark Red'); goalCountNoticesBgColor = setBgColor('Cream'); sequenceNoticesTxtColor = setTextColor('Dark Red'); sequenceNoticesBgColor = setBgColor('Cream'); tipjarNoticesTxtColor = setTextColor('Dark Red'); tipjarNoticesBgColor = setBgColor('Light Red'); 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'); peepNoticesTxtColor = setTextColor('Dark Red'); peepNoticesBgColor = setBgColor('Cream'); peepTextColorFan = setTextColor('Black'); peepBgColorFan = setBgColor('Light Green'); peepTextColorVIP = setTextColor('Black'); peepBgColorVIP = setBgColor('Light Purple'); peepTextColorMod = setTextColor('Black'); peepBgColorMod = setBgColor('Light Red'); } function setThemeTextColor(sttcsendto) { var selectedthemetextcolor = '#000000'; if (colorTheme == 'Custom') { var temptextcolor = cb.settings.colorThemeCustText; if (/^#[0-9A-F]{6}$/i.test(temptextcolor)) { selectedthemetextcolor = temptextcolor; } else if (/^[0-9A-F]{6}$/i.test(temptextcolor)) { selectedthemetextcolor = '#' + temptextcolor; } else { cb.sendNotice(botName + ' ' + temptextcolor + ' - Error while setting the text color. It must be in a HEX code format. Using default value of black text.', sttcsendto, appNoticeColor); } } else { if (cbjs.arrayContains(themeArray.name,colorTheme)) { let themeidx = themeArray.name.indexOf(colorTheme); return themeArray.textcolor[themeidx]; } else { cb.sendNotice(botName + ' Warning! Invalid theme setting of "' + colorTheme + '". \nDefaulting to no theme used.', sttcsendto, appNoticeColor); colorTheme = 'None'; } } return selectedthemetextcolor; } function setThemeBgColor(setthemeby) { var selectedthemebgcolor = '#FFFFFF'; if (colorTheme == 'Custom') { var graddir = ''; if (cb.settings.colorsGradientDirection == 'Linear, left to right') { graddir = 'to right'; } else if (cb.settings.colorsGradientDirection == 'Linear, top to bottom') { graddir = 'to bottom'; } else if (cb.settings.colorsGradientDirection == 'Linear, diagonally') { graddir = 'to right bottom'; } else if (cb.settings.colorsGradientDirection == 'No') { graddir = 'to right bottom'; } var themecustbg1 = ''; var themecustbg2 = ''; var themecustbg3 = ''; if (cb.settings.colorThemeCustBg1) { if (/^#[0-9A-F]{6}$/i.test(cb.settings.colorThemeCustBg1)) { themecustbg1 = cb.settings.colorThemeCustBg1; } else if (/^[0-9A-F]{6}$/i.test(cb.settings.colorThemeCustBg1)) { themecustbg1 = '#' + cb.settings.colorThemeCustBg1; } else { cb.sendNotice(botName + ' Warning! Custom color theme selected but Background 1 is not in the correct hex color code format. \nDefaulting to no theme used.', setthemeby, appNoticeColor); colorTheme = 'None'; } } else { cb.sendNotice(botName + ' Warning! Custom color theme selected but Background 1 is not configured. \nDefaulting to no theme used.', setthemeby, appNoticeColor); colorTheme = 'None'; } if (cb.settings.colorThemeCustBg2) { if (/^#[0-9A-F]{6}$/i.test(cb.settings.colorThemeCustBg2)) { themecustbg2 = cb.settings.colorThemeCustBg2; } else if (/^[0-9A-F]{6}$/i.test(cb.settings.colorThemeCustBg2)) { themecustbg2 = '#' + cb.settings.colorThemeCustBg2; } else { themecustbg2 = '#FFFFFF'; cb.sendNotice(botName + ' Warning! Custom color theme selected but Background 2 is not configured with a proper hex color code. \nDefaulting second color to white.', setthemeby, appNoticeColor); } } else { themecustbg2 = '#FFFFFF'; cb.sendNotice(botName + ' Warning! Custom color theme selected but Background 2 is not configured. \nAt least two colors are required, defaulting second color to white.', setthemeby, appNoticeColor); } var numbercustcolors = 2; if (cb.settings.colorThemeCustBg3) { numbercustcolors = 3; if (/^#[0-9A-F]{6}$/i.test(cb.settings.colorThemeCustBg3)) { themecustbg3 = cb.settings.colorThemeCustBg3; } else if (/^[0-9A-F]{6}$/i.test(cb.settings.colorThemeCustBg3)) { themecustbg3 = '#' + cb.settings.colorThemeCustBg3; } else { themecustbg3 = '#FFFFFF'; } } if (colorTheme != 'None') { if (numbercustcolors == 2) { selectedthemebgcolor = 'linear-gradient(' + graddir + ', ' + themecustbg1 + ', ' + themecustbg2 + ')'; } else { selectedthemebgcolor = 'linear-gradient(' + graddir + ', ' + themecustbg1 + ', ' + themecustbg2 + ', ' + themecustbg3 + ')'; } } } else { if (cbjs.arrayContains(themeArray.name,colorTheme)) { let themeidx = themeArray.name.indexOf(colorTheme); selectedthemebgcolor = themeArray.colorID[themeidx]; } else { cb.sendNotice(botName + ' Warning! Invalid theme setting of "' + colorTheme + '". \nDefaulting to no theme used.', setthemeby, appNoticeColor); colorTheme = 'None'; } } return selectedthemebgcolor; } 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 addRmvExtFan(arefuser,arefmode) { if (arefmode == 'a') { if (cbjs.arrayContains(extFanListArray,arefuser)) { return; } else { extFanListArray.push(arefuser); } } else if (arefmode == 'r') { if (cbjs.arrayContains(extFanListArray,arefuser)) { cbjs.arrayRemove(extFanListArray,arefuser); } else { return; } } } function addRmvExtFan2(aref2user,aref2mode) { if (aref2mode == 'a') { if (cbjs.arrayContains(extFanList2Array,aref2user)) { return; } else { extFanList2Array.push(aref2user); } } else if (aref2mode == 'r') { if (cbjs.arrayContains(extFanList2Array,aref2user)) { cbjs.arrayRemove(extFanList2Array,aref2user); } else { return; } } } function addRmvExtFanInShow(arefisuser,arefismode) { if (arefismode == 'a') { if (cbjs.arrayContains(extFansInShow,arefisuser)) { return; } else { extFansInShow.push(arefisuser); } } else if (arefismode == 'r') { if (cbjs.arrayContains(extFansInShow,arefisuser)) { cbjs.arrayRemove(extFansInShow,arefisuser); } else { return; } } } function addRmvExtFanInShow2(aref2isuser,aref2ismode) { if (aref2ismode == 'a') { if (cbjs.arrayContains(extFansInShow2,aref2isuser)) { return; } else { extFansInShow2.push(aref2isuser); } } else if (aref2ismode == 'r') { if (cbjs.arrayContains(extFansInShow2,aref2isuser)) { cbjs.arrayRemove(extFansInShow2,aref2isuser); } else { return; } } } function loadNoticeIntervals() { noticeIntervals.name[0] = 'goals'; noticeIntervals.fieldname[0] = 'progGoalNoticeInt'; noticeIntervals.settingID[0] = '2D1'; updateIntervalArray('goals',cb.settings.progressiveGoalNoticeInterval,4.1,noticeIntervals.settingID[0]); noticeIntervals.name[1] = 'goalcount'; noticeIntervals.fieldname[1] = 'goalCountNoticeInt'; noticeIntervals.settingID[1] = '3C1'; updateIntervalArray('goalcount',cb.settings.goalcounterNoticeInterval,3.7,noticeIntervals.settingID[1]); noticeIntervals.name[2] = 'sequence'; noticeIntervals.fieldname[2] = 'tipSeqNoticeInt'; noticeIntervals.settingID[2] = '4H1'; updateIntervalArray('sequence',cb.settings.tipsequenceNoticeInterval,3.3,noticeIntervals.settingID[2]); noticeIntervals.name[3] = 'goalrace'; noticeIntervals.fieldname[3] = 'goalRaceNoticeInt'; noticeIntervals.settingID[3] = '7L'; updateIntervalArray('goalrace',cb.settings.goalRaceNoticeInterval,3.5,noticeIntervals.settingID[3]); noticeIntervals.name[4] = 'tipjar'; noticeIntervals.fieldname[4] = 'tipJarNoticeInt'; noticeIntervals.settingID[4] = '5D1'; updateIntervalArray('tipjar',cb.settings.tipjarNoticeInterval,3.7,noticeIntervals.settingID[4]); noticeIntervals.name[5] = 'ticket'; noticeIntervals.fieldname[5] = 'ticketNoticeInt'; noticeIntervals.settingID[5] = '6AC'; updateIntervalArray('ticket',cb.settings.ticketNoticeInterval,1.7,noticeIntervals.settingID[5]); noticeIntervals.name[6] = 'presale'; noticeIntervals.fieldname[6] = 'presaleNoticeInt'; noticeIntervals.settingID[6] = '6AQ'; updateIntervalArray('presale',cb.settings.ticketShowPresaleNoticeInterval,3.1,noticeIntervals.settingID[6]); noticeIntervals.name[7] = 'spank'; noticeIntervals.fieldname[7] = 'spankNoticeInt'; noticeIntervals.settingID[7] = '8B1'; updateIntervalArray('spank',cb.settings.spankNoticeInterval,3.8,noticeIntervals.settingID[7]); noticeIntervals.name[8] = 'autoreset'; noticeIntervals.fieldname[8] = 'autoresetNoticeInt'; noticeIntervals.settingID[8] = '10G'; updateIntervalArray('autoreset',cb.settings.autoresetNoticeInterval,4.3,noticeIntervals.settingID[8]); } function updateIntervalArray(updtinttype,updintint,updintdft,updintsetting) { if (!updintint || updintint <= 0) { updintint = updintdft; cb.sendNotice(botName + 'The "' + 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 + 'The "' + updtinttype + '" Chat Notice Interval is too short, must be at least 1 minute. Using default value of ' + updintdft + ' minutes.', BC, appNoticeColor); } let timersetting = parseFloat(updintint); if (isNaN(timersetting)) { updintint = updintdft; cb.sendNotice(botName + 'The "' + updtinttype + '" Chat Notice Interval is configured (setting "' + updintsetting + '"), but is not numeric, it must be a numeric value of at least one (decimals allowed). Using default value of ' + updintdft + ' minutes.', BC, appNoticeColor); } updateIntervalValues(updtinttype,updintint); } function updateIntervalValues(updvaltype,updvalint) { let intnameidx = noticeIntervals.name.indexOf(updvaltype); let actualint = parseInt(updvalint*60000); this[noticeIntervals.fieldname[intnameidx]] = 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.', setby, appNoticeColor, '', 'bold'); } else { switch (whichApp) { case 'goals': { setProgressiveGoalsToggle('off', setby); break; } case 'goalcount': { setGoalCounterToggle('off', setby); break; } case 'ticket': { setTicketShowToggle('off', setby); break; } case 'sequence': { setSequenceToggle('off', setby); break; } case 'tipjar': { setTipJarToggle('off', setby); break; } case 'goalrace': { setGoalRaceToggle('off', setby); break; } case 'spank': { setSpankToggle('off', setby); break; } case 'peep': { setPeepShowToggle('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 'sequence': { setSequenceToggle('on', setby); break; } case 'tipjar': { setTipJarToggle('on', setby); break; } case 'goalrace': { setGoalRaceToggle('on', setby); break; } case 'spank': { spankSepChar(); setSpankToggle('on', setby); break; } case 'peep': { setPeepShowToggle('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': { initProgGoal(); break; } case 'goalcount': { initGoalCounter(); break; } case 'sequence': { initSequence(); break; } case 'tipjar': { initTipJar(); break; } case 'goalrace': { initGoalRace(); break; } case 'spank': { initSpanks(); break; } case 'autoreset': { initAutoresetGoal(); break; } } cb.drawPanel(); } //********** Validate minimum required settings for each feature ************** function validateFeature(whichApp) { switch (whichApp) { case 'goals': { if (totalProgGoals <= 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 { 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 'sequence': { if (sequenceArray.desc.length <= 0) { cb.sendNotice(botName + 'The Feature of "Asc/Desc Tip Sequence 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 (cb.settings.tipsequenceLowNumber >= cb.settings.tipsequenceHighNumber) { cb.sendNotice(botName + 'The Feature of "Asc/Desc Tip Sequence Goals" was chosen, but the lower end of the sequence is configured as higher than the high end of the sequence. Please restart the UltraApp and define the high and low end sequence appropriately.', BC, appNoticeColor); return false; } else { var allowseq = true; if (tipSequenceDirection == 'up') { let i = 0; while (sequenceArray.amt[i+1] > 0) { if (sequenceArray.amt[i] > sequenceArray.amt[i+1]) { allowseq = false; } i++; } } else if (tipSequenceDirection == 'down') { let i = 0; while (sequenceArray.amt[i+1] > 0) { if (sequenceArray.amt[i] < sequenceArray.amt[i+1]) { allowseq = false; } i++; } } if (allowseq) { return true; } else { cb.sendNotice(botName + 'The Feature of "Asc/Desc Tip Sequence Goals" was chosen, but the goals are not sequenced correctly per the ascending or descending direction. Please restart the UltraApp and make sure the goal sequence matches the ascending/descending setting (for example, use 5,10,15 for ascending, or 15,10,5 for descending).', BC, appNoticeColor); return false; } } } case 'tipjar': { if (tipjarGoalArray.desc.length <= 0) { cb.sendNotice(botName + 'The Feature of "Tip Jar" 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 { return true; } break; } 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 'peep': { if (peepshowPrice <= 0) { cb.sendNotice('Unable to start the Peep Show feature, a "per minute" price was not set on the start page. You can either set the price now using the command "/peepprice [amt]" where [amt] is in the price per minute in tokens, or restart the UltraApp and set the price. The Peep Show feature can be enabled after setting a price using the command "/chgapp peepshow". ', BC, appNoticeColor); return false; } else { return true; } if (peepshowLengthOfShow <= 0) { cb.sendNotice('Unable to start the Peep Show feature, a "Length of Show" was not set on the start page. You can either set the length now using the command "/pslength [time]" where [time] is in the expected length of the show in minutes, or restart the UltraApp and set the Peep Show Length. The Peep Show feature can be enabled after setting a price using the command "/chgapp peepshow". ', BC, appNoticeColor); return false; } else { return true; } } 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); // dorothy is the testbed user if (BC == 'dorothy') { if (backgroundArray.command[bgindex] == 'custom') { if (cbjs.arrayContains(backgroundArrayCustom.command,BC)) { var custindex1 = backgroundArrayCustom.command.indexOf(BC); backgroundImage = backgroundArrayCustom.devfile[custindex1]; botPanel = true; currentPanel = newbackground; cb.drawPanel(); if (custpanelmode != 'init') { cb.sendNotice('You have updated the panel background to "' + newbackground + '".', sendto, appNoticeColor, ''); } } else { cb.sendNotice('You have requested a personalized background, but you are not the room owner for a custom background, please choose again.', sendto, appNoticeColor, ''); } } else { 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 (backgroundArray.command[bgindex] == 'custom') { if (cbjs.arrayContains(backgroundArrayCustom.command,BC)) { var custindex2 = backgroundArrayCustom.command.indexOf(BC); if (appVersion == 'Show1') { backgroundImage = backgroundArrayCustom.livefile[custindex2]; } else if (appVersion == 'Show2') { backgroundImage = backgroundArrayCustom.livefile2[custindex2]; } botPanel = true; currentPanel = newbackground; cb.drawPanel(); if (custpanelmode != 'init') { cb.sendNotice('You have updated the panel background to "' + newbackground + '".', sendto, appNoticeColor, ''); } } else { cb.sendNotice('You have requested a personalized background, but you are not the room owner for a custom background, please choose again.', 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 == 'dorothy') { 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,tippedisextfan,tippedisextfan2,tippedisvip,tippedbydisp,countstowardgoals) { switch (whichApp) { case 'goals': { if (countstowardgoals) { if (tippedby != 'bc') { currentAppTotalGoal += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { if (cb.settings.progressiveUpDown == 'Count Up To Goal') { savedCurrentGoalTips = currentGoalTips; currentGoalTips += tippedamount; if (currentGoalTips >= currentGoalTotal) { if (progressiveAutoNext == 'Auto-start next') { exceedGoal(tippedamount); } else { if (!currentGoalMet) { goalComplete(); } } } } else { savedCurrentGoalTips = currentGoalTips; currentGoalTips -= tippedamount; if (currentGoalTips <= 0) { if (progressiveAutoNext == 'Auto-start next') { exceedGoal(tippedamount); } else { if (!currentGoalMet) { goalComplete(); } } } } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } cb.drawPanel(); break; } case 'goalcount': { if (countstowardgoals) { if (tippedby != 'bc') { currentAppTotalCounter += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { savedCurrentGoalTips = currentGoalTips; currentGoalTips += tippedamount; if (currentGoalTips >= currentGoalTotal) { exceedGoal(tippedamount); } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } cb.drawPanel(); break; } case 'ticket': { if (tippedby != 'bc') { currentAppTotalTicket += tippedamount; currentSessionTotal += tippedamount; } if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (!ticketShowEnded && !ticketSalesEnded && (!cb.limitCam_userHasAccess(tippedby) || cbjs.arrayContains(ticketShowPreViewerList,tippedby))) { if (tippedisfan) { if (tippedamount >= ticketShowPriceFC) { addRmvTicket('addtip',tippedby,'fan',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketShowPriceFC,'fan','ticket',tippedbydisp); } } else if (tippedisextfan) { if (tippedamount >= ticketShowPriceEFC) { addRmvTicket('addtip',tippedby,'extfan',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketShowPriceEFC,'extfan','ticket',tippedbydisp); } } else if (tippedisextfan2) { if (tippedamount >= ticketShowPriceEFC2) { addRmvTicket('addtip',tippedby,'extfan2',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketShowPriceEFC2,'extfan2','ticket',tippedbydisp); } } else if (tippedisvip) { if (tippedamount >= ticketShowPriceVIP) { addRmvTicket('addtip',tippedby,'vip',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketShowPriceVIP,'vip','ticket',tippedbydisp); } } else { if (tippedamount >= ticketPrice) { addRmvTicket('addtip',tippedby,'common',tippedamount,tippedbydisp); } else { checkCumulative(tippedby,tippedamount,ticketPrice,'common','ticket',tippedbydisp); } } } else if (!ticketShowEnded && !ticketSalesEnded && cb.limitCam_userHasAccess(tippedby) && !cbjs.arrayContains(ticketShowPreViewerList,tippedby) && ticketShowAllowGift == 'Yes') { if (tippedamount >= ticketPrice) { addRmvTicket('addextra', tippedby, 'common', tippedamount,tippedbydisp); } } ticketShowGoalProgress(tippedamount); } cb.drawPanel(); break; } case 'sequence': { if (countstowardgoals) { if (tippedby != 'bc') { currentAppTotalSequence += tippedamount; currentSessionTotal += tippedamount; } if (!finalGoalMet) { if (tippedamount < nextSequence) { if (groupTipToggle) { currentGroupTipAmt += tippedamount; if (currentGroupTipAmt >= nextSequence) { exceedGoal(currentGroupTipAmt-nextSequence); } } } else { exceedGoal(tippedamount-nextSequence); } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } cb.drawPanel(); break; } case 'tipjar': { if (countstowardgoals) { if (tippedby != 'bc') { currentAppTotalTipJar += tippedamount; currentSessionTotal += tippedamount; } if (tipJarRunning) { tipJarCurrentTokens += tippedamount; } else { if (!finalGoalMet) { savedCurrentGoalTips = currentGoalTips; currentGoalTips += tippedamount; if (currentGoalTips >= currentGoalTotal) { exceedGoal(tippedamount); } if (cb.settings.genericRoomSubjectRemGoal != 'No') { changeRoomSubject(); } } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } cb.drawPanel(); break; } case 'goalrace': { if (countstowardgoals) { 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 (tippedisextfan && cb.settings.goalraceBonusEFC > 0) { tippedamount = parseInt(tippedamount * (1 + cb.settings.goalraceBonusEFC/100)); } else if (tippedisextfan2 && cb.settings.goalraceBonusEFC2 > 0) { tippedamount = parseInt(tippedamount * (1 + cb.settings.goalraceBonusEFC2/100)); } else if (tippedisvip && cb.settings.goalraceBonusVIP > 0) { tippedamount = parseInt(tippedamount * (1 + cb.settings.goalraceBonusVIP/100)); } if (tippednote == goalraceDesc1) { currentGoalRace1Tips += tippedamount; if (currentGoalRace1Tips >= goalraceAmount1) { goalRaceWinner = goalraceDesc1; goalComplete(); } } else if (tippednote == goalraceDesc2) { currentGoalRace2Tips += tippedamount; if (currentGoalRace2Tips >= goalraceAmount2) { goalRaceWinner = goalraceDesc2; goalComplete(); } } else { raceAddUnclaimed(tippedby,originaltipamount); } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } cb.drawPanel(); break; } case 'spank': { if (countstowardgoals) { 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(); } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } cb.drawPanel(); break; } case 'peep': { if (tippedby != 'bc') { currentAppTotalPeep += tippedamount; currentSessionTotal += tippedamount; } if (showStage == 'peepfinale') { cb.sendNotice('Thank you for tipping, however, the show is nearly over and ' + bcText + ' has suspended purchases of Peep Show time.', tippedby, peepBgColor, peepTextColor, 'bold'); } if (!peepshowEnded && !peepshowSalesEnded && !cbjs.arrayContains(peepshowFreeViewers,tippedby)) { if (tippedamount >= minimumPeepShowTip) { peepshowAddRmvTime('addtip', tippedby, 'common', tippedamount); } else { if (tippedamount > 1) { cb.sendNotice('Thank you for tipping, sorry that amount is below the minimum required to add time to your show (' + minimumPeepShowTip + ' tokens).', tippedby, peepBgColor, peepTextColor, 'bold'); } } } cb.drawPanel(); break; } case 'autoreset': { if (countstowardgoals) { 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(); } } } else { if (tippedby != 'bc') { currentSessionTotal += tippedamount; } } 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); } else if (saletype == 'presale') { addRmvPresale('addtip',tippedbycum,'common',tippedbycumdisp); } ticketCumulative.amount[chkcumidx] = 0; } else { ticketCumulative.amount[chkcumidx] += tippedamountcum; } } } } function exceedGoal(tippedamountexc) { var nextgif = ''; var excesstip = 0; switch (whichApp) { case 'ticket': { break; } case 'peep': { break; } case 'goals': { if (cb.settings.progressiveUpDown == 'Count Up To Goal') { excesstip = tippedamountexc - (currentGoalTotal - savedCurrentGoalTips); goalComplete(); if (!finalGoalMet && !goalsReset) { nextGoal(); currentGoalTips = excesstip; if (currentGoalTips >= currentGoalTotal) { savedCurrentGoalTips = 0; exceedGoal(currentGoalTips); } } goalsReset = false; } else { excesstip = tippedamountexc - savedCurrentGoalTips; goalComplete(); if (!finalGoalMet && !goalsReset) { nextGoal(); currentGoalTips = currentGoalTotal - excesstip; if (currentGoalTips <= 0) { savedCurrentGoalTips = 0; exceedGoal(0 - currentGoalTips); } } goalsReset = false; } break; } case 'goalcount': { cb.sendNotice('Goal #' + (currentGoalCountAmt + 1) + ' has been Completed!', '', goalCountBgColor, goalCountTextColor, 'bold'); 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 'sequence': { if (tipSequenceDirection == 'up') { nextSequence++; if (chatMsgToggle && nextSequence <= endSequence) { nextgif = ':next' + nextSequence; cb.sendNotice('Tip Sequence ' + (nextSequence-1) + ' has been Completed --- ' + nextgif, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); } if (nextSequence > sequenceArray.amt[currentGoalLevel-1]) { goalComplete(); if (nextSequence > endSequence) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Tip Sequence has been Completed!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); updateFinalGoalMet(); } else { nextGoal(); } } else { if (nextSequence > endSequence) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Tip Sequence has been Completed!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); updateFinalGoalMet(); } } } else if (tipSequenceDirection == 'down') { nextSequence--; if (chatMsgToggle && nextSequence >= endSequence) { nextgif = ':next' + nextSequence; cb.sendNotice('Tip Sequence ' + (nextSequence+1) + ' has been Completed --- ' + nextgif, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); } if (nextSequence < sequenceArray.amt[currentGoalLevel-1]) { goalComplete(); if (nextSequence < endSequence) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Tip Sequence has been Completed!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); updateFinalGoalMet(); } else { nextGoal(); } } else { if (nextSequence < endSequence) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Tip Sequence has been Completed!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); updateFinalGoalMet(); } } } currentGroupTipAmt = 0; excesstip = tippedamountexc; if (excesstip > 0) { if (!finalGoalMet) { if (excesstip < nextSequence) { if (groupTipToggle) { currentGroupTipAmt += excesstip; } } else { exceedGoal(excesstip-nextSequence); } } } break; } case 'tipjar': { goalComplete(); tipJarCurrentTokens = currentGoalTips; startTipJar(); 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 'peep': { break; } case 'goals': { currentGoalMet = true; if (totalProgGoals == 1) { cb.sendNotice(borderAllNoticesShort + '\n :CGGoal15 The Goal has been met!! :CGGoal15 \n' + borderAllNoticesShort, '', progGoalBgColor, progGoalTextColor, 'bold'); updateFinalGoalMet(); } else if (currentGoal < totalProgGoals || 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') { if (modLevel == 'Standard' || modLevel == 'Advanced') { 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') { if (modLevel == 'Standard' || modLevel == 'Advanced') { 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 == totalProgGoals) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Final Goal (' + progGoalArray.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' + 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 'sequence': { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Tip Sequence Goal #' + currentGoalLevel + ' (' + sequenceArray.desc[currentGoalLevel-1] + ') has been met!! :CGGoal15 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); break; } case 'tipjar': { cb.sendNotice(borderAllNotices + '\n :CGGoal15 Tip Jar Goal #' + currentGoalLevel + ' (' + currentGoalDesc + ') has been met!! :CGGoal15 \n ***** The Tip Jar will start to empty, tip to keep it full! *****\n' + borderAllNotices, '', tipjarBgColor, tipjarTextColor, 'bold'); changeRoomSubject(); 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') { ticketCountdownRunning = true; cb.setTimeout(autostartTicketShow,30000); cb.sendNotice('Ticket Sales will start automatically in 30 seconds.', '', appNoticeColor, '', 'bold'); cb.sendNotice(botName + 'Per configuration (setting "1A2"), 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); if (modLevel == 'Standard' || modLevel == 'Advanced') { 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, '', '', 'red'); } } else if (progressiveRestartGoals == 'Yes' && whichApp == 'goals') { initProgGoal(); goalsReset = true; changeRoomSubject(); cb.drawPanel(); cb.setTimeout(goalResetNotice,1000); } else { changeRoomSubject(); } } function goalResetNotice() { cb.sendNotice(botName + 'Per configuration (setting "2B2"), the app is restarting the goal list upon completion of the last goal.', BC, appNoticeColor); cb.sendNotice(botName + 'Per configuration, the app is restarting the goal list upon completion of the last goal.', BC, appNoticeColor, '', '', 'red'); cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Goal List has been reset and is starting again! :CGGoal15 \n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, 'bold'); } 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 'sequence': { exceedGoal(); break; } case 'tipjar': { tipJarRunning = false; if (currentGoalRecycleCount >= tipjarGoalArray.recyc[currentGoalLevel-1]) { if (currentGoalLevel < totalTipJarLevels) { nextGoal(); } else { currentGoalTips = 0; cb.sendNotice('The Tip Jar is empty and the last goal is completed!!', '', tipjarNoticesBgColor, tipjarNoticesTxtColor, 'bold'); updateFinalGoalMet(); } } else { cb.sendNotice('The Tip Jar has emptied and the current goal recycled, tip to reach the goal to start the prize again!', '', tipjarNoticesBgColor, tipjarNoticesTxtColor, 'bold'); currentGoalRecycleCount++; currentGoalTips = 0; changeRoomSubject(); } 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; } case 'sequence': { if (tipSequenceDirection == 'up') { nextSequence = sequenceArray.amt[currentGoalLevel-1] + 1; if (chatMsgToggle && nextSequence <= endSequence) { nextgifadv = ':next' + nextSequence; cb.sendNotice('Tip Sequence ' + (nextSequence-1) + ' has been Completed --- ' + nextgifadv, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); } goalComplete(); if (nextSequence > endSequence) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Tip Sequence has been Completed!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); updateFinalGoalMet(); } else { nextGoal(); } } else if (tipSequenceDirection == 'down') { nextSequence = sequenceArray.amt[currentGoalLevel-1] - 1; if (chatMsgToggle && nextSequence >= endSequence) { nextgifadv = ':next' + nextSequence; cb.sendNotice('Tip Sequence ' + (nextSequence+1) + ' has been Completed --- ' + nextgifadv, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); } goalComplete(); if (nextSequence < endSequence) { cb.sendNotice(borderAllNotices + '\n :CGGoal15 The Tip Sequence has been Completed!! :CGGoal15 \n \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 ' + getTopTipper() + ' was the top tipper!!! \uD83C\uDFC6 \uD83C\uDFC6 \uD83C\uDFC6 \n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, 'bold'); updateFinalGoalMet(); } else { nextGoal(); } } 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 'peep': { break; } case 'goals': { currentGoal++; currentGoalDesc = progGoalArray.desc[currentGoal-1]; currentGoalTotal = progGoalArray.amt[currentGoal-1]; if (cb.settings.progressiveUpDown == 'Count Up To Goal') { currentGoalTips = 0; } else { currentGoalTips = currentGoalTotal; } currentGoalMet = false; changeRoomSubject(); break; } case 'goalcount': { currentGoalCountAmt++; if (currentGoalCountAmt >= goalCounterArray.amt[currentGoalLevel-1]) { currentGoalLevel++; currentGoalDesc = goalCounterArray.desc[currentGoalLevel-1]; changeRoomSubject(); } break; } case 'sequence': { currentGoalLevel++; currentGoalSequence = sequenceArray.amt[currentGoalLevel-1]; currentGoalDesc = sequenceArray.desc[currentGoalLevel-1]; changeRoomSubject(); break; } case 'tipjar': { currentGoalLevel++; currentGoalRecycleCount = 0; currentGoalTips = 0; currentGoalRecycleTotal = tipjarGoalArray.recyc[currentGoalLevel-1]; currentGoalTotal = tipjarGoalArray.amt[currentGoalLevel-1]; currentGoalDesc = tipjarGoalArray.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 == 'tipjar' || whichApp == 'spank' || whichApp == 'autoreset') { if (whichApp == 'goals' && cb.settings.progressiveUpDown == 'Count Down To Zero') { 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 (cb.settings.progressiveUpDown == 'Count Up To Goal') { addsubject = 'Current Goal: ' + currentGoalDesc + ' at ' + currentGoalTotal + ' tokens'; newsubject = buildSubject(newsubject,addsubject); } else { addsubject = 'Current Goal: ' + currentGoalDesc + ' once countdown reaches zero'; newsubject = buildSubject(newsubject,addsubject); } if (genericRoomSubjectType == 'All') { if (totalProgGoals >= (currentGoal + 1)) { newsubject += ' -- Next Goal: ' + progGoalArray.desc[currentGoal]; } else if (totalProgGoals > 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') { if (cb.settings.progressiveUpDown == 'Count Up To Goal') { newtokens = (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)); } else { newtokens = (currentGoalTips > 0 ? currentGoalTips : 0); } newsubject += ' [' + newtokens + ' token' + (newtokens == 1 ? '' : 's') + ' to goal]'; } 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]'; } 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 'sequence': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { var startlevelseq = currentGoalLevel - 1; if (tipSequenceDirection == 'up' && nextSequence <= sequenceArray.amt[sequenceArray.amt.length-1] || tipSequenceDirection == 'down' && nextSequence >= sequenceArray.amt[sequenceArray.amt.length-1]) { if (genericRoomSubjectType == 'All') { addsubject = 'Remaining Sequence Goals: '; newsubject = buildSubject(newsubject,addsubject); for (let i = startlevelseq; i < sequenceArray.desc.length; i++) { if (sequenceArray.desc[i] != '' && sequenceArray.desc[i] != null) { newsubject += (i > startlevelseq ? ', at ' : 'at ') + sequenceArray.amt[i] + ' (' + sequenceArray.desc[i] + ')'; } } } else { if (sequenceArray.desc[startlevelseq] != '' && sequenceArray.desc[startlevelseq] != null) { addsubject = 'Next Sequence Goal: ' + sequenceArray.amt[startlevelseq] + ' (' + sequenceArray.desc[startlevelseq] + ') '; newsubject = buildSubject(newsubject,addsubject); } } } else { addsubject = 'Final Tip Sequence'; newsubject = buildSubject(newsubject,addsubject); currentGoalSequence = endSequence; currentGoalDesc = 'Final Tip Sequence'; } } else { addsubject = 'All Goals Have Been Completed!!! '; newsubject = buildSubject(newsubject,addsubject); } } if (sequenceSubjectText) { newsubject = buildSubject(newsubject,sequenceSubjectText); } if (genericRoomSubjectPosn == 'End' && genericRoomSubjectSfx) { newsubject = buildSubject(newsubject,genericRoomSubjectSfx); } cb.changeRoomSubject(newsubject); break; } case 'tipjar': { if (genericRoomSubjectType == 'All' || genericRoomSubjectType == 'All - Current goal only') { if (!finalGoalMet) { if (tipJarRunning) { addsubject = 'Tip Jar Prize has started! Tip to keep it full! The prize for this goal (' + tipjarGoalArray.desc[currentGoalLevel-1] + ') will end if the jar empties'; newsubject = buildSubject(newsubject,addsubject); } else { addsubject = 'Tip Jar Goal: ' + tipjarGoalArray.amt[currentGoalLevel-1] + ' tokens'; newsubject = buildSubject(newsubject,addsubject); addsubject = 'At goal, the prize (' + tipjarGoalArray.desc[currentGoalLevel-1] + ') will be performed until the jar empties'; newsubject = buildSubject(newsubject,addsubject); } } else { addsubject = 'All Goals Have Been Completed!!!'; newsubject = buildSubject(newsubject,addsubject); } } if (tipjarSubjectText) { newsubject = buildSubject(newsubject,tipjarSubjectText); } 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]'; } 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); } 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]'; } cb.changeRoomSubject(newsubject); break; } case 'peep': { if (showStage == 'peepsales') { newsubject += 'PEEP SHOW SALES [' + peepshowPrice + ' tokens/min]: '; } else if (showStage == 'peepshow') { newsubject += 'PEEP SHOW IN PROGRESS [' + peepshowPrice + ' tokens/min]: '; } else if (showStage == 'peepwarn') { newsubject += 'PEEP SHOW ENDING SOON [' + peepshowPrice + ' tokens/min]: '; } else if (showStage == 'peepfinale') { newsubject += 'PEEP SHOW SALES ENDED -- DO NOT TIP TO JOIN!: '; } else if (showStage == 'peepaftershow') { newsubject += 'PEEP SHOW HAS ENDED: '; } else { newsubject += 'PEEP SHOW '; } newsubject += peepshowSubjectText; if (genericRoomSubjectPosn == 'End') { newsubject += ' ' + genericRoomSubjectSfx; } 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]'; } 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'; } 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.'; if (cb.settings.progressiveUpDown == 'Count Up To Goal') { lgoutstring += wordWrap('\nCurrent Goal (#' + (currentGoal) + '): ' + currentGoalDesc + ' at ' + currentGoalTotal + ' tokens (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' left)'); } else { lgoutstring += wordWrap('\nCurrent Goal (#' + (currentGoal) + '): ' + currentGoalDesc + ', once countdown reaches zero (' + (currentGoalTips < 0 ? 0 : currentGoalTips) + ' remaining)'); } if (totalProgGoals <= 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 (((cb.settings.progressiveIncludeGoals == 'Yes' && !finalGoalMet) || lgcallmode == 'cmd') && totalProgGoals > 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 (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, progGoalBgColor, progGoalTextColor, boldTextLiteral); 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 += wordWrap('\nPrize Level #' + (listindex+1) + ' : ' + goalCounterArray.desc[listindex] + ' (' + goalCounterArray.amt[listindex] + ' goals)'); listindex++; } } if (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, goalCountBgColor, goalCountTextColor, boldTextLiteral); break; } case 'sequence': { lgoutstring = borderSequenceTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nAll Sequence Goals have been completed!'; } else { lgoutstring += '\nThe sequence is counting ' + tipSequenceDirection + ' to a final tip amount of ' + endSequence + '.'; lgoutstring += '\nThe next tip sequence needed is ' + nextSequence + ' tokens.'; lgoutstring += '\nThere are prizes at specific count sequence levels (' + (cbjs.arrayJoin(sequenceArray.amt, ', ')) + ').'; lgoutstring += '\nThe next prize level will be at tip sequence ' + currentGoalSequence + ' (' + currentGoalDesc + ')'; if (groupTipToggle) { lgoutstring += '\nGroup tipping is enabled, all tips count toward the current sequence number.'; } else { lgoutstring += '\nYou must tip at least ' + nextSequence + ' tokens to advance the sequence.'; } } if (((cb.settings.tipsequenceIncludeGoals == 'Yes' && !finalGoalMet) || lgcallmode == 'cmd') && sequenceArray.desc.length > 0 && currentGoalLevel > 0) { if (listmode == 'r') { listindex = (currentGoalLevel-1); } lgoutstring += '\n' + (listmode == 'a' ? 'Full Sequence Level List: ' : 'Remaining Sequence Level List: '); while (sequenceArray.amt[listindex] > 0 && sequenceArray.desc[listindex] != null && sequenceArray.desc[listindex] != '') { lgoutstring += wordWrap('\nGoal Level #' + (listindex+1) + ' : ' + sequenceArray.desc[listindex] + ' (at sequence ' + sequenceArray.amt[listindex] + ')'); listindex++; } lgoutstring += '\nFinal Count Sequence (at sequence ' + endSequence + ')'; } if (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, tipsequenceBgColor, tipsequenceTextColor, boldTextLiteral); break; } case 'tipjar': { lgoutstring = borderTipJarTop; if (lggroup == 'all') { lgoutstring += '\nSent to All:'; } else if (lggroup != 'timer') { lgoutstring += '\nSent to YOU:'; listgoalssendto = lgreqby; } if (finalGoalMet) { lgoutstring += '\nAll Tip Jar Goals have been completed!'; } else { if (tipJarRunning) { if (tipJarCurrentTokens > 0) { lgoutstring += '\nThe Tip Jar goal was met and the jar has started draining.'; lgoutstring += wordWrap('\nKeep the jar full and the prize will be performed until the tip jar is empty.'); } else { lgoutstring += '\nThe Tip Jar is empty!'; lgoutstring += '\nThe jar can be refilled to continue performing the goal.'; } } else { lgoutstring += '\nFill the Tip Jar until the goal is reached.'; lgoutstring += wordWrap('\nThe jar will begin draining and the goal will be performed until the jar is empty.'); } } if (((cb.settings.tipjarIncludeGoals == 'Yes' && !finalGoalMet) || lgcallmode == 'cmd') && tipjarGoalArray.desc.length > 0 && currentGoalLevel > 0) { if (listmode == 'r') { listindex = (currentGoalLevel-1); } lgoutstring += '\n' + (listmode == 'a' ? 'Full Tip Jar Goal List: ' : 'Remaining Tip Jar Goal List: '); while (tipjarGoalArray.amt[listindex] > 0 && tipjarGoalArray.desc[listindex] != null && tipjarGoalArray.desc[listindex] != '') { lgoutstring += wordWrap('\nGoal Level #' + (listindex+1) + ' : ' + tipjarGoalArray.desc[listindex] + ' (' + tipjarGoalArray.amt[listindex] + ' tokens) [recycle count: ' + tipjarGoalArray.recyc[listindex] + ']'); listindex++; } } if (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, tipjarBgColor, tipjarTextColor, boldTextLiteral); 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 += wordWrap('\nIn the Goal Race, you can tip to vote for one of the goals.'); lgoutstring += wordWrap('\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 += wordWrap('\nYou can switch between voting and sending tip notes by using commands:'); lgoutstring += wordWrap('\n- To change your setting to allow tipnotes when tipping, type: /tipnoteon'); lgoutstring += wordWrap('\n- To change your setting to allow voting when tipping, type: /tipnoteoff'); if (cb.settings.goalraceAllowClaim == 'Yes') { lgoutstring += wordWrap('\nIf you don\'t choose goal when tipping, you can still claim the votes later.'); lgoutstring += wordWrap('\nUse the commands "/addrace1" or "/addrace2" to assign your tips to a goal.'); } } if (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, goalraceBgColor, goalraceTextColor, boldTextLiteral); 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 += wordWrap('\nThe "Spank-a-thon" is active, you can tip for the types of spanks listed below.'); lgoutstring += wordWrap('\nThe tips will accumulate in the "spank bank" until the goal is reached.'); lgoutstring += wordWrap('\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 += wordWrap('\nGoal #' + (listindex+1) + ' : ' + spankGoalArray.desc[listindex] + ' (' + spankGoalArray.amt[listindex] + ' tokens)'); listindex++; } } if (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, spankBgColor, spankTextColor, boldTextLiteral); 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 += wordWrap('\nThe Auto-Reset Goal has a single repeating goal every ' + autoresetGoalAmount + ' tokens.'); lgoutstring += wordWrap('\nGoal Description: ' + autoresetEachGoal); if (autoresetEndAfter > 0) { lgoutstring += '\nShow Ends after ' + autoresetEndAfter + ' goals.'; if (autoresetFinalGoal) { lgoutstring += wordWrap('\nFinal Goal Prize: ' + autoresetFinalGoal); } } else { lgoutstring += wordWrap('\nGoals will keep repeating, there is no goal count limit.'); } } if (maxTipToGoal > 0) { lgoutstring += wordWrap('\nNote: Tips over ' + maxTipToGoal + ' tokens have been configured to not apply to goals.'); } if (cb.settings.noticeSepStyle != 'No Border') { lgoutstring += '\n' + borderUniversalBottom; } cb.sendNotice(lgoutstring, listgoalssendto, autoresetBgColor, autoresetTextColor, boldTextLiteral); 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 { initProgGoal(); } changeRoomSubject(); initProgGoalsNoticeTimer(); if (totalProgGoals <= 1) { cb.sendNotice(borderAllNotices + '\n' + setpgname + ' has started the Progressive Goal Feature.\nThere is a single goal for the show.\n' + borderAllNotices, '', progGoalBgColor, progGoalTextColor, boldTextLiteral); } else { cb.sendNotice(borderAllNotices + '\n' + setpgname + ' has started the Progressive Goal Feature.\nThere are ' + totalProgGoals + ' goals in the current show.\n' + borderAllNotices, '', progGoalBgColor, progGoalTextColor, boldTextLiteral); } } else if (setpgoption == 'off') { saveProgGoals(); cb.sendNotice(borderAllNotices + '\n' + setpgname + ' has suspended the Progressive Goal Feature.\n' + borderAllNotices, '', progGoalBgColor, progGoalTextColor, boldTextLiteral); } } function initProgGoal() { finalGoalMet = false; currentGoalMet = false; goalsReset = false; currentGoalTips = 0; currentGoal = 1; currentGoalDesc = progGoalArray.desc[currentGoal-1]; currentGoalTotal = progGoalArray.amt[currentGoal-1]; if (cb.settings.progressiveUpDown == 'Count Down To Zero') { 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 { 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, boldTextLiteral); } else if (setgcoption == 'off') { saveGoalCount(); cb.sendNotice(borderAllNotices + '\n' + setgcname + ' has suspended the Tip Goal Counter Feature.\n' + borderAllNotices, '', goalCountBgColor, goalCountTextColor, boldTextLiteral); } } function initGoalCounter() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoalCountAmt = 0; currentGoal = 1; currentGoalLevel = 1; 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(); } // *********************************** Tip Jar Functions ************************************** function setTipJarToggle(settjoption, settjby) { var settjname = settjby; if (settjby == BC) { settjname = bcText; } if (settjoption == 'on') { whichApp = 'tipjar'; if (savedTipJar) { restoreTipJar(); } else { initTipJar(); } changeRoomSubject(); cb.sendNotice(borderAllNotices + '\n' + settjname + ' has started the Tip Jar Feature.\nOnce goal is met, the prize will be performed until the tip jar is empty.\n' + borderAllNotices, '', tipjarBgColor, tipjarTextColor, boldTextLiteral); } else if (settjoption == 'off') { saveTipJar(); cb.sendNotice(borderAllNotices + '\n' + settjname + ' has suspended the Tip Jar Feature.\n' + borderAllNotices, '', tipjarBgColor, tipjarTextColor, boldTextLiteral); } } function initTipJar() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoalCountAmt = 0; currentGoal = 1; currentGoalLevel = 1; currentGoalRecycleCount = 0; tipJarRunning = false; totalTipJarLevels = tipjarGoalArray.desc.length; currentGoalTotal = tipjarGoalArray.amt[currentGoalLevel-1]; currentGoalDesc = tipjarGoalArray.desc[currentGoalLevel-1]; currentGoalTotal = tipjarGoalArray.amt[currentGoalLevel-1]; currentGoalRecycleTotal = tipjarGoalArray.recyc[currentGoalLevel-1]; if (tipJarNoticeInt > 0) { cb.setTimeout(tipJarNotice, tipJarNoticeInt); } } function saveTipJar() { tjSave_finalGoalMet = finalGoalMet; tjSave_currentGoalTips = currentGoalTips; tjSave_currentGoal = currentGoal; tjSave_currentGoalDesc = currentGoalDesc; tjSave_currentGoalTotal = currentGoalTotal; tjSave_currentGoalLevel = currentGoalLevel; tjSave_currentGoalRecycleTotal = currentGoalRecycleTotal; tjSave_currentGoalRecycleCount = currentGoalRecycleCount; savedTipJar = true; } function restoreTipJar() { finalGoalMet = tjSave_finalGoalMet; currentGoalTips = tjSave_currentGoalTips; currentGoal = tjSave_currentGoal; currentGoalDesc = tjSave_currentGoalDesc; currentGoalTotal = tjSave_currentGoalTotal; currentGoalLevel = tjSave_currentGoalLevel; currentGoalRecycleTotal = tjSave_currentGoalRecycleTotal; currentGoalRecycleCount = tjSave_currentGoalRecycleCount; savedTipJar = false; } function tipJarColors(tjcby) { if (cb.settings.tipjarTextColor == 'Custom') { tipjarTextColor = setTextColor(cb.settings.tipjarCustomTextColor,'Tip Jar',tjcby); } else { tipjarTextColor = setTextColor(cb.settings.tipjarTextColor,'Tip Jar',tjcby); } if (cb.settings.tipjarBgColor == 'Custom') { tipjarBgColor = setBgColor(cb.settings.tipjarCustomBgColor,'Tip Jar',tjcby); } else { tipjarBgColor = setBgColor(cb.settings.tipjarBgColor,'Tip Jar',tjcby); } } function tipJarNotice() { if (whichApp == 'tipjar') { goalNotices('timer','','r','timer'); cb.setTimeout(tipJarNotice, tipJarNoticeInt); } } function startTipJar() { tipJarRunning = true; changeRoomSubject(); cb.setTimeout(checkJar, jarEmptyRate); } function pauseTipJar() { tipJarPaused = true; } function resumeTipJar() { tipJarPaused = false; cb.setTimeout(checkJar, jarEmptyRate); } function checkJar() { if (tipJarRunning && !tipJarPaused) { if (tipJarCurrentTokens > 0) { tipJarCurrentTokens -= jarEmptyTokens; if (tipJarCurrentTokens <= 0) { tipJarCurrentTokens = 0; tipjarIsEmpty(); } } cb.setTimeout(checkJar, jarEmptyRate); } cb.drawPanel(); } function setDrainRate(level) { switch (level) { case 1: { jarEmptyRate = 1000; jarEmptyTokens = 5; drainLevel = 1; break; } case 2: { jarEmptyRate = 1000; jarEmptyTokens = 4; drainLevel = 2; break; } case 3: { jarEmptyRate = 1000; jarEmptyTokens = 3; drainLevel = 3; break; } case 4: { jarEmptyRate = 1000; jarEmptyTokens = 2; drainLevel = 4; break; } case 5: { jarEmptyRate = 1000; jarEmptyTokens = 1; drainLevel = 5; break; } case 6: { jarEmptyRate = 2000; jarEmptyTokens = 1; drainLevel = 6; break; } case 7: { jarEmptyRate = 3000; jarEmptyTokens = 1; drainLevel = 7; break; } case 8: { jarEmptyRate = 4000; jarEmptyTokens = 1; drainLevel = 8; break; } case 9: { jarEmptyRate = 5000; jarEmptyTokens = 1; drainLevel = 9; break; } case 10: { jarEmptyRate = 10000; jarEmptyTokens = 1; drainLevel = 10; break; } } } function drainRateText() { switch (drainLevel) { case 1: { return '5 tok/sec'; } case 2: { return '4 tok/sec'; } case 3: { return '3 tok/sec'; } case 4: { return '2 tok/sec'; } case 5: { return '1 tok/sec'; } case 6: { return '1 tok/2 sec'; } case 7: { return '1 tok/3 sec'; } case 8: { return '1 tok/4 sec'; } case 9: { return '1 tok/5 sec'; } case 10: { return '1 tok/10 sec'; } default: { return 'Invalid'; } } } function tipjarIsEmpty() { if (cb.settings.tipjarWhenEmpty == 'Advance to Next Goal/Cycle') { cb.sendNotice('The Tip Jar is empty, current goal is ending.', '', tipjarNoticesBgColor, tipjarNoticesTxtColor, boldTextLiteral); advanceTipJar(); } else { var currenttime = new Date(); if ((currenttime - lastTipJarEmptyNotice) > 30000) { lastTipJarEmptyNotice = currenttime; cb.sendNotice('The Tip Jar is empty, you can tip to fill the jar again and re-start the prize', '', tipjarNoticesBgColor, tipjarNoticesTxtColor, boldTextLiteral); cb.sendNotice(bcText + ': The Tip Jar is configured to allow tipping to continue on the current goal when empty, therefore it will not advance to the next goal or cycle until you use the command "/skip" to advance.', BC, appNoticeColor); cb.sendNotice('Moderators: The Tip Jar is configured to allow tipping to continue on the current goal when empty, therefore it will not advance to the next goal or cycle until you use the command "/skip" to advance.', '', appNoticeColor, '', '', 'red'); } } } function advanceTipJar() { tipJarRunning = false; if (currentGoalRecycleCount >= tipjarGoalArray.recyc[currentGoalLevel-1]) { if (currentGoalLevel < totalTipJarLevels) { nextGoal(); } else { currentGoalTips = 0; cb.sendNotice('The Tip Jar is empty and the last goal is completed!!', '', tipjarNoticesBgColor, tipjarNoticesTxtColor, boldTextLiteral); updateFinalGoalMet(); } } else { cb.sendNotice('The Tip Jar has emptied and the current goal recycled, tip to reach the goal to start the prize again!', '', tipjarNoticesBgColor, tipjarNoticesTxtColor, boldTextLiteral); currentGoalRecycleCount++; currentGoalTips = 0; changeRoomSubject(); } cb.drawPanel(); } function updateTipjarGoal() { currentGoalDesc = tipjarGoalArray.desc[currentGoalLevel-1]; currentGoalTotal = tipjarGoalArray.amt[currentGoalLevel-1]; changeRoomSubject(); cb.drawPanel(); } // *********************************** Sequence Goal Functions ************************************** function setSequenceToggle(setseqoption, setseqby) { var setseqname = setseqby; if (setseqby == BC) { setseqname = bcText; } if (setseqoption == 'on') { whichApp = 'sequence'; if (savedGoalSequence) { restoreSequence(); } else { initSequence(); } changeRoomSubject(); cb.sendNotice(borderAllNotices + '\n' + setseqname + ' has started the Tip Sequence Feature.\nTip in ' + (tipSequenceDirection == 'up' ? 'Ascending' : 'Descending') + ' Sequence to the final goal.\nThere are prizes at specific count sequence levels (' + (cbjs.arrayJoin(sequenceArray.amt, ', ')) + ').\n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, boldTextLiteral); } else if (setseqoption == 'off') { saveSequence(); cb.sendNotice(borderAllNotices + '\n' + setseqname + ' has suspended the Tip Sequence Feature.\n' + borderAllNotices, '', tipsequenceBgColor, tipsequenceTextColor, boldTextLiteral); } } function initSequence() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoalCountAmt = 0; currentGoal = 1; currentGoalLevel = 1; currentGroupTipAmt = 0; currentGoalSequence = sequenceArray.amt[currentGoalLevel-1]; currentGoalDesc = sequenceArray.desc[currentGoalLevel-1]; if (tipSequenceDirection == 'up') { nextSequence = lowSequence; endSequence = highSequence; } else if (tipSequenceDirection == 'down') { endSequence = lowSequence; nextSequence = highSequence; } if (cb.settings.tipsequenceChatMsg == 'Yes') { setChatMsgToggle('on', BC, 'init'); } if (cb.settings.tipsequenceGroupTips == 'Group Tipping') { setGroupTipToggle('on', BC, 'init'); } sequenceTotalTipsGoal = sequenceTotal(lowSequence,highSequence); if (tipSeqNoticeInt > 0) { cb.setTimeout(tipSeqNotice, tipSeqNoticeInt); } } function saveSequence() { sqSave_finalGoalMet = finalGoalMet; sqSave_currentGoalDesc = currentGoalDesc; sqSave_currentGroupTipAmt = currentGroupTipAmt; sqSave_currentGoalLevel = currentGoalLevel; savedGoalSequence = true; } function restoreSequence() { finalGoalMet = sqSave_finalGoalMet; currentGoalDesc = sqSave_currentGoalDesc; currentGroupTipAmt = sqSave_currentGroupTipAmt; currentGoalLevel = sqSave_currentGoalLevel; savedGoalSequence = false; } function sequenceTotal(lowseq,highseq) { var sum = 0; for (var i = lowseq; i <= highseq ; i++) { sum += i; } return sum } function sequenceColors(seqcby) { if (cb.settings.tipsequenceTextColor == 'Custom') { tipsequenceTextColor = setTextColor(cb.settings.tipsequenceCustomTextColor,'Sequence Goals',seqcby); } else { tipsequenceTextColor = setTextColor(cb.settings.tipsequenceTextColor,'Sequence Goals',seqcby); } if (cb.settings.tipsequenceBgColor == 'Custom') { tipsequenceBgColor = setBgColor(cb.settings.tipsequenceCustomBgColor,'Sequence Goals',seqcby); } else { tipsequenceBgColor = setBgColor(cb.settings.tipsequenceBgColor,'Sequence Goals',seqcby); } } function tipSeqNotice() { if (whichApp == 'sequence') { goalNotices('timer','','r','timer'); cb.setTimeout(tipSeqNotice, tipSeqNoticeInt); } } function updateSequenceGoal() { currentGoalDesc = sequenceArray.desc[currentGoalLevel-1]; currentGoalSequence = sequenceArray.amt[currentGoalLevel-1]; changeRoomSubject(); cb.drawPanel(); } function setChatMsgToggle(chatmsgoption, chatmsgsendto, chatmsgmode) { if (chatmsgoption == 'on') { if (chatMsgToggle) { if (chatmsgmode != 'init') { cb.sendNotice('The chat notice toggle is already turned on.', chatmsgsendto, appNoticeColor); } } else { chatMsgToggle = true; if (chatmsgmode != 'init') { cb.sendNotice('You have enabled the display of chat notices for tip sequences completed.', chatmsgsendto, appNoticeColor); } } } else if (chatmsgoption == 'off') { if (!chatMsgToggle) { if (chatmsgmode != 'init') { cb.sendNotice('The chat notice toggle is already turned off.', chatmsgsendto, appNoticeColor); } } else { chatMsgToggle = false; if (chatmsgmode != 'init') { cb.sendNotice('You have disabled the display of chat notices for tip sequences completed.', chatmsgsendto, appNoticeColor); } } } } function setGroupTipToggle(grouptipoption, grouptipsendto, grouptipmode) { if (grouptipoption == 'on') { if (groupTipToggle) { if (grouptipmode != 'init') { cb.sendNotice('The group tipping feature is already turned on.', grouptipsendto, appNoticeColor); } } else { groupTipToggle = true; if (grouptipmode != 'init') { cb.sendNotice('You have enabled the group tipping feature.', grouptipsendto, appNoticeColor); } cb.drawPanel(); } } else if (grouptipoption == 'off') { if (!groupTipToggle) { if (grouptipmode != 'init') { cb.sendNotice('The group tipping feature is already turned off.', grouptipsendto, appNoticeColor); } } else { groupTipToggle = false; if (grouptipmode != 'init') { cb.sendNotice('You have disabled the group tipping feature.', grouptipsendto, appNoticeColor); } cb.drawPanel(); } } } function changeEndSequence(newseq) { endSequence = newseq; if (tipSequenceDirection == 'up') { highSequence = newseq; } else { lowSequence = newseq; } sequenceTotalTipsGoal = sequenceTotal(lowSequence,highSequence); cb.drawPanel(); } // *********************************** Ultra App Ticket Show Functions ************************************** function setTicketShowToggle(setticketoption, setticketuser) { var setticketname = setticketuser; 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 configured Fan Club Members will be admitted to the show!\nPlease consider joining the Fan Club!' + borderTicketBottom, '', ticketBgColor, ticketTextColor, boldTextLiteral); } else { cb.sendNotice(borderTicketTop + '\n' + bcText + ' has started selling tickets for the show for ' + ticketPrice + ' tokens.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, boldTextLiteral); } } 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; 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, boldTextLiteral); } } function initTicketShow(inittktsendto) { whichApp = 'ticket'; if (ticketHolderList.length > 0) { countTickets = ticketHolderList.length; } else { countTickets = 0; } countTicketsFromPreview = 0; if (presalesToggle) { setPresalesToggle('off',inittktsendto,0); } showStage = 'ticketsales'; changeRoomSubject(); setTicketMode('init', inittktsendto); ticketShowEnded = false; ticketSalesEnded = false; loadFreeTickets(); if (!tktNoticeRunning) { 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() { if (whichApp == 'ticket') { var tktmsg = ''; 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); tktmsg += '\n\u23E9 Ticket show active for ' + ((Date.now() - hiddenTime)/60000).toFixed(1) + ' min'; if (freePreviewLength > 0) { if (cb.settings.ticketShowPreviewGrays == 'Yes') { tktmsg += '\n\u23E9 ' + wordWrap('Free preview is enabled (' + freePreviewLengthText + ')'); } else { tktmsg += '\n\u23E9 ' + wordWrap(' Free preview is enabled for users with tokens (' + freePreviewLengthText + ')'); } } } 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 :siren1 :siren1 :siren1 Ticket Sales have ended, do not tip for a ticket! :siren1 :siren1 :siren1'; } else if (showStage == 'aftershow') { tktmsg += '\n\u23E9 ' + wordWrap('Show has ended! -- ' + cb.settings.ticketShowAfterNotice); } if (showStage != 'aftershow' && showStage != 'showfinale') { 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 (ticketShowPriceFC > 1 && ticketShowPriceFC < ticketPrice && cb.settings.ticketShowFreeFC != 'Yes') { tktmsg += '\n\u23E9 Chaturbate Fanclub ticket price: ' + ticketShowPriceFC + ' token' + (ticketShowPriceFC > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeFC == 'Yes') { tktmsg += '\n\u23E9 Chaturbate Fanclub members get a free ticket!'; } if (extFanListArray.length > 0) { if (ticketShowPriceEFC > 1 && ticketShowPriceEFC < ticketPrice && cb.settings.ticketShowFreeEFC != 'Yes') { tktmsg += '\n\u23E9 ' + EFCname + ' ticket price: ' + ticketShowPriceEFC + ' token' + (ticketShowPriceEFC > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeEFC == 'Yes') { tktmsg += '\n\u23E9 ' + EFCname + ' members get a free ticket!'; } } if (extFanList2Array.length > 0) { if (ticketShowPriceEFC2 > 1 && ticketShowPriceEFC2 < ticketPrice && cb.settings.ticketShowFreeEFC2 != 'Yes') { tktmsg += '\n\u23E9 ' + EFCname2 + ' ticket price: ' + ticketShowPriceEFC2 + ' token' + (ticketShowPriceEFC2 > 1 ? 's.' : '.'); } else if (cb.settings.ticketShowFreeEFC2 == 'Yes') { tktmsg += '\n\u23E9 ' + EFCname2 + ' members get a free ticket!'; } } if (VIPListArray.length > 0) { if (ticketShowPriceVIP > 1 && ticketShowPriceVIP < ticketPrice && cb.settings.ticketShowFreeVIP != 'Yes') { tktmsg += '\n\u23E9 ' + VIPname + ' ticket price: ' + ticketShowPriceVIP + ' token' + (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, boldTextLiteral); tktNoticeRunning = true; cb.setTimeout(ticketNoticeDisplay,ticketNoticeInt); } } function setTicketShowOtToggle(mode,sendto) { if (mode == 'on') { if (ticketShowOtToggle) { cb.sendNotice('The Outstanding Ticket feature is already enabled.',sendto,appNoticeColor); } else { ticketShowOtToggle = true; if (presalesToggle && showStage == 'presales') { let otlinitmessage = '\nThe Outstanding Ticket List feature is enabled for Pre-sales.'; otlinitmessage += '\nTo redeem an outstanding ticket, type: /useticket'; otlinitmessage += '\nTo view the OT List, type: /otlist'; cb.sendNotice(borderPresaleTop + otlinitmessage + borderPresaleBottom, '', ticketBgColor, ticketTextColor, boldTextLiteral); } else if (whichApp == 'ticket') { let otlinitmessage = '\nThe Outstanding Ticket List feature is enabled for the Ticket Show.'; otlinitmessage += '\nTo save your ticket for later, type: /saveticket'; otlinitmessage += '\nThis will REMOVE you from the current show\'s ticket list.'; otlinitmessage += '\nTo redeem an outstanding ticket, type: /useticket'; otlinitmessage += '\nTo view the OT List, type: /otlist'; cb.sendNotice(borderTicketTop + otlinitmessage + borderTicketBottom, '', ticketBgColor, ticketTextColor, boldTextLiteral); } cb.sendNotice('***Notice Regarding Outstanding Tickets***: \nThe Outstanding tickets added and removed during the show must be made permanent\nby updating the list on the App Launch page.\nThe list can be viewed by typing: /otlist.\nThe changes for the current session can be seen by typing: /otchanges.\nIt is recommended to save the list and update the OT list before the next show.\nChanges made during the show are not saved beyond the current session.', BC, appNoticeColor); } } else if (mode == 'off') { if (!ticketShowOtToggle) { cb.sendNotice('The Outstanding Ticket feature is already disabled.',sendto,appNoticeColor); } else { ticketShowOtToggle = false; cb.sendNotice('You have disabled the Outstanding Ticket (OT) feature.\nThe outstanding ticket list is still intact, but no more tickets will be sold.\nThe list can be viewed by typing: /otlist.\nThe changes for the current session can be seen by typing: /otchanges \nBe sure to update these entries into the OT List on the App Launch Page, they cannot be saved automatically.',sendto,appNoticeColor); } } } 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.ticketShowFreeEFC == 'Yes') { for (let i = 0; i < extFansInShow.length; i++) { if (!cb.limitCam_userHasAccess(extFansInShow[i])) { addRmvTicket('add',extFansInShow[i],'extfan',0,extFansInShow[i]); } } } if (cb.settings.ticketShowFreeEFC2 == 'Yes') { for (let i = 0; i < extFansInShow2.length; i++) { if (!cb.limitCam_userHasAccess(extFansInShow2[i])) { addRmvTicket('add',extFansInShow2[i],'extfan2',0,extFansInShow2[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,checkfreeextfan,checkfreeextfan2,checkfreevip,checkmode) { if (!cb.limitCam_userHasAccess(checkfreeuser) && showStage != 'aftershow') { if (checkfreemod) { if (cb.settings.ticketShowFreeMods == 'Yes') { if (whichApp == 'ticket') { 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 (presalesToggle) { addRmvPresale('add',checkfreeuser,'mod',checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve been added to the pre-sale list 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'); } } else if (checkfreefan) { if (cb.settings.ticketShowFreeFC == 'Yes') { if (whichApp == 'ticket') { 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 (presalesToggle) { addRmvPresale('add',checkfreeuser,'fan',checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the pre-sale list because you\'re in the CB Fan Club! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } else if (ticketShowPriceFC > 1 && checkmode == 'enter') { if (whichApp == 'ticket') { cb.sendNotice(borderAllNotices + '\nAs a CB Fan Club member, you can buy a ticket for ' + ticketShowPriceFC + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (presalesToggle && ticketShowPriceFC < presalePrice) { cb.sendNotice(borderAllNotices + '\nAs a CB Fan Club member, you can buy a pre-sale ticket for ' + ticketShowPriceFC + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } } else if (checkfreeextfan) { if (cb.settings.ticketShowFreeEFC == 'Yes') { if (whichApp == 'ticket') { addRmvTicket('add',checkfreeuser,'extfan',0,checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the show because you\'re in ' + EFCname + '! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (presalesToggle) { addRmvPresale('add',checkfreeuser,'extfan',checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the pre-sale list because you\'re in ' + EFCname + '! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } else if (ticketShowPriceEFC > 1 && checkmode == 'enter') { if (whichApp == 'ticket') { cb.sendNotice(borderAllNotices + '\nAs a member of ' + EFCname + ', you can buy a ticket for ' + ticketShowPriceEFC + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (presalesToggle && ticketShowPriceEFC < presalePrice) { cb.sendNotice(borderAllNotices + '\nAs a member of ' + EFCname + ', you can buy a pre-sale ticket for ' + ticketShowPriceEFC + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } } else if (checkfreeextfan2) { if (cb.settings.ticketShowFreeEFC2 == 'Yes') { if (whichApp == 'ticket') { addRmvTicket('add',checkfreeuser,'extfan2',0,checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the show because you\'re in ' + EFCname2 + '! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (presalesToggle) { addRmvPresale('add',checkfreeuser,'extfan2',checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the pre-sale list because you\'re in ' + EFCname2 + '! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } else if (ticketShowPriceEFC2 > 1 && checkmode == 'enter') { if (whichApp == 'ticket') { cb.sendNotice(borderAllNotices + '\nAs a member of ' + EFCname2 + ', you can buy a ticket for ' + ticketShowPriceEFC2 + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (presalesToggle && ticketShowPriceEFC2 < presalePrice) { cb.sendNotice(borderAllNotices + '\nAs a member of ' + EFCname2 + ', you can buy a pre-sale ticket for ' + ticketShowPriceEFC2 + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } } else if (checkfreevip) { if (cb.settings.ticketShowFreeVIP == 'Yes') { if (whichApp == 'ticket') { 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 (presalesToggle) { addRmvPresale('add',checkfreeuser,'vip',checkfreeuser); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the pre-sale list because you\'re a ' + VIPname + ' member! \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } else if (ticketShowPriceVIP > 1 && checkmode == 'enter') { if (whichApp == 'ticket') { cb.sendNotice(borderAllNotices + '\nAs a ' + VIPname + ' member, you can buy a ticket for ' + ticketShowPriceVIP + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } else if (presalesToggle && ticketShowPriceVIP < presalePrice) { cb.sendNotice(borderAllNotices + '\nAs a ' + VIPname + ' member, you can buy a pre-sale ticket for ' + ticketShowPriceVIP + ' tokens. \n' + borderAllNotices, checkfreeuser, appNoticeColor, '', 'bold'); } } } } } function setTicketMode(settktmode,settktsendto) { var newticketmode = ''; var newticketauto = ''; 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 == '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 == '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, boldTextLiteral); } else { cb.sendNotice(borderTicketTop + '\nAn automatic timer was started, ticket show price will be ' + ticketPrice + ' tokens.' + borderTicketBottom, '', ticketBgColor, ticketTextColor, boldTextLiteral); } } 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, boldTextLiteral); } 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, '', boldTextLiteral); } 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, '', boldTextLiteral); } 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, '', boldTextLiteral); } 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, '', boldTextLiteral); } 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, '', boldTextLiteral); break; case 1: cb.sendNotice('\u23f1 \u23f1 \u23f1 There is 1 second left! \u23f1 \u23f1 \u23f1', '', appErrorColor, '', boldTextLiteral); 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, boldTextLiteral); startTicketShow(bcText); } else { cb.sendNotice('\u23f0 \u23f0 \u23f0 Time is up! ' + bcText + ' will be starting the show! \u23f0 \u23f0 \u23f0', '', ticketNoticesBgColor, ticketNoticesTextColor, boldTextLiteral); } } } 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': { if (cbjs.arrayContains(freePreviewUserArray,addrmvtktuser)) { removePreview(addrmvtktuser); } 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 == 'extfan') { cb.sendNotice(EFCname + ' member ' + addrmvtktdispuser + ' has been added to the ticket show list.', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvtktusertype == 'extfan2') { cb.sendNotice(EFCname2 + ' 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'); } 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': { if (cbjs.arrayContains(freePreviewUserArray,addrmvtktuser)) { removePreview(addrmvtktuser); countTicketsFromPreview++; } cb.limitCam_addUsers([addrmvtktuser]); addViewer(addrmvtktuser); addTicketHolder(addrmvtktuser); ticketShowTotalTicketsBought++; 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 == 'extfan') { cb.sendNotice(ticketEmoji + ' Welcome ' + EFCname + ' member ' + addrmvtktdispuser + ', you have bought a ticket to the show!', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvtktusertype == 'extfan2') { cb.sendNotice(ticketEmoji + ' Welcome ' + EFCname2 + ' 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'); } } if (ticketShowAllowGift == 'Yes' && addrmvtkttipamt >= (2*ticketPrice)) { addRmvTicket('addextra',addrmvtktuser,'common',(addrmvtkttipamt - ticketPrice),addrmvtktdispuser); } break; } case 'addextra': { var numtickets = Math.floor(parseInt(addrmvtkttipamt / ticketPrice)); if (numtickets > 0) { if (!cbjs.arrayContains(ticketShowExtraTickets.name,addrmvtktuser)) { ticketShowExtraTickets.name.push(addrmvtktuser); ticketShowExtraTickets.count.push(numtickets); } else { var addextraindex = ticketShowExtraTickets.name.indexOf(addrmvtktuser); ticketShowExtraTickets.count[addextraindex] = ticketShowExtraTickets.count[addextraindex] + numtickets; } cb.sendNotice(addrmvtktuser + ', you have purchased ' + numtickets + ' extra tickets to the show. You can gift these to other users with the command "/giftticket user". Each time you use the command uses up one of your extra tickets. Please make sure to spell the user name right, gifted tickets cannot be recovered if they are gifted incorrectly.', addrmvtktuser, ticketBgColor, ticketTextColor, "bold"); } cb.drawPanel(); break; } } } function removePreview(rmvprvuser) { let rmvprvidx = freePreviewUserArray.indexOf(rmvprvuser); freePreviewUserArray.splice(rmvprvidx,1); freePreviewTimeArray.splice(rmvprvidx,1); removePreViewer(rmvprvuser); } 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)) { let rmvvindexh = ticketHolderList.indexOf(rmvtktholder); ticketHolderList.splice(rmvvindexh,1); if (countTickets > 0) { countTickets --; } } } function addPreViewer(addedprvwer) { if (!cbjs.arrayContains(ticketShowPreViewerList,addedprvwer)) { ticketShowPreViewerList.push(addedprvwer); } } function removePreViewer(rmvprvwr) { if (cbjs.arrayContains(ticketShowPreViewerList,rmvprvwr)) { let rmvpindex = ticketShowPreViewerList.indexOf(rmvprvwr); ticketShowPreViewerList.splice(rmvpindex,1); } } function addRmvPreview(addrmvprvmode,addrmvprvwr) { switch (addrmvprvmode) { case 'add': { cb.limitCam_addUsers([addrmvprvwr]); addViewer(addrmvprvwr); addPreViewer(addrmvprvwr); let endtime = Date.now() + (freePreviewLength * 1000); freePreviewUserArray.push(addrmvprvwr); let addrmvpidx = freePreviewUserArray.indexOf(addrmvprvwr); freePreviewTimeArray[addrmvpidx] = endtime; break; } case 'rmv': { cb.limitCam_removeUsers([addrmvprvwr]); removeViewer(addrmvprvwr); removePreViewer(addrmvprvwr); break; } } } function previewMonitorTimer() { cb.setTimeout(previewMonitor, 5000); } function previewMonitor() { if (showStage == 'ticketshow' || showStage == 'showwarn' || showStage == 'showfinale') { previewCheckViewers(); previewMonitorTimer(); } cb.drawPanel(); } function previewCheckViewers() { var nowtime = Date.now(); for (var chk = 0; chk < freePreviewUserArray.length; chk++) { var prvuser = freePreviewUserArray[chk]; if (cb.limitCam_userHasAccess(prvuser)) { if (freePreviewTimeArray[chk] < nowtime) { addRmvPreview('rmv',prvuser); cb.sendNotice(borderTicketTop + '\n Your free preview period has ended. \nPlease buy a ticket for ' + ticketPrice + ' tokens to see the rest of the show.' + borderTicketBottom, prvuser, ticketBgColor, ticketTextColor, 'bold'); } } } } function giftTicket(fromuser,touser) { cb.limitCam_addUsers([touser]); addViewer(touser); addTicketHolder(touser); cb.sendNotice('Welcome ' + touser + ', ' + fromuser + ' has gifted you a ticket to the show!', '', ticketBgColor, ticketTextColor, 'bold'); cb.drawPanel(); } function giveAwayTicket(fromuser,touser) { cb.limitCam_addUsers([touser]); addViewer(touser); addTicketHolder(touser); cb.limitCam_removeUsers([fromuser]); removeViewer(fromuser); removeTicketHolder(fromuser); cb.sendNotice('Welcome ' + touser + ', ' + fromuser + ' has gifted you a ticket to the show!', '', ticketBgColor, ticketTextColor, 'bold'); cb.drawPanel(); } function addRmvOutstandingTicket(mode,user) { switch (mode) { case 'add': { if (!cbjs.arrayContains(outstandingTicketArray,user)) { outstandingTicketArray.push(user); } populateOtChangesArray(user,'add'); break; } case 'rmv': { if (cbjs.arrayContains(outstandingTicketArray,user)) { cbjs.arrayRemove(outstandingTicketArray,user); } populateOtChangesArray(user,'rmv'); break; } } } function populateOtChangesArray(user,type) { if (cbjs.arrayContains(otChangesArray.name,user)) { var otindex = otChangesArray.name.indexOf(user); if (otChangesArray.type[otindex] != type) { otChangesArray.name.push(user); otChangesArray.type.push(type); } else { return; } } else { otChangesArray.name.push(user); otChangesArray.type.push(type); } } 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, boldTextLiteral); 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, boldTextLiteral); 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, boldTextLiteral); 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(); if (freePreviewLength > 0) { previewMonitorTimer(); } 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, boldTextLiteral); 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(); if (freePreviewLength > 0) { previewMonitorTimer(); } 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, boldTextLiteral); 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, boldTextLiteral); showStage = 'ticketshow'; } ticketSalesEnded = false; changeRoomSubject(); cb.drawPanel(); } function warnShowEnding(warnby) { showStage = 'showwarn'; let ticketwarnmsg = borderTicketTop; if (cb.settings.ticketShowFanAppreciation == 'Yes') { ticketwarnmsg += '\n \u26A0\uFE0F :siren1 ' + warnby + ' has indicated the Fan Appreciation Show is nearly finished :siren1 \u26A0\uFE0F '; } else { if (cb.settings.ticketShowReducePriceWarn > 0 && cb.settings.ticketShowReducePriceWarn < ticketPrice) { ticketPrice = ticketPrice - cb.settings.ticketShowReducePriceWarn; ticketwarnmsg += '\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 '; if (ticketShowPriceFC > 1 && cb.settings.ticketShowReducePriceWarn < ticketShowPriceFC && cb.settings.ticketShowFreeFC != 'Yes') { ticketShowPriceFC = ticketShowPriceFC - cb.settings.ticketShowReducePriceWarn; if (ticketShowPriceFC <= ticketPrice) { ticketwarnmsg += '\n The CB Fanclub Ticket Price has been reduced to ' + ticketShowPriceFC + ' tokens'; } } if (ticketShowPriceEFC > 1 && cb.settings.ticketShowReducePriceWarn < ticketShowPriceEFC && cb.settings.ticketShowFreeEFC != 'Yes') { ticketShowPriceEFC = ticketShowPriceEFC - cb.settings.ticketShowReducePriceWarn; if (ticketShowPriceEFC <= ticketPrice) { ticketwarnmsg += '\n The ' + EFCname + ' Ticket Price has been reduced to ' + ticketShowPriceEFC + ' tokens'; } } if (ticketShowPriceEFC2 > 1 && cb.settings.ticketShowReducePriceWarn < ticketShowPriceEFC2 && cb.settings.ticketShowFreeEFC2 != 'Yes') { ticketShowPriceEFC2 = ticketShowPriceEFC2 - cb.settings.ticketShowReducePriceWarn; if (ticketShowPriceEFC2 <= ticketPrice) { ticketwarnmsg += '\n The ' + EFCname2 + ' Ticket Price has been reduced to ' + ticketShowPriceEFC2 + ' tokens'; } } if (ticketShowPriceVIP > 1 && cb.settings.ticketShowReducePriceWarn < ticketShowPriceVIP && cb.settings.ticketShowFreeVIP != 'Yes') { ticketShowPriceVIP = ticketShowPriceVIP - cb.settings.ticketShowReducePriceWarn; if (ticketShowPriceVIP <= ticketPrice) { ticketwarnmsg += '\n The ' + VIPname + ' Ticket Price has been reduced to ' + ticketShowPriceVIP + ' tokens'; } } } else { ticketwarnmsg += '\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 '; } } ticketwarnmsg += borderTicketBottom; cb.sendNotice(ticketwarnmsg,'',ticketBgColor,ticketNoticesTextColor,boldTextLiteral); 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,boldTextLiteral); } 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,boldTextLiteral); } showStage = 'showfinale'; changeRoomSubject(); ticketSalesEnded = true; cb.drawPanel(); } 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,boldTextLiteral); cb.limitCam_stop(); hiddenTime = 0; ticketShowEnded = true; ticketSalesEnded = true; showStage = 'aftershow'; savedTicketSubjectText = ticketSubjectText; ticketSubjectText = cb.settings.ticketShowAfterNotice; changeRoomSubject(); cb.drawPanel(); if (ticketShowOtToggle) { if (otChangesArray.name.length > 0) { var outstringot = ''; for (var i = 0; i < otChangesArray.name.length; i++) { if (otChangesArray.name[i] == null) { break } else { outstringot += (i > 0 ? ',' : '') + otChangesArray.name[i] + '(' + otChangesArray.type[i] + ')'; } } cb.sendNotice('Listing of Changes to the Outstanding Ticket List from this show : \n' + outstringot + '\nEnd of List', BC, appNoticeColor); cb.sendNotice('Listing of Changes to the Outstanding Ticket List from this show : \n' + outstringot + '\nEnd of List', '', appNoticeColor, '', '', 'red'); } else { cb.sendNotice('No entries have been added to the Outstanding Ticket Changes list from this show.', BC, appNoticeColor); } } if (countTicketsFromPreview > 0) { cb.sendNotice('Good news! ' + countTicketsFromPreview + ' viewers bought tickets from watching the free preview period.', BC, appNoticeColor); } } function saveTicket(user) { addRmvOutstandingTicket('add',user); cb.limitCam_removeUsers([user]); removeTicketHolder(user); removeViewer(user); cb.drawPanel(); } 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); } } } // *********************************** Ticket Show Pre-sales ************************************** function setPresalesToggle(setpsmode, setpsuser, setpsprice) { var setpsname = setpsuser; if (setpsuser == BC) { setpsname = bcText; } if (setpsmode == 'on') { if (presalesToggle) { cb.sendNotice('The Pre-Sales Ticket feature is already enabled.',setpsuser,appNoticeColor); } else { presalePrice = setpsprice; presalesToggle = true; showStage = 'presales'; cb.sendNotice('***Warning Regarding Presales***: Once tickets have been sold, do not deactivate or restart the UltraApp without saving the pre-sale ticket list. If it is necessary to restart, please save the pre-sale list first (use /tickets to view and copy) and then add those users back to the presale (/addpresale) or the actual ticket show (/add).', BC, appNoticeColor); presaleSkipNotice = false; setPresaleMode('init',setpsuser); countPresaleSold = 0; presaleIncrements = 0; stopIncrement = false; loadFreeTicketsPresale(); cb.setTimeout(presaleNoticeTimer, presaleNoticeInt); } } else if (setpsmode == 'off') { if (!presalesToggle) { cb.sendNotice('The Pre-Sales Ticket feature is already disabled.', setpsuser, appNoticeColor); } else { presalesToggle = false; stopPresaleTimer(setpsuser); presaleSkipNotice = true; cb.sendNotice(setpsname + ' has disabled the Pre-sales Feature, you can no longer buy Pre-sale tickets.', '', appNoticeColor, '', boldTextLiteral); } } } function loadFreeTicketsPresale() { if (cb.settings.ticketShowFreeMods == 'Yes') { for (let i = 0; i < modsInShow.length; i++) { if (!cb.limitCam_userHasAccess(modsInShow[i])) { addRmvPresale('add',modsInShow[i],'mod',modsInShow[i]); } } } if (cb.settings.ticketShowFreeFC == 'Yes') { for (let i = 0; i < fanClubInShow.length; i++) { if (!cb.limitCam_userHasAccess(fanClubInShow[i])) { addRmvPresale('add',fanClubInShow[i],'fan',fanClubInShow[i]); } } } if (cb.settings.ticketShowFreeEFC == 'Yes') { for (let i = 0; i < extFansInShow.length; i++) { if (!cb.limitCam_userHasAccess(extFansInShow[i])) { addRmvPresale('add',extFansInShow[i],'extfan',extFansInShow[i]); } } } if (cb.settings.ticketShowFreeEFC2 == 'Yes') { for (let i = 0; i < extFansInShow2.length; i++) { if (!cb.limitCam_userHasAccess(extFansInShow2[i])) { addRmvPresale('add',extFansInShow2[i],'extfan2',extFansInShow2[i]); } } } if (cb.settings.ticketShowFreeVIP == 'Yes') { for (let i = 0; i < VIPsInShow.length; i++) { if (!cb.limitCam_userHasAccess(VIPsInShow[i])) { addRmvPresale('add',VIPsInShow[i],'vip',VIPsInShow[i]); } } } } function setPresaleMode(mode,sendto) { var newpresalemode = ''; if (mode === 'init') { if (cb.settings.ticketShowPresalesMode === 'Yes, Mode 1: Increment Manually') { newpresalemode = 'manual'; } else if (cb.settings.ticketShowPresalesMode === 'Yes, Mode 2: Increment on X Tickets Sold') { newpresalemode = 'count'; } else if (cb.settings.ticketShowPresalesMode === 'Yes, Mode 3: Increment on Automatic Timer') { newpresalemode = 'timer'; } else { newpresalemode = 'initmanual'; } } else { countPresaleSold = 0; presaleIncrements = 0; stopIncrement = false; newpresalemode = mode; } if (presaleMode == 'timer' && newpresalemode != 'timer') { stopPresaleTimer(sendto); } if (newpresalemode == 'manual') { presaleMode = 'manual'; cb.sendNotice('Pre-sales is set to manual mode. \nThe broadcaster or a moderator can change the presale price on demand using the command "/presaleprice [newprice]".', sendto, appNoticeColor); } else if (newpresalemode == 'timer') { if (presaleIncAmt >= 1 && presaleTimeAmt >= 1) { presaleMode = 'timer'; presaleAutoTimer(presaleTimeAmt); cb.sendNotice('Pre-sales is set to Mode 3: Automatic timer mode. \nTimer cycle is set to ' + presaleTimeAmt + ' minutes, and price will increase ' + presaleIncAmt + ' tokens each time, with a maximum of ' + cb.settings.ticketShowPresaleMaxIncrements + ' increases.', sendto, appNoticeColor); } else { presaleMode = 'manual'; cb.sendNotice('Pre-sales is set to manual mode. \nMode was requested for automatic timer, but either the price increment or number of minutes per increment is not set. \nYou can restart the bot and update the missing values, update the pre-sale price manually without a timer, or manually start a single increment timer.', sendto, appNoticeColor); } } else if (newpresalemode == 'count') { if (presaleIncAmt >= 1 && presaleCountAmt >= 1) { presaleMode = 'count'; ticketsLeft = presaleCountAmt; cb.sendNotice('Pre-sales is set to Mode 2: Increment by tickets sold. \nTicket count cycle is set to ' + presaleCountAmt + ' tickets sold, \nand price will increase ' + presaleIncAmt + ' tokens each time, \nwith a maximum of ' + cb.settings.ticketShowPresaleMaxIncrements + ' increases.', sendto, appNoticeColor); cb.sendNotice('Pre-sale tickets are limited. \nThe pre-sale price will increase from ' + presalePrice + ' to ' + (presalePrice + presaleIncAmt) + ' tokens once ' + presaleCountAmt + ' tickets are sold. \nBuy now before the price goes up!', '', ticketBgColor, ticketTextColor, boldTextLiteral); } else { presaleMode = 'manual'; cb.sendNotice('Pre-sales is set to manual mode. Mode was requested for increment by tickets sold, but either the price increment or number of tickets sold per increment is not set. You can restart the bot and update the missing values, update the pre-sale price manually with a count, or manually start a single increment timer.', sendto, appNoticeColor); } } else if (newpresalemode == 'initmanual') { presaleMode = 'manual'; cb.sendNotice('Pre-sales is set to manual mode since pre-sale was turned off on the start page. You can change the mode using the command "/chgpresalemode [mode]", where [mode] is "manual", "count", or "timer".', sendto, appNoticeColor); } } function presalePriceChange(price,mod) { cb.sendNotice('\u21D2 . . . . Ticket Show Pre-Sales Price Updated!!!. . . . . \u21D0 \n \u21D2 . . . . . Pre-sale Tickets are now ' + price + ' tokens!!! . . . . . \u21D0' , '', ticketBgColor, ticketTextColor, 'bold'); presalePrice = price; } function presaleNoticeTimer() { if (presalesToggle && enablePresales) { var presalenottimemsg = 'Ticket Show Pre-sales are active. \nYou can buy advance tickets to the show now at a price of ' + presalePrice + ' tokens.'; if (presaleMode == 'manual') { presalenottimemsg += '\nThe planned ticket price for the show is ' + ticketPrice + ' tokens.' } else if (presaleMode == 'timer') { if (stopIncrement == true || (presalePrice + presaleIncAmt) > ticketPrice || presaleIncrements > cb.settings.ticketShowPresaleMaxIncrements) { presalenottimemsg += '\The planned ticket price for the show is ' + ticketPrice + ' tokens.'; } else { presalenottimemsg += '\nPre-sale tickets are being sold at this price for a limited period of time.'; presalenottimemsg += '\nThe pre-sale price will increase from ' + presalePrice + ' to ' + (presalePrice + presaleIncAmt) + ' tokens once the timer runs out.'; presalenottimemsg += '\nBuy now before the price goes up!'; } } else if (presaleMode == 'count') { if (stopIncrement == true || (presalePrice + presaleIncAmt) > ticketPrice || (presaleIncrements + 1) > cb.settings.ticketShowPresaleMaxIncrements) { presalenottimemsg += '\The planned ticket price for the show is ' + ticketPrice + ' tokens.'; } else { presalenottimemsg += '\nPre-sale tickets are limited.'; presalenottimemsg += '\nThe pre-sale price will increase from ' + presalePrice + ' to ' + (presalePrice + presaleIncAmt) + ' tokens once ' + ticketsLeft + ' more ticket' + (ticketsLeft > 1 ? "s are" : " is") + ' sold.'; presalenottimemsg += '\nBuy now before the price goes up!'; } } cb.sendNotice(borderPresaleTop + '\n' + presalenottimemsg + borderPresaleBottom, '', ticketBgColor, ticketTextColor, boldTextLiteral); if (!presaleSkipNotice) { cb.setTimeout(presaleNoticeTimer, presaleNoticeInt); } else { presaleSkipNotice = false; } } } function presaleAutoTimer(addtime) { cb.sendNotice('An automatic timer was started, pre-sale ticket prices will increase from ' + presalePrice + ' to ' + (presalePrice + presaleIncAmt) + ' tokens in ' + addtime + ' minutes. Buy now before the price goes up! Ticket show price will be ' + ticketPrice + ' tokens.', '', ticketBgColor, ticketTextColor, boldTextLiteral); if (presaleMode != 'timer') { presaleMode = 'timer'; } presaleMinsRemain = addtime; presaleSecsRemain = 0; presaleStartTime = new Date(); presaleStopTime = new Date(presaleStartTime.getTime() + presaleMinsRemain * 60000); presaleTimerStopping = false; presaleTimerMin(); } function presaleTimerMin() { if (!presaleTimerStopping && presaleMode == 'timer') { switch (presaleMinsRemain) { case 120: case 90: 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: cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + presaleMinsRemain + ' minutes left on the timer to buy a pre-sale ticket for ' + presalePrice + ' tokens! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', boldTextLiteral); break; case 1: cb.sendNotice('\u23f1 \u23f1 \u23f1 There is 1 minute left to buy a presale ticket for ' + presalePrice + ' tokens before the show starts! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', boldTextLiteral); break; } presaleMinsRemain--; if (presaleMinsRemain > 0) { presaleDisplaySeconds = false; } else { presaleDisplaySeconds = true; } presaleSecsRemain = 60; presaleTimerSec(); } } function presaleTimerSec() { if (!presaleTimerStopping && presaleMode == 'timer') { if (presaleDisplaySeconds) { switch (presaleSecsRemain) { case 30: case 10: case 5: case 4: case 3: case 2: case 1: cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + presaleSecsRemain + ' second' + (presaleSecsRemain > 1 ? "s" : "") + ' left on the timer to buy a pre-sale ticket for ' + presalePrice + ' tokens! \u23f1 \u23f1 \u23f1', "", appErrorColor, "", "bold"); } } if (presaleSecsRemain < 1) { if (presaleMinsRemain >= 1) { presaleTimerMin(); } else { cb.sendNotice("\u23f0 \u23f0 \u23f0 Time is up! Pre-sale Price is changing! \u23f0 \u23f0 \u23f0", '', appNoticeColor, '', "bold"); nextPresalePrice = presalePrice + presaleIncAmt; presalePriceChange(nextPresalePrice,BC); presalePrice = nextPresalePrice; presaleIncrements ++; if (presalePrice + presaleIncAmt > ticketPrice) { stopIncrement = true; cb.sendNotice('The automated timer has ended. \nPre-sale tickets will remain at ' + presalePrice + ' tokens until start of show unless manually updated by ' + bcText + '.','', ticketBgColor, ticketTextColor, boldTextLiteral); cb.sendNotice('No additional timer started, next increment would have exceeded planned ticket show price of ' + ticketPrice + '.', BC, appNoticeColor); } else if ((presaleIncrements + 1) > cb.settings.ticketShowPresaleMaxIncrements) { stopIncrement = true; cb.sendNotice('The automated timer has ended. \nPre-sale tickets will remain at ' + presalePrice + ' tokens until start of show unless manually updated by ' + bcText + '.','', ticketBgColor, ticketTextColor, boldTextLiteral); cb.sendNotice('No additional timer started, next increment would have exceeded max increments of ' + cb.settings.ticketShowPresaleMaxIncrements + '.', BC, appNoticeColor); } else { presaleAutoTimer(presaleTimeAmt); } } } else { presaleSecsRemain--; cb.setTimeout(presaleTimerSec, 1000); } } } function presaleAddTime(presaletimetoadd, psatuser) { presaleStopTime = new Date(presaleStopTime.getTime() + presaletimetoadd * 60000); var prstimetoaddabs = Math.abs(presaletimetoadd); if (presaletimetoadd > 0) { cb.sendNotice(psatuser + ' has added ' + presaletimetoadd + ' minute' + (presaletimetoadd == 1 ? '' : 's') + ' to the presale timer.',BC,'','', 'bold'); cb.sendNotice(presaletimetoadd + ' minute' + (presaletimetoadd == 1 ? ' has' : 's have') + ' been added to the timer. Now ' + presaleTimeLeft(),'',appNoticeColor,'', 'bold'); } else { cb.sendNotice(psatuser + ' has removed ' + prstimetoaddabs + ' minute' + (prstimetoaddabs == 1 ? '' : 's') + ' from the presale timer.',BC,'','', 'bold'); cb.sendNotice(prstimetoaddabs + ' minute' + (prstimetoaddabs == 1 ? ' has' : 's have') + ' been removed from the timer. Now ' + presaleTimeLeft(),'',appNoticeColor,'', 'bold'); } presaleMinsRemain = presaleMinsRemain + presaletimetoadd; if (presaleMinsRemain > 0) { presaleDisplaySeconds = false; } else { presaleDisplaySeconds = true; } return; } function presaleTimeCal() { presaleStartTime = new Date(); return presaleStopTime - presaleStartTime.getTime(); } function stopPresaleTimer(psstoppedby) { if (presaleSecsRemain > 0 || presaleMinsRemain > 0) { presaleStopTime = new Date(); presaleMinsRemain = 0; presaleSecsRemain = 0; presaleTimerStopping = true; if (psstoppedby) { cb.sendNotice(psstoppedby + ' has stopped the ticket show pre-sales timer.', '', appNoticeColor, '', 'bold'); } } } function presaleTimeLeft(user) { var presaleTimeLeft = presaleTimeCal(); var presaleMS = presaleTimeLeft % 1000; var presaleSeconds = ((presaleTimeLeft - presaleMS) % 60000); var presaleMinutes = ((presaleTimeLeft - presaleSeconds - presaleMS) % 3600000); var presaleHours = (presaleTimeLeft - presaleMinutes - presaleSeconds - presaleMS); presaleSeconds = presaleSeconds / 1000; presaleMinutes = presaleMinutes / 60000; presaleHours = presaleHours / 3600000; if (presaleHours > 0) { return presaleHours + ' hour' + (presaleHours > 1 ? 's' : '') + ' and ' + presaleMinutes + ' minute' + (presaleMinutes > 1 ? 's' : '') + ' remaining to vote.'; } else if (presaleMinutes > 0 && presaleSeconds === 0) { return presaleMinutes + ' minute' + (presaleMinutes > 1 ? 's' : '') + ' remaining on the timer.'; } else if (presaleMinutes > 0) { return presaleMinutes + ' minute' + (presaleMinutes > 1 ? 's' : '') + ' and ' + presaleSeconds + ' second' + (presaleSeconds > 1 ? 's' : '') + ' remaining on the timer.'; } else { return presaleSeconds + ' second' + (presaleSeconds > 1 ? 's' : '') + ' remaining on the timer.'; } } function addRmvPresale(addrmvprsmode,addrmvprsuser,addrmvprsusertype,addrmvprsdisp) { switch(addrmvprsmode) { case 'add': { cb.limitCam_addUsers([addrmvprsuser]); addViewer(addrmvprsuser); addTicketHolder(addrmvprsuser); if (addrmvprsusertype == 'fan') { cb.sendNotice('CB Fan Club member ' + addrmvprsdisp + ' has been added to the pre-sale ticket list.', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvprsusertype == 'extfan') { cb.sendNotice(EFCname + ' member ' + addrmvprsdisp + ' has been added to the pre-sale ticket list.', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvprsusertype == 'extfan2') { cb.sendNotice(EFCname2 + ' member ' + addrmvprsdisp + ' has been added to the pre-sale ticket list.', '', ticketBgColorFan, ticketTextColorFan, 'bold'); } else if (addrmvprsusertype == 'vip') { cb.sendNotice(VIPname + ' member ' + addrmvprsdisp + ' has been added to the pre-sale ticket list.', '', ticketBgColorVIP, ticketTextColorVIP, 'bold'); } else if (addrmvprsusertype == 'mod') { cb.sendNotice('Moderator ' + addrmvprsdisp + ' has been added to the pre-sale ticket list.', '', ticketBgColorMod, ticketTextColorMod, 'bold'); } else { cb.sendNotice(addrmvprsdisp + ' has been added to the pre-sale ticket list.', '', ticketHolderBgColor, '', 'bold'); } countPresaleSold ++; countPresaleTickets ++; if (presaleMode == 'count' && stopIncrement === false) { checkPresaleCount(); } break; } case 'rmv': { cb.limitCam_removeUsers([addrmvprsuser]); removeTicketHolder(addrmvprsuser); removeViewer(addrmvprsuser); if (countPresaleTickets > 0) { countPresaleTickets --; } break; } case 'addtip': { cb.limitCam_addUsers([addrmvprsuser]); addViewer(addrmvprsuser); addTicketHolder(addrmvprsuser); cb.sendNotice('Welcome ' + addrmvprsdisp + ', you have been added to the pre-sale ticket list.', '', ticketHolderBgColor, '', 'bold'); countPresaleSold ++; countPresaleTickets ++; if (presaleMode == 'count' && stopIncrement === false) { checkPresaleCount(); } break; } } } function checkPresaleCount() { ticketsLeft = (presaleCountAmt - countPresaleSold) if (ticketsLeft > 0) { cb.sendNotice('There ' + (ticketsLeft > 1 ? 'are ' : 'is ') + (ticketsLeft) + ' pre-sale ticket' + (ticketsLeft > 1 ? 's' : '') + ' remaining at a price of ' + presalePrice + ' tokens!','', ticketBgColor, ticketTextColor, 'bold'); } else { if (presalePrice + presaleIncAmt > ticketPrice) { stopIncrement = true; cb.sendNotice('Pre-sale tickets will remain at ' + presalePrice + ' tokens until start of show','', ticketBgColor, ticketTextColor, 'bold'); cb.sendNotice('No additional price changes made, next increment would have exceeded planned ticket show price of ' + ticketPrice + '.',BC, appNoticeColor); } else if (presaleIncrements + 1 > cb.settings.ticketShowPresaleMaxIncrements) { stopIncrement = true; cb.sendNotice('Pre-sale tickets will remain at ' + presalePrice + ' tokens until start of show','', ticketBgColor, ticketTextColor, 'bold'); cb.sendNotice('No additional price changes made, next increment would have exceeded max increments of ' + cb.settings.ticketShowPresaleMaxIncrements + '.',BC, appNoticeColor); } else { cb.sendNotice('All pre-sale tickets at a price of ' + presalePrice + ' tokens have been sold!','', ticketBgColor, ticketTextColor, 'bold'); presaleIncrements ++; nextPresalePrice = presalePrice + presaleIncAmt; presalePriceChange(nextPresalePrice,BC); countPresaleSold = 0; ticketsLeft = presaleCountAmt; } } } // *********************************** Goal Race ************************************** function setGoalRaceToggle(setgroption, setgrsetby) { var setgrname = setgrsetby; if (setgrsetby == BC) { setgrname = bcText; } if (setgroption == 'on') { whichApp = 'goalrace'; if (savedGoalRace) { restoreGoalRace(); } else { 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, boldTextLiteral); } else if (setgroption == 'off') { saveGoalRace(); cb.sendNotice(borderAllNotices + '\n' + setgrname + ' has suspended the Goal Race feature.\n' + borderAllNotices, '', goalraceBgColor, goalraceTextColor, boldTextLiteral); } } function initGoalRace() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoalCountAmt = 0; currentGoal = 1; currentGoalLevel = 1; currentGroupTipAmt = 0; fontSize = 12; 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 { 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, boldTextLiteral); } else if (setspkoption == 'off') { saveSpankGoals(); cb.sendNotice(borderAllNotices + '\n' + setspkname + ' has suspended the Spank-a-thon Feature.\n' + borderAllNotices, '', spankBgColor, spankTextColor, boldTextLiteral); } } function initSpanks() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoalCountAmt = 0; currentGoal = 1; 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 = spankGoalArray.desc[currentGoal-1]; currentGoalTotal = spankGoalArray.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,boldTextLiteral); } 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, boldTextLiteral); 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, boldTextLiteral); } 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; } // *********************************** Peep Show Functions ************************************** function setPeepShowToggle(setpeepoption, setpeepby) { var setpeepname = setpeepby; if (setpeepby == BC) { setpeepname = bcText; } if (setpeepoption == 'on') { initPeepShow(setpeepby); cb.sendNotice(borderAllNotices + '\n' + bcText + ' has started selling time for the Peep Show.\nYou can buy time in the show at a rate of ' + peepshowPrice + ' tokens per minute.\nYou must tip at least ' + minimumPeepShowTip + ' tokens to buy time!!!\nTo see how much time you have remaining, type: /mytime \n' + peepshowModeMessage + '\n' + borderAllNotices, '', peepBgColor, peepTextColor, boldTextLiteral); } else if (setpeepoption == 'off') { if (cb.limitCam_isRunning()) { cb.limitCam_stop(); cb.sendNotice('The Peep Show hidden cam feature has ended.', setpeepby, appNoticeColor); hiddenTime = 0; peepshowEnded = true; showStage = ''; } if (peepshowStartMinsRemain > 0 || peepshowStartSecsRemain > 0) { stopPeepShowStartTimer(); } cb.sendNotice('You have disabled the Peep Show feature, everyone\'s remaining time is left intact.', setpeepby, appNoticeColor); cb.sendNotice(borderAllNotices + '\n' + setpeepname + ' has ended the Peep Show Feature.\n' + borderAllNotices, '', peepBgColor, peepTextColor, boldTextLiteral); } } function initPeepShow(sendto) { if (presalesToggle) { setPresalesToggle('off', sendto, 0); } whichApp = 'peep'; minimumPeepShowTip = Math.floor(peepshowPrice/6); if (minimumPeepShowTip < 1) { minimumPeepShowTip = 1; } monitorMinsRem = peepshowLengthOfShow; monitorMins = 0; monitorSecs = 0; showStage = 'peepsales'; changeRoomSubject(); setPeepStartMode('init',sendto); peepshowEnded = false; peepshowSalesEnded = false; peepshowLoadFree(); cb.drawPanel(); } function peepshowColors(ppscby) { if (cb.settings.peepshowTextColor == 'Custom') { peepTextColor = setTextColor(cb.settings.peepshowCustomTextColor,'Peep Show',ppscby); } else { peepTextColor = setTextColor(cb.settings.peepshowTextColor,'Peep Show',ppscby); } if (cb.settings.peepshowBgColor == 'Custom') { peepBgColor = setBgColor(cb.settings.peepshowCustomBgColor,'Peep Show',ppscby); } else { peepBgColor = setBgColor(cb.settings.peepshowBgColor,'Peep Show',ppscby); } } function setPeepShowPrice(amount,announce) { peepshowPrice = parseInt(amount); minimumPeepShowTip = Math.floor(peepshowPrice/6); if (minimumPeepShowTip < 1) { minimumPeepShowTip = 1; } if (announce === 'yes') { cb.sendNotice( '\u21D2 . . . . Peep Show Price Updated!!!. . . . . \u21D0 \n' + '\u21D2 . . . . Price is now ' + peepshowPrice + ' tokens per min!!! . . . . . \u21D0 \n' + '\u21D2 . . . . You must tip at least ' + minimumPeepShowTip + ' tokens to buy time!!! . . . . . \u21D0' , '', peepNoticesBgColor, peepNoticesTxtColor, 'bold'); changeRoomSubject(); cb.drawPanel(); } } function setPeepShowLength(length,announce) { peepshowLengthOfShow = parseInt(length); monitorMinsRem = peepshowLengthOfShow - monitorMins; if (whichApp == 'peep' && showStage != 'peepsales') { peepShowEndTime = peepshowStartHiddenTime + (peepshowLengthOfShow * 60000); } if (announce === 'yes') { cb.sendNotice( '\u21D2 . . . . Peep Show Length Updated!!!. . . . . \u21D0 \n' + '\u21D2 . . . . Length of Show is now ' + peepshowLengthOfShow + ' minutes!!! . . . . . \u21D0' , '', peepNoticesBgColor, peepNoticesTxtColor, 'bold'); changeRoomSubject(); cb.drawPanel(); } } function peepshowLoadFree() { if (cb.settings.peepshowFreeMods == 'Yes') { for (let freeindex = 0; freeindex < modsInShow.length; freeindex++) { if (!cb.limitCam_userHasAccess(modsInShow[freeindex])) { peepshowAddRmvTime('addfree',modsInShow[freeindex],'mod'); } } } if (cb.settings.peepshowFreeFC == 'Yes') { for (let freeindex = 0; freeindex < fanClubInShow.length; freeindex++) { if (!cb.limitCam_userHasAccess(fanClubInShow[freeindex])) { peepshowAddRmvTime('addfree',fanClubInShow[freeindex],'fan'); } } } if (cb.settings.peepshowFreeEFC == 'Yes') { for (let freeindex = 0; freeindex < extFansInShow.length; freeindex++) { if (!cb.limitCam_userHasAccess(extFansInShow[freeindex])) { peepshowAddRmvTime('addfree',extFansInShow[freeindex],'extfan'); } } } if (cb.settings.peepshowFreeEFC2 == 'Yes') { for (let freeindex = 0; freeindex < extFansInShow2.length; freeindex++) { if (!cb.limitCam_userHasAccess(extFansInShow2[freeindex])) { peepshowAddRmvTime('addfree',extFansInShow2[freeindex],'extfan2'); } } } if (cb.settings.peepshowFreeVIP == 'Yes') { for (let freeindex = 0; freeindex < VIPsInShow.length; freeindex++) { if (!cb.limitCam_userHasAccess(VIPsInShow[freeindex])) { peepshowAddRmvTime('addfree',VIPsInShow[freeindex],'vip'); } } } } function checkFreePeepShow(user,checkfreepeepmod,checkfreepeepfan,checkfreepeepextfan,checkfreepeepextfan2,checkfreepeepvip) { if (checkfreepeepmod) { if (cb.settings.peepshowFreeMods == 'Yes') { peepshowAddRmvTime('addfree',user,'mod',0); cb.sendNotice(borderAllNotices + '\nYou\'ve been added to the Peep Show because you\'re a moderator! \n' + borderAllNotices, user, peepBgColor, peepTextColor, 'bold'); } cb.sendNotice(borderAllNotices + '\nModerators: The Peep Show feature is enabled. \nType "/uahelp peepshow" to see details on available commands for this feature. \n' + borderAllNotices, user, peepBgColor, peepTextColor, 'bold'); } else if (checkfreepeepfan) { if (cb.settings.peepshowFreeFC == 'Yes') { peepshowAddRmvTime('addfree',user,'fan',0); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the Peep Show because you\'re in the CB Fan Club! \n' + borderAllNotices, user, peepBgColor, peepTextColor, 'bold'); } } else if (checkfreepeepextfan) { if (cb.settings.peepshowFreeEFC == 'Yes') { peepshowAddRmvTime('addfree',user,'extfan',0); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the Peep Show because you\'re in the ' + EFCname + '! \n' + borderAllNotices, user, peepBgColor, peepTextColor, 'bold'); } } else if (checkfreepeepextfan2) { if (cb.settings.peepshowFreeEFC2 == 'Yes') { peepshowAddRmvTime('addfree',user,'extfan2',0); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the Peep Show because you\'re in the ' + EFCname2 + '! \n' + borderAllNotices, user, peepBgColor, peepTextColor, 'bold'); } } else if (checkfreepeepvip) { if (cb.settings.peepshowFreeVIP == 'Yes') { peepshowAddRmvTime('addfree',user,'vip',0); cb.sendNotice(borderAllNotices + '\nYou\'ve automatically been added to the Peep Show because you\'re in the ' + VIPname + '! \n' + borderAllNotices, user, peepBgColor, peepTextColor, 'bold'); } } } function setPeepStartMode(startmode,sendto) { var peepshowtimeamt = cb.settings.peepshowStartTimer; if (cb.settings.peepshowStartAuto == 'Manual Start from Command') { peepshowModeAuto = 'bc'; peepshowStartMode = 'manual'; } else { peepshowModeAuto = 'auto'; } if (peepshowModeAuto == 'auto') { if (cb.settings.peepshowStartTimer >= 1) { peepshowStartMode = 'timer'; if (peepshowModeAuto == 'auto') { peepshowStartAutoTimer(peepshowtimeamt); } } else { peepshowStartMode = 'manual'; cb.sendNotice('Peep 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 start the show manually at the end of a timer or at a time of your choosing.', sendto, appNoticeColor); } } if (peepshowStartMode == 'manual') { peepshowModeMessage = 'Peep Show start mode is set to "Manual"\nThe show will be started by ' + bcText + ' or a moderator.'; } else if (peepshowStartMode == 'timer') { if (peepshowModeAuto == 'auto') { peepshowModeMessage = 'Peep Show start mode is set to "Automatic timer" with a duration of ' + peepshowtimeamt + ' minutes.\nThe Peep Show will start automatically when the timer runs out.'; } else if (peepshowModeAuto == 'bc') { peepshowModeMessage = 'Peep Show start mode is set to "Broadcaster managed timer". \n' + bcText + ' will set a timer and start the show when the timer runs out.'; } } } function startPeepShowStartTimer(stpeepnumtimer) { peepshowStartMinsRemain = stpeepnumtimer; peepshowStartMode = 'timer'; peepshowStartAutoTimer(peepshowStartMinsRemain); cb.drawPanel(); } function peepshowStartAutoTimer(addtime) { if (peepshowModeAuto == 'auto') { cb.sendNotice('An automatic timer was started, Peep Show price will be ' + peepshowPrice + ' tokens/min.', '', peepBgColor, peepTextColor, 'bold'); } else if (peepshowModeAuto == 'bc') { cb.sendNotice('A timer was started to provide an countdown to approximate showtime. \n ' + bcText + ' will start the show after the timer runs out.', '', peepBgColor, peepTextColor, 'bold'); } peepshowStartMinsRemain = addtime; peepshowStartSecsRemain = 0; peepshowStartStartTime = new Date(); peepshowStartStopTime = new Date(peepshowStartStartTime.getTime() + peepshowStartMinsRemain * 60000); peepshowTimerStopping = false; peepshowStartTimerMin(); } function peepshowStartTimerMin() { if (!peepshowTimerStopping && peepshowStartMode == 'timer' && whichApp == 'peep') { switch (peepshowStartMinsRemain) { case 120: case 90: 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: cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + peepshowStartMinsRemain + ' minutes left until the Peep Show starts! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', 'bold'); break; case 1: cb.sendNotice('\u23f1 \u23f1 \u23f1 There is 1 minute left until the Peep Show starts! \u23f1 \u23f1 \u23f1', '', appNoticeColor, '', 'bold'); break; } cb.drawPanel(); peepshowStartMinsRemain--; if (peepshowStartMinsRemain > 0) { peepDisplaySeconds = false; } else { peepDisplaySeconds = true; } peepshowStartSecsRemain = 60; peepshowStartTimerSec(); } } function peepshowStartTimerSec() { if (!peepshowTimerStopping && peepshowStartMode == 'timer' && whichApp == 'peep') { if (peepDisplaySeconds) { switch (peepshowStartSecsRemain) { case 30: case 10: case 5: case 4: case 3: case 2: cb.sendNotice('\u23f1 \u23f1 \u23f1 There are ' + peepshowStartSecsRemain + ' 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 (peepshowStartSecsRemain < 1) { if (peepshowStartMinsRemain >= 1) { peepshowStartTimerMin(); } else { if (peepshowStartMode = 'timer' && peepshowModeAuto === 'auto') { cb.sendNotice('\u23f0 \u23f0 \u23f0 Time is up! Peep Show is starting! \u23f0 \u23f0 \u23f0', '', peepNoticesBgColor, peepNoticesTxtColor, 'bold'); startPeepShow(bcText); } else { cb.sendNotice('\u23f0 \u23f0 \u23f0 Time is up! ' + bcText + ' will be starting the Peep Show! \u23f0 \u23f0 \u23f0', '', peepNoticesBgColor, peepNoticesTxtColor, 'bold'); } } } else { peepshowStartSecsRemain--; cb.setTimeout(peepshowStartTimerSec, 1000); } } } function peepshowStartAddTime(peeptimetoadd, pttauser) { peepshowStartStopTime = new Date(peepshowStartStopTime.getTime() + peeptimetoadd * 60000); cb.sendNotice(pttauser + ' has added ' + peeptimetoadd + ' minute' + (peeptimetoadd === 1 || peeptimetoadd === -1 ? '' : 's') + ' to the Peep Show start timer.',BC,'','', 'bold'); cb.sendNotice(peeptimetoadd + ' minute' + (peeptimetoadd === 1 || peeptimetoadd === -1 ? '' : 's') + ' have been added to the Peep Show start timer. Now ' + peepshowStartTimeLeft(),'',appNoticeColor,'', 'bold'); peepshowStartMinsRemain = peepshowStartMinsRemain + peeptimetoadd; if (peepshowStartMinsRemain > 0) { peepDisplaySeconds = false; } else { peepDisplaySeconds = true; } cb.drawPanel(); return; } function peepshowStartTimeCal() { peepshowStartStartTime = new Date(); return peepshowStartStopTime - peepshowStartStartTime.getTime(); } function stopPeepShowStartTimer(mod) { peepshowStartStopTime = new Date(); peepshowTimerStopping = true; peepshowStartMinsRemain = 0; peepshowStartSecsRemain = 0; if(mod != null && mod != '') { cb.sendNotice(mod + ' has stopped the Peep Show start timer.','',appNoticeColor,'', 'bold'); } cb.drawPanel(); } function peepshowStartTimeLeft(user) { var peepshowStartTimeLeft = peepshowStartTimeCal(); var peepMS = peepshowStartTimeLeft % 1000; var peepSeconds = ((peepshowStartTimeLeft - peepMS) % 60000); var peepMinutes = ((peepshowStartTimeLeft - peepSeconds - peepMS) % 3600000); var peepHours = (peepshowStartTimeLeft - peepMinutes - peepSeconds - peepMS); peepSeconds = peepSeconds / 1000; peepMinutes = peepMinutes / 60000; peepHours = peepHours / 3600000; if (peepHours > 0) { return peepHours + ' hour' + (peepHours > 1 ? 's' : '') + ' and ' + peepMinutes + ' minute' + (peepMinutes > 1 ? 's' : '') + ' remaining on the Peep Show start timer.'; } else if (peepMinutes > 0 && peepSeconds === 0) { return peepMinutes + ' minute' + (peepMinutes > 1 ? 's' : '') + ' remaining on the Peep Show start timer.'; } else if (peepMinutes > 0) { return peepMinutes + ' minute' + (peepMinutes > 1 ? 's' : '') + ' and ' + peepSeconds + ' second' + (peepSeconds > 1 ? 's' : '') + ' remaining on the Peep Show start timer.'; } else { return peepSeconds + ' second' + (peepSeconds > 1 ? 's' : '') + ' remaining on the Peep Show start timer.'; } } function peepshowStartTimeLeftPanel() { var peepshowStartTimeLeft = peepshowStartTimeCal(); var peepMS = peepshowStartTimeLeft % 1000; var peepSeconds = ((peepshowStartTimeLeft - peepMS) % 60000); var peepMinutes = ((peepshowStartTimeLeft - peepSeconds - peepMS) % 3600000); var peepHours = (peepshowStartTimeLeft - peepMinutes - peepSeconds - peepMS); peepSeconds = peepSeconds / 1000; peepMinutes = peepMinutes / 60000; if (peepMinutes > 0) { return 'Less than ' + (peepMinutes+1) + ' min rem'; } else { return 'Less than ' + (peepSeconds) + ' sec rem';; } } function peepshowAddRmvTime(mode,user,usertype,tipamt) { switch(mode) { case 'addtip': { var peeptimetoadd = peepshowCheckTimeAdd(tipamt); var peeptotaltime = peepshowAddTime(user,peeptimetoadd); if (cb.limitCam_isRunning()) { cb.sendNotice('Thank you ' + user + ', you have purchased ' + timeMinSecLiteral(peeptimetoadd) + ' of time in the show.', user, peepBgColor, peepTextColor, 'bold'); if (!cb.limitCam_userHasAccess(user)) { cb.limitCam_addUsers([user]); if (!cbjs.arrayContains(peepshowViewers,user)) { peepshowViewers.push(user); } } else { cb.sendNotice('You now have ' + timeMinSecLiteral(peeptotaltime) + ' remaining.', user, peepBgColor, peepTextColor, 'bold'); } } else { if (peeptotaltime > peeptimetoadd) { cb.sendNotice('Thank you ' + user + ', you have purchased ' + timeMinSecLiteral(peeptimetoadd) + ' of time in the show. You now have ' + timeMinSecLiteral(peeptotaltime) + ' that will be applied to the show when it starts.', user, peepBgColor, peepTextColor, 'bold'); } else { cb.sendNotice('Thank you ' + user + ', you have purchased ' + timeMinSecLiteral(peeptimetoadd) + ' of time in the show, which will be applied when the show starts.', user, peepBgColor, peepTextColor, 'bold'); } } cb.drawPanel(); break; } case 'addfree': { if (cbjs.arrayContains(peepshowPurchasers,user)) { var afindex = peepshowPurchasers.indexOf(user); peepshowPurchasers.splice(afindex,1); peepshowPurchasedTime.splice(afindex,1); } cb.limitCam_addUsers([user]); if (!cbjs.arrayContains(peepshowFreeViewers,user)) { peepshowFreeViewers.push(user); } if (!cbjs.arrayContains(peepshowViewers,user)) { peepshowViewers.push(user); } if (usertype == 'fan') { cb.sendNotice('CB Fan Club member ' + user + ' has been added to the Peep Show viewer list.', '', peepBgColorFan, peepTextColorFan, 'bold'); } else if (usertype == 'extfan') { cb.sendNotice(EFCname + ' member ' + user + ' has been added to the Peep Show viewer list.', '', peepBgColorFan, peepTextColorFan, 'bold'); } else if (usertype == 'extfan2') { cb.sendNotice(EFCname2 + ' member ' + user + ' has been added to the Peep Show viewer list.', '', peepBgColorFan, peepTextColorFan, 'bold'); } else if (usertype == 'vip') { cb.sendNotice(VIPname + ' member ' + user + ' has been added to the Peep Show viewer list.', '', peepBgColorVIP, peepTextColorVIP, 'bold'); } else if (usertype == 'mod') { cb.sendNotice('Moderator ' + user + ' has been added to the Peep Show viewer list.', '', peepBgColorMod, peepTextColorMod, 'bold'); } else { cb.sendNotice(user + ' has been added to the Peep Show viewer list.', '', peepBgColor, peepTextColor, 'bold'); } cb.drawPanel(); break; } case 'remove': { cb.limitCam_removeUsers([user]); cbjs.arrayRemove(peepshowViewers,user); cbjs.arrayRemove(peepshowFreeViewers,user); if (cbjs.arrayContains(peepshowPurchasers,user)) { var rmvppindex = peepshowPurchasers.indexOf(user); peepshowPurchasers.splice(rmvppindex,1); peepshowPurchasedTime.splice(rmvppindex,1); } cb.sendNotice(user + ' has been removed from the Peep Show.', '', peepBgColor, peepTextColor, 'bold'); cb.drawPanel(); break; } case 'expire': { cb.limitCam_removeUsers([user]); cbjs.arrayRemove(peepshowViewers,user); cb.sendNotice('Sorry ' + user + ', your time has expired, you can tip to continue watching the Peep Show.', user, peepBgColor, peepTextColor, 'bold'); cb.drawPanel(); break; } } } function timeMinSecLiteral(totaltimesec) { var pminutes = Math.floor(totaltimesec / 60); var pseconds = Math.floor(totaltimesec - (pminutes * 60)); var pliteral = ''; if (pminutes > 0) { pliteral = pminutes + (pminutes > 1 ? ' minutes' : ' minute'); if (pseconds > 0) { pliteral += ' and ' + pseconds + (pseconds > 1 ? ' seconds' : ' second'); } } else if (pseconds > 0) { pliteral = pseconds + (pseconds > 1 ? ' seconds' : ' second'); } else { pliteral = 'no time' } return pliteral; } function peepshowMyTimeLeft(user) { if (cbjs.arrayContains(peepshowPurchasers,user)) { var mtindex = peepshowPurchasers.indexOf(user); purchasedTimeRemaining = peepshowPurchasedTime[mtindex]; if (purchasedTimeRemaining > 0) { return timeMinSecLiteral(purchasedTimeRemaining); } else { return 'expired'; } } else { return 'none'; } } function peepshowCheckTimeAdd(tippedamountpeep) { if (tippedamountpeep >= minimumPeepShowTip) { return Math.floor((tippedamountpeep / peepshowPrice) * 60); } else { return 0; } } function peepshowAddTime(user,time) { if (cbjs.arrayContains(peepshowPurchasers,user)) { var psatindex = peepshowPurchasers.indexOf(user); peepshowPurchasedTime[psatindex] += time; } else { peepshowPurchasers.push(user); var psatindex2 = peepshowPurchasers.indexOf(user); peepshowPurchasedTime[psatindex2] = time; } return peepshowPurchasedTime[psatindex2]; } function peepshowAddPrepurchases() { for (var preindex = 0; preindex < peepshowPurchasers.length; preindex++) { if (peepshowPurchasedTime[preindex] > 0) { cb.limitCam_addUsers([peepshowPurchasers[preindex]]); if (!cbjs.arrayContains(peepshowViewers,peepshowPurchasers[preindex])) { peepshowViewers.push(peepshowPurchasers[preindex]); } if (peepshowPurchasedTime[preindex] < 10 ) { peepshowPurchasedTime[preindex] = 0; } else { peepshowPurchasedTime[preindex] -= 10; } } } } function peepshowMonitorTimer() { cb.setTimeout(peepShowMonitor, 10000); } function peepShowMonitor() { monitorSecs += 10; if (whichApp == 'peep' && (showStage == 'peepshow' || showStage == 'peepwarn' || showStage == 'peepfinale')) { peepshowCheckViewers(); monitorCalls++; if (monitorCalls >= 6) { monitorMins++; monitorMinsRem = peepshowLengthOfShow - monitorMins; monitorCalls = 0; monitorSecs = 0; var monitormessage = 'The Peep Show has been active for ' + monitorMins + ' minute' + (monitorMins > 1 ? 's.' : '.'); var peepshowlist = cb.limitCam_allUsersWithAccess().slice(); numberPeepViewers = peepshowlist.length; if (monitorMinsRem > 1) { monitormessage += '\nThere are approximately ' + monitorMinsRem + ' minutes remaining in the show.'; } else if (monitorMins > peepshowLengthOfShow) { monitormessage += '\nThe show has gone beyond the planned length of ' + peepshowLengthOfShow + ' minute' + (peepshowLengthOfShow > 1 ? 's.' : '.'); } if ((showStage == 'peepshow' || showStage == 'peepwarn') && (monitorMinsRem != 0 || peepshowAutoEnd != 'Yes')) { monitormessage += '\nYou can buy time in the show for ' + peepshowPrice + ' tokens/min.\nThe minimum tip amount to buy time is ' + minimumPeepShowTip + ' token' + (minimumPeepShowTip > 1 ? 's.' : '.'); } if (numberPeepViewers > 0) { monitormessage += '\nThere ' + (numberPeepViewers > 1 ? 'are ' : 'is ') + numberPeepViewers + ' viewer' + (numberPeepViewers > 1 ? 's' : '') + ' currently in the Peep Show!'; } cb.sendNotice(monitormessage, '', peepBgColor, peepNoticesTxtColor, boldTextLiteral); } if (monitorMinsRem == 1) { cb.sendNotice('The Peep Show is planned to end in approximately ' + (60 - monitorSecs) + ' seconds.', '', peepBgColor, peepNoticesTxtColor,boldTextLiteral); } else if (monitorMinsRem == 0) { if (peepshowAutoEnd == 'Yes') { cb.sendNotice('The Peep Show planned time is completed, the show is ending!', '', peepBgColor, peepNoticesTxtColor,boldTextLiteral); stopPeepShow(BC); } else { if (monitorCalls == 0) { cb.sendNotice('The Peep Show planned time is completed, ' + bcText + ' will end the show when finished.', '', peepBgColor, peepNoticesTxtColor,boldTextLiteral); } } } peepshowMonitorTimer(); } cb.drawPanel(); } function peepshowCheckViewers() { for (var chkv = 0; chkv < peepshowPurchasers.length; chkv++) { var pcvuser = peepshowPurchasers[chkv]; if (peepshowPurchasedTime[chkv] > 0) { if (peepshowPurchasedTime[chkv] > 20 && peepshowPurchasedTime[chkv] <= 30) { if (showStage == 'peepshow' || showStage == 'peepwarn') { cb.sendNotice(pcvuser + ': Warning! You have approximately 30 seconds left in your show purchase. \nYou can tip to add more time at the rate of ' + peepshowPrice + ' tokens/min.', pcvuser, peepBgColor, peepTextColor, boldTextLiteral); } else { cb.sendNotice(pcvuser + ': Warning! You have approximately 30 seconds left in your show purchase, and ' + bcText + ' has ended sales of additional time.', pcvuser, peepBgColor, peepTextColor, boldTextLiteral); } } if (peepshowPurchasedTime[chkv] < 10 ) { peepshowPurchasedTime[chkv] = 0 } else { peepshowPurchasedTime[chkv] -= 10; } } else { if (cb.limitCam_userHasAccess(pcvuser)) { peepshowAddRmvTime('expire',pcvuser,'',0); } } } } function startPeepShow(startedby) { cb.sendNotice(borderAllNotices + '\n :siren1 :siren1 :siren1 ' + startedby + ' has started the Peep Show! :siren1 :siren1 :siren1 \n' + borderAllNotices, '', peepBgColor, peepNoticesTxtColor, 'bold'); cb.limitCam_start('Ultra App Peep Show\n\nHidden Cam show in progress \nCheck room notices for instructions'); showStage = 'peepshow'; peepshowAddPrepurchases(); monitorCalls = 0; monitorMins = 0; peepshowMonitorTimer(); changeRoomSubject(); if (peepshowStartMinsRemain > 0 || peepshowStartSecsRemain > 0) { stopPeepShowStartTimer(); } peepshowStartHiddenTime = Date.now(); peepShowEndTime = peepshowStartHiddenTime + (peepshowLengthOfShow * 60000); cb.drawPanel(); } function restartPeepShow(startedby) { cb.sendNotice(borderAllNotices + '\n :siren1 \u25C8 \u25C8 \u25C8 ' + startedby + ' has restarted the Peep Show! \u25C8 \u25C8 \u25C8 :siren1 \n \u25C8 \u25C8 \u25C8 All current Peep Show time purchases are intact. \u25C8 \u25C8 \u25C8 \n' + borderAllNotices,'',peepBgColor, peepNoticesTxtColor, 'bold'); cb.limitCam_start('Ultra App Peep Show\n\nHidden Cam show in progress \nCheck room notices for instructions'); if (showStage == 'peepaftershow') { peepshowSubjectText = savedpeepshowSubjectText; } showStage = 'peepshow'; changeRoomSubject(); peepshowMonitorTimer(); peepshowEnded = false; peepshowSalesEnded = false; if (peepshowStartMinsRemain > 0 || peepshowStartSecsRemain > 0) { stopPeepShowStartTimer(); } peepshowStartHiddenTime = Date.now(); cb.drawPanel(); } function peepShowEnding(pswarnby) { showStage = 'peepwarn'; cb.sendNotice(borderAllNotices + '\n :siren1 \u25C8 \u25C8 \u25C8 ' + pswarnby + ' has indicated the show is nearly finished. \u25C8 \u25C8 \u25C8 :siren1 \n \u25C8 \u25C8 It is not recommended to buy additional time, but sales are still open. \u25C8 \u25C8 \n' + borderAllNotices,'',peepBgColor,peepNoticesTxtColor,'bold'); changeRoomSubject(); cb.drawPanel(); } function stopPeepShowSales(pssalesstoppedby) { cb.sendNotice(borderAllNotices + '\n :siren1 :siren1 :siren1 \u25B7 \u25B7 ' + pssalesstoppedby + ' has ended sales for the Peep Show. \u25C1 \u25C1 :siren1 :siren1 :siren1 \n' + borderAllNotices,'',peepBgColor,peepNoticesTxtColor,'bold'); showStage = 'peepfinale'; changeRoomSubject(); peepshowSalesEnded = true; cb.drawPanel(); setPeepNoticeTimeout(); } function stopPeepShow(psstoppedby) { cb.sendNotice(borderAllNotices + '\n :siren1 \u25C8 \u25C8 \u25C8 ' + psstoppedby + ' has ended the Peep Show \u25C8 \u25C8 \u25C8 :siren1 \n \u25C8 \u25C8 \u25C8 \u25C8 Show length ' + ((Date.now() - peepshowStartHiddenTime)/60000).toFixed(1) + ' minutes. \u25C8 \u25C8 \u25C8 \u25C8 \n' + borderAllNotices,'',peepBgColor,peepNoticesTxtColor,'bold'); cb.limitCam_stop(); cb.sendNotice(borderAllNotices + '\n :siren1 :siren1 :siren1 The broadcast is returning to Public Chat. :siren1 :siren1 :siren1 \n' + borderAllNotices, '',peepBgColor,peepNoticesTxtColor,'bold'); peepshowStartHiddenTime = 0; peepshowEnded = true; peepshowSalesEnded = true; showStage = 'peepaftershow'; savedpeepshowSubjectText = peepshowSubjectText; peepshowSubjectText = cb.settings.peepshowAfterNotice; changeRoomSubject(); cb.drawPanel(); } function setPeepNoticeTimeout() { cb.setTimeout(peepNoticeTimer, peepNoticeInt); } function peepNoticeTimer() { if (showStage == 'peepfinale') { cb.sendNotice(' :siren1 Peep Show Sales Ended! Do not tip to join the show! :siren1 ','',peepBgColor,peepNoticesTxtColor,boldTextLiteral); setPeepNoticeTimeout(); } } // *********************************** 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 { 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, boldTextLiteral); } else if (setaroption == 'off') { saveAutoresetGoals(); cb.sendNotice(borderAllNotices + '\n' + setarname + ' has suspended the Auto-Reset Goal Goal Feature.\n' + borderAllNotices, '', autoresetBgColor, autoresetTextColor, boldTextLiteral); } } function initAutoresetGoal() { finalGoalMet = false; currentGoalMet = false; currentGoalTips = 0; currentGoal = 1; 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.7 was released on March 19, 2022, 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://www.freesexcam.ca/apps/app_details/dorothys-ultraapp/ '; aboutmessage += '\n\n \u2705 Highlights of fixes and improvements for version 3.7:'; aboutmessage += '\n \u2022 ' + wordWrap('Added commands /ticketsales and /startsales to function same as /chgapp ticket'); aboutmessage += '\n \u2022 ' + wordWrap('Added command /startpeep to immediately start a peep show (can be used instead of two steps switch feature and start show immediately.'); aboutmessage += '\n \u2022 ' + wordWrap('Added Bold Text or Plain Text configuration for notices (setting 1Z2)'); aboutmessage += '\n \u2022 ' + wordWrap('Fixed issue with setting notice intervals to 0.'); aboutmessage += '\n \u2022 ' + wordWrap('Added /uadspint command to view all current notice intervals'); 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 or peep shows. 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 Presales =='; helptxtmsg += '\n \u2022 /presalelist: Display the list of users on the pre-sale list.'; 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('/previewers: Display the list of users that are currently viewing a free preview. If the parameter of "alpha" is added, the list is displayed alphabetically.'); helptxtmsg += '\n \u2022 ' + wordWrap('/otlist: Display the list of outstanding ticket holders, can be used by anyone if the Outstanding Ticket feature is enabled.'); helptxtmsg += '\n \u2022 ' + wordWrap('/saveticket: If ' + bcText + ' has enabled Outstanding Tickets (and is tracking them) - If you have bought a ticket and will not be able to stay for the show, you can save it for a future show. You will no longer be able to see the current show. IMPORTANT: If in the same session, the ticket will be available automatically. However for future shows or if ' + bcText + ' restarts the bot, they must add the saved tickets to the outstanding ticket list to be able to use them with /useticket.'); helptxtmsg += '\n \u2022 ' + wordWrap('/useticket: If ' + bcText + ' has enabled Outstanding Tickets (and is tracking them) - Redeem an outstanding ticket and use it for access to this show. You can use the command "/otlist" to view the list of outstanding ticket holders if ' + bcText + ' has enabled this feature.'); 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.'); helptxtmsg += '\n \u2022 ' + wordWrap('/giftticket username: If the "gifting" feature is enabled, when you tip enough to buy extra tickets, you can gift those tickets to other users using this command. Each time you gift, it removes one of your "extra" tickets. You can only gift extra tickets with this command, to give away your own ticket, you can use /givemyticketto as noted below. Be sure to type the user name correctly for the person you are gifting to, extra tickets cannot be recovered once they are gifted. This can be done before and during the show.'); helptxtmsg += '\n \u2705 == Peep Show =='; helptxtmsg += '\n \u2022 ' + wordWrap('/mytime: Display the amount of time you have purchased so far, and time remaining.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepviewers: Display the list of users that have bought 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('/peeptimeleft: Display the time left in the Peep Show based on the planned show length (estimate).'); 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, /uadspint)'); 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('"sequence" : (/setseq1, /setseq2, /setseq3...(thru /setseq10), /rmvseq, /setsequencetext, /usechatmsg, /usegrouptips, /skip, /skiplevel, /chgendseq )'); helptxtmsg += '\n \u2022 ' + wordWrap('"tipjar" : (/setjar1, /setjar2, /setjar3...(thru /setjar10), /rmvjar, /setjartext, /faster, slower, /skip, /pause, /resume)'); helptxtmsg += '\n \u2022 ' + wordWrap('"goalrace" : (/setrace1, /setrace2, /restartrace, /addrace1, /addrace2, /tipnoteon, tipnoteoff, /setracetext, /setracepaneltext, /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 "presales" : (see full command list in submenu "presales")'; helptxtmsg += '\n \u2022 "peep" : (see full command list in submenu for the Peepshow)'; helptxtmsg += '\n \u2022 "modlevels" : See a summary of the different mod level capabilities'; 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", "sequence", "tipjar", and "ticket" to go along with the 5 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('/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('/setmodlevel [1,2 or 3]: Update the mod trust level. 1=Basic, 2=Standard, 3=Advanced. See the "modlevels" help section for more detail.'); 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 or peep shows.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgtheme [newcolortheme]: Change the color theme that is used for the background of the recurring notice. You can enter the command with no parameters to see the available choices.'); helptxtmsg += '\n \u2022 ' + wordWrap('/uachgtheme [newcolortheme]: Same command as /chgtheme above, but only updates for the UltraApp (the /chgtheme command will update the color theme across all Dorothy Apps currently running)'); helptxtmsg += '\n \u2022 ' + wordWrap('/uaseethemes: Display a sample listing of the available pre-defined color themes for the UltraApp'); 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('/uadspint: Display all of the current values for the recurring notice intervals.'); 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 'modlevels': { validhelpcmd = true; let modlevelmsg = '\u23E9 Dorothy\'s UltraApp - Moderator Trust levels'; modlevelmsg += '\n \u2705 "Basic" Moderator Abilities'; modlevelmsg += '\n \u2022 ' + wordWrap('Receive the "moderator" group benefits, such as ticket shows, if configured'); modlevelmsg += '\n \u2022 Start and manage all countdown timers'; modlevelmsg += '\n \u2022 Use the List Goals and List Remaining Goals commands'; modlevelmsg += '\n \u2022 Update the room title and other goal related text'; modlevelmsg += '\n \u2705 "Standard" Moderator Abilities'; modlevelmsg += '\n \u2022 All "Basic" ablities plus the below'; modlevelmsg += '\n \u2022 ' + wordWrap('Switch between the App features during show (from goals to ticket show, etc)'); modlevelmsg += '\n \u2022 Edit and maintain goals, restart a goal'; modlevelmsg += '\n \u2022 Start and end the ticket show and peep show'; modlevelmsg += '\n \u2022 Update the panel background and text color'; modlevelmsg += '\n \u2022 Skip and manually advance goals'; modlevelmsg += '\n \u2022 Manually add tips to a goal'; modlevelmsg += '\n \u2022 ' + wordWrap('Increase or Decrease the Tip Jar drain rate, pause and resume draining'); modlevelmsg += '\n \u2705 "Advanced" Moderator Abilities'; modlevelmsg += '\n \u2022 All "Standard" ablities plus the below'; modlevelmsg += '\n \u2022 Add Fans and VIPs during the show'; modlevelmsg += '\n \u2022 ' + wordWrap('Add users to a ticket show, including themselves (this can also be enabled at the "standard" level with the specific setting "6S")'); modlevelmsg += '\n \u2022 ' + wordWrap('Change ticket price or presales price for a ticket show (this can also be enabled at the "standard" level with the specific setting "6T")'); modlevelmsg += '\n \u2022 Add and remove app moderators'; modlevelmsg += '\n \u2022 View the show stats using the /stats command'; modlevelmsg += '\n \u2022 Reset the current app feature and start over'; modlevelmsg += '\n \u2705 ' + wordWrap('Only the broadcaster can use the /setmodlevel command to update the moderator trust level setting during the show.'); cb.sendNotice(modlevelmsg, 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('/addtips [tokens] : 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 more 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('/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.'); helptxtmsg += '\n \u2022 ' + wordWrap('/resetapp: Reset the App to the very beginning to start a new show, starting over at goal 1.'); 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('/addtips [tokens] : 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 more 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('/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('/addtips [tokens] : 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 more 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('/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 'sequence': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Sequence Goals'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Tip Sequence 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 Tip Sequence goal show, you can setup the starting and ending points in a sequence that users will tip through, and there can be intermediate goals defined at certain sequence thresholds. The tip sequence can be ascending or descending. For example, you could define the sequence as ascending from 1 to 50, with a goal threshold at 15, 20, 30, and 40... you could also define a sequence as descending from 60 to 10, with goal thresholds at 50, 40, 30, 20, 10, the direction, the range, and the goal levels are all configurable.'); helptxtmsg += '\n \u2022 ' + wordWrap('By default, the room subject will show the remaining goals sequence 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 "/setsequencetext" command as noted below.'); helptxtmsg += '\n \u2022 ' + wordWrap('Group tipping is available, which allows smaller tips to be accumulated against the current tip sequence number, and advancing when the accumulation meets or exceeds the next number.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/addtips [tokens] : This can be used to simulate users having tipped and advance the sequence 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 sequence, but you cannot subtract more than has been tipped in the current sequence.'); 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('/setseq1, /setseq2, /setseq3...(thru /setseq10) [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 /rmvseq).'); helptxtmsg += '\n ' + wordWrap('--- The 1-10 designation as part of the command identifies which Sequence 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 once the count sequence reaches and exceeds the number 10). Even if you are not changing the sequence goal level (only changing the description), you must still enter the existing value for the sequence 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 sequence goal level or past sequence goal levels, only future sequence goal levels.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvseq [level] : Remove the goal from sequence 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('/setsequencetext [newsubject] : Update the text that is shown in the Room Subject after the listing of the remaining goal sequence levels.'); helptxtmsg += '\n \u2022 ' + wordWrap('/usechatmsg [on/off] : Toggle the flag on and off for whether a message is posted in the chat for each sequence as it is achieved.'); helptxtmsg += '\n \u2022 ' + wordWrap('/usegrouptips [on/off] : Toggle the flag on and off for whether group tipping is allowed.'); helptxtmsg += '\n \u2022 ' + wordWrap('/skip : Advance to the next sequence regardless of the status of the current sequence.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgendseq [newseq] : Update the value for the end of the sequence. When ascending this is the higher number of the range, and when descending it is the low number of the range. The value cannot be updated to a sequence that has already been passed, or affect the current goal level target.'); cb.sendNotice(helptxtmsg, helpreqby, appNoticeColor); break; } case 'tipjar': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Tip Jar Goals'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Tip Jar specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2022 ' + wordWrap('For the Tip Jar show, you can setup a single goal, or multiple goals that will be progressed through in sequence as users tip. As each goal is reach, a countdown begins using the number of tokens in the jar (total tips when goal was hit or passed). Tokens begin to "drain" out of the jar at the specified rate of tokens per second. During this time, the user is performing the prize for that goal, and they would stop performing once the tip jar runs out/empties. While it is draining, users can continue to tip to keep it full and keep the performance going.'); helptxtmsg += '\n \u2022 ' + wordWrap('One the jar is empty, there is a configuration flag that defines if the app automatically moves from the completed goal to the next, or allows viewers to fill up the jar again (requires the use of the /skip command to advance). If set to automatic advance, the next goal starts, and viewers will now be able to tip towards the next goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('The goals also have a recyle count setting, which lets the broadcaster identify that some goals will be repeated over and over, to the defined threshold. Note that a recycle of "1", means that the goal will be performed twice (original plus one recycle).'); helptxtmsg += '\n \u2022 ' + wordWrap('By default, the room subject will show the current goal amount and description, and a configurable block of text that you cn use to describe what happens when the tip jar is running, or even just put searchable hashtags to draw more people into the show. This room subject text can be edited using the "/setjartext" command as noted below.'); helptxtmsg += '\n\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/addtips [tokens] : 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 more 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('/setjar1, /setjar2, /setjar3...(thru /setjar10) [goal] [recyc] [description] : These are the commands that let you edit the goals for your show. All three of the [goal], [recyc], and [description] parameters must be entered every time and it will update all three 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 /rmvjar for that).'); helptxtmsg += '\n ' + wordWrap('--- The 1-10 designation as part of the command identifies which entry you are modifying.'); helptxtmsg += '\n ' + wordWrap('--- The [goal] parameter is the value you are setting for the goal amount. Even if you are not changing the goal (only changing the description), you must still re-enter the existing value for the goal.'); helptxtmsg += '\n ' + wordWrap('--- The [recyc] parameter is the value you are setting for the recycle count. Even if you are not changing the recycle count (only changing the goal amount or description), you must still re-enter the existing value for the recycle count. Note that the default for recycle is "0", which means the goal is done once. With a recycle of "1", the goal is done twice, and if the recycle is "2", the goal is done three times, etc.'); helptxtmsg += '\n ' + wordWrap('--- The [description] Parameter is the value you are setting for the goal description. Even if you are not changing the description (only changing the goal amount), you must still re-enter the existing value for the description.'); helptxtmsg += '\n ' + wordWrap('--- And example of the syntax for this command would be "/setjar2 400 Blow job", which would set goal 2 to begin a Blow Job once you reach 400 tokens and continue that prize as long as the tip jar has tokens in it.'); helptxtmsg += '\n ' + wordWrap('--- Note that you can\'t make updates to the current goal or past goals, only future goals.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvjar [level] : Remove the goal for the tip jar 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('/setjartext [newsubject] : Update the text that is shown in the Room Subject after the current goal.'); helptxtmsg += '\n \u2022 ' + wordWrap('/faster [levels] : Speeds up the drain rate by the specified number of levels. If no level is specified it is assumed to be by one level. If a level is specified beyond the maximum, the maximum is used.'); helptxtmsg += '\n \u2022 ' + wordWrap('/slower [levels] : Slows down up the drain rate by the specified number of levels. If no level is specified it is assumed to be by one level. If a level is specified below the minimum, the minimum is used.'); helptxtmsg += '\n \u2022 ' + wordWrap('/pause : Pause the draining of the jar.'); helptxtmsg += '\n \u2022 ' + wordWrap('/resume : Resume the draining of the jar.'); helptxtmsg += '\n \u2022 ' + wordWrap('/skip : Advance to the next goal regardless of the status of the current goal. This will also bypass the prize/draining of the jar for the current goal (will get a warning and must enter twice when tokens in the jar).'); 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('/previewers : (all users) Display the list of users that are currently viewing in their free preview period.'); 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('/usegift [on/off]: (mods/bc only) Toggle the setting for whether the ability to gift tickets is "on" or "off". Overrides the initial setting from the launch page, and allows you to turn the gifting of tickets on or off during the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/useot [on/off]: (mods/bc only) Toggle the setting for whether the Outstanding Ticket feature of the Ticket Show is "on" or "off". Overrides the initial setting, and allows you to turn the Outstanding Ticket usage feature on or off during the show. .'); helptxtmsg += '\n \u2022 ' + wordWrap('/otlist : (all users) Display the list of outstanding ticket holders (if displaying for copying to the app launch page, please use /otlistexp instead to get a better format to copy and paste). Command can be used by anyone if the Outstanding Ticket feature is enabled.'); helptxtmsg += '\n \u2022 ' + wordWrap('/otlistexp : (mods/bc only) Display the list of outstanding ticket holders in a format that can be copied and pasted into the bot lauch page for future shows.'); helptxtmsg += '\n \u2022 ' + wordWrap('/otchanges : (mods/bc only) ** IMPORTANT when using the OT feature ** Displays a list of tickets that have been saved or used during the current session so the permanent list can be updated.'); helptxtmsg += '\n \u2022 ' + wordWrap('/saveticket: (all ticket buyers) If the broadcaster has enabled Outstanding Tickets (and is tracking them) - If you have bought a ticket and will not be able to stay for the show, you can save it for a future show. You will no longer be able to see the current show. IMPORTANT: If in the same session, the ticket will be available automatically. However for future shows or if the broadcaster restarts the bot, the broadcaster must add the saved tickets to the outstanding ticket list to be able to use them with /useticket.'); helptxtmsg += '\n \u2022 ' + wordWrap('/useticket: (all users with an outstanding ticket) If the broadcaster has enabled Outstanding Tickets (and is tracking them) - Redeem an outstanding ticket and use it for access to this show. You can use the command "/otlist" to view the list of outstanding ticket holders if the broadcaster has enabled this feature.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addot : (mods, bc if granted privileges) If the outstanding ticket feature is in use, the broadcaster can manually give a user an outstanding ticket. Moderators can also add if they have authority. The addition still must be made permanent by updating the launch page outstanding ticket list.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvot : (mods/bc only) Remove a user from the outstanding ticket list within the current show. The removal still must be made permanent by updating the launch page outstanding ticket list.'); 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('/giftticket [user]: (all users, once you have extra tickets) If the "gifting" feature is enabled, when you tip enough to buy extra tickets, you can gift those tickets to other users using this command. Each time you gift, it removes one of your "extra" tickets. You can only gift extra tickets with this command, to give away your own ticket, you can use /givemyticketto as noted below. Be sure to type the user name correctly for the person you are gifting to, extra tickets cannot be recovered once they are gifted. This can be done before and during the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/givemyticketto [user]: (all ticket buyers) If you cannot stay for a show, and outstanding ticket feature is no used to allow saving your ticket, you can give your ticket to another user. This can only be done before the show starts, and you will be removed from the ticket show list!'); 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('/previewlength [newlength]: (mods/bc only) Update the length of the free preview period. Note that setting it to zero ("0") will disable the free preview. Please make sure to use one of the following values as the parameter: "0", "10sec", "20sec", "30sec", "1min", "2min", "3min", "4min", or "5min"'); 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 'presales': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Ticket Show Pre-sales'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Pre-sales specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/uapresale [on/off]: (bc/mods only) overrides the launch page setting for whether Pre-sales are conducted in the UltraApp or the Fembot. Using a parameter of "on" enables presales in the UltraApp. Note you can still start and stop the actual pre-sale of tickets once enabled for the ultraApp using the two commands below.'); helptxtmsg += '\n \u2022 ' + wordWrap('/startpresale: (bc/mods only) Begin selling pre-sales tickets. Only allowed if Pre-sales are enabled for the UltraApp (see above command).'); helptxtmsg += '\n \u2022 ' + wordWrap('/stoppresale: (bc/mods only) Stop selling pre-sales tickets. Only allowed if Pre-sales are enabled for the UltraApp (see above command). Note the ticket list remains intact, but new pre-sale purchases are disabled.'); helptxtmsg += '\n \u2022 ' + wordWrap('/presalelist: (all users) Display the list of users on the ticket holder list when performing pre-sales. Once the ticket show sales are started, use the "/tickets" command instead.'); helptxtmsg += '\n \u2022 ' + wordWrap('/presaleprice [price]: (mods/bc only) Set the pre-sales ticket price to a value of [price]. This can either be done to set the initial price before the pre-sales start, if not set in the bot start page, or it can be used after pre-sales start to change the price to a new price. Note the timer can also be set to change the price using the command below.'); helptxtmsg += '\n \u2022 ' + wordWrap('/pspricetimer [time] [price]: (mods/bc only) When using manual mode, set the pre-sales ticket price to a new value of [price] when the timer for [time] minutes runs out. Timer warnings will be displayed and the price will automatically update at the end of the timer. Time can be added or removed from the timer using the command below.'); helptxtmsg += '\n \u2022 ' + wordWrap('/presaletimer [time]: (mods/bc only) When using manual mode, start a timer to increase the price at the end of the timer by the pre-defined increment value specified on the start page.'); helptxtmsg += '\n \u2022 ' + wordWrap('/presaleaddtime [time]: (mods/bc only) Add [time] minutes to the timer. If a negative number is used, time is subtracted.'); helptxtmsg += '\n \u2022 ' + wordWrap('/presalestoptimer: (mods/bc only) Stops the pre-sale timer and no price update is made.'); helptxtmsg += '\n \u2022 ' + wordWrap('/presaletimeleft: (mods/bc only) Displays the time left on the current pre-sale timer, whether started manually or automated.'); helptxtmsg += '\n \u2022 ' + wordWrap('/chgpresalemode [mode]: (mods/bc only) Change the presale mode that is currently running. The default value is set on the start page, and can be change on the fly during the show to "manual", "timer", or "count".'); helptxtmsg += '\n ' + wordWrap('--- Manual Mode: The broadcaster or moderator controls the price increases (or there are no price increases). Price can either be changed immediately using /presaleprice, or a timer can be started to change it by a specified amount (/presalepricetimer) or by the default increment amount (/presalestarttimer).'); helptxtmsg += '\n ' + wordWrap('--- Timer Mode: The price is changed automatically on a recycling timer as defined on the start page. The broadcaster or a moderator can add or remove time from the timer as needed, or stop the timer. At the end of each cycle, the price will increase by the configured increment and a new timer will be kicked off as long as it does not exceed the maximum number of increments and would not exceed the ticket price with the next cycle.'); helptxtmsg += '\n ' + wordWrap('--- Count Mode: The price is changed automatically on a recycling ticket count as defined on the start page. At the end of each cycle when the defined number of tickets is sold, the price will increase by the configured increment as long as it does not exceed the maximum number of increments and does not exceed the ticket price with the next cycle.'); helptxtmsg += '\n \u2022 ' + wordWrap('/addpresale: (mods/bc only) Add a user or list of users to the pre-sale ticket list.'); helptxtmsg += '\n \u2022 ' + wordWrap('/rmvpresale: (mods/bc only) Remove an individual user from the pre-sale list.'); 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('/addtips [tokens] : 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('/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('/addtips [tokens] : 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('/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; } case 'peep': case 'peepshow': { validhelpcmd = true; let helptxtmsg = '\u23E9 Dorothy\'s Ultra App - Peep Show'; helptxtmsg += '\n \u2022 ' + wordWrap('This is the Peep Show specific help menu. Type "/uahelp cmd" to access the main help menu and see all of the submenus.'); helptxtmsg += '\n \u2705 COMMANDS:'; helptxtmsg += '\n \u2022 ' + wordWrap('/mytime: (all users) display the amount of time you have purchased so far, and time remaining.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepviewers (or /pv) : (all users) Display the list of all users with access to the hidden show. Prior to show start, this will be anyone granted free access (mods, Fans, VIPs, etc). After the hidden peep show starts, it will be all users with access - free viewers and viewers who have purchased time remaining.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepbuyers (or /pb) : (all users) Display the list of all users who have purchased time in the show, either before show start or during the show. The list will also show the amount of time remaining for each viewer. After a viewer\'s time expires, they are still in this list with 0 time remaining (but will not be in the viewer list above).'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepadd [user]: (mods/bc only) Manually add a specific [user] to the Peep Show Free Viewer list - they will be able to see the whole show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peeprmv [user]: (mods/bc only) Manually remove a specific user from the Peep Show. Applies to both Free Viewers or viewers with purchased time.'); helptxtmsg += '\n \u2022 ' + wordWrap('/startshow: (mods/bc only) Start the Peep Show when not set to automatic start. Once started, the show will only be visible to Free Viewers and those who have purchased time. Hint: start the show when you are in a good position for the preview pic to be frozen that will attract more buyers after the show starts.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showwarn (or /showover): (mods/bc only) Display a warning that the Peep Show will be ending soon and that time purchases are allowed but not recommended.'); helptxtmsg += '\n \u2022 ' + wordWrap('/showend (or /stopsales): (mods/bc only) Suspend Peep Show sales, no more time 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.'); helptxtmsg += '\n \u2022 ' + wordWrap('/stopshow: (mods/bc only) End the hidden Peep Show and return to a public broadcast if not configured to autmatically end.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peeprestart: (mods/bc only) Go back into hidden cam mode if the show was accidentally ended too soon. The viewer list and time purchase amounts are kept intact.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepprice [newprice]: (mods/bc only) Update the peep show per minute price to the [newprice] in tokens per minute. If changed during the show, it will not affect viewers who have already purchased time. Once a purchase is made, they are locked in to the time they have bought.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peeplength [time]: (mods/bc only) Update the planned length of the peep show to [time] in terms of minutes. This is only an estimate.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peeptimer [time]: (mods/bc only) Start a [time] minute timer for the start of the peep show when in "timer" mode The timer will count down but not automatically start the show.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepaddtime [time]: (mods/bc only) Add [time] minutes to the timer for the Peep Show start countdown. The [time] value can be a negative number to subtract time, but cannot be greater than the remaining time.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepstoptimer: (mods/bc only) Stop the Peep Show Start timer for either automatic or manual drawing mode.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peeptimeleft : (mods/bc only) Display the time left in the Peep Show based on the broadcaster\'s planned show length (estimate).'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepautoend [on/off]: (mods/bc only) Overrides the initial launch page setting and changes the parameter for whether the show automatically ends after the planned show length. A parameter of "on" enables the automatic end, a parameter of "off" disabled the automatic end.'); helptxtmsg += '\n \u2022 ' + wordWrap('/peepsubject [newsubject]: (mods/bc only) Change the room description/subject/title to a new value. This keeps the standard Peep Show Room Subject formatting and only changes the configurable part of the subject.'); 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 msgisextfan1 = cbjs.arrayContains(extFanListArray,msguser); var msgisextfan2 = cbjs.arrayContains(extFanList2Array,msguser); 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; if (modLevel == 'Standard') { ismodlvlmsg2 = true; } else if (modLevel == 'Advanced') { ismodlvlmsg2 = true; ismodlvlmsg3 = true; } } if (msgismod) { addRmvModsInShow(msguser,'a'); } if (msgisfan) { addRmvFanClubInShow(msguser,'a'); } if (msgisvip) { addRmvVIPInShow(msguser,'a'); } if (msgisextfan1) { addRmvExtFanInShow(msguser,'a'); } if (msgisextfan2) { addRmvExtFanInShow2(msguser,'a'); } if (msgismod || msgisfan || msgisextfan1 || msgisextfan2 || msgisvip) { if (whichApp == 'ticket' || presalesToggle) { checkFreeTickets(msguser,msgismod,msgisfan,msgisextfan1,msgisextfan2,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 != 'sequence' && newapp != 'tipjar' && newapp != 'goalrace' && newapp != 'peep' && 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", "sequence", "tipjar", "goalrace", "spank", "peep", 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", "sequence", "tipjar", "goalrace", "spank", "peep", 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 '/chgtheme': case '/uachgtheme': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { let newtheme = msgarray[1].toLowerCase(); if (newtheme == 'none') { colorTheme = 'None'; loadAllFeatureColors(msguser); cb.sendNotice('You have updated the App to not use a color theme. The notice coloring will now default to the individual App features settings.', msguser, appNoticeColor, ''); } else if (newtheme == 'custom') { cb.sendNotice('A "custom" theme cannot be updated during the show, only the pre-defined themes, or "none".', msguser, appNoticeColor, ''); } else if (cbjs.arrayContains(themeArray.shortcut,newtheme)) { let themeindex = themeArray.shortcut.indexOf(newtheme); colorTheme = themeArray.name[themeindex]; loadThemeColors(msguser); cb.sendNotice('You have updated the color theme to "' + colorTheme + '".', msguser, appNoticeColor, ''); } else { cb.sendNotice('Invalid color theme name. The valid names are: \nnone, ' + cbjs.arrayJoin(themeArray.shortcut, ', '), msguser, appNoticeColor, ''); } } else { cb.sendNotice(botName + 'The /chgtheme command requires the entry of a parameter following the command, such as "/chgtheme pastelrainbow". The valid formats are: \nnone, ' + cbjs.arrayJoin(themeArray.shortcut, ', '), msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/uaseetheme': case '/uaseethemes': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { // if (cb.settings.fembotRunning == 'No') { for (let stidx = 0; stidx < themeArray.name.length; stidx++) { let tempseebgcolor = themeArray.colorID[stidx]; let tempseetextcolor = themeArray.textcolor[stidx]; cb.sendNotice('********************************************\nTheme "' + themeArray.name[stidx] + '" (shortcut = "' + themeArray.shortcut[stidx] + '")\n********************************************\n', msguser, tempseebgcolor, tempseetextcolor); } // } else { // cb.sendNotice(botName + 'Since setting "1N" indicates the Fembot is also running, the themes list is not displayed by this App, it is expected they will be shown by the Fembot using the same command.', msguser, appNoticeColor); // } } else { cb.sendNotice(noticeOnlyBCMod1, 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 (cb.settings.progressiveUpDown == 'Count Up To Goal' && currentGoalTips < currentGoalTotal) { 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.progressiveUpDown == 'Count Down To Zero' && 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) > totalProgGoals) { 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 == 'tipjar' || whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'sequence' || 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 if (whichApp == 'tipjar' && tipJarRunning && tipJarCurrentTokens > 0) { if (skipWarned) { cb.sendNotice(botName + 'Skipping the draining of the rest of the jar and advancing to next goal/cycle.', msguser, appNoticeColor); skipGoal(); skipWarned = false; } else { skipWarned = true; cb.sendNotice(botName + 'Warning! The "/skip" command should not be used while tip jar is running and there are tokens in the jar, but you can enter the /next command again and it will skip the draining of the rest of the jar and advance. Tokens can be removed from the tip jar using "/addtips" with a negative number.', 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 Tip Jar, Progressive Goals, Auto-Reset Goal, Goal Counter, or Tip Sequence apps (current app is ' + whichApp + ').', msguser, appNoticeColor); } break; } case '/skiplevel': { recognizedcmd = true; if (whichApp == 'goalcount' || whichApp == 'sequence') { 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 if (whichApp == 'sequence' && (currentGoalLevel) > sequenceArray.amt.length) { cb.sendNotice(botName + 'No further Sequence 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 or Tip Sequence apps (current app is ' + whichApp + ').', msguser, appNoticeColor); } break; } case '/addtip': case '/addtips': { recognizedcmd = true; if (whichApp == 'tipjar' || whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'goalrace' || whichApp == 'spank' || whichApp == 'autoreset' || whichApp == 'sequence') { if (msgisbc || ismodlvlmsg2) { let tipcompareamt = currentGoalTips; if (whichApp == 'goals' && cb.settings.progressiveUpDown == 'Count Down To Zero') { tipcompareamt = currentGoalTotal - currentGoalTips; } 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 (!tipJarRunning && commandvar1 < 0 && Math.abs(commandvar1) > tipcompareamt) { cb.sendNotice(botName + 'You cannot subtract more tokens than have been tipped for the current goal/sequence.', msguser, appNoticeColor); } else if (whichApp == 'tipjar' && tipJarRunning && commandvar1 < 0 && Math.abs(commandvar1) > tipJarCurrentTokens) { cb.sendNotice(botName + 'You cannot subtract more tokens than are in the tip jar.', 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 { let addtipnote = ''; if (whichApp == 'goalrace' && commandvar2 == 1) { addtipnote = goalraceDesc1; } else if (whichApp == 'goalrace' && commandvar2 == 2) { addtipnote = goalraceDesc2; } recordTip(commandvar1,'bc',addtipnote,msgisfan,msgisextfan1,msgisextfan2,msgisvip,'broadcaster',true); if (commandvar1 > 0) { cb.sendNotice(botName + 'You have added ' + commandvar1 + ' token' + (commandvar1 == 1 ? '' : 's') + '.', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'You have subtracted ' + Math.abs(commandvar1) + ' token' + (Math.abs(commandvar1) == 1 ? '' : 's') + '.', 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' : '') + (currentAppTotalSequence > 0 ? '\n \u21D2 \u21D2 \u21D2 Tip Sequence Goal Total.... ' + currentAppTotalSequence + ' tokens' : '') + (currentAppTotalTipJar > 0 ? '\n \u21D2 \u21D2 \u21D2 Tip Jar Total...................... ' + currentAppTotalTipJar + ' 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' : '') + (countPresaleTickets > 0 ? '\n \u21D2 \u21D2 \u21D2 \u21D2 Pre-sale Tickets Sold........... ' + countPresaleTickets + ' tickets' : '') + (countTickets > 0 ? '\n \u21D2 \u21D2 \u21D2 \u21D2 Total Tickets Sold.............. ' + countTickets + ' tickets' : '') + (countTicketsFromPreview > 0 ? '\n \u21D2 \u21D2 \u21D2 \u21D2 Tickets Sold From Preview.. ' + countTicketsFromPreview + ' tickets' : '') + (currentAppTotalPeep > 0 ? '\n \u21D2 \u21D2 \u21D2 Peep Show Total............... ' + currentAppTotalPeep + ' tokens' : '') + (currentAppTotalNone > 0 ? '\n \u21D2 \u21D2 \u21D2 No App Running Total....... ' + currentAppTotalNone + ' tokens' : ''), msguser, appNoticeColor, '', boldTextLiteral); } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/lg': case '/listgoals': { recognizedcmd = true; if (whichApp == 'tipjar' || whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'sequence' || 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, Tip Sequence, Spank-a-thon, Goal Race, and Tip Jar apps.', msguser, appNoticeColor); } break; } case '/lrg': case '/listremgoals': { recognizedcmd = true; if (whichApp == 'tipjar' || whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'sequence' || 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, Tip Sequence, Spank-a-thon, Goal Race, and Tip Jar apps.', msguser, appNoticeColor); } break; } case '/resetapp': { recognizedcmd = true; if (whichApp == 'tipjar' || whichApp == 'goals' || whichApp == 'goalcount' || whichApp == 'sequence' || 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, Goal Count, Tip Sequence and Tip Jar 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, pre-sales, and the peep show.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/setmodlevel': { recognizedcmd = true; if (msgisbc) { if (msgarray[1]) { if (msgarray[1] != '1' && msgarray[1] != '2' && msgarray[1] != '3') { cb.sendNotice(botName + 'Invalid parameter value, valid values are "1" for "Basic" level, "2" for "Standard" level, or "3" for "Advanced" level. See the "modlevels" help section for more detail (/uahelp modlevels)', msguser, appNoticeColor); } else { if (msgarray[1] == '1') { if (modLevel == 'Basic') { cb.sendNotice(botName + 'The Moderator Trust Level is already set to "Basic".', msguser, appNoticeColor); } else { modLevel = 'Basic'; cb.sendNotice(botName + 'The Moderator Trust Level has been updated to "Basic".', msguser, appNoticeColor); } } else if (msgarray[1] == '2') { if (modLevel == 'Standard') { cb.sendNotice(botName + 'The Moderator Trust Level is already set to "Standard".', msguser, appNoticeColor); } else { modLevel = 'Standard'; cb.sendNotice(botName + 'The Moderator Trust Level has been updated to "Standard".', msguser, appNoticeColor); } } else if (msgarray[1] == '3') { if (modLevel == 'Advanced') { cb.sendNotice(botName + 'The Moderator Trust Level is already set to "Advanced".', msguser, appNoticeColor); } else { modLevel = 'Advanced'; cb.sendNotice(botName + 'The Moderator Trust Level has been updated to "Advanced".', msguser, appNoticeColor); } } } } else { cb.sendNotice(botName + 'No parameter was specified for the new mod trust level, valid values are "1" for "Basic" level, "2" for "Standard" level, or "3" for "Advanced" level. See the "modlevels" help section for more detail (/uahelp modlevels)', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBC, msguser, appNoticeColor); } break; } case '/ninjatipon': case '/ninjatip': { recognizedcmd = true; if (whichApp == 'ticket' || whichApp == 'peep') { cb.sendNotice(botName + 'This command cannot be used during a ticket show or peep 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 '/chgint': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { cb.sendNotice(botName + 'Note that if you are attempting to change an interval in the UltraApp, the command is /uachgint', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod1, 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(botName + 'The value entered for the notice type to change is not a valid UltraApp interval type. The valid types are: \n' + cbjs.arrayJoin(noticeIntervals.name, ', '), msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice(botName + '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. \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 if (isNaN(newtimer)) { cb.sendNotice(botName + '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. \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 if (newtimer > 0 && newtimer < 1) { cb.sendNotice(botName + '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). \nSetting to zero will disable the Notice.', msguser, appNoticeColor); } else { updateIntervalValues(intervaltochange,newtimer); cb.sendNotice(botName + 'The "' + intervaltochange + '" notice timer has been updated to a new interval of ' + newtimer + ' minutes. \nThis will take effect after the next display of the notice.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + '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 '/uadspint': { recognizedcmd = true; if (ismodlvlmsg1 || msgisbc) { let dspintstr = ''; if (noticeIntervals.name.length > 0) { dspintstr = '***** Listing of Current Notice Intervals (and setting IDs) *****'; for (let dspintidx = 0; dspintidx < noticeIntervals.name.length; dspintidx++) { dspintstr += '\n' + (dspintidx+1) + '. ' + noticeIntervals.name[dspintidx] + ' (' + noticeIntervals.settingID[dspintidx] + '): ' + Math.round((this[noticeIntervals.fieldname[dspintidx]]/60000)*100)/100 + ' min'; } } else { dspintstr = 'No notice intervals have been defined.'; } cb.sendNotice(dspintstr, 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': case '/setgoal11': case '/setgoal12': case '/setgoal13': case '/setgoal14': case '/setgoal15': case '/setgoal16': case '/setgoal17': case '/setgoal18': case '/setgoal19': case '/setgoal20': { 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 (cb.settings.progressiveUpDown == 'Count Up To Goal' && goalnum == currentGoal && commandvar1 <= 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 (cb.settings.progressiveUpDown == 'Count Down To Zero' && 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; totalProgGoals = progGoalArray.desc.length; if (goalnum == currentGoal) { currentGoalDesc = goallabel; let savcurrenttotal = currentGoalTotal; currentGoalTotal = commandvar1; if (cb.settings.progressiveUpDown == 'Count Down To Zero' && savcurrenttotal != currentGoalTotal) { currentGoalTips = currentGoalTotal - (savcurrenttotal - currentGoalTips); } } if (finalGoalMet == true && goalnum == (currentGoal + 1)) { finalGoalMet = false; if (cb.settings.progressiveUpDown == 'Count Up To Goal') { currentGoalTips = 0; } else { 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 = totalProgGoals; 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 (cb.settings.progressiveUpDown == 'Count Up To Goal' && currentGoalTips > 0 && currentGoalTips < currentGoalTotal) { 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 if (cb.settings.progressiveUpDown == 'Count Down To Zero' && 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 > totalProgGoals) { 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]; totalProgGoals = progGoalArray.desc.length; 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; } //********* Tip Sequence Commands case '/setseq1': case '/setseq2': case '/setseq3': case '/setseq4': case '/setseq5': case '/setseq6': case '/setseq7': case '/setseq8': case '/setseq9': case '/setseq10': { recognizedcmd = true; if (whichApp == 'sequence') { if (msgisbc || ismodlvlmsg2) { var seqgoalnum = parseInt(msgarray[0].substring(7)); if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice('The first parameter is the new tip sequence for goal ' + seqgoalnum + ' and has be be a number greater than 0. For example, "/setseq' + seqgoalnum + ' 20 Shirt Off" to set goal ' + seqgoalnum + ' as Shirt off at tip sequence 20.', msguser, appNoticeColor); } else if (seqgoalnum <= currentGoal) { cb.sendNotice('You cannot update the attributes of the current goal or a goal that has already been passed in the tip sequence.', msguser, appNoticeColor); } else if (!msgarray[2]) { cb.sendNotice('The second parameter is the description of tip sequence goal ' + seqgoalnum + ' (can be multiple words). For example, "/setseq' + seqgoalnum + ' 20 Shirt Off" to set goal ' + seqgoalnum + ' as Shirt off at tip sequence 20.', msguser, appNoticeColor); } else if (tipSequenceDirection == 'up' && commandvar1 > endSequence || tipSequenceDirection == 'down' && commandvar1 < endSequence ) { cb.sendNotice('You cannot set a goal level sequence target outside of the configured tip sequence range (ending sequence is at ' + endSequence + ').', msguser, appNoticeColor); } else if (tipSequenceDirection == 'up' && commandvar1 <= nextSequence || tipSequenceDirection == 'down' && commandvar1 >= nextSequence) { cb.sendNotice('You cannot set a goal level sequence target to a sequence value that has already been passed.', msguser, appNoticeColor); } else if (seqgoalnum > 1 && !sequenceArray.amt[seqgoalnum-2] >= 1) { cb.sendNotice('You cannot skip goal levels. If setting a value for level ' + seqgoalnum + ', then level ' + (seqgoalnum-1) + ' must already have a sequence value defined (and be greater than or less than based on sequence direction).', msguser, appNoticeColor); } else if (seqgoalnum > 1 && tipSequenceDirection == 'up' && commandvar1 <= sequenceArray.amt[seqgoalnum-2]) { cb.sendNotice('For ascending sequence, you cannot update the sequence for a goal level to be less than or equal to the sequence for the previous level (level ' + (seqgoalnum-1) + ' has a sequence of ' + sequenceArray.amt[seqgoalnum-2] + ', so you must use a sequence greater than ' + sequenceArray.amt[seqgoalnum-2] + ' for level ' + seqgoalnum + ').', msguser, appNoticeColor); } else if (seqgoalnum < 10 && tipSequenceDirection == 'up' && commandvar1 >= sequenceArray.amt[seqgoalnum] && sequenceArray.amt[seqgoalnum] > 0) { cb.sendNotice('For ascending sequence, you cannot update the sequence for a goal level to be greater than or equal to the sequence for the next level (level ' + (seqgoalnum+1) + ' has a sequence of ' + sequenceArray.amt[seqgoalnum] + ', so you must use a sequence less than ' + sequenceArray.amt[seqgoalnum] + ' for level ' + seqgoalnum + ').', msguser, appNoticeColor); } else if (seqgoalnum > 1 && tipSequenceDirection == 'down' && commandvar1 >= sequenceArray.amt[seqgoalnum-2]) { cb.sendNotice('For descending sequence, you cannot update the sequence for a goal level to be greater than or equal to the sequence for the previous level (level ' + (seqgoalnum-1) + ' has a sequence of ' + sequenceArray.amt[seqgoalnum-2] + ', so you must use a sequence less than ' + sequenceArray.amt[seqgoalnum-2] + ' for level ' + seqgoalnum + ').', msguser, appNoticeColor); } else if (seqgoalnum < 10 && tipSequenceDirection == 'down' && commandvar1 <= sequenceArray.amt[seqgoalnum]) { cb.sendNotice('For descending sequence, you cannot update the sequence for a goal level to be less than or equal to the sequence for the next level (level ' + (seqgoalnum+1) + ' has a sequence of ' + sequenceArray.amt[seqgoalnum] + ', so you must use a sequence greater than ' + sequenceArray.amt[seqgoalnum] + ' for level ' + seqgoalnum + ').', msguser, appNoticeColor); } else { var seqlabel = ''; for (let m1 = 2; m1 < msgarray.length; m1++) { if (m1 == 2) { seqlabel = msgarray[m1]; } else { seqlabel += " " + msgarray[m1]; } } sequenceArray.amt[seqgoalnum-1] = commandvar1; sequenceArray.desc[seqgoalnum-1] = seqlabel; updateSequenceGoal(); cb.sendNotice('Sequence Goal #' + seqgoalnum + ' was added/updated to occur at sequence ' + commandvar1 + ' and with a description of "' + seqlabel + '".', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Sequence Goal #' + seqgoalnum + ' to occur at sequence ' + commandvar1 + ' and with a description of "' + seqlabel + '".', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' added/updated Sequence Goal #' + seqgoalnum + ' to occur at sequence ' + commandvar1 + ' and with a description of "' + seqlabel + '".', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setseqX" command is for use with the Tip Sequence app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/rmvseq': { recognizedcmd = true; if (whichApp == 'sequence') { if (msgisbc || ismodlvlmsg2) { var rmvseqgoalnum = msgarray[1]; if (isNaN(rmvseqgoalnum) || rmvseqgoalnum < 1 || rmvseqgoalnum > 10) { cb.sendNotice('The first parameter is the sequence goal level being removed, and must be a number from 1 to 10 to indicate the goal level number that should be removed. For example, "/rmvseq 3" will remove the goal and description info for sequence goal level 3.', msguser, appNoticeColor); } else if (rmvseqgoalnum > sequenceArray.amt.length) { cb.sendNotice('There is no goal entry at level ' + rmvseqgoalnum + ', there are currently only entries up to goal level ' + sequenceArray.amt.length + '.', msguser, appNoticeColor); } else { sequenceArray.amt.splice((rmvseqgoalnum-1),1); sequenceArray.desc.splice((rmvseqgoalnum-1),1); updateSequenceGoal(); cb.sendNotice('Sequence Goal #' + rmvseqgoalnum + ' was removed from the goal list', msguser, appNoticeColor); cb.sendNotice(msguser + ' removed Sequence Goal #' + rmvseqgoalnum + ' from the goal list', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' removed Sequence Goal #' + rmvseqgoalnum + ' from the goal list', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/rmvseq" command is for use with the Tip Sequence app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/usechatmsg': { recognizedcmd = true; if (whichApp == 'sequence') { if (msgisbc || ismodlvlmsg2) { if (!msgarray[1] || (msgarray[1] != 'on' && msgarray[1] != 'off')) { cb.sendNotice('The "/usechatmsg" command requires a parameter of either "on" or "off".', msguser, appNoticeColor); } else { setChatMsgToggle(msgarray[1], msguser, 'updt'); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/usechatmsg" command is for use with the Tip Sequence app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/chgendseq': { recognizedcmd = true; if (whichApp == 'sequence') { if (msgisbc || ismodlvlmsg2) { var lastgoalindex = sequenceArray.amt.length - 1; if (commandvar1 <= 0 || isNaN(commandvar1)) { cb.sendNotice('The parameter is the new Tip Sequence end goal and has be be a number greater than 0. For example, use "/chgendseq 50" to set the new sequence end goal to 50.', msguser, appNoticeColor); } else if (tipSequenceDirection == 'up' && commandvar1 <= sequenceArray.amt[lastgoalindex]){ cb.sendNotice('For ascending sequence, cannot change the end sequence to be less then or equal to the sequence for the last tip sequence goal (' + sequenceArray.amt[lastgoalindex] + ').', msguser, appNoticeColor); } else if (tipSequenceDirection == 'down' && commandvar1 >= sequenceArray.amt[lastgoalindex]){ cb.sendNotice('For descending sequence, cannot change the end sequence to be greater then or equal to the sequence for the last tip sequence goal (' + sequenceArray.amt[lastgoalindex] + ').', msguser, appNoticeColor); } else { changeEndSequence(commandvar1); cb.sendNotice('The ending sequence number has been updated to ' + commandvar1 + '.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/chgendseq" command is for use with the Tip Sequence App, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setseqtext': { recognizedcmd = true; if (whichApp == 'sequence') { if (msgisbc || ismodlvlmsg1) { sequenceSubjectText = rawmsg.substring(12).trim(); if (sequenceSubjectText != '' && sequenceSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new Sequence Goal room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setseqtext" command is for use with the Tip Sequence App, and that feature is not running.', msguser, appNoticeColor); } break; } case '/usegrouptips': { recognizedcmd = true; if (whichApp == 'sequence') { if (msgisbc || ismodlvlmsg2) { if (!msgarray[1] || (msgarray[1] != 'on' && msgarray[1] != 'off')) { cb.sendNotice('The "/usegrouptips" command requires a parameter of either "on" or "off".', msguser, appNoticeColor); } else { setGroupTipToggle(msgarray[1], msguser, 'updt'); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/usegrouptips" command is for use with the Tip Sequence app, and that feature is not running.', msguser, appNoticeColor); } break; } //********* Tip Jar Commands case '/slower': { recognizedcmd = true; if (whichApp == 'tipjar') { if (msgisbc || ismodlvlmsg2) { var ratechange = parseInt(msgarray[1]); if(isNaN(ratechange)) { if (drainLevel >= 10) { cb.sendNotice('The Tip Jar Drain rate is already at the slowest setting. Drain rate is ' + drainRateText(), msguser, appNoticeColor); } else { setDrainRate(drainLevel+1); cb.sendNotice('You have slowed the Drain Rate by 1 level. Drain rate is now ' + drainRateText(), msguser, appNoticeColor); } } else { if ((drainLevel+ratechange) > 10) { cb.sendNotice('Slowing the Drain Rate by ' + ratechange + ' levels would exceed the slowest setting, defaulting to the slowest setting of 1 token every 10 seconds', msguser, appNoticeColor); setDrainRate(10); } else { setDrainRate(drainLevel+ratechange); cb.sendNotice('You have slowed the Drain Rate by ' + ratechange + ' levels. Drain rate is now ' + drainRateText(), msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/slower" command is for use with the Tip Jar, and the Tip Jar feature is not running.', msguser, appNoticeColor); } break; } case '/faster': { recognizedcmd = true; if (whichApp === 'tipjar') { if (msgisbc || ismodlvlmsg2) { var ratechange2 = parseInt(msgarray[1]); if(isNaN(ratechange2)) { if (drainLevel <= 1) { cb.sendNotice('The Tip Jar Drain rate is already at the fastest setting. Drain rate is ' + drainRateText(), msguser, appNoticeColor); } else { setDrainRate(drainLevel-1); cb.sendNotice('You have increased the Drain Rate by 1 level. Drain rate is now ' + drainRateText(), msguser, appNoticeColor); } } else { if ((drainLevel-ratechange2) < 1) { cb.sendNotice('Speeding up the Drain Rate by ' + ratechange2 + ' levels would exceed the fastest setting, defaulting to the fastest setting of 5 tokens per second.', msguser, appNoticeColor); setDrainRate(1); } else { setDrainRate(drainLevel-ratechange2); cb.sendNotice('You have increased the Drain Rate by ' + ratechange2 + ' levels. Drain rate is now ' + drainRateText(), msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/faster" command is for use with the Tip Jar, and the Tip Jar feature is not running.', msguser, appNoticeColor); } break; } case '/setjar1': case '/setjar2': case '/setjar3': case '/setjar4': case '/setjar5': case '/setjar6': case '/setjar7': case '/setjar8': case '/setjar9': case '/setjar10': { recognizedcmd = true; if (whichApp == 'tipjar') { if (msgisbc || ismodlvlmsg2) { var jargoalnum = parseInt(msgarray[0].substring(7)); if (isNaN(commandvar1) || commandvar1 < 1 || commandvar1 > 100000) { cb.sendNotice('The first parameter is the amount for tip jar goal #' + jargoalnum + ' and has be be a number from 1 to 100,000. For example, "/setjar' + jargoalnum + ' 300 0 Shirt Off" to set goal ' + jargoalnum + ' to be for "Shirt off" at 300 tokens, and only do the goal once with no recycle.', msguser, appNoticeColor); } else if (isNaN(commandvar2) || commandvar2 < 0 || commandvar2 > 99) { cb.sendNotice('The second parameter is the recycle count for tip jar goal #' + jargoalnum + ' and has be be a number from 0 to 99. Note since it is a REcycle count, the value should be "0" to use a goal once (default), "1" to use a goal twice, etc. For example, "/setjar' + jargoalnum + ' 300 0 Shirt Off" to set goal #' + jargoalnum + ' to be for "Shirt off" at 300 tokens, and only do the goal once with no recycle.', msguser, appNoticeColor); } else if (jargoalnum == currentGoal && commandvar1 < currentGoalTips) { cb.sendNotice('The current tip jar goal cannot be updated to an amount less than what has already been tipped for this goal. The "/restartgoal" command can be used to clear the current goal tip jar goal totals, but this is not recommended.', msguser, appNoticeColor); } else if (!msgarray[3]) { cb.sendNotice('The third parameter is the description of tip jar goal ' + jargoalnum + ' (can be multiple words). For example, "/setjar' + jargoalnum + ' 300 0 give a massage" to set goal ' + jargoalnum + ' to give a massage at 300 tokens, and only do the goal once with no recycle.', msguser, appNoticeColor); } else if (jargoalnum > 1 && !tipjarGoalArray.amt[jargoalnum-2] >= 1) { cb.sendNotice('You cannot skip goal levels. If setting a value for level ' + jargoalnum + ', then level ' + (jargoalnum-1) + ' must already have a goal value defined.', msguser, appNoticeColor); } else { var jarlabel = ''; for (let m3 = 3; m3 < msgarray.length; m3++) { if (m3 == 3) { jarlabel = msgarray[m3]; } else { jarlabel += " " + msgarray[m3]; } } tipjarGoalArray.amt[jargoalnum-1] = commandvar1; tipjarGoalArray.recyc[jargoalnum-1] = commandvar2; tipjarGoalArray.desc[jargoalnum-1] = jarlabel; updateTipjarGoal(); cb.sendNotice('Tip Jar Goal #' + jargoalnum + ' was updated to the amount of ' + commandvar1 + ', a description of "' + jarlabel + '", and a recycle count of ' + commandvar2 + '.', msguser, appNoticeColor); cb.sendNotice(msguser + ' added/updated Tip Jar Goal #' + jargoalnum + ' to the goal count of ' + commandvar1 + ', a description of "' + jarlabel + '", and a recycle count of ' + commandvar2 + '.', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' added/updated Tip Jar Goal #' + jargoalnum + ' to the goal count of ' + commandvar1 + ', a description of "' + jarlabel + '", and a recycle count of ' + commandvar2 + '.', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setjarX" command is for use with the Tip Jar app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/rmvjar': { recognizedcmd = true; if (whichApp == 'tipjar') { if (msgisbc || ismodlvlmsg2) { var rmvjargoalnum = msgarray[1]; if (isNaN(rmvjargoalnum) || rmvjargoalnum < 1 || rmvjargoalnum > 10) { cb.sendNotice('The first parameter is the Tip Jar goal level being removed, and must be a number from 1 to 10 to indicate the goal level number that should be removed. For example, "/rmvjar 3" will remove the goal and description info for sequence goal level 3.', msguser, appNoticeColor); } else if (rmvjargoalnum > tipjarGoalArray.amt.length) { cb.sendNotice('There is no goal entry at level ' + rmvjargoalnum + ', there are currently only entries up to goal level ' + tipjarGoalArray.amt.length + '.', msguser, appNoticeColor); } else { tipjarGoalArray.amt.splice((rmvjargoalnum-1),1); tipjarGoalArray.desc.splice((rmvjargoalnum-1),1); tipjarGoalArray.recyc.splice((rmvjargoalnum-1),1); updateTipjarGoal(); cb.sendNotice('Tip Jar Goal Level #' + rmvjargoalnum + ' was removed from the goal list', msguser, appNoticeColor); cb.sendNotice(msguser + ' removed Tip Jar Goal Level #' + rmvjargoalnum + ' from the goal list', '', appNoticeColor, '', '', 'red'); cb.sendNotice(msguser + ' removed Tip Jar Goal Level #' + rmvjargoalnum + ' from the goal list', BC, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The "/rmvjar" command is for use with the Tip Jar app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/setjartext': { recognizedcmd = true; if (whichApp == 'tipjar') { if (msgisbc || ismodlvlmsg1) { tipjarSubjectText = rawmsg.substring(12).trim() if (tipjarSubjectText != '' && tipjarSubjectText != null) { changeRoomSubject(); } else { cb.sendNotice('No value was specified for the new Tip Jar room subject suffix.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The "/setjartxt" command is for use with the Tip Jar app, and that feature is not running.', msguser, appNoticeColor); } break; } case '/pause': { recognizedcmd = true; if (whichApp == 'tipjar') { if (msgisbc || ismodlvlmsg2) { if (tipJarRunning) { if (tipJarCurrentTokens > 0) { if (!tipJarPaused) { pauseTipJar(); cb.sendNotice(botName + 'You have paused the tip jar. You can resume the draining of the jar with the command "/resume".', msguser, appNoticeColor); if (msguser != BC) { cb.sendNotice(botName + '' + msguser + ' has paused the tip jar. The draining of the jar can be resumed with the command "/resume".', BC, appNoticeColor); } } else { cb.sendNotice(botName + 'The Tip Jar is already paused.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Tip Jar is empty and cannot be paused.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Tip Jar is not draining, so it cannot be paused.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/pause" command is for use with the Tip Jar, and the Tip Jar feature is not running.', msguser, appNoticeColor); } break; } case '/resume': { recognizedcmd = true; if (whichApp == 'tipjar') { if (msgisbc || ismodlvlmsg2) { if (tipJarPaused) { resumeTipJar(); cb.sendNotice(botName + 'You have resumed the tip jar.', msguser, appNoticeColor); if (msguser != BC) { cb.sendNotice(botName + '' + msguser + ' has resumed the tip jar.', BC, appNoticeColor); } } else { cb.sendNotice(botName + 'The Tip Jar is not paused.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The "/resume" command is for use with the Tip Jar, and the Tip Jar 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,msgisextfan1,msgisextfan2,msgisvip,'broadcaster',true); 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 (whichApp == 'ticket') { if (ticketHolderList.length > 0) { if (msgarray[1] == 'a' || msgarray[1] == 'A' || msgarray[1] == 'alpha') { let sortedticketlist = ticketHolderList.slice(); sortedticketlist.sort(); cb.sendNotice(botName + 'Alphabetic listing of users currently in the ticket holder list (' + countTickets + ' ticket holders) :\n' + cbjs.arrayJoin(sortedticketlist, ', ') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'Users currently in the ticket holder list, 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); if (enablePresales) { cb.sendNotice('You can use the command "/presalelist" to see the list of viewers who have bought a pre-sale ticket', msguser, appNoticeColor); } } break; } case '/ticketscb': { recognizedcmd = true; if (whichApp == 'ticket' || enablePresales) { let numberwithaccess = cb.limitCam_allUsersWithAccess().length; if (numberwithaccess > 0) { if (msgarray[1] == 'a' || msgarray[1] == 'A' || msgarray[1] == 'alpha') { let sortedcbaccesslist = cb.limitCam_allUsersWithAccess().slice(); sortedcbaccesslist.sort(); cb.sendNotice(botName + 'Alphabetic listing of users currently with access to the Hidden Ticket Show (' + numberwithaccess + ' users added) :\n' + cbjs.arrayJoin(sortedcbaccesslist, ', ') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'Users currently with access to the Hidden Ticket Show, in order of when added (' + numberwithaccess + ' users added). Use the "alpha" parameter for a sorted list : \n' + cbjs.arrayJoin(cb.limitCam_allUsersWithAccess(), ', ') + '\nEnd of List', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No users granted CB hidden show access yet.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show feature has not yet been started and presales are not enabled.', msguser, appNoticeColor); } break; } case '/previewers': { recognizedcmd = true; if (whichApp == 'ticket') { if (freePreviewLength > 0) { if (ticketShowPreViewerList.length > 0) { if (msgarray[1] == 'a' || msgarray[1] == 'A' || msgarray[1] == 'alpha') { var sortedpvlist = ticketShowPreViewerList.slice(); sortedpvlist.sort(); cb.sendNotice(botName + 'Alphabetic listing of users currently seeing a free preview of the Ticket Show (' + sortedpvlist.length + ' previewers) : \n' + cbjs.arrayJoin(sortedpvlist, ', ') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'Users currently seeing a free preview of the Ticket Show, in order of when added (' + ticketShowPreViewerList.length + ' previewers). Use the "alpha" parameter for a sorted list : \n' + cbjs.arrayJoin(ticketShowPreViewerList, ', ') + '\nEnd of List', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No free preview viewers at this time.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Free Preview is disabled.', 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' || presalesToggle) { if (ticketHolderList.length > 0) { cb.sendNotice('Copy and paste the below command into the chat to backup the current presale or 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 '/ticketsales': case '/startsales': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (validateFeature('ticket')) { if (whichApp === 'ticket') { cb.sendNotice(botName + 'The Ticket Show feature is already active, command ignored.', msguser, appNoticeColor); } else { if (cb.limitCam_isRunning()) { cb.sendNotice('The Show is already hidden for a Peep Show or private show, cannot start ticket sales.', msguser, appNoticeColor); } else { setAppFeature('ticket', msguser); } } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/usegift': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { if (msgarray[1].toLowerCase() != 'on' && msgarray[1].toLowerCase() != 'off') { cb.sendNotice('The value ' + msgarray[1] + ' is not a valid option for the "/usegift" command, please try again, valid values are "on" or "off".', msguser, appNoticeColor); } else if (cb.settings.ticketShowFanAppreciation == 'Yes' && msgarray[1].toLowerCase() == 'on') { cb.sendNotice('Gifting of Tickets is not enabled for a Fan Appreciation show.', msguser, appNoticeColor); } else { if (msgarray[1].toLowerCase() == 'on') { if (ticketShowAllowGift == 'Yes') { cb.sendNotice('The Gifting of Tickets is already enabled.', msguser, appNoticeColor); } else { ticketShowAllowGift = 'Yes'; cb.sendNotice('You have enabled the Gifting of Tickets.', msguser, appNoticeColor); if (msguser != BC) { cb.sendNotice(msguser + 'has enabled the Gifting of Tickets.', BC, appNoticeColor); } } } else if (msgarray[1].toLowerCase() == 'off') { if (ticketShowAllowGift == 'No') { cb.sendNotice('The Gifting of Tickets is already disabled.', msguser, appNoticeColor); } else { ticketShowAllowGift = 'No'; cb.sendNotice('You have disabled the Gifting of Tickets.', msguser, appNoticeColor); if (msguser != BC) { cb.sendNotice(msguser + 'has disabled the Gifting of Tickets.', BC, appNoticeColor); } } } } } else { cb.sendNotice('No parameter was specified for the "/usegift" command, valid values are "on" or "off".', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/useot': { recognizedcmd = true; if (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { if (msgarray[1].toLowerCase() != 'on' && msgarray[1].toLowerCase() != 'off') { cb.sendNotice('The value ' + msgarray[1] + ' is not a valid option for the "/useot" command, please try again, valid values are "on" or "off".', msguser, appNoticeColor); } else if (cb.settings.ticketShowFanAppreciation == 'Yes' && msgarray[1].toLowerCase() == 'on') { cb.sendNotice('Outstanding Tickets are not enabled for a Fan Appreciation show.', msguser, appNoticeColor); } else { setTicketShowOtToggle(msgarray[1].toLowerCase(),msguser); } } else { cb.sendNotice('No parameter was specified for the "/usegift" command, valid values are "on" or "off".', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/otlist': { recognizedcmd = true; var otlistcmdmsg = 'Outstanding Ticket List:'; var sortedotlist = outstandingTicketArray.slice(); sortedotlist.sort(); if (msgisbc || ismodlvlmsg1) { otlistcmdmsg += '\nIf displaying list to copy and paste into the app launch page, use command /otlistexp to get a better formatted list.'; } otlistcmdmsg += '\nUsers currently on the Outstanding Ticket List (sorted alphabetically): ' + sortedotlist.length; otlistcmdmsg += '\n' + (sortedotlist.length > 0 == true ? cbjs.arrayJoin(sortedotlist, ', ') : 'No outstanding ticket holders.'); otlistcmdmsg += '\nEnd of List'; cb.sendNotice(otlistcmdmsg, msguser, appNoticeColor); break; } case '/otlistexp': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { cb.sendNotice('Outstanding Ticket List (copy and paste into the app launch page, setting "6X": ' + outstandingTicketArray.length, msguser, appNoticeColor); cb.sendNotice((outstandingTicketArray.length > 0 == true ? cbjs.arrayJoin(outstandingTicketArray, ',') : 'No outstanding ticket holders.'), msguser); cb.sendNotice('End of List', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/otchanges': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { if (otChangesArray.name.length > 0) { var outstringotc = ''; for (var oti = 0; oti < otChangesArray.name.length; oti++) { if (otChangesArray.name[oti] == null) { break } else { outstringotc += (oti > 0 ? ',' : '') + otChangesArray.name[oti] + '(' + otChangesArray.type[oti] + ')'; } } cb.sendNotice('Listing of Changes to the Outstanding Ticket List : \n' + outstringotc + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice('No entries have been added to the Outstanding Ticket Change list.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, 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 (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 '/previewlength': { recognizedcmd = true; if (whichApp == 'ticket' && showStage != 'ticketsales') { cb.sendNotice('The preview length can only be changed up until the show starts.', msguser, appNoticeColor); } else { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { if (msgarray[1] != '0' && msgarray[1] != '10sec' && msgarray[1] != '20sec' && msgarray[1] != '30sec' && msgarray[1] != '1min' && msgarray[1] != '2min' && msgarray[1] != '3min' && msgarray[1] != '4min' && msgarray[1] != '5min') { cb.sendNotice(botName + 'The value entered for the new preview length is not valid, please make sure to use one of the following values: "0", "10sec", "20sec", "30sec", "1min", "2min", "3min", "4min", or "5min".', msguser, appNoticeColor); } else { switch (msgarray[1]) { case '0': { freePreviewLength = 0; freePreviewLengthText = 'No Preview'; break; } case '10sec': { freePreviewLength = 10; freePreviewLengthText = '10 seconds'; break; } case '20sec': { freePreviewLength = 20; freePreviewLengthText = '20 seconds'; break; } case '30sec': { freePreviewLength = 30; freePreviewLengthText = '30 seconds'; break; } case '1min': { freePreviewLength = 60; freePreviewLengthText = '1 minute'; break; } case '2min': { freePreviewLength = 120; freePreviewLengthText = '2 minutes'; break; } case '3min': { freePreviewLength = 180; freePreviewLengthText = '3 minutes'; break; } case '4min': { freePreviewLength = 240; freePreviewLengthText = '4 minutes'; break; } case '5min': { freePreviewLength = 300; freePreviewLengthText = '5 minutes'; break; } } cb.sendNotice(botName + 'The Ticket Show Free Preview length has been updated to "' + freePreviewLengthText + '".', msguser, appNoticeColor); if (msguser != BC) { cb.sendNotice(botName + 'The Ticket Show Free Preview length has been updated to "' + freePreviewLengthText + '".', BC, appNoticeColor); } } } else { cb.sendNotice(botName + 'No parameter was specified for the new preview length.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, 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 (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (cmdval) { var cmdvalsplit = cmdval.split(listregexp); if (cmdvalsplit.length > 1) { cb.sendNotice(botName + 'Adding multiple users to the ticket show list.', msguser, appNoticeColor); for (var i = 0; i < cmdvalsplit.length; i++) { if (cmdvalsplit[i]) { let nametoadd = cmdvalsplit[i].toLowerCase(); if (nametoadd.charAt(0) == '@') { nametoadd = nametoadd.substring(1); } 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 { let nametoadd2 = msgarray[1].toLowerCase(); if (nametoadd2.charAt(0) == '@') { nametoadd2 = nametoadd2.substring(1); } if (cb.limitCam_userHasAccess(nametoadd2)) { if (cbjs.arrayContains(ticketHolderList,nametoadd2)) { cb.sendNotice(botName + 'Note: User ' + nametoadd2 + ' is already in the ticket show list and has access to the hidden show according to CB.', msguser, appNoticeColor); } else if (cbjs.arrayContains(freePreviewUserArray,nametoadd2)) { cb.sendNotice(botName + 'Note: Switching ' + nametoadd2 + ' from free preview to ticket show access.', msguser, appNoticeColor); addRmvTicket('add',nametoadd2,'',0,nametoadd2); } else { cb.sendNotice(botName + 'Note: User ' + nametoadd2 + ' already has access to the hidden show according to CB, but has not been added to the ticket list in the UltraApp. \nThey should be able to see the show, but will not show up when you use /tickets to display the ticket holder list.', msguser, appNoticeColor); } } else if (cbjs.arrayContains(ticketHolderList,nametoadd2)) { cb.sendNotice(botName + 'Note: User ' + nametoadd2 + ' is already in the Ticket Show List, but there is a problem with CB that has not given them access to the show. \nPlease try manually removing them with the /rmv command, and then adding them back with the /add command.', msguser, appNoticeColor); } else { addRmvTicket('add',nametoadd2,'',0,nametoadd2); } } } else { if (cb.limitCam_userHasAccess(msguser)) { if (cbjs.arrayContains(ticketHolderList,msguser)) { cb.sendNotice(botName + 'Note: You are already in the ticket show list and have access to the hidden show according to CB.', msguser, appNoticeColor); } else if (cbjs.arrayContains(freePreviewUserArray,msguser)) { cb.sendNotice(botName + 'Note: Switching you from free preview to ticket show access.', msguser, appNoticeColor); addRmvTicket('add',msguser,'',0,msguser); } else { cb.sendNotice(botName + 'Note: You already have access to the hidden show according to CB, but have not been added to the ticket list in the UltraApp. \nYou should be able to see the show, but you will not show up when you use /tickets to display the ticket holder list.', msguser, appNoticeColor); } } else if (cbjs.arrayContains(ticketHolderList,msguser)) { cb.sendNotice(botName + 'Note: You are already in the Ticket Show List, but there is a problem with CB that has not given you access to the show. \nPlease try manually removing yourself with the /rmv command, and then adding them back with the /add command.', msguser, appNoticeColor); } else { addRmvTicket('add',msguser,'',0,msguser); } } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice(botName + '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 (whichApp == 'ticket') { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (msgarray[1]) { let nametormv = msgarray[1].toLowerCase(); if (nametormv.charAt(0) == '@') { nametormv = nametormv.substring(1); } if (cb.limitCam_userHasAccess(nametormv) || cbjs.arrayContains(ticketHolderList,nametormv)) { addRmvTicket('rmv',nametormv,'',0,nametormv); } else { cb.sendNotice(botName + 'User ' + nametormv + ' is not in the UltraApp Ticket Show List and does not currently have access to the hidden show.', msguser, appNoticeColor); } } else { if (cb.limitCam_userHasAccess(msguser) || cbjs.arrayContains(ticketHolderList,msguser)) { addRmvTicket('rmv',msguser,'',0,msguser); } else { cb.sendNotice(botName + 'You are not in the UltraApp Ticket Show List and do not currently have access to the hidden show.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The ticket show feature is not yet active in the UltraApp.', msguser, appNoticeColor); } break; } case '/giftticket': { recognizedcmd = true; if (whichApp == 'ticket') { if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (ticketShowAllowGift == 'Yes') { if (cbjs.arrayContains(ticketShowExtraTickets.name,msguser)) { if (msgarray[1]) { let exttindex = ticketShowExtraTickets.name.indexOf(msguser); if (ticketShowExtraTickets.count[exttindex] > 0) { let nametogift = msgarray[1].toLowerCase(); if (nametogift.charAt(0) == '@') { nametogift = nametogift.substring(1); } giftTicket(msguser,nametogift); ticketShowExtraTickets.count[exttindex]--; cb.sendNotice('You have gifted a ticket to ' + nametogift + '. Thank you for your generosity.', msguser, appNoticeColor, '', 'bold'); cb.sendNotice('Moderators: ' + msguser + ' has gifted a ticket to ' + nametogift + '.', '', appNoticeColor, '', '', 'red'); cb.sendNotice('Broadcaster: ' + msguser + ' has gifted a ticket to ' + nametogift + '.', BC, appNoticeColor); } else { cb.sendNotice('Sorry, you do not have any extra tickets remaining.', msguser, appNoticeColor); } } else { cb.sendNotice('No parameter was specified for who to gift the ticket to, please specify the name of a user currently in the room.', msguser, appNoticeColor); } } else { cb.sendNotice('Sorry, you have not purchased any extra tickets.', msguser, appNoticeColor); } } else { cb.sendNotice('Sorry, ' + bcText + ' has not enabled the use of gifting tickets for this show.', msguser, appNoticeColor); } } else { cb.sendNotice('Tickets cannot be gifted for a Fan Appreciation show.', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/givemyticketto': { recognizedcmd = true; if (whichApp == 'ticket') { if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (ticketShowAllowGift == 'Yes') { if (msgarray[1]) { if (cb.limitCam_userHasAccess(msguser) && showStage == 'ticketsales' && !cbjs.arrayContains(ticketShowPreViewerList,msguser)) { let nametogive = msgarray[1].toLowerCase(); if (nametogive.charAt(0) == '@') { nametogive = nametogive.substring(1); } giveAwayTicket(msguser,nametogive); cb.sendNotice('You have gifted your ticket to ' + nametogive + '. You will now be removed from the show. Thank you for your generosity.',msguser,appNoticeColor,'','bold'); cb.sendNotice('Mods: ' + msguser + ' has gifted their own ticket to ' + nametogive + '.', '', appNoticeColor, '', '', 'red'); cb.sendNotice('Broadcaster: ' + msguser + ' has gifted their own ticket to ' + nametogive + '.', BC, appNoticeColor); } else { cb.sendNotice('Sorry, you do not have a ticket purchased or the show has already started.', msguser, appNoticeColor); } } else { cb.sendNotice('No parameter was specified for who to give the ticket to, please specify the name of a user currently in the room.', msguser, appNoticeColor); } } else { cb.sendNotice('Sorry, ' + bcText + ' has not enabled the gifting of tickets for this show.', msguser, appNoticeColor); } } else { cb.sendNotice('Tickets cannot be gifted for a Fan Appreciation show.', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', 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 '/useticket': { recognizedcmd = true; if (whichApp == 'ticket') { if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (ticketShowOtToggle) { if (cbjs.arrayContains(ticketHolderList,msguser)) { cb.sendNotice('You already have a ticket to the show.', msguser, appNoticeColor); } else { if (cbjs.arrayContains(outstandingTicketArray,msguser)) { addRmvOutstandingTicket('rmv',msguser); addRmvTicket('add',msguser,'',0,msguser); cb.sendNotice('Welcome ' + msguser + ', your Outstanding Ticket has been redeemed.', '', ticketHolderBgColor, '', 'bold'); cb.sendNotice('Broadcaster: ' + msguser + ' has used their Outstanding Ticket to join this show. \nThey should be removed from the permanent OT list on the bot start page.', BC, appNoticeColor); cb.sendNotice('Mods: ' + msguser + ' has used their Outstanding Ticket. \n' + bcText + ' has been notified to remove them from the permanent OT list on the bot start page.', BC, appNoticeColor, '', '', 'red'); } else { cb.sendNotice('Sorry, you do not have an outstanding ticket available.', msguser, appNoticeColor); } } } else { cb.sendNotice('Sorry, ' + bcText + ' has not enabled the use of outstanding tickets for this show.', msguser, appNoticeColor); } } else { cb.sendNotice('Outstanding Ticket features are not enabled for a Fan Appreciation Show.', msguser, appNoticeColor); } } else if (presalesToggle) { cb.sendNotice('Outstanding tickets are not used during pre-sales, please wait until the Ticket App feature is started to redeem your ticket.', msguser, appNoticeColor); } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/saveticket': { recognizedcmd = true; if (whichApp == 'ticket') { if (cb.settings.ticketShowFanAppreciation != 'Yes') { if (ticketShowOtToggle) { if (cb.limitCam_userHasAccess(msguser) && (showStage == 'ticketsales' || showStage == 'presales')) { saveTicket(msguser); cb.sendNotice('Your ticket from this show has been saved and you have been removed from the ticket list for this show.', msguser, appNoticeColor, '', 'bold'); cb.sendNotice('Broadcaster: ' + msguser + ' has saved their ticket from this show to the outstanding ticket list. They should be added to the permanent OT list on the bot start page upon next restart.', BC, appNoticeColor); cb.sendNotice('Mods: ' + msguser + ' has saved their ticket from this show to the outstanding ticket list. ' + bcText + ' has been notified to add them to the permanent OT list on the bot start page.', '', appNoticeColor, '', '', 'red'); } else { cb.sendNotice('Sorry, you have no ticket purchase to save, or the show has already started.', msguser, appNoticeColor); } } else { cb.sendNotice('Sorry, ' + bcText + ' has not enabled the ability to save an outstanding tickets for this show.', msguser, appNoticeColor); } } else { cb.sendNotice('Outstanding Ticket features are not enabled for a Fan Appreciation Show.', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/addot': { recognizedcmd = true; if (ticketShowOtToggle) { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if(msgarray[1]) { let addotname = msgarray[1].toLowerCase(); if (addotname.charAt(0) == '@') { addotname = addotname.substring(1); } if (!cbjs.arrayContains(outstandingTicketArray,addotname)) { addRmvOutstandingTicket('add',addotname); cb.sendNotice('User ' + addotname + ' has been added to the Outstanding Ticket List.', msguser, appNoticeColor); } else { cb.sendNotice('Cannot add, user is already in the Outstanding Ticket List.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice('The Outstanding Ticket list feature is disabled. It can be enabled with the command "/useot on".', msguser, appNoticeColor); } break; } case '/rmvot': { recognizedcmd = true; if (whichApp == 'ticket') { if (ticketShowOtToggle) { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (msgarray[1]) { let rmvotname = msgarray[1].toLowerCase(); if (rmvotname.charAt(0) == '@') { rmvotname = rmvotname.substring(1); } if (cbjs.arrayContains(outstandingTicketArray,rmvotname)) { addRmvOutstandingTicket('rmv', msgarray[1]); cb.sendNotice('User ' + rmvotname + ' has been removed from the Outstanding Ticket List.', msguser, appNoticeColor); } else { cb.sendNotice('Cannot remove, user is not in the Outstanding Ticket List.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice('The Outstanding Ticket list feature is disabled. It can be enabled with the command "/useot on".', msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Ticket Show feature is not running.', msguser, appNoticeColor); } break; } case '/startshow': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var enteredby = msguser; if (msgisbc) { enteredby = bcText; } if (whichApp == 'ticket') { if (!cb.limitCam_isRunning() && ticketShowEnded == false) { startTicketShow(enteredby); if (presalesToggle) { setPresalesToggle('off', enteredby, 0); } } 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 if (whichApp == 'peep') { if (!cb.limitCam_isRunning() && peepshowEnded == false) { startPeepShow(enteredby); } else if (!cb.limitCam_isRunning() && peepshowEnded == true) { cb.sendNotice('The Peep Show was already started and stopped, please use the command "/peeprestart" to resume the hidden show.', msguser, appNoticeColor); } else { cb.sendNotice('The Peep Show is already underway.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show / Peep 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 if (whichApp == 'peep') { if (cb.limitCam_isRunning()) { if (showStage === 'peepshow') { peepShowEnding(soenteredby); } else { cb.sendNotice('The /showwarn or /showover command is used for a first warning. The show has already progressed past the point this command should be used. To end the show and return to a public broadcast, use the /stopshow command.', msguser, appNoticeColor); } } else { cb.sendNotice('The Peep Show has not been started or has already ended.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show / Peep 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 (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 if (whichApp == 'peep') { if (cb.limitCam_isRunning()) { stopPeepShowSales(seenteredby); } else { cb.sendNotice('The Peep Show has not been started or has already ended.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show / Peep 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 if (whichApp == 'peep') { if (cb.limitCam_isRunning()) { stopPeepShow(ssenteredby); } else { cb.sendNotice('The Peep Show has not been started or has already ended.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show / Peep Show feature is not running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/ticketsubject': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { if (msgarray[1]) { ticketSubjectText = rawmsg.substring(15).trim(); if (whichApp == 'ticket') { changeRoomSubject(); } else { cb.sendNotice('Ticket Show description was updated but the Ticket Show feature is not yet active, therefore Room Title not changed.', msguser, appNoticeColor); } } else { cb.sendNotice('A new Ticket Show description was not specified, command ignored. Please include the new description as a parameter for the command', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/ctsubject': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { if (msgarray[1]) { ticketSubjectText = rawmsg.substring(11).trim(); if (whichApp == 'ticket') { changeRoomSubject(); } else { cb.sendNotice('Ticket Show description was updated but the Ticket Show feature is not yet active, therefore Room Title not changed.', msguser, appNoticeColor); } } else { cb.sendNotice('A new Ticket Show description was not specified, command ignored. Please include the new description as a parameter for the command', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, 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' || enablePresales) && (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; } //********* Pre-sales Commands case '/presalelist': { recognizedcmd = true; if (whichApp == 'ticket') { cb.sendNotice(botName + 'The Hidden Ticket Show feature has been started, you can view ticket holders with the "/tickets" command.', msguser, appNoticeColor); } else { if (enablePresales) { if (ticketHolderList.length > 0) { if (msgarray[1] === 'a' || msgarray[1] === 'A' || msgarray[1] === 'alpha') { let sortedpresalelist = ticketHolderList.slice(); sortedpresalelist.sort(); cb.sendNotice(botName + 'Alphabetic listing of users who have purchased a pre-sale ticket (' + sortedpresalelist.length + ' ticket holders): \n' + cbjs.arrayJoin(sortedpresalelist, ', ') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'Users who have purchased a pre-sale ticket, in order of when added (' + ticketHolderList.length + ' ticket holders). Use the "alpha" parameter (or "a") for a sorted list: \n' + cbjs.arrayJoin(ticketHolderList, ', ') + '\nEnd of List', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No ticket buyers yet.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Pre-sales feature is not currently enabled to be used in the UltraApp, you can enable it with the command "/uapresale on". Once enabled, you can start selling pre-sale ticekts with the command "/startpresale".', msguser, appNoticeColor); } } break; } case '/uapresales': case '/uapresale': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if ((msgarray[1] == 'on' || msgarray[1] == 'ON' || msgarray[1] == 'On') && enablePresales) { cb.sendNotice('UltraApp pre-sales are already enabled.', msguser, appNoticeColor); } else if ((msgarray[1] == 'off' || msgarray[1] == 'OFF' || msgarray[1] == 'Off') && !enablePresales) { cb.sendNotice('UltraApp pre-sales are already disabled.', msguser, appNoticeColor); } else if ((msgarray[1] == 'on' || msgarray[1] == 'ON' || msgarray[1] == 'On') && !enablePresales) { enablePresales = true; cb.sendNotice('UltraApp pre-sales have been enabled. Please be sure you are not also configured for Pre-sales in the Fembot. In future shows, UltraApp pre-sales can be enabled automatically by selecting "UltraApp" as the location for Pres-sales on the UltraApp launch page. You can start selling pre-sale tickets with the command "/startpresale".', msguser, appNoticeColor); } else if ((msgarray[1] == 'off' || msgarray[1] == 'OFF' || msgarray[1] == 'Off') && enablePresales) { enablePresales = false; if (presalesToggle) { setPresalesToggle('off', msguser, 0); } cb.sendNotice('UltraApp pre-sales have been disabled.', msguser, appNoticeColor); } else { cb.sendNotice('Invalid parameter for this command, valid values are "on" or "off".', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/startpresales': case '/startpresale': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg2) { if (whichApp == 'ticket') { cb.sendNotice(botName + 'Unable to start pre-sales, the actual ticket show feature is already started.', msguser, appNoticeColor); } else if (msgarray[1] == 'on' && cb.settings.ticketShowFanAppreciation == 'Yes') { cb.sendNotice(botName + 'Unable to start pre-sales since the Ticket Show feature is configured for a Fan Appreciation Show.', msguser, appNoticeColor); } else { if (presalePrice <= 0) { presalePrice = cb.settings.ticketShowPresalePrice; } if (presalePrice > 0) { if (ticketPrice <= 0) { cb.sendNotice(botName + 'Unable to start pre-sales, the actual ticket show price must be set first using the command "/ticketprice xx" where xx will be the the Ticket App show price.', msguser, appNoticeColor); } else if (presalePrice > ticketPrice) { cb.sendNotice(botName + 'Unable to start pre-sales, the Pre-Sales Price is greater than the ticket show price.', msguser, appNoticeColor); } else { setPresalesToggle('on', msguser, presalePrice); } } else { cb.sendNotice(botName + 'Unable to start pre-sales, a pre-sale ticket price must be set first using the command "/presaleprice xx" where xx is in the initial price to start selling pre-sales tickets at.', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'Pre-sales not started as they are disabled in the UltraApp (Note - this is expected if Fembot pre-sales are used).', msguser, appNoticeColor); } break; } case '/stoppresales': case '/stoppresale': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg2) { setPresalesToggle('off', msguser, 0); } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'Pre-sales not stopped as they are disabled in the UltraApp (Note - this is expected if Fembot pre-sales are used).', msguser, appNoticeColor); } break; } case '/usepresale': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg2) { cb.sendNotice(botName + 'Pre-sales not started/stopped. Note that the commands have changed to "/startpresale" and "/stoppresale", please use these commands instead', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'Pre-sales not started/stopped as they are disabled and the commands have changed. To enable on presales, ensure the UltraApp is selected for where Pre-sales are performed on the launch page (or using the command "/uapresale on"), and then either select a pre-sale mode on the launch page, or start and stop pre-sales with the commands "/startpresale" or "/stoppresale".', msguser, appNoticeColor); } break; } case '/psprice': case '/presaleprice': { recognizedcmd = true; if (whichApp == 'ticket') { cb.sendNotice('Cannot update pre-sale price, ticket show feature has already been started.', msguser, appNoticeColor); } else { if (enablePresales) { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsChgPrice === 'Yes')) { var psnumprice = parseInt(msgarray[1]) if (isNaN(psnumprice)) { cb.sendNotice('The value entered for the Ultra-App pre-sale ticket price is not numeric, please try again.', msguser, appNoticeColor); break; } else if (psnumprice < 1 || psnumprice > 1000) { cb.sendNotice('The value entered for the Ultra-App pre-sale ticket price is outside allowable values from 1 to 1000, please try again.', msguser, appNoticeColor); } else if (psnumprice > ticketPrice) { cb.sendNotice('The value entered for the Ultra-App pre-sale ticket price is higher than the actual ticket price of ' + ticketPrice + '. Please update the ticket price first using the command "/ticketprice [newprice]".', msguser, appNoticeColor); } else { if (psnumprice < presalePrice) { cb.sendNotice('Warning: Value is updated but this price is lower than the previous Ultra-App pre-sale price of ' + presalePrice + '. It is not advisiable to decrease the presale price once tickets have been sold.', msguser, appNoticeColor); } if (presalesToggle) { presalePriceChange(psnumprice,msguser); cb.sendNotice('The Ultra-App Pre-sale Ticket Price has been updated to ' + psnumprice + ' tokens, all tips of at least this amount will add a user to the Pre-sale Ticket List. You can view the pre-sales list with the command /presalelist. UltraApp Pre-sales are automaticlly added to the UltraApp Ticket Show when the app feature is changed to the ticket show.', msguser, appNoticeColor); } else { presalePrice = psnumprice; cb.sendNotice('The Ultra-App Pre-sale Ticket Price has been set to ' + psnumprice + ' tokens, and the pre-sale can now be started with the command "/startpresale". ', msguser, appNoticeColor); } } } else { cb.sendNotice(noticeOnlyBCMod3P, msguser, appNoticeColor); } } } break; } case '/pspt': case '/pspricetimer': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg1) { if (presalesToggle) { var validtimer = 0; var psnumtimer = parseInt(msgarray[1]) if(isNaN(psnumtimer)) { cb.sendNotice('The timer value entered for the pre-sale price change is not numeric, please try again. The syntax for the command is "/presalepricetimer [t] [p]", where [t] is the number of minutes to use for the timer, and [p] is the new ticket price that will be used when the timer expires.', msguser, appNoticeColor); } else if (psnumtimer < 1 || psnumtimer > 60) { cb.sendNotice('The timer value entered for the pre-sale price change is outside allowable values from 1 to 60 minutes, please try again. The syntax for the command is "/presalepricetimer [t] [p]", where [t] is the number of minutes to use for the timer, and [p] is the new ticket price that will be used when the timer expires.', msguser, appNoticeColor); } else if (presaleMinsRemain > 0 || presaleSecsRemain > 0) { cb.sendNotice('A timer is already running for the next price change. You can add time to the timer with the command "/presaleaddtime [t]" where [t] is the number of minutes to add, and can be positive to add time or negative to subtract time.', msguser, appNoticeColor); } else if (!presalesToggle) { cb.sendNotice('The Pre-sales function has not been turned on, you can use the command "/startpresale" to enable this function if an initial price has been set.', msguser, appNoticeColor); } else { validtimer = 1; } var psptnumprice = parseInt(msgarray[2]) if(isNaN(psptnumprice)) { cb.sendNotice('The new pre-sales price value entered for the price change timer is not numeric, please try again.', msguser, appNoticeColor); validtimer = 0; } else if(psptnumprice < 1 || psptnumprice > 1000) { cb.sendNotice('The new pre-sales price value entered for the price change timer is outside allowable values from 1 to 1000, please try again.', msguser, appNoticeColor); validtimer = 0; } else if(psptnumprice > cb.settings.ticketShowPrice) { cb.sendNotice('The new pre-sales price value entered of ' + psptnumprice + ' should not be greater than the ticket show price of ' + cb.settings.ticketShowPrice + ', please try again. If the ticket show price is incorrect or not set, it can be set using the command "/ticketprice pp", where pp is the planned ticket show price.', msguser, appNoticeColor); validtimer = 0; } else { if (validtimer = 1) { presaleMinsRemain = psnumtimer; nextPresalePrice = psptnumprice; presaleAutoTimer(presaleMinsRemain); cb.sendNotice('The timer has been started to update the pre-sale price to ' + psptnumprice + ' tokens in ' + psnumtimer + ' minutes.', msguser, appNoticeColor); } } } else { cb.sendNotice('The UltraApp Pre-sales have not been started, you can use the command "/uapresale on" to enable this function if an initial price has been set.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } break; } case '/presalestarttimer': case '/presaletimer': { recognizedcmd = true; if (enablePresales) { if (presalesToggle) { if (msgisbc || ismodlvlmsg1) { var pssnumtimer = parseInt(msgarray[1]) if(isNaN(pssnumtimer)) { cb.sendNotice('The value entered for the minutes to use for the pre-sale timer is not numeric, please try again.', msguser, appNoticeColor); break; } else if(pssnumtimer < 1 || pssnumtimer > 60) { cb.sendNotice('The value entered for the minutes to use for the pre-sale timer is outside allowable values from 1 to 60, please try again.', msguser, appNoticeColor); } else { presaleAutoTimer(pssnumtimer); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Pre-sales are not active.', msguser, appNoticeColor); } } break; } case '/psat': case '/presaleaddtime': { recognizedcmd = true; if (enablePresales) { if (presalesToggle) { if (msgisbc || ismodlvlmsg1) { var psanumtimer = parseInt(msgarray[1]) if (!msgarray[1]) { cb.sendNotice('Invalid command, you must specify the amount of time to add to (or subtract from) the Presale timer in minutes. Example: use "/presaleaddtime 3" to add 3 minutes to the timer. A negative number can be used to subtract time.', msguser, appNoticeColor); } else if (isNaN(psanumtimer)) { cb.sendNotice('The value entered for the time to add to (or subtract from) the pre-sale timer is not numeric, please try again.', msguser, appNoticeColor); } else if (psanumtimer == 0) { cb.sendNotice('The value entered for the time to add to the pre-sale timer cannot be zero, please try again.', msguser, appNoticeColor); } else if ((presaleMinsRemain + 1) + psanumtimer <= 0) { cb.sendNotice('The time to subtract is greater than the amount of time left. You can use "/presalestoptimer" to stop the timer.'); } else if ((presaleMinsRemain + 1) + psanumtimer > 120) { cb.sendNotice('The added time will increase the timer to greater than 2 hours, please use a smaller value.'); } else { presaleAddTime(psanumtimer, msguser); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Pre-sales are not active.', msguser, appNoticeColor); } } break; } case '/psstoptime': case '/presalestoptime': case '/presalestoptimer': { recognizedcmd = true; if (enablePresales) { if (presalesToggle) { if (msgisbc || ismodlvlmsg1) { stopPresaleTimer(msguser); } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice('The UltraApp Pre-sales are not active.', msguser, appNoticeColor); } } break; } case '/addps': case '/addpresale': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (presalesToggle) { if (cmdval) { var apscmdvalsplit = cmdval.split(listregexp); if (apscmdvalsplit.length > 1) { cb.sendNotice('Adding multiple users to the Pre-sale list.', msguser, appNoticeColor); for (var apsidx = 0; apsidx < apscmdvalsplit.length; apsidx++) { if (apscmdvalsplit[apsidx]) { let addnametops = apscmdvalsplit[apsidx].toLowerCase(); if (addnametops.charAt(0) == '@') { addnametops = addnametops.substring(1); } if (!cb.limitCam_userHasAccess(addnametops)) { addRmvPresale('add',addnametops,'common',addnametops); cb.sendNotice('Added ' + addnametops + ' to the pre-sale list.', msguser); cb.sendNotice(msguser + ' has added you to the Pre-sale Ticket Show list.', addnametops, appNoticeColor); } else { cb.sendNotice(addnametops + ' is already on the ticket list. Skipping.', msguser); } } } cb.sendNotice('All users were added and notified.', msguser, appNoticeColor) cb.sendNotice(msguser + ' has added multiple users to the pre-sale list.\n' + 'Users added: ' + cbjs.arrayJoin(apscmdvalsplit, ', '), '', appNoticeColor, '', 'normal', 'red'); } else { let addnametops2 = msgarray[1].toLowerCase(); if (addnametops2.charAt(0) == '@') { addnametops2 = addnametops2.substring(1); } if (!cb.limitCam_userHasAccess(addnametops2)) { addRmvPresale('add',addnametops2,'common',addnametops2); } else { cb.sendNotice(addnametops2 + ' is already on the ticket list.', msguser); } } } else { cb.sendNotice('You didn\'t specify what user(s) you want to add to the Pre-sale list.', msguser, appNoticeColor); } } else { cb.sendNotice('Note: User not added to pre-sale ticket list, the UltraApp Pre-sales are not active.', msguser, appNoticeColor); } } } break; } case '/rmvps': case '/rmvpresale': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (presalesToggle) { if(msgarray[1]) { let rmvnamefromps = msgarray[1].toLowerCase(); if (rmvnamefromps.charAt(0) == '@') { rmvnamefromps = rmvnamefromps.substring(1); } if (cb.limitCam_userHasAccess(rmvnamefromps)) { addRmvPresale('rmv',rmvnamefromps,'common',rmvnamefromps); cb.sendNotice(botName + 'User ' + rmvnamefromps + ' has been removed from the Pre-Sale Ticket List.', msguser, appNoticeColor); } else { cb.sendNotice('UltraApp Note: User is not in the Pre-Sale Ticket List.',msguser,appNoticeColor); } } } else { cb.sendNotice(botName + 'The Ticket Show Pre-sales are not active.',msguser,appNoticeColor); } } } break; } case '/psmode': case '/chgpresalemode': { recognizedcmd = true; if (enablePresales) { if (msgisbc || ismodlvlmsg2) { if (presalesToggle) { var newpresalemodec = msgarray[1].toLowerCase(); if (newpresalemodec != 'manual' && newpresalemodec != 'timer' && newpresalemodec != 'count') { cb.sendNotice(botName + 'The value entered for the new mode is not valid, please try again using a value of "manual", "timer", or "count".', msguser, appNoticeColor); } else { if (newpresalemodec === presaleMode) { cb.sendNotice(botName + 'The value entered for the new mode is the same as the existing mode, command ignored.', msguser, appNoticeColor); } else { setPresaleMode(newpresalemodec,msguser); } } } else { cb.sendNotice(botName + 'The Ticket Show Pre-sales are not active.',msguser,appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } break; } case '/pstimeleft': case '/presaletimeleft': { recognizedcmd = true; if (enablePresales) { if (presalesToggle) { if (msgisbc || ismodlvlmsg1) { if (presaleMinsRemain >= 1 || presaleSecsRemain >= 1) { cb.sendNotice(presaleTimeLeft(), '', appNoticeColor, '', 'bold'); } else { cb.sendNotice(botName + 'A pre-sale timer is not running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Ticket Show Pre-sales are not active.',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 ' + spankGoalArray.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; } //********* Peep Show Commands case '/pv': case '/peepviewers': { recognizedcmd = true; if (whichApp == 'peep') { var peepshowlist = cb.limitCam_allUsersWithAccess().slice(); if (peepshowlist.length > 0) { if (msgarray[1] == 'a' || msgarray[1] == 'A' || msgarray[1] == 'alpha') { var sortedpeepshowlist = peepshowlist.slice(); sortedpeepshowlist.sort(); cb.sendNotice(botName + 'Alphabetic listing of users currently with access to the Peep Show (' + sortedpeepshowlist.length + ' viewers) : \n' + cbjs.arrayJoin(sortedpeepshowlist, ', ') + '\nEnd of List\nNote this does not viewers who have purchased time before the show starts (use /pb to see a list of people who have bought time for the show and how much time they have left).', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'Users currently with access to the Peep Show, in order of when added (' + peepshowlist.length + ' viewers). Use the "alpha" qualifier for a sorted list: \n' + cbjs.arrayJoin(peepshowlist, ', ') + '\nEnd of List\nNote that prior to the show starting, this will only show the "free" viewers. Use the "/pb" command to see a list of people who have bought time for the show and how much time they have left, before or during the show.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No Peep Show viewers at this time.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature has not yet been started.', msguser, appNoticeColor); } break; } case '/pb': case '/peepbuyers': { recognizedcmd = true; if (whichApp == 'peep') { if (peepshowPurchasers.length > 0) { var buyerlist = ''; for (var pbidx = 0; pbidx < peepshowPurchasers.length; pbidx++) { buyerlist += '\n' + [pbidx+1] + '. ' + peepshowPurchasers[pbidx] + ' (' + peepshowPurchasedTime[pbidx] + ' secs)'; } cb.sendNotice(botName + 'Users who have purchased time for the Peep Show (and time remaining): ' + buyerlist + '\nEnd of List\nNote this does not include members given free access (use /pv to see everyone viewing the show).', msguser, appNoticeColor); } else { cb.sendNotice(botName + 'No Peep Show buyers at this time.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature has not yet been started.', msguser, appNoticeColor); } break; } case '/peepprice': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.peepshowModsChgPrice == 'Yes')) { var peepnumprice = parseInt(msgarray[1]) if (isNaN(peepnumprice)) { cb.sendNotice(botName + 'The value entered for the peep show price is not numeric, please try again.', msguser, appNoticeColor); break; } else if (peepnumprice < 1 || peepnumprice > 9999) { cb.sendNotice(botName + 'The value entered for the peep show price is outside allowable values from 1 to 9999, please try again.', msguser, appNoticeColor); } else { var ppannounce = 'no'; if (whichApp == 'peep') { cb.sendNotice(botName + 'The Peep Show price has been updated to ' + peepnumprice + ' tokens/min.', msguser, appNoticeColor); ppannounce = 'yes'; } else { cb.sendNotice(botName + 'The Peep Show price has been updated to ' + peepnumprice + ' tokens/min, however the UltraApp Peep Show feature is not yet active.', msguser, appNoticeColor); } setPeepShowPrice(peepnumprice,ppannounce); } } else { cb.sendNotice(noticeOnlyBCMod3P, msguser, appNoticeColor); } break; } case '/peeplength': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { var peepnumlength = parseInt(msgarray[1]) if (isNaN(peepnumlength)) { cb.sendNotice(botName + 'The value entered for the peep show length is not numeric, please try again.', msguser, appNoticeColor); break; } else if (peepnumlength < 1 || peepnumlength > 300) { cb.sendNotice(botName + 'The value entered for the peep show length is outside allowable values from 1 to 300, please try again.', msguser, appNoticeColor); } else { var plannounce = 'no'; if (whichApp == 'peep') { cb.sendNotice(botName + 'The Peep Show Length has been updated to ' + peepnumlength + ' minutes.', msguser, appNoticeColor); plannounce = 'yes'; } else { cb.sendNotice(botName + 'The Peep Show Length has been updated to ' + peepnumlength + ' minutes, however the Peep Show feature is not yet active.', msguser, appNoticeColor); } setPeepShowLength(peepnumlength,plannounce); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/peepstarttimer': case '/peeptimer': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg1) { var peepnumtimer = parseInt(msgarray[1]) if(isNaN(peepnumtimer)) { cb.sendNotice(botName + 'The value entered for the minutes to use for the peep show start timer is not numeric, please try again.', msguser, appNoticeColor); break; } else if (peepnumtimer < 1 || peepnumtimer > 60) { cb.sendNotice(botName + 'The value entered for the minutes to use for the peep show start timer is outside allowable values from 1 to 60 minutes, please try again.', msguser, appNoticeColor); } else { startPeepShowStartTimer(peepnumtimer); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/peepaddtime': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg1) { var peepaddtimer = parseInt(msgarray[1]); if(isNaN(peepaddtimer)) { cb.sendNotice(botName + 'The value entered for the minutes to add to the peep show start timer is not numeric, please try again.', msguser, appNoticeColor); } else if(peepaddtimer < -60 || peepaddtimer > 60) { cb.sendNotice(botName + 'The value entered for the minutes to add to the peep 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(peepaddtimer == 0) { cb.sendNotice(botName + 'Cannot add zero time.', msguser, appNoticeColor); } else if(peepaddtimer < 0 && Math.abs(peepaddtimer) > peepshowStartMinsRemain) { cb.sendNotice(botName + '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(peepaddtimer > 0 && (Math.abs(peepaddtimer) + peepshowStartMinsRemain) > 60) { cb.sendNotice(botName + '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 { peepshowStartAddTime(peepaddtimer, msguser); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/peepstoptimer': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg1) { if (peepshowStartMinsRemain > 0 || peepshowStartSecsRemain > 0) { stopPeepShowStartTimer(msguser); } else { cb.sendNotice(botName + 'A Peep Show timer is not currently running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/peepadd': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if (msgarray[1]) { let peeptoadd = msgarray[1].toLowerCase(); if (peeptoadd.charAt(0) == '@') { peeptoadd = peeptoadd.substring(1); } peepshowAddRmvTime('addfree',peeptoadd,'common',0); } else { cb.sendNotice(botName + 'A parameter is required to specify the user that is to be given additional time. The command should be in the format "/peepadduser username"', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not yet active in the UltraApp.', msguser, appNoticeColor); } break; } case '/peeprmv': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg3 || (ismodlvlmsg2 && cb.settings.ticketShowModsAdd == 'Yes')) { if(msgarray[1]) { let peeptormv = msgarray[1].toLowerCase(); if (peeptormv.charAt(0) == '@') { peeptormv = peeptormv.substring(1); } peepshowAddRmvTime('remove',peeptormv,'',0); } else { cb.sendNotice(botName + 'No user was specified for the /peeprmv command.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3A, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/peeptimeleft': { recognizedcmd = true; if (whichApp == 'peep') { if (showStage == 'peepaftershow') { cb.sendNotice(botName + 'The Peep Show has finished.', msguser, appNoticeColor); } else if (showStage == 'peepsales') { cb.sendNotice(botName + 'The Peep Show has not yet started.', msguser, appNoticeColor); } else { if (monitorMinsRem > 1) { cb.sendNotice(botName + 'There are approximately ' + monitorMinsRem + ' minutes remaining in the planned show time.', '', appNoticeColor, '', 'bold'); } else if (monitorMinsRem == 1) { cb.sendNotice(botName + 'There is less than a minute remaining in the planned show time.', '', appNoticeColor, '', 'bold'); } else { cb.sendNotice(botName + 'The planned show time has expired.', msguser, appNoticeColor); } } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/mytime': { recognizedcmd = true; if (whichApp == 'peep') { if (cbjs.arrayContains(peepshowFreeViewers,msguser)) { cb.sendNotice('You have free access to the full show, you do not need to purchase time.', msguser, peepBgColor, peepTextColor, 'bold'); } else { var timeleftliteral = peepshowMyTimeLeft(msguser); if (timeleftliteral == 'none') { cb.sendNotice('You have not purchased any time for this show, you can tip to buy time for the show.', msguser, peepBgColor, peepTextColor, 'bold'); } else if (timeleftliteral == 'expired') { cb.sendNotice('The time you have purchased has run out, you can tip to buy more time for the show.', msguser, peepBgColor, peepTextColor, 'bold'); } else { if (showStage == 'peepaftershow') { cb.sendNotice('The Peep Show has finished. You have ' + timeleftliteral + ' remaining that can be used if ' + bcText + ' starts another Peep Show.', msguser, peepBgColor, peepTextColor, 'bold'); } else if (showStage == 'peepsales') { cb.sendNotice('The Peep Show has not yet started. You have purchased ' + timeleftliteral + ' that will be applied toward the show once ' + bcText + ' starts the Peep Show.', msguser, peepBgColor, peepTextColor, 'bold'); } else { cb.sendNotice('You have ' + timeleftliteral + ' remaining that you have purchased. There are approximately ' + monitorMinsRem + ' minutes remaining in the show. You can add more time at the rate of ' + peepshowPrice + ' tokens/min.', msguser, peepBgColor, peepTextColor, 'bold'); } } } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/peepsubject': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg1) { peepshowSubjectText = rawmsg.substring(13).trim(); if (peepshowSubjectText != '' && peepshowSubjectText != 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 Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/startpeep': { recognizedcmd = true; if (msgisbc || ismodlvlmsg2) { if (validateFeature('peep')) { if (whichApp === 'peep') { if (!cb.limitCam_isRunning() && peepshowEnded == true) { restartPeepShow(msguser); } else if (!cb.limitCam_isRunning()) { startPeepShow(msguser); } else { cb.sendNotice(botName + 'The peep show is already active, command ignored.', msguser, appNoticeColor); } } else { if (cb.limitCam_isRunning()) { cb.sendNotice('The Show is already hidden for a ticket show or private show, cannot start a Peep Show.', msguser, appNoticeColor); } else { setAppFeature('peep', msguser); startPeepShow(msguser); } } } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } break; } case '/peeprestart': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg2) { if (!cb.limitCam_isRunning()) { var psstartedby = msguser; if (msgisbc) { psstartedby = bcText; } restartPeepShow(psstartedby); } else { cb.sendNotice(botName + 'A Peep Show is already running.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } case '/peepautoend': { recognizedcmd = true; if (whichApp == 'peep') { if (msgisbc || ismodlvlmsg2) { if (msgarray[1]) { var penewmode = msgarray[1].toLowerCase(); if (penewmode == 'on') { if (peepshowAutoEnd == 'Yes') { cb.sendNotice(botName + 'The Peep Show is already configured to end automatically after the planned show length.', msguser, appNoticeColor); } else { peepshowAutoEnd = 'Yes'; cb.sendNotice(botName + 'The "Auto-end" setting has been changed to end automatically after the planned show length.', msguser, appNoticeColor); } } else if (penewmode == 'off') { if (peepshowAutoEnd == 'No') { cb.sendNotice(botName + 'The Peep Show is already configured to NOT end automatically after the planned show length.', msguser, appNoticeColor); } else { peepshowAutoEnd = 'No'; cb.sendNotice(botName + 'The "Auto-end" setting has been changed to NOT end automatically after the planned show length.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'An invalid parameter was entered, a parameter of "on" or "off" is required for this command.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'No parameter was entered, a parameter of "on" or "off" is required for this command.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod2, msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'The Peep Show feature is not running.', msguser, appNoticeColor); } break; } //******** VIP Commands *********** case '/addvip': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (cmdval != null) { let vipcmdvalsplit = cmdval.split(listregexp); if (vipcmdvalsplit.length > 1) { let 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 (let vipi = 0; vipi < vipcmdvalsplit.length; vipi++) { if (vipcmdvalsplit[vipi]) { let viptoadd = vipcmdvalsplit[vipi].toLowerCase(); if (viptoadd.charAt(0) == '@') { viptoadd = viptoadd.substring(1); } 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 { let viptoadd2 = msgarray[1].toLowerCase(); if (viptoadd2.charAt(0) == '@') { viptoadd2 = viptoadd2.substring(1); } 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]) { let viptormv = msgarray[1].toLowerCase(); if (viptormv.charAt(0) == '@') { viptormv = viptormv.substring(1); } 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; } //******** External Fan Club Commands *********** case '/addfan': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (cmdval != null) { let fancmdvalsplit = cmdval.split(listregexp); if (fancmdvalsplit.length > 1) { let fanaddnotice = 'Adding multiple users to the UltraApp ' + EFCname + ' list.'; fanaddnotice += '\nNote this is only applied during this session, permanent ' + EFCname + ' list changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.'; for (let efci = 0; efci < fancmdvalsplit.length; efci++) { if (fancmdvalsplit[efci]) { let fantoadd = fancmdvalsplit[efci].toLowerCase(); if (fantoadd.charAt(0) == '@') { fantoadd = fantoadd.substring(1); } if (!cbjs.arrayContains(extFanListArray,fantoadd)) { addRmvExtFan(fantoadd,'a'); fanaddnotice += '\nAdded ' + fantoadd + ' to the UltraApp ' + EFCname + ' list.'; cb.sendNotice(msguser + ' has added you to the UltraApp ' + EFCname + ' list.', fantoadd, appNoticeColor); } else { fanaddnotice += '\n' + fantoadd + ' is already on the UltraApp ' + EFCname + ' list. Skipping.'; } } } fanaddnotice += '\nAll users were added and notified.'; cb.sendNotice(fanaddnotice, msguser, appNoticeColor); cb.sendNotice(msguser + ' has added multiple users to the UltraApp ' + EFCname + ' list.\n' + 'Users added: ' + cbjs.arrayJoin(fancmdvalsplit, ', '), '', appNoticeColor, '', 'normal', 'red'); } else { let fantoadd2 = msgarray[1].toLowerCase(); if (fantoadd2.charAt(0) == '@') { fantoadd2 = fantoadd2.substring(1); } if (cbjs.arrayContains(extFanListArray,fantoadd2)) { cb.sendNotice(botName + fantoadd2 + ' is already on the ' + EFCname + ' list.', msguser, appNoticeColor); } else { addRmvExtFan(fantoadd2,'a'); cb.sendNotice(botName + 'You have added ' + fantoadd2 + ' to the ' + EFCname + ' list.\nNote this is only applied during this session, permanent ' + EFCname + ' 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 ' + EFCname + ' list!', fantoadd2, appNoticeColor); } } } else { cb.sendNotice(botName + 'You did not specify the username you want to add to the ' + EFCname + ' list.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/addfan2': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (cmdval != null) { let fan2cmdvalsplit = cmdval.split(listregexp); if (fan2cmdvalsplit.length > 1) { let fan2addnotice = 'Adding multiple users to the UltraApp ' + EFCname2 + ' list.'; fan2addnotice += '\nNote this is only applied during this session, permanent ' + EFCname2 + ' list changes must be made to the list defined when starting the UltraApp. This list should also be saved to a separate document.'; for (let efcidx = 0; efcidx < fan2cmdvalsplit.length; efcidx++) { if (fan2cmdvalsplit[efcidx]) { let fan2toadd = fan2cmdvalsplit[efcidx].toLowerCase(); if (fan2toadd.charAt(0) == '@') { fan2toadd = fan2toadd.substring(1); } if (!cbjs.arrayContains(extFanList2Array, fan2toadd)) { addRmvExtFan2(fan2toadd,'a'); fan2addnotice += '\nAdded ' + fan2toadd + ' to the UltraApp ' + EFCname2 + ' list.'; cb.sendNotice(msguser + ' has added you to the UltraApp ' + EFCname2 + ' list.', fan2toadd, appNoticeColor); } else { fan2addnotice += '\n' + fan2toadd + ' is already on the UltraApp ' + EFCname2 + ' list. Skipping.'; } } } fan2addnotice += '\nAll users were added and notified.'; cb.sendNotice(fan2addnotice, msguser, appNoticeColor); cb.sendNotice(msguser + ' has added multiple users to the UltraApp ' + EFCname2 + ' list.\n' + 'Users added: ' + cbjs.arrayJoin(fan2cmdvalsplit, ', '), '', appNoticeColor, '', 'normal', 'red'); } else { let fan2toadd2 = msgarray[1].toLowerCase(); if (fan2toadd2.charAt(0) == '@') { fan2toadd2 = fan2toadd2.substring(1); } if (cbjs.arrayContains(extFanList2Array,fan2toadd2)) { cb.sendNotice(botName + fan2toadd2 + ' is already on the ' + EFCname2 + ' list.', msguser, appNoticeColor); } else { addRmvExtFan2(fan2toadd2,'a'); cb.sendNotice(botName + 'You have added ' + fan2toadd2 + ' to the ' + EFCname2 + ' list.\nNote this is only applied during this session, permanent ' + EFCname2 + ' 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 ' + EFCname2 + ' list!', fan2toadd2, appNoticeColor); } } } else { cb.sendNotice(botName + 'You did not specify the username you want to add to the ' + EFCname2 + ' list.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/rmvfan': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (msgarray[1]) { let fantormv = msgarray[1].toLowerCase(); if (fantormv.charAt(0) == '@') { fantormv = fantormv.substring(1); } if (cbjs.arrayContains(extFanListArray,fantormv)) { addRmvExtFan(fantormv,'r'); cb.sendNotice(botName + 'You have removed ' + fantormv + ' from the ' + EFCname + ' list.\nNote this is only applied during this session, permanent ' + EFCname + ' 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 + fantormv + ' is not on the ' + EFCname + ' list.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'You did not specify the username you want to remove from the ' + EFCname + ' list.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/rmvfan2': { recognizedcmd = true; if (msgisbc || ismodlvlmsg3) { if (msgarray[1]) { let fan2tormv = msgarray[1].toLowerCase(); if (fan2tormv.charAt(0) == '@') { fan2tormv = fan2tormv.substring(1); } if (cbjs.arrayContains(extFanList2Array,fan2tormv)) { addRmvExtFan2(fan2tormv,'r'); cb.sendNotice(botName + 'You have removed ' + fan2tormv + ' from the ' + EFCname2 + ' list.\nNote this is only applied during this session, permanent ' + EFCname2 + ' 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 + fan2tormv + ' is not on the ' + EFCname2 + ' list.', msguser, appNoticeColor); } } else { cb.sendNotice(botName + 'You did not specify the username you want to remove from the ' + EFCname2 + ' list.', msguser, appNoticeColor); } } else { cb.sendNotice(noticeOnlyBCMod3, msguser, appNoticeColor); } break; } case '/fanlist': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { cb.sendNotice(botName + 'Users currently in the ' + EFCname + ' List: ' + extFanListArray.length + '\n' + (extFanListArray.length > 0 == true ? cbjs.arrayJoin(extFanListArray, ', ') : 'No users.') + '\nEnd of List', msguser, appNoticeColor); } else { cb.sendNotice(noticeOnlyBCMod1, msguser, appNoticeColor); } break; } case '/fanlist2': { recognizedcmd = true; if (msgisbc || ismodlvlmsg1) { cb.sendNotice(botName + 'Users currently in the ' + EFCname2 + ' List: ' + extFanList2Array.length + '\n' + (extFanList2Array.length > 0 == true ? cbjs.arrayJoin(extFanList2Array, ', ') : '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) { let modcmdvalsplit = cmdval.split(listregexp); if (modcmdvalsplit.length > 1) { let 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 (let modi = 0; modi < modcmdvalsplit.length; modi++) { if (modcmdvalsplit[modi]) { let modtoadd = modcmdvalsplit[modi].toLowerCase(); if (modtoadd.charAt(0) == '@') { modtoadd = modtoadd.substring(1); } 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 { let modtoadd2 = msgarray[1].toLowerCase(); if (modtoadd2.charAt(0) == '@') { modtoadd2 = modtoadd2.substring(1); } 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]) { let modtormv = msgarray[1].toLowerCase(); if (modtormv.charAt(0) == '@') { modtormv = modtormv.substring(1); } 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] == '/startclock2' || msgarray[0] == '/addtoclock2' || msgarray[0] == '/timeleft2' || msgarray[0] == '/stopclock2' || msgarray[0] == '/startclock3' || msgarray[0] == '/addtoclock3' || msgarray[0] == '/timeleft3' || msgarray[0] == '/stopclock3' || msgarray[0] == '/startclock4' || msgarray[0] == '/addtoclock4' || msgarray[0] == '/timeleft4' || msgarray[0] == '/stopclock4' || msgarray[0] == '/st' || msgarray[0] == '/st2' || msgarray[0] == '/st3' || msgarray[0] == '/st4' || msgarray[0] == '/sc' || msgarray[0] == '/sc2' || msgarray[0] == '/sc3' || msgarray[0] == '/sc4' || 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] == '/fbchgtheme' || msgarray[0] == '/seethemes' || msgarray[0] == '/seetheme' || 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 and Peep show users if (whichApp == 'ticket' || presalesToggle) { if (cb.limitCam_userHasAccess(msguser) && showStage != 'aftershow' && !cbjs.arrayContains(ticketShowPreViewerList, msguser)) { msg.background = ticketHolderBgColor; if (cb.settings.ticketshowEnableIcon == 'Yes') { msg.m = ticketEmoji + ' ' + msg.m; } } if (cbjs.arrayContains(ticketShowPreViewerList, msguser)) { msg.background = freePreviewerBgColor; } } else if (whichApp == 'peep') { if (cb.limitCam_userHasAccess(msguser) && showStage != 'peepaftershow') { msg.background = peepshowViewerBgColor; } } 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 enterisextfan1 = cbjs.arrayContains(extFanListArray,enteruser); var enterisextfan2 = cbjs.arrayContains(extFanList2Array,enteruser); 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 (enterisextfan1) { addRmvExtFanInShow(enteruser,'a'); } if (enterisextfan2) { addRmvExtFanInShow2(enteruser,'a'); } if (whichApp == 'ticket' || presalesToggle) { if (cb.limitCam_userHasAccess(enteruser)) { addViewer(enteruser); if (cbjs.arrayContains(freePreviewUserArray,enteruser)) { addPreViewer(enteruser); } cb.drawPanel(); } } if (whichApp == 'peep') { if(cb.limitCam_userHasAccess(enteruser)) { if (!cbjs.arrayContains(peepshowViewers,enteruser)) { peepshowViewers.push(enteruser); } } } // **** 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 'sequence': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, tipsequenceBgColor, tipsequenceTextColor, 'bold'); } goalNotices(enteruser,enteruser,'r','entry'); break; } case 'tipjar': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, tipjarBgColor, tipjarTextColor, '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 'peep': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, peepBgColor, peepTextColor, 'bold'); } break; } case 'none': { if (genentrymsg && entrymessage) { cb.sendNotice(entrymessage, enteruser, '', '', 'bold'); } break; } default : { break; } } } // **** Presales and Ticket Show entry functions and messages if (!enterisbc && (whichApp == 'ticket' || presalesToggle)) { if (enterismod || enterisfan || enterisextfan1 || enterisextfan2 || enterisvip) { checkFreeTickets(enteruser,enterismod,enterisfan,enterisextfan1,enterisextfan2,enterisvip,'enter'); } if (presalesToggle) { let enterpresalemessage = '\nTicket Show Pre-sales are active.'; enterpresalemessage += '\nYou can buy advance tickets to the show now at a price of ' + presalePrice + ' tokens.'; enterpresalemessage += '\nYou will automatically be added to the Ticket Show when it is started later.'; enterpresalemessage += '\nBuy now before the price goes up! Ticket show price will be ' + ticketPrice + ' tokens.'; cb.sendNotice(borderPresaleTop + enterpresalemessage + borderPresaleBottom, enteruser, ticketBgColor, ticketTextColor, 'bold'); } else 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 { if (freePreviewLength > 0 && (showStage == 'ticketshow' || showStage == 'showwarn')) { if (!cbjs.arrayContains(freePreviewUserArray,enteruser)) { if ((cb.settings.ticketShowPreviewGrays == 'No' && enterisnotgray) || cb.settings.ticketShowPreviewGrays == 'Yes') { addRmvPreview('add',enteruser); enterticketmessage += '\nThe Ticket Show has started!'; enterticketmessage += '\n' + bcText + ' has enabled a Free Preview period for users joining late.'; enterticketmessage += '\nYou will be able to see the show for ' + freePreviewLengthText + '.'; enterticketmessage += '\nAfter that you must buy a ticket to continue watching the show.'; } } } 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 { if (cbjs.arrayContains(ticketShowPreViewerList,enteruser)) { enterticketmessage += '\nYou are currently in your free preview period.'; enterticketmessage += '\nOnce it ends, you must buy a ticket to continue watching the show.'; } 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!'; } } if (ticketShowAllowGift == 'Yes') { enterticketmessage += '\n' + bcText + ' has enabled the the gifting of tickets.'; enterticketmessage += '\nYou can buy extra tickets and gift them to others.'; enterticketmessage += '\nSee help text for info on gifting tickets using the "/uahelp" command.'; } } 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'); } } } if (ticketShowOtToggle && cbjs.arrayContains(outstandingTicketArray,enteruser)) { let otnotice = '\n' + bcText + ' has enabled the Outstanding Ticket feature'; otnotice += '\nYou have an outstanding ticket you can use to watch the show!'; otnotice += '\nTo use your outstanding ticket, type: /useticket'; cb.sendNotice(borderTicketTop + otnotice + borderTicketBottom, enteruser, ticketBgColor, ticketTextColor, 'bold'); } } // **** Peep Show functions and message if (!enterisbc && whichApp == 'peep') { if (enterismod || enterisfan || enterisextfan1 || enterisextfan2 || enterisvip) { if (!cbjs.arrayContains(peepshowFreeViewers,enteruser) && showStage != 'peepaftershow') { checkFreePeepShow(enteruser,enterismod,enterisfan,enterisextfan1,enterisextfan2,enterisvip); } } if (!cbjs.arrayContains(peepshowFreeViewers,enteruser)) { if (cb.limitCam_isRunning()) { var peepmessage = borderAllNotices + '\nThe Peep Show has been in progress for ' + timeMinSecLiteral((Date.now() - peepshowStartHiddenTime)/1000); peepmessage += '\nApproximately ' + timeMinSecLiteral((peepShowEndTime - Date.now())/1000) + ' remain until planned end of show.'; if (!cb.limitCam_userHasAccess(enteruser)) { if (showStage == 'peepshow') { peepmessage += '\nYou can buy time during the show for ' + peepshowPrice + ' tokens/min.\nTime is added in 10 second increments based on tip amount.\nThe minimum tip amount to buy time is ' + minimumPeepShowTip + ' tokens.\nBased on the planned end time, tipping ' + (monitorMinsRem*peepshowPrice) + ' tokens would buy time for\nthe remainder of the show.\n' + borderAllNotices; } else if (showStage == 'peepshowwarn') { peepmessage += '\n' + bcText + ' has indicated the show is nearly over. \nBuying time is not recommended, but you may buy for ' + peepshowPrice + ' tokens per minute.\nThe minimum tip amount to buy time is ' + minimumPeepShowTip + ' tokens.\n' + borderAllNotices; } else if (showStage == 'peepshowfinale') { peepmessage += '\n' + bcText + ' has indicated the show is nearly over. \nPeep Show time purchases are suspended. \n' + bcText + ' may be returning to public chat shortly.\n' + borderAllNotices; } } else { peepmessage += borderAllNotices; } cb.sendNotice(peepmessage,enteruser, peepBgColor, peepTextColor, 'bold'); } else { if (showStage == 'peepsales') { if (cbjs.arrayContains(peepshowPurchasers,enteruser)) { var pppidx = peepshowPurchasers.indexOf(enteruser); purchasedTimeRemaining = peepshowPurchasedTime[pppidx]; var purchasetime = '0 minutes'; if (purchasedTimeRemaining > 0) { purchasetime = timeMinSecLiteral(purchasedTimeRemaining); } cb.sendNotice(borderAllNotices + '\nWelcome back! You have purchased ' + purchasetime + ' for the show and the show has not yet started.\n' + borderAllNotices, enteruser, peepBgColor, peepTextColor, "bold"); } else { cb.sendNotice(borderAllNotices + '\n' + bcText + ' will be doing a Peep Show today. \nYou can buy time before or during the show for ' + peepshowPrice + ' tokens/min.\nTime is added in 10 second increments based on tip amount.\nBased on the planned show length, tipping ' + (monitorMinsRem*peepshowPrice) + ' tokens would buy time for\nthe entire show.\n' + borderAllNotices, enteruser, peepBgColor, peepTextColor, "bold"); } } else if (showStage == 'aftershow' || peepshowEnded == true) { cb.sendNotice(borderAllNoticesShort + '\nThe Peep Show has finished.\n' + borderAllNoticesShort, enteruser, peepBgColor, peepTextColor, 'bold'); } } } } }); // *********************************** Actions upon leaving ************************************** cb.onLeave(function(user) { var leaveuser = user.user; if (leaveuser != BC) { if (whichApp == 'ticket' || presalesToggle) { removeViewer(leaveuser); removePreViewer(leaveuser); } if (whichApp == 'peep') { if (cbjs.arrayContains(peepshowViewers,leaveuser)) { cbjs.arrayRemove(peepshowViewers,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 Ticket Show'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Only Fan Club and ' + VIPname + ' 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 = ticketEmoji + ' Ticket Price: ' + ticketPrice + ' tokens ' + ticketEmoji; 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 = 'Start Mode: Ticket Goal \u25FE ' + ticketShowTotalTicketsBought + ' / ' + ticketShowGoalTickets + ' tickets'; leftjust3 = leftJustify(panel.row3_value,3); } else if (ticketStartMode == 'tokengoal') { panel.row1_value = ticketEmoji + ' Ticket Price: ' + ticketPrice + ' tokens ' + ticketEmoji; 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 = 'Start Mode: Token Goal \u25FE ' + ticketShowTotalTips + ' / ' + ticketShowGoalTokens + ' tokens'; leftjust3 = leftJustify(panel.row3_value,3); } else if (ticketStartMode == 'timer') { panel.row1_value = ticketEmoji + ' Ticket Price: ' + ticketPrice + ' tokens ' + ticketEmoji; 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 = 'Start Mode: Timer (' + ((ticketSecsRemain > 0 || ticketMinsRemain > 0) ? ticketTimeLeftPanel() : 'Timer not yet started') + ')'; leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = ticketEmoji + ' Ticket Price: ' + ticketPrice + ' tokens ' + ticketEmoji; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Ticket Holders: ' + countTickets; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Start Mode: Manual ' + (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); if (freePreviewLength > 0 && ticketShowPreViewerList.length > 0) { panel.row2_value = 'Viewers: ' + ticketShowViewerList.length + ' (' + ticketShowPreViewerList.length + ' previewer' + (ticketShowPreViewerList.length == 1 ? '' : 's') + ' included)'; } else { 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 = ticketEmoji + ' Ticket Price: ' + ticketPrice + ' tokens ' + ticketEmoji; leftjust1 = leftJustify(panel.row1_value,1); if (freePreviewLength > 0 && ticketShowPreViewerList.length > 0) { panel.row2_value = 'Viewers: ' + ticketShowViewerList.length + ' (' + ticketShowPreViewerList.length + ' previewer' + (ticketShowPreViewerList.length == 1 ? '' : 's') + ' included)'; } else { panel.row2_value = 'Viewers: ' + ticketShowViewerList.length; } leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Ticket Holders: ' + countTickets; leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'goals') { if (!finalGoalMet) { if (cb.settings.progressiveUpDown == 'Count Up To Goal') { 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 (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 { panel.row1_value = 'To Goal: ' + (currentGoalTips <= 0 ? '0 \u25FE Goal Reached!' : currentGoalTips + ' \u25FE Countdown to 0') ; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Progress: ' + goalBar((currentGoalTips <= 0 ? currentGoalTotal : (currentGoalTotal-currentGoalTips)),currentGoalTotal) + ' (' + Math.floor((currentGoalTips <= 0 ? currentGoalTotal : (currentGoalTotal-currentGoalTips))/currentGoalTotal*100) + ' %)'; leftjust2 = 42; 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 { 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 = ''; } leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'autoreset') { if (!finalGoalMet) { if (resetShowCount) { 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 { 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 { 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; } else { panel.row3_value = ''; } leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'goalcount') { if (!finalGoalMet) { 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 { 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 == 'sequence') { if (!finalGoalMet) { panel.row1_value = 'Next Tip Needed: ' + nextSequence + ' \u25FE Counting ' + tipSequenceDirection + ' to ' + endSequence; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Next Prize at: ' + currentGoalSequence + ' (' + currentGoalDesc + ')'; leftjust2 = leftJustify(panel.row2_value,2); if (cb.settings.showTotals == 'Yes') { panel.row3_value = 'Group Tipping: ' + (groupTipToggle ? ('ON (' + currentGroupTipAmt + '/' + nextSequence + ')') : 'OFF') + ' \u25FE Seq Tips: ' + currentAppTotalSequence + (cb.settings.tipsequenceShowTotal == 'Yes' ? (' / ' + sequenceTotalTipsGoal) : ''); } else { panel.row3_value = 'Group Tipping: ' + (groupTipToggle ? ('ON (' + currentGroupTipAmt + '/' + nextSequence + ')') : 'OFF'); } leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = 'Tip Sequence Completed!'; leftjust1 = leftJustify(panel.row1_value,1); if (cb.settings.showTotals == 'Yes') { panel.row2_value = 'Total Sequence Goal Tips: ' + currentAppTotalSequence; } else { panel.row2_value = ' '; } leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = ' '; leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'tipjar') { if (!finalGoalMet) { if (!tipJarRunning) { panel.row1_value = 'Tip Jar Start Goal #' + currentGoalLevel + ' : ' + (currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips) + ' / ' + currentGoalTotal + ' \u25FE (' + (currentGoalTotal-(currentGoalTips > currentGoalTotal ? currentGoalTotal : currentGoalTips)) + ' rem)'; 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 = 'Cycle: ' + (currentGoalRecycleCount+1) + ' / ' + (currentGoalRecycleTotal+1) + ' \u25FE At Goal: ' + currentGoalDesc; leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = 'Tokens in Jar: ' + tipJarCurrentTokens + ' \u25FE Drain Rate: ' + drainRateText(); leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'While full: ' + currentGoalDesc; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'When Empty: ' + (cb.settings.tipjarWhenEmpty == 'Tip to continue current goal' ? 'Keep tipping to fill the jar' : 'Start Next Goal/Cycle'); leftjust3 = leftJustify(panel.row3_value,3); } } 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 Tip Jar Tips: ' + currentAppTotalTipJar; } else { panel.row3_value = ' '; } leftjust3 = leftJustify(panel.row3_value,3); } } else if (whichApp == 'goalrace') { if (!finalGoalMet) { 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 { 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') { 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 == 'peep') { if (showStage == 'peepsales') { if (peepshowStartMode == 'timer') { panel.row1_value = 'Start Mode: Timer (' + ((peepshowStartSecsRemain > 0 || peepshowStartMinsRemain > 0) ? peepshowStartTimeLeftPanel() : 'Timer not yet started') + ')'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Buy time before the show starts'; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Price: ' + peepshowPrice + ' tokens/min \u25FE Est length: ' + peepshowLengthOfShow + ' min' + (peepshowLengthOfShow > 1 ? 's' : ''); leftjust3 = leftJustify(panel.row3_value,3); } else { panel.row1_value = 'Peep Show Start Mode: Manual'; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Buy time before the show starts'; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Price: ' + peepshowPrice + ' tokens/min \u25FE Est length: ' + peepshowLengthOfShow + ' min' + (peepshowLengthOfShow > 1 ? 's' : ''); leftjust3 = leftJustify(panel.row3_value,3); } } else if (showStage == 'peepfinale') { if (monitorMinsRem > 0) { panel.row1_value = 'Peep Show! Approx ' + monitorMinsRem + ' min' + (monitorMinsRem == 1 ? '' : 's') + ' left in show'; } else { panel.row1_value = 'Peep Show! Planned time complete' } leftjust1 = leftJustify(panel.row1_value,1); var peepshowlist = cb.limitCam_allUsersWithAccess().slice(); numberPeepViewers = peepshowlist.length; panel.row2_value = 'Viewers: ' + numberPeepViewers; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Show ending soon!'; leftjust3 = leftJustify(panel.row3_value,3); } else if (showStage == 'peepaftershow') { panel.row1_value = 'Peep 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 = ' '; leftjust3 = leftJustify(panel.row3_value,3); } else { if (monitorMinsRem > 0) { panel.row1_value = 'Peep Show! Approx ' + monitorMinsRem + ' min' + (monitorMinsRem == 1 ? '' : 's') + ' left in show'; } else { panel.row1_value = 'Peep Show! Planned time complete' } peepshowlist = cb.limitCam_allUsersWithAccess().slice(); numberPeepViewers = peepshowlist.length; leftjust1 = leftJustify(panel.row1_value,1); panel.row2_value = 'Viewers: ' + numberPeepViewers; leftjust2 = leftJustify(panel.row2_value,2); panel.row3_value = 'Tip to join! Price: ' + peepshowPrice + ' tokens/min'; 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 tipisextfan1 = cbjs.arrayContains(extFanListArray,tipfromuser); var tipisextfan2 = cbjs.arrayContains(extFanList2Array,tipfromuser); 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 let counttip = true; if (whichApp != 'ticket' && whichApp != 'peep') { if (maxTipToGoal > 0 && tipamount > maxTipToGoal) { counttip = false; cb.sendNotice(botName + tipfromuser + ' tipped over the configured maximum tip for goals (' + maxTipToGoal + ' tokens), their tip was not counted toward the goal.', BC, appNoticeColor); cb.sendNotice(botName + tipfromuser + ' tipped over the configured maximum tip for goals (' + maxTipToGoal + ' tokens), their tip was not counted toward the goal.', BC, appNoticeColor, '', '', 'red'); cb.sendNotice(botName + 'Note you have tipped over the configured maximum tip for goals (' + maxTipToGoal + ' tokens), your tip was not counted toward the goal.', tipfromuser, appNoticeColor); } else if (tipnote) { let tipnotemessagearray = []; tipnotemessagearray = tipnote.split(' '); if (cbjs.arrayContains(tipnotemessagearray,'--NOGOAL--')) { counttip = false; cb.sendNotice(botName + tipfromuser + ' tipped with Ninja Tipping enabled, their tip was not counted toward the goal.', BC, appNoticeColor); cb.sendNotice(botName + tipfromuser + ' tipped with Ninja Tipping enabled, their tip was not counted toward the goal.', BC, appNoticeColor, '', '', 'red'); cb.sendNotice(botName + 'Per the text in the tip note, your tip was not counted toward the goal.', tipfromuser, appNoticeColor); } } } recordTip(tipamount,tipfromuser,tipnote,tipisfan,tipisextfan1,tipisextfan2,tipisvip,tipfromdisplayuser,counttip); // ***** Add to Pre-Sale Ticket List if (presalesToggle && !cb.limitCam_userHasAccess(tipfromuser) && cb.settings.ticketShowFanAppreciation != 'Yes') { if (tipamount >= presalePrice) { addRmvPresale('addtip',tipfromuser,'common',tipfromdisplayuser); } else { checkCumulative(tipfromuser,tipamount,presalePrice,'common','presale',tipfromdisplayuser); } } });
© Copyright Freesexcam 2011- 2024. All Rights Reserved.