Apps Home
|
Create an App
Tip Menu Panel
Author:
nicehairy
Description
Source Code
Launch App
Current Users
Created by:
Nicehairy
// Tip Menu Panel App by nicehairy v4.0 // Fork of Loveyourbear and Faynight // 2021-02-03 // Variables var total_tips = 0; var goal_tips = 0; var tippers = {}; var last_tipper = null; var last_tip = 0; var ht_username = null; var ht_amount = 0; var goal = 0; // Settings cb.settings_choices = [{ name: 'goal', type: 'int', minValue: 0, defaultValue: 100, label: "Goal Amount (Set to 0 to Disable)" }, { name: 'reset', type: 'choice', choice1: 'Yes', choice2: 'No', label: "Reset the Goal when it is Met?" }, { name: 'goalAd', type: 'str', minLength: 0, MaxLength: 300, label: "Goal Description. Will automatically post in chat when Goal is reached (Optional)", required: false }, { name: 'fav_users', type: 'str', required: false, label: "List of special users" }, { name: 'chat_ad', type: 'int', minValue: 0, maxValue: 999, defaultValue: 10, label: 'Advertise Menu in Chat Every _____ Mins (Set 0 to Disable)' }, { name: 'drain_rate', type: 'choice', choice1: "No drain rate and token price is in a separate field", choice2: "No drain rate, but token price is a number before the name", choice3: 'tokens per minute', choice4: 'tokens per second', choice5: 'seconds per token', defaultValue: "tokens per minute", label: "The first digits of a menu item is the price (in tokens). If followed by a slash '/', the next digits are the drain rate. The rest of the menu item is the name of the item." }, { name: 'item1', type: 'str', minLength: 1, maxLength: 30, label: "Tip Menu Item 1 (required)" }, { name: 'item2', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 2 (required)" }, { name: 'item3', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 3", required: false }, { name: 'item4', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 4", required: false }, { name: 'item5', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 5", required: false }, { name: 'item6', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 6", required: false }, { name: 'item7', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 7", required: false }, { name: 'item8', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 8", required: false }, { name: 'item9', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 9", required: false }, { name: 'item10', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 10", required: false }, { name: 'item11', type: 'str', minLength: 1, maxLength: 30, label: "Tip Menu Item 11", required: false }, { name: 'item12', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 12", required: false }, { name: 'item13', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 13", required: false }, { name: 'item14', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 14", required: false }, { name: 'item15', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 15", required: false }, { name: 'item16', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 16", required: false }, { name: 'item17', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 17", required: false }, { name: 'item18', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 18", required: false }, { name: 'item19', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 19", required: false }, { name: 'item20', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 20", required: false }, { name: 'item21', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 21", required: false }, { name: 'item22', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 22", required: false }, { name: 'item23', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 23", required: false }, { name: 'item24', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 24", required: false }, { name: 'item25', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 25", required: false }, { name: 'item26', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 26", required: false }, { name: 'item27', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 27", required: false }, { name: 'item28', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 28", required: false }, { name: 'item29', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 29", required: false }, { name: 'item30', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 30", required: false }, { name: 'item31', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 31", required: false }, { name: 'item32', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 32", required: false }, { name: 'item33', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 33", required: false }, { name: 'item34', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 34", required: false }, { name: 'item35', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 35", required: false }, { name: 'item36', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 36", required: false }, { name: 'item37', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 37", required: false }, { name: 'item38', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 38", required: false }, { name: 'item39', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 39", required: false }, { name: 'item40', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 40", required: false }, { name: 'item41', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 41", required: false }, { name: 'item42', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 42", required: false }, { name: 'item43', type: 'str', minLength: 1, maxLength: 50, label: "Tip Menu Item 43", required: false }, { name: 'price1', type: 'int', minValue: 0, label: "Price of Item 1, if not with name above", required: false }, { name: 'price2', type: 'int', minValue: 0, label: "Price of Item 2, if not with name above", required: false }, { name: 'price3', type: 'int', minValue: 0, label: "Price of Item 3, if not with name above", required: false }, { name: 'price4', type: 'int', minValue: 0, label: "Price of Item 4, if not with name above", required: false }, { name: 'price5', type: 'int', minValue: 0, label: "Price of Item 5, if not with name above", required: false }, { name: 'price6', type: 'int', minValue: 0, label: "Price of Item 6, if not with name above", required: false }, { name: 'price7', type: 'int', minValue: 0, label: "Price of Item 7, if not with name above", required: false }, { name: 'price8', type: 'int', minValue: 0, label: "Price of Item 8, if not with name above", required: false }, { name: 'price9', type: 'int', minValue: 0, label: "Price of Item 9, if not with name above", required: false }, { name: 'price10', type: 'int', minValue: 0, label: "Price of Item 10, if not with name above", required: false }, { name: 'price11', type: 'int', minValue: 0, label: "Price of Item 11, if not with name above", required: false }, { name: 'price12', type: 'int', minValue: 0, label: "Price of Item 12, if not with name above", required: false }, { name: 'price13', type: 'int', minValue: 0, label: "Price of Item 13, if not with name above", required: false }, { name: 'price14', type: 'int', minValue: 0, label: "Price of Item 14, if not with name above", required: false }, { name: 'price15', type: 'int', minValue: 0, label: "Price of Item 15, if not with name above", required: false }, { name: 'price16', type: 'int', minValue: 0, label: "Price of Item 16, if not with name above", required: false }, { name: 'price17', type: 'int', minValue: 0, label: "Price of Item 17, if not with name above", required: false }, { name: 'price18', type: 'int', minValue: 0, label: "Price of Item 18, if not with name above", required: false }, { name: 'price19', type: 'int', minValue: 0, label: "Price of Item 19, if not with name above", required: false }, { name: 'price20', type: 'int', minValue: 0, label: "Price of Item 20, if not with name above", required: false }, { name: 'price21', type: 'int', minValue: 0, label: "Price of Item 21, if not with name above", required: false }, { name: 'price22', type: 'int', minValue: 0, label: "Price of Item 22, if not with name above", required: false }, { name: 'price23', type: 'int', minValue: 0, label: "Price of Item 23, if not with name above", required: false }, { name: 'price24', type: 'int', minValue: 0, label: "Price of Item 24, if not with name above", required: false }, { name: 'price25', type: 'int', minValue: 0, label: "Price of Item 25, if not with name above", required: false }, { name: 'price26', type: 'int', minValue: 0, label: "Price of Item 26, if not with name above", required: false }, { name: 'price27', type: 'int', minValue: 0, label: "Price of Item 27, if not with name above", required: false }, { name: 'price28', type: 'int', minValue: 0, label: "Price of Item 28, if not with name above", required: false }, { name: 'price29', type: 'int', minValue: 0, label: "Price of Item 29, if not with name above", required: false }, { name: 'price30', type: 'int', minValue: 0, label: "Price of Item 30, if not with name above", required: false }, { name: 'price31', type: 'int', minValue: 0, label: "Price of Item 31, if not with name above", required: false }, { name: 'price32', type: 'int', minValue: 0, label: "Price of Item 32, if not with name above", required: false }, { name: 'price33', type: 'int', minValue: 0, label: "Price of Item 33, if not with name above", required: false }, { name: 'price34', type: 'int', minValue: 0, label: "Price of Item 34, if not with name above", required: false }, { name: 'price35', type: 'int', minValue: 0, label: "Price of Item 35, if not with name above", required: false }, { name: 'price36', type: 'int', minValue: 0, label: "Price of Item 36, if not with name above", required: false }, { name: 'price37', type: 'int', minValue: 0, label: "Price of Item 37, if not with name above", required: false }, { name: 'price38', type: 'int', minValue: 0, label: "Price of Item 38, if not with name above", required: false }, { name: 'price39', type: 'int', minValue: 0, label: "Price of Item 39, if not with name above", required: false }, { name: 'price40', type: 'int', minValue: 0, label: "Price of Item 40, if not with name above", required: false }, { name: 'price41', type: 'int', minValue: 0, label: "Price of Item 41, if not with name above", required: false }, { name: 'price42', type: 'int', minValue: 0, label: "Price of Item 42, if not with name above", required: false }, { name: 'price43', type: 'int', minValue: 0, label: "Price of Item 43, if not with name above", required: false } ]; // Handlers cb.onTip(function(tip) { total_tips += tip['amount']; goal_tips += tip['amount']; tippers[tip.from_user] = (+tippers[tip.from_user]||0) + +tip.amount; const item = getAllItems().find(item => tip['amount'] == item.price) || getActiveItem(); cb.chatNotice(item.name, tip.from_user); cb.chatNotice(item.name, cb.room_slug, '#ff3', '#000', 'bold'); drain(item, tip['amount']); update_app(); last_tip = tip['amount']; last_tipper = tip['from_user']; if (tip['amount'] > ht_amount) { ht_amount = tip['amount']; ht_username = tip['from_user']; } var now = new Date(); var time = [now.getHours(), now.getMinutes(), now.getSeconds()].map(pad).join(':'); cb.chatNotice([time, tip.from_user, 'has now tipped',tippers[tip.from_user],'tk'].join(' '), cb.room_slug); cb.drawPanel(); }); function pad(num) { return +num < 10 ? `0${+num}` : num.toString(); } // Chat Commands cb.onEnter(function(user) { if (user.has_tokens) { var ourtarget = user.user.trim().toLowerCase(); // Makings of the Menu const listMenu = getAllItems().map(item => '' + item.name + ' For ' + item.price + 'tks '); var rfcustom = "Please remember to Rate & Follow. Type: /menu at any time to see this again"; var msg = ['-----' + cb.room_slug + ' Tip Menu -----'].concat(listMenu, rfcustom).join('\n'); setTimeout(function() { cb.sendNotice(msg, ourtarget); }, 100); } if (isActive(user)) { cb.chatNotice(user['user'] + ' has returned', cb.room_slug, getColour(user)); } else if (user['tipped_tons_recently'] || user['tipped_alot_recently'] || user['is_mod'] || user['in_fanclub']) { cb.chatNotice(user['user'] + ' has entered', cb.room_slug, getColour(user)); } }); cb.onMessage(function(msg) { const listMenu = getAllItems().map(item => '' + item.name + ' For ' + item.price + 'tks '); msg['background'] = getColour(msg) || msg['background']; if (msg['m'] == '/showmenu' && msg['user'] == cb.room_slug) { msg['X-Spam'] = true; cb.chatNotice(['-----' + cb.room_slug + ' Tip Menu -----'].concat(listMenu).join('\n')); } else if (msg['m'] == '/menu' || msg['m'] == '/tipmenu' || msg['m'] == '/showmenu') { msg['X-Spam'] = true; cb.chatNotice(['-----' + cb.room_slug + ' Tip Menu -----'].concat(listMenu).join('\n'), msg['user']); cb.chatNotice([msg['user'], 'is looking at /menu'].join(' '), cb.room_slug, getColour(msg)); } else if (msg['user'] == cb.room_slug) { if (msg['m'].startsWith('/drainrate ')) { const active = getActiveItem(); const rate = +msg['m'].substring(msg['m'].lastIndexOf(' ')+1); if (rate && active) { if (cb.settings.drain_rate == 'seconds per token') { active.drain_per_sec = 1 / rate; // drain x tokens per second } else if (cb.settings.drain_rate == 'tokens per minute') { active.drain_per_sec = rate / 60; // drain x tokens per second } else if (cb.settings.drain_rate == 'tokens per second') { active.drain_per_sec = rate; // drain x tokens per second } else { cb.chatNotice("Drain rate is not setup :(", cb.room_slug); } } else if (active) { cb.chatNotice("Expected /drainrate followed by an number", cb.room_slug); } else { cb.chatNotice("Can only set the drainrate when there is an active menu item", cb.room_slug); } } else if (msg['m'].startsWith('/setjar ')) { const active = getActiveItem(); const tokens = +msg['m'].substring(msg['m'].lastIndexOf(' ')+1); if (tokens && active) { active.remaining_tokens = tokens; drain(active, 0); } else if (active) { cb.chatNotice("Expected /setjar followed by the number of tokens", cb.room_slug); } else { cb.chatNotice("Can only set the jar balance when there is an active menu item", cb.room_slug); } } else if (msg['m'] == '/reset') { const active = getActiveItem(); if (active) { active.remaining_tokens = 0; drain(active, 0); } else { cb.chatNotice("Can only reset the jar balance when there is an active menu item", cb.room_slug); } } else if (msg['m'].startsWith('/menu ')) { const price = +msg['m'].substring(msg['m'].lastIndexOf(' ')+1); const active = getAllItems().find(item => price == item.price); if (price && active) { drain(active, price); } else if (active) { cb.chatNotice("Expected /menu followed by the price of an item", cb.room_slug); } else { cb.chatNotice("Can only change the active menu item with the exact price", cb.room_slug); } } } else if (~msg['m'].toLowerCase().indexOf('menu')) { cb.chatNotice('To see ' + cb.room_slug + ' Tip Menu type: /menu'); } cb.drawPanel(); return msg; }); cb.onLeave(function(user) { if (isActive(user)) { cb.chatNotice(user['user'] + ' has left', cb.room_slug, getColour(user)); } }); function isActive(msg) { return cb.settings.fav_users && ~cb.settings.fav_users.indexOf(msg['user']) || +tippers[msg['user']]; } function getColour(msg) { if (cb.settings.fav_users && ~cb.settings.fav_users.indexOf(msg['user'])) { return '#6F3'; } else if (msg['user'] == ht_username) { return '#FF3'; } else if (+tippers[msg['user']]) { if (msg['tipped_tons_recently']) { return '#FDE'; } else if (msg['tipped_alot_recently']) { return '#FDD'; } else if (msg['tipped_recently']) { return '#CEF'; } else if (msg['in_fanclub']) { return '#FDB'; } else if (msg['is_mod']) { return '#FBB'; } else if (msg['has_tokens']) { return '#DFF'; } } } // Display Panels cb.onDrawPanel(function(user) { const active = getActiveItem(); const ad = item => 'Tip ' + item.price + 'tks For "' + item.name + '"'; let template = '3_rows_12_22_31'; let row1_label = 'Tip Menu is Active!'; let row1_value = 'Type /menu to see it.'; let row2_label = ad(getRandomItem()); let row2_value = ad(getRandomItem()); let row3_value = ad(getRandomItem()); if (ht_username) { row2_label = 'Highest Single Tip:'; row2_value = ht_username + '(' + ht_amount + ')'; } if (cb.settings.goal != 0 && cb.settings.reset === "Yes") { row1_label = 'Goal:'; row1_value = goal_tips + '/' + cb.settings.goal + '(' + total_tips + ')'; } else if (cb.settings.goal != 0) { row1_label = 'Goal:'; row1_value = goal_tips + '/' + cb.settings.goal; } if (active) { const minutes = Math.round(active.remaining_sec / 60); const seconds = Math.round(active.remaining_sec / 5) * 5; if (seconds > 90) { row2_label = active.name; row2_value = 'for another ' + minutes + 'm'; } else if (seconds >= 5) { row2_label = active.name; row2_value = 'for another ' + seconds + 's'; row3_value = "Tip any amount to keep this going!"; } else { row2_label = active.name; row2_value = 'is running out of tips!'; row3_value = "Tip any amount to keep this going!"; } } return { template, row1_label, row1_value, row2_label, row2_value, row3_value }; }); // Function Junction function update_app() { if (cb.settings.goal === 0) {} else if (goal_tips >= cb.settings.goal) { goal_reached(); } } function goal_reached() { if (tips_remaining() === 0) { if (cb.settings.reset === "Yes") { reset_goal(); } else; goal += 1; { if (goal <= 1) { cb.chatNotice("Goal Reached! " + cb.settings.goalAd); } } } } function reset_goal() { goal_tips -= cb.settings.goal; goal -= 1; } function tips_remaining() { var r = cb.settings.goal - goal_tips; if (r < 0) { return 0; } else { return r; } } function chatAd() { const item = getRandomItem(); cb.chatNotice('/// Tip Menu Is Active! Tip ' + item.price + 'tks for "' + item.name + '" Or, Type /menu to see the full menu.'); cb.setTimeout(chatAd, (cb.settings.chat_ad * 60000)); } if (cb.settings.chat_ad > 0) { cb.setTimeout(chatAd, (cb.settings.chat_ad * 60000)); } const drain_stack = []; function drain(item, contribution) { if (!drain_stack.some(i => i.name == item.name)) { drain_stack.push(item); } const top = drain_stack.splice(drain_stack.findIndex(i => i.name == item.name), 1)[0]; drain_stack.push(top); top.remaining_tokens = (top.remaining_tokens||0) + (contribution||0); top.remaining_sec = Math.floor(top.remaining_tokens / top.drain_per_sec * 100)/100; const interval = () => { if (top == drain_stack[drain_stack.length-1]) { if (top.remaining_tokens) { top.remaining_tokens = Math.max(top.remaining_tokens - top.drain_per_sec, 0); top.remaining_sec = Math.floor(top.remaining_tokens / top.drain_per_sec * 100)/100; } else { drain_stack.pop(); if (drain_stack.length) { drain(drain_stack[drain_stack.length-1], 0); } } cb.drawPanel(); cb.setTimeout(interval, 1000); } }; cb.setTimeout(interval, 1000); cb.chatNotice(top.name + ' has ' + top.remaining_tokens + 'tk for ' + top.remaining_sec + 's', cb.room_slug); } function getActiveItem() { return drain_stack[drain_stack.length-1]; } function getRandomItem() { const items = getAllItems(); if (!items.length) return {}; const idx = Math.floor(Math.random() * items.length); return items[idx]; } function getAllItems() { const items = []; for (var i=1; ('item' + i) in cb.settings; i++) { const item = getItem(i); if (item) { items[item.price] = item; } } return items.filter(item => item); } function getItem(index) { const code = cb.settings['item' + index]; if (!code) return null; const m = code.match(/^\s*([0-9]+)\s*(?:\/\s*([0-9.]+)\s*)?(.+)$/); if (!m && !+cb.settings['price' + index]) { cb.chatNotice('Item ' + index + ' is missing a price', cb.room_slug); return null; } else if (!m) return { index, name: code, price: +cb.settings['price' + index] }; else if (+m[2] && cb.settings.drain_rate == 'seconds per token') return { index, name: m[3], price: +m[1], drain_per_sec: 1 / +m[2] // drain x tokens per second }; else if (+m[2] && cb.settings.drain_rate == 'tokens per minute') return { index, name: m[3], price: +m[1], drain_per_sec: +m[2] / 60 // drain x tokens per second }; else if (+m[2] && cb.settings.drain_rate == 'tokens per second') return { index, name: m[3], price: +m[1], drain_per_sec: +m[2] // drain x tokens per second }; else return { index, name: m[3], price: +m[1] }; } function init() { update_app(); } init(); // Now go forth and profit!
© Copyright Chaturbate 2011- 2024. All Rights Reserved.