Bots Home
|
Create an App
Gender Select, Knock and Welcome
Author:
aquar1us
Description
Source Code
Launch Bot
Current Users
Created by:
Aquar1us
/************************************************************* ** Title : Gender Select, Knock and Welcome ** Author : aquar1us ** Description : Make cam visible only for desired gender(s). Others may pay for viewing. Plus Gender-specific broadcaster notes and user welcome messages on enter and leave. Plus gender avatar on messages. Selective Cam Viewing implemented from scratch. Other functions based on "Knock Knock" and inspired by "Auto Welcome with Gender" and "GenderBot". Credits go to fenixy, uk_dave_43 and alysandrefan *************************************************************/ const botName = "Gender Select, Knock and Welcome v1.96a"; cb.settings_choices = [{ name: 'allowMods', type: 'choice', choice1: 'Yes', choice2: 'All but not starting/stopping the cam', choice3: 'No', defaultValue: 'All but not starting/stopping the cam', label: 'Allow Bot commands for moderators?' }, { name: 'genderPic', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes', label: 'Show gender avatar with each message?' }, { name: 'knock', type: 'choice', choice1: 'On enter and leave', choice2: 'Only on enter', choice3: 'Never', defaultValue: 'On enter and leave', label: 'Inform broadcaster and moderators about gender of viewers on entry/leave?' }, { name: 'welcome', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes', label: 'Welcome viewers? (All viewers will be greeted: "Welcome to my room +username" followed by specific message below...)' }, { name: 'msg_14', type: 'str', minLength: 1, maxLength: 255, label: 'Default message for viewers with tokens if any below is left blank.', required: false }, { name: 'msg_04', type: 'str', minLength: 1, maxLength: 255, label: 'Default message for viewers without tokens if any below is left blank.', required: false }, { name: 'msg_10', type: 'str', minLength: 1, maxLength: 255, label: 'Message for females with tokens', required: false }, { name: 'msg_00', type: 'str', minLength: 1, maxLength: 255, label: 'Message for females without tokens', required: false }, { name: 'msg_11', type: 'str', minLength: 1, maxLength: 255, label: 'Message for males with tokens', required: false }, { name: 'msg_01', type: 'str', minLength: 1, maxLength: 255, label: 'Message for males without tokens', required: false }, { name: 'msg_12', type: 'str', minLength: 1, maxLength: 255, label: 'Message for trans with tokens', required: false }, { name: 'msg_02', type: 'str', minLength: 1, maxLength: 255, label: 'Message for trans without tokens', required: false }, { name: 'msg_13', type: 'str', minLength: 1, maxLength: 255, label: 'Message for couples with tokens', required: false }, { name: 'msg_03', type: 'str', minLength: 1, maxLength: 255, label: 'Message for couples without tokens', required: false }, { name: 'useLimitCam', type: 'choice', choice1: 'Yes', choice2: 'No', defaultValue: 'Yes', label: 'Use Selective Cam feature? (Price must be set higher than 0 below for at least one gender!)' }, { name: 'price0', type: 'int', minValue: '0', maxValue: '1000', defaultValue: '0', label: 'Females: Price (in tokens per minute) to access Selective Cam. Set to 0 to allow free viewing for females.' }, { name: 'price1', type: 'int', minValue: '0', maxValue: '1000', defaultValue: '0', label: 'Males: Price (in tokens per minute) to access Selective Cam. Set to 0 to allow free viewing for males.' }, { name: 'price2', type: 'int', minValue: '0', maxValue: '1000', defaultValue: '0', label: 'Transgenders: Price (in tokens per minute) to access Selective Cam. Set to 0 to allow free viewing for transgenders.' }, { name: 'price3', type: 'int', minValue: '0', maxValue: '1000', defaultValue: '0', label: 'Couples: Price (in tokens per minute) to access Selective Cam. Set to 0 to allow free viewing for couples.' }, { name: 'camAdvert', type: 'str', minLength: 1, maxLength: 255, defaultValue: 'Cum Show!', label: 'Additional message to advertise Selective Cam (the "motto").', required: false }, { name: 'camAdvertTime', type: 'int', minValue: '0', maxValue: '1000', defaultValue: '5', label: 'Time interval in minutes to announce active Selective Cam in room. (Set to 0 to disable)' }, { name: 'vips', type: 'str', minLength: 1, maxLength: 32000, label: 'VIP viewers: They can always see your cam for free. (space or comma separated)', required: false }, { name: 'msg_vip', type: 'str', minLength: 1, maxLength: 255, label: 'Additional message for VIP viewers', required: false } ]; const botMsgBg = '#E33B3B'; const roomHost = cb.room_slug; // This is the broadcaster, the owner of the room const cbGend = { 'f': 0, 'm': 1, 's': 2, 'c': 3, '': 4 }; // Object used as assoziative array const genders = ['female', 'male', 'transgender', 'couple', 'unknown']; const colours = ['#CC0099', '#0099CC', '#00CC99', '#9900CC', '#666666']; const avatars = [':avatar_cb_female', ':avatar_cb_male', ':avatar_cb_transsex', ':avatar_cb_couple', ':avatar_gen_unknown']; const welcome = cb.settings["welcome"] == "Yes" ? 1 : 0; const genderPic = cb.settings["genderPic"] == "Yes" ? 1 : 0; const knockEnter = cb.settings["knock"] == "Never" ? 0 : 1; const knockLeave = cb.settings["knock"] == "On enter and leave" ? 1 : 0; const allowMods = cb.settings["allowMods"] == "No" ? 0 : 1; const allowModsControl = cb.settings["allowMods"] == "Yes" ? 1 : 0; const useLimitCam = cb.settings["useLimitCam"] == "Yes" ? 1 : 0; const camAdvert0 = 'Reload my room or tip to see the show.'; const camAdvertTime = cb.settings["camAdvertTime"] * 1.0; const msgVIP = cb.settings["msg_vip"]; var chargeTimer; var activity = false; // switch to surpress periodic messages when room is quiet var delay = 1; // timeout counter for proper delay of delayNotice calls var counts = [0, 0, 0, 0, 0]; var advertCounter = camAdvertTime; var prices = [cb.settings["price0"], cb.settings["price1"], cb.settings["price2"], cb.settings["price3"]]; var camAdvert = cb.settings["camAdvert"]; var camAdvertFull = (camAdvert == "" ? "" : camAdvert + '\n') + camAdvert0; var greetings = [ [ ], [ ] ]; // empty 2-dimensional Array. Will be filled on bot launch var tippers = new Set(); // to keep track of amounts tipped and run dry by time var vips = new Set(); // ******* EVENT HANDLERS ******* cb.onEnter(function(user) { const u = user['user']; if (u != roomHost) { const g = cbGend[user['gender']]; const solvent = (user['has_tokens']) ? 1 : 0; const vip = (useLimitCam && vips.has(u)) var weight = 'normal'; if (solvent) weight = 'bold'; counts[g] = counts[g] + 1; // Inform the room host and moderators if (knockEnter) { const msg = avatars[g] + (vip ? ':VIPCookie ' : ' ') + u + ' - ' + genders[g] + (vip ? ' VIP' : '') + ' - has joined'; cb.sendNotice(msg, roomHost, '', colours[g], weight); cb.sendNotice(msg, '', '', colours[g], weight, 'red'); } // Greet the user // cb.log(solvent.toString() + " " + g.toString()); if (welcome) { cb.sendNotice("Welcome to my room " + u + greetings[solvent][g], u, '', colours[g], weight); if (vip && msgVIP != "") { delayNotice(msgVIP, u, '', colours[g], weight); } } // Open cam for free or for fee if (useLimitCam) { if (!cb.limitCam_userHasAccess(u)) { // check - just in case ... const tipped = addTip(u, g, 0); // Note: addTip(u, g, 0) has no side effects const price = prices[g]; const limitCam = cb.limitCam_isRunning(); if (tipped >= price || user['is_mod'] || user['in_fanclub'] || vips.has(u)) { cb.limitCam_addUsers([u]); if (price > 0 && limitCam) { // paying viewer cb.sendNotice(avatars[g] + ' ' + u + ' - ' + genders[g] + ' - starts watching.', roomHost, '', colours[g], 'bold'); cb.sendNotice(avatars[g] + ' ' + u + ' - ' + genders[g] + ' - starts watching.', '', '', colours[g], 'bold', 'red'); } } else if (limitCam) { const minTip = price - tipped; delay = 2; delayNotice('Selective Cam Viewing running!\n' + camAdvertFull, u, botMsgBg); delayNotice('You may tip at least ' + minTip + ((tipped > 0) ? ' more ' : '')+ ' token' + ((minTip == 1) ? '' : 's') + ' to see my cam.' + '\nPrice is ' + price + ' tokens per minute.', u, '', colours[g], 'bolder'); } } } } }); cb.onLeave(function(user) { const u = user['user']; if (u != roomHost) { const g = cbGend[user['gender']]; if (counts[g] > 0) counts[g] = counts[g] - 1; // revoke cam access if (useLimitCam && cb.limitCam_userHasAccess(u)) { cb.limitCam_removeUsers([u]); } // Inform the room host and the moderators if (knockLeave || cb.limitCam_isRunning()) { const vip = (useLimitCam && vips.has(u)) var weight = 'normal'; if (user['has_tokens']) weight = 'bold'; var msg = avatars[g] + (vip ? ':VIPCookie ' : ' ') + u + ' - ' + genders[g] + (vip ? ' VIP' : '') + ' - has left'; cb.sendNotice(msg, roomHost, '', colours[g], weight); cb.sendNotice(msg, '', '', colours[g], weight, 'red'); } } }); cb.onTip(function(tip) { activity = true; if (useLimitCam) { const u = tip['from_user']; const g = cbGend[tip['from_user_gender']]; const price = prices[g]; if (price > 0 && !tip['from_user_in_fanclub']) { // Attention Side effect: addTip(u, g, amount) updates the users entry in tippers const tipped = addTip(u, g, tip['amount']); const hasAccess = cb.limitCam_userHasAccess(u); const limitCam = cb.limitCam_isRunning(); if (tipped >= price && !hasAccess) { cb.limitCam_addUsers([u]); if (limitCam) { cb.sendNotice(avatars[g] + ' ' + u + ' - ' + genders[g] + ' - starts watching.', roomHost, '', colours[g], 'bold'); cb.sendNotice(avatars[g] + ' ' + u + ' - ' + genders[g] + ' - starts watching.', '', '', colours[g], 'bold', 'red'); } } else if (limitCam && !hasAccess) { const minTip = price - tipped; cb.sendNotice('You may tip at least ' + minTip + ((tipped > 0) ? ' more ' : '') + ' token' + ((minTip == 1) ? '' : 's') + ' to see my cam.' + '\nPrice is ' + price + ' tokens per minute.', u, '', colours[g], 'bolder'); } } } }); cb.onMessage(function(msg) { // Attention Side effect: parseCommand(msg) executes the command if (parseCommand(msg)) { // msg was treated as a command msg['X-Spam'] = true; // hide msg from other users msg['background'] = '#DDDDDD'; } else { activity = true; if (genderPic) { // prepend gender avatar msg['m'] = avatars[cbGend[msg['gender']]] + ' ' + msg['m']; } } return msg; }); // ******* HELPER FUNCTIONS ******* function parseCommand(msg) { /* Look for command in message. If a command is found return 1 after executing proper command or after hinting user regarding foulty command. If no command is found return 0. This function is written in a way that tries to create very low overhead for the parsing process. It tries to return 0 as fast as possible as soon as it is clear that the message contains no command. Command responses are deliberately delayed to ensure proper sequence */ const u = msg['user']; if (u != roomHost && (!allowMods || !msg['is_mod'])) return 0; const m = msg['m']; const cmdStart = m.indexOf('/') + 1; if (cmdStart == 0 || m.charAt(cmdStart) != 'g') return 0; var cmdEnd = m.indexOf(' ', cmdStart); if (cmdEnd == -1) cmdEnd = m.length; if (cmdEnd - cmdStart < 5) return 0; // smallest command is gstop: 5 letters delay = 1; // init timeout counter for proper delay of delayNotice calls const cmd = m.substring(cmdStart + 1, cmdEnd).toLowerCase(); // omit first letter g var argStr = m.substring(cmdEnd + 1).trim(); var args = argStr.replace(/,/g, ' ').split(" ").remove(""); const arg0 = args[0]; // could be undefined const limitCam = cb.limitCam_isRunning(); /* cb.log((cmdEnd - cmdStart).toString() + " #" + cmd + "# [" + args + "] " + args.length + " :" + args[0]); */ switch (cmd) { default: // none of the known commands listed below return 0; case "help": delayNotice('Known commands are:\n/gstart and /gstop for the ' + 'Selective Cam feature,\n/gremove (all) for clearing ' + 'the viewers list or removing one or more viewers,' + '\n/gmotto (none) for setting/clearing the motto of the Selective Cam,' + '\n/gstatus for status of cam and list of current viewers,' + '\n/gaddvip, /gdelvip and /gviplist for controlling the VIP viewers list ' + 'and\n/gcounts for number of current visitors per gender.' + '\n(Note: These numbers may be wrong because they ' + 'are based on enter/leave only while the bot is running)', u, '#DDDDDD'); return 1; case "counts": var found = false; for (var i = 0; i < 4; i++) { if (counts[i] > 0) { if (!found) delayNotice('Number of viewers by gender:', u, botMsgBg); delayNotice(avatars[i] + ' (' + genders[i] + ') - ' + counts[i], u, '', colours[i]); found = true; } } if (!found) delayNotice('No viewers counted so far.', u); return 1; case "status": case "motto": case "prices": case "addvip": case "delvip": case "viplist": case "remove": case "start": case "stop": if (!useLimitCam) { delayNotice('Selective Cam Viewing feature is disabled by room host.', u, botMsgBg); return 1; } if (cmd == "status") { delayNotice('Selective Cam Viewing is currently ' + (limitCam ? 'on.' : 'off.'), u, botMsgBg); listViewers(u); return 1; } if (cmd == "viplist") { delayNotice('Viewers with free cam access:\n' + [...vips].join(' '), u, botMsgBg); return 1; } if (! (u == roomHost || allowModsControl || (argStr == "" && (cmd == "motto" || cmd == "prices")))) { delayNotice('Sorry, but you are not allowed to control the cam.', u, botMsgBg); return 1; } switch (cmd) { case "motto": if (argStr == "") { delayNotice('Current Selective Cam motto is:' + (camAdvert == "" ? " <none>." : '\n' + camAdvert), u); } else { camAdvert = (argStr == "none" ? "" : argStr); camAdvertFull = (camAdvert == "" ? "" : camAdvert + '\n') + camAdvert0; if (limitCam) { cb.limitCam_stop(); cb.limitCam_start(camAdvertFull); } argStr = 'Selective Cam motto was set to:' + (camAdvert == "" ? " <none>." : '\n' + camAdvert); delayNotice(argStr, roomHost, botMsgBg); delayNotice(argStr, '', botMsgBg, '', '', 'red'); } return 1; case "prices": var msg = 'P'; if (args[3]) { prices = [arg0, args[1], args[2], args[3] ]; msg = 'New p'; delayNotice(msg + 'rices are: :avatar_cb_female ' + prices[0] + ', :avatar_cb_male ' + prices[1] + ', :avatar_cb_transsex ' + prices[2] + ', :avatar_cb_couple ' + prices[3] , '', botMsgBg,'', '','red'); } delayNotice(msg + 'rices are: :avatar_cb_female ' + prices[0] + ', :avatar_cb_male ' + prices[1] + ', :avatar_cb_transsex ' + prices[2] + ', :avatar_cb_couple ' + prices[3] , u, botMsgBg); return 1; case "addvip": if (arg0) { // add to vip list and inform user args.forEach(function(arg) { vips.add(arg); cb.sendNotice('You were granted cam access for free!', arg, botMsgBg, '', 'bold'); }); if (limitCam) { cb.limitCam_addUsers(args); } } else delayNotice('Command /gaddvip: You must supply at least one viewer name.', u, botMsgBg); return 1; case "delvip": if (arg0) { // add to vip list and inform user args.forEach(function(arg) { vips.delete(arg); cb.sendNotice('Your free cam access was cancelled!', arg, botMsgBg, '', 'bold'); }); if (limitCam) { cb.limitCam_removeUsers(args); } } else delayNotice('Command /gdelvip: You must supply at least one viewer name.', u, botMsgBg); return 1; case "remove": if (arg0 == "all") { // cb.limitCam_removeAllUsers(); // find all non-VIP users args = cb.$$limitCamAllUsersWithAccess.slice(0).filter(e => !vips.has(e)); } if (arg0) { cb.limitCam_removeUsers(args); args.forEach(function(arg) { if (arg0 != "all") { // check is only for performance reasons vips.delete(arg); } if (limitCam) { cb.sendNotice('Your cam access was cancelled!' + camAdvertFull, arg, botMsgBg, '', 'bold'); } }); } else delayNotice('Command /gremove needs at least one argument:\n' + '"all" or some viewer name(s).', u, botMsgBg); return 1; case "start": if (limitCam) { delayNotice('Selective Cam Viewing already running...', u, botMsgBg); listViewers(u); } else { cb.limitCam_start(camAdvertFull); chargeTimer = cb.setTimeout(chargeViewers, 15000); delayNotice('Selective Cam Viewing started!\n' + camAdvertFull, '', botMsgBg, '', 'bold'); listViewers(u); } return 1; case "stop": if (limitCam) { cb.limitCam_stop(); cb.cancelTimeout(chargeTimer); delayNotice('Selective Cam Viewing stopped!', roomHost, botMsgBg); delayNotice('Selective Cam Viewing stopped!', '', botMsgBg, '', '', 'red'); } else { delayNotice('Selective Cam Viewing was not running...', u, botMsgBg); } return 1; } // end inner switch } // end outer switch }; function listViewers(forUser) { var found = false; // First list paying viewers for (var i = 0; i < 4; i++) { var payers = ''; var c = 0; if (prices[i] > 0) { tippers.forEach(function(tipper) { if (tipper.gender == i) { payers = payers + tipper.user + ' (' + Math.ceil(tipper.tips/prices[i]) + ') '; c++; } }) if (c > 0) { if (!found) delayNotice('Current subscribers:', forUser, 'botMsgBg'); delayNotice(avatars[i] + ' (' + genders[i] + ') :' + c + ' - ' + payers, forUser, '', colours[i]); found = true; } } } if (!found) delayNotice('No paying viewers', forUser); // then list free viewers var freeViewers = cb.$$limitCamAllUsersWithAccess.slice(0); // a copy of the internal system array tippers.forEach(function(tipper) { // remove all tippers freeViewers = freeViewers.filter(e => e !== tipper.user); }) if (freeViewers.length > 0) { delayNotice('Current free viewers:', forUser, 'botMsgBg'); delayNotice(freeViewers.join(' '), forUser); } else { delayNotice('No free viewers', forUser); } delayNotice('Prices are: :avatar_cb_female ' + prices[0] + ', :avatar_cb_male ' + prices[1] + ', :avatar_cb_transsex ' + prices[2] + ', :avatar_cb_couple ' + prices[3] , forUser, botMsgBg); }; function addTip(user, gender, amount) { /* Update users current tip amount in global tippers set. If necessary create a new entry. Return new current tip amount. How the global tippers set is used: Only tips of tip-to-view genders are held in tippers set. Users are included only after first tip. Per-minute-charging diminuishes users tip amounts. (see function chargeViewers below). That function also removes entries falling below zero. */ var tips = amount; var found = false; for (let tipper of tippers) { // if user in tippers if (user == tipper['user']) { found = true; // update entry with new total tips and return new total tips tips = tips + tipper['tips']; tipper['tips'] = tips; break; } } if (!found && tips > 0) { // insert user with new tips and return new tips tippers.add({ user, gender, tips }); } return tips; }; function chargeViewers() { /* This function runs once every 15 seconds - but only while Selective Cam is actually active. Only viewers actually watching are charged. The Selective Cam Advert is also produced here. See also description of addTip function above for details on the global tippers set. */ tippers.forEach(function(tipper) { const u = tipper.user; if (cb.limitCam_userHasAccess(u) && !user['is_mod'] && !user['in_fanclub'] && !vips.has(u)) { const g = tipper.gender; const p = prices[g]; const tips = tipper.tips - p / 4.0; if (tips <= 0) { cb.limitCam_removeUsers([u]); tippers.delete(tipper); cb.sendNotice('Viewing time is over. :sad24 ' + 'You may tip again to resume watching', u, 'botMsgBg', '', 'bold'); cb.sendNotice(avatars[g] + ' ' + u + ' - ' + genders[g] + ' - stops watching.', roomHost, '', colours[g], 'bold'); cb.sendNotice(avatars[g] + ' ' + u + ' - ' + genders[g] + ' - stops watching.', '', '', colours[g], 'bold', 'red'); } else { tipper.tips = tips; if (p < tips && tips <= p * 1.25) { cb.sendNotice(':alert_crystal Viewing time is nearly over. ' + 'You may tip more to continue watching', u, '', colours[g], 'bold'); } } } }) if (activity && (camAdvertTime > 0.0)) { advertCounter = advertCounter - 0.25; if (advertCounter <= 0.0) { advertCounter = camAdvertTime; activity = false; cb.sendNotice('Selective Cam Viewing running!\n' + camAdvertFull, '', botMsgBg); } } chargeTimer = cb.setTimeout(chargeViewers, 15000); }; function delayNotice(msg, user, bg, fg, weight, group) { /* This function relies on the global variable delay, used to increase the timeout for each message in a group. Before each new group of delayed messages the variable must be reset to 1 */ setTimeout(function() { cb.sendNotice(msg, user, bg, fg, weight, group); }, (delay * 500)); delay++; }; Array.prototype.remove = function(element) { return this.filter(e => e !== element); }; /* // overwrite for robustness limitCam_allUsersWithAccess = function() { return cb.$$limitCamAllUsersWithAccess.slice(0); }; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }; */ // ******* INIT BOT ******* vips = new Set(new String(cb.settings["vips"]).replace(/,/g, ' ').split(" ").remove("")); // initialize greetings from cb.settings // first the defaults greetings[0][4] = (cb.settings["msg_04"] == '') ? '' : '\n' + cb.settings["msg_04"]; greetings[1][4] = (cb.settings["msg_14"] == '') ? '' : '\n' + cb.settings["msg_14"]; // then the four genders greetings[0][0] = (cb.settings["msg_00"] == '') ? greetings[0][4] : '\n' + cb.settings["msg_00"]; greetings[1][0] = (cb.settings["msg_10"] == '') ? greetings[1][4] : '\n' + cb.settings["msg_10"]; greetings[0][1] = (cb.settings["msg_01"] == '') ? greetings[0][4] : '\n' + cb.settings["msg_01"]; greetings[1][1] = (cb.settings["msg_11"] == '') ? greetings[1][4] : '\n' + cb.settings["msg_11"]; greetings[0][2] = (cb.settings["msg_02"] == '') ? greetings[0][4] : '\n' + cb.settings["msg_02"]; greetings[1][2] = (cb.settings["msg_12"] == '') ? greetings[1][4] : '\n' + cb.settings["msg_12"]; greetings[0][3] = (cb.settings["msg_03"] == '') ? greetings[0][4] : '\n' + cb.settings["msg_03"]; greetings[1][3] = (cb.settings["msg_13"] == '') ? greetings[1][4] : '\n' + cb.settings["msg_13"]; delayNotice('***** ' + botName + ' *****', '', botMsgBg, '', 'bold'); delayNotice('Type /ghelp for help.', roomHost, botMsgBg, '', 'bold'); delayNotice('Type /ghelp for help.', '', botMsgBg, '', 'bold', 'red'); if (useLimitCam && prices.join() == '0,0,0,0') { delayNotice(':alert1 Warning: :alert1 To use Selective Cam Viewing feature ' + 'please set price for at least one gender higher than 0!\n\n' + 'With all prices 0 the only effect of Selective Cam is exclude ' + 'anonymous viewers', roomHost, botMsgBg, '', 'bolder'); } // ******* END INIT BOT *******
© Copyright Chaturbate 2011- 2024. All Rights Reserved.