Apps Home
|
Create an App
VoteAndChar
Author:
camille360
Description
Source Code
Launch App
Current Users
Created by:
Camille360
function init() { initVariables(); cb.setTimeout(timerLoop, 10000); cb.onMessage(atMessage); cb.onTip(atTip); } var out_bg="#CCCCFF"; var out_fg="#000033"; var err_bg="#FFCCCC"; var err_fg="#330000"; function timerLoop() { atInterval(); cb.setTimeout(timerLoop, 10000); } cb.settings_choices = [ {name:'tokens_per_vote', type:'int', minValue:1, maxValue:100, defaultValue:10, label:"Tokens per vote"}, {name:'time_window', type:'int', minValue:60, maxValue:1000, defaultValue:180, label:"Time window (in s, rounded to multiple of 10)"}, {name:'msgs_per_vote', type:'int', minValue:1, maxValue:200, defaultValue:10, label:"Messages in time window per vote"}, {name:'initial_votes', type:'int', minValue:1, maxValue:100, defaultValue:10, label:"Initial number of votes for each option"}, {name:'win_ratio', type:'int', minValue:10, maxValue:1000, defaultValue:100, label:"Margin (in %) by which an option must lead to win"}, {name:'spam_limit', type:'int', minValue:1, maxValue:20, defaultValue:3, label:"Number of message in 10s to be considered as spam"}, {name:'countdown_votes', type:'int', minValue:0, maxValue:10, defaultValue:3, label:"Number of votes needed when starting to advertise the leading option"}, {name:'option1', type:'str', minLength:5, maxLength:240, label:"Option 1"}, {name:'option2', type:'str', minLength:5, maxLength:240, label:"Option 2"}, {name:'option3', type:'str', minLength:5, maxLength:240, label:"Option 3", required:false}, {name:'option4', type:'str', minLength:5, maxLength:240, label:"Option 4", required:false}, {name:'option5', type:'str', minLength:5, maxLength:240, label:"Option 5", required:false}, {name:'option6', type:'str', minLength:5, maxLength:240, label:"Option 6", required:false} ]; var tokens_per_vote, time_window, msgs_per_vote, initial_votes, win_ratio, spam_limit, countdown_votes, option1, option2, option3, option4, option5, option6, options; var vote_suspended, votes_per_user, votes_per_option; var tips_per_user; var msgs_in_window, msgs_per_user, new_msgs; function isNotNull(v) { return ((v!== null) && (v!=="")); } function k_initial_votes(v) { return initial_votes; } function initVariables() { var settings_choices_length = cb.settings_choices.length; for(var i =0; i < settings_choices_length; i++){ var c = cb.settings_choices[i]; var v = null; if ('defaultValue' in c) { v = c.defaultValue; } if (typeof cb.settings[c.name] !== 'undefined') { v = cb.settings[c.name]; } this[c.name] = v; } time_window /= 10; votes_per_user = new Object(); tips_per_user = new Object(); msgs_in_window = new Array(time_window); msgs_per_user = new Object(); new_msgs = new Object(); doReset(); } function addVotes(user, amount) { if(isNaN(amount)){amount = 0;} votes_per_user[user] = (votes_per_user[user]||0)+amount; if ((votes_per_user[user]>0) && (amount>0)) { cb.sendNotice("You can now vote! Chat «/stat» for details.", user, out_bg, out_fg); } } function atCommand(user, msg) { var opts = msg.split(' '); var cmd = opts.shift(); switch (cmd) { case "/stat": doStat(user); break; case "/vote": doVote(user, opts[0], opts[1]||1); break; case "/help": doHelp(user); break; case "/adjust": if(user == cb.room_slug) { addVotes(opts[0], Number(opts[1]||1)); } break; case "/punish": if(user == cb.room_slug) { new_msgs[opts[0]] = -10000; } break; case "/pause": if(user == cb.room_slug) { vote_suspended = true; } break; case "/resume": if(user == cb.room_slug) { vote_suspended = false; } break; case "/set": if((user == cb.room_slug) && (opts[0] in this)) { this[opts[0]] = ((typeof this[opts[0]]) === "number")?Number(this[opts[1]]):this[opts[1]]; } break; case "/reset": if(user == cb.room_slug) { doReset(); } break; default: cb.sendNotice("Unrecognized command: "+cmd, user, err_bg, err_fg); } } function doHelp(user) { var txt = "You can get voting rights by participating in the public chat or by tipping.\n"; txt += "The performer can also give (or take) voting rights from anyone at will.\n"; txt += "You can vote as many times as you have voting rights, multiple votes do count!\n"; txt += "Once we have a clear winner, it will be announced to everyone.\n"; txt += "Try to coordinate with the others to make every vote useful!\n"; txt += "Some special commands are recognized in the chat, they all start with «/» (without the quotes).\n"; var opt_length = options.length; for (var i = 0; i < opt_length; i++) { var o = options[i]; txt += "To vote for «"+o+"», chat «/vote "+(i+1)+"»\n"; } txt += "To vote 3 times for «"+options[0]+"», chat «/vote 1 3» (and similarly for other numbers)\n"; txt += "To see your voting rights and votes needed, chat «/stat»\n"; txt += "To see this message, chat «/help»\n"; if (user === cb.room_slug) { txt += "To remove 5 voting rights from user «foo», chat «/adjust foo -5»\n"; txt += "To punish user «foo» (not let them get new chat votes for one time window), chat «/punish foo»\n"; txt += "To pause the votes, chat «/pause»\n"; txt += "To resume the votes, chat «/resume»\n"; txt += "To reset (and resume) the votes, chat «/reset»\n"; txt += "To change variable «foo» to value «bar», chat «/set foo bar»\n"; txt += "The variables are «tokens_per_vote», «msgs_per_vote» or «win_ratio» (effective immediately)\n"; txt += "or «initial_votes», «option1» to «option6» (effective on reset)"; } cb.sendNotice(txt, user, out_bg, out_fg); } function doStat(user) { var txt = "You currently have "+(votes_per_user[user]||0)+" voting rights.\n"; txt += "Voting is "+(vote_suspended ? "on hold": "under way")+".\n"; if ((!vote_suspended) && ((votes_per_user[user]||0) > 0)) { txt += "Voting options:\n"; var opt_length = options.length; var opts = options.map(function(o, i){return {option:o, index:i, votes:votes_per_option[i]};}).sort(function(a,b){return b.votes-a.votes;}); var leader = opts[0]; leader.needs = Math.floor(opts[1].votes*(1+(win_ratio/100)))+1; var otherneeds = Math.floor(opts[0].votes*(1+(win_ratio/100)))+1; for (var i = 1; i < opt_length; i++) { opts[i].needs = otherneeds; } opts.sort(function(a,b){return a.index-b.index;}); for (var i = 0; i < opt_length; i++) { var o = opts[i]; txt += "> To vote for «"+o.option+"» ("+(o.needs-o.votes)+" votes to win), chat «/vote "+(o.index+1)+"»\n"; } } if (user === cb.room_slug) { txt += "Voting rights:\n"; for (u in votes_per_user) { txt += "> "+u+": "+votes_per_user[u]+"\n"; } txt += "Votes:\n"; var vpo_length = votes_per_option.length; for (var i = 0; i < vpo_length; i++) { txt += "> "+options[i]+": "+votes_per_option[i]+"\n"; } } cb.sendNotice(txt, user, out_bg, out_fg); } function doVote(user, opt, count) { count = Number(count); opt = Number(opt); if (isNaN(count)|| (count < 1) || (count !== Math.floor(count)) || isNaN(opt) || (opt < 1) || (opt > options.length) || (opt !== Math.floor(opt))){ cb.sendNotice("The /vote command was not recognized, chat «/help» for details how to vote.",user, err_bg, err_fg); return; } if (vote_suspended){ cb.sendNotice("You cannot vote right now, the broadcaster must enable it", user, err_bg, err_fg); return; } if ((votes_per_user[user]||0)<count){ cb.sendNotice("You cannot vote right now, chat more (constructively) or tip to get voting rights!", user, err_bg, err_fg); return; } if (opt-1 in options) { votes_per_user[user] -= count; votes_per_option[opt-1] += count; cb.sendNotice(user+" voted for «"+options[opt-1]+"»\n Thanks for your vote!", "", out_bg, out_fg); checkVotes(); return; } cb.sendNotice("The /vote command was not recognized, chat «/help» for details how to vote.", user, err_bg, err_fg); } function checkVotes() { var sorted_votes = votes_per_option.map(function (v, i){return {votes:v, option:options[i]};}).sort(function(a, b){return b.votes-a.votes;}); if (sorted_votes[0].votes > (1+(win_ratio/100))*sorted_votes[1].votes) { cb.sendNotice("The room has voted and chose «"+sorted_votes[0].option+"»!", "", out_bg, out_fg, 'bolder' ); vote_suspended = true; } else if ((sorted_votes[0].votes+countdown_votes)>(1+(win_ratio/100))*sorted_votes[1].votes) { cb.sendNotice("Only a few more votes needed to choose «"+sorted_votes[0].option+"»!", "", out_bg, out_fg, 'bold'); } } function doReset() { options = [option1, option2, option3, option4, option5, option6].filter(isNotNull); votes_per_option = options.map(k_initial_votes); vote_suspended = false; doHelp(""); } function atTip(tip) { if(tip.to_user === cb.room_slug) { tips_per_user[tip.from_user] = (tips_per_user[tip.from_user]||0)+tip.amount; if(tips_per_user[tip.from_user]>=tokens_per_vote){ addVotes(tip.from_user, Math.floor(tips_per_user[tip.from_user]/tokens_per_vote)); tips_per_user[tip.from_user] = tips_per_user[tip.from_user] % tokens_per_vote; } if(tip.message.startsWith("/")) atCommand(tip.from_user, tip.message); } } function atInterval() { for (u in new_msgs) { if (new_msgs[u] > spam_limit) { new_msgs[u]=0; } msgs_per_user[u] = (msgs_per_user[u]||0)+new_msgs[u]; } msgs_in_window.push(new_msgs); new_msgs = new Object(); var old_msgs = msgs_in_window.shift(); for (u in old_msgs) { msgs_per_user[u] = (msgs_per_user[u]||0)-old_msgs[u]; } for (u in msgs_per_user) { if (msgs_per_user[u]>=msgs_per_vote) { addVotes(u, Math.floor(msgs_per_user[u]/msgs_per_vote)); new_msgs[u] = -msgs_per_vote*Math.floor(msgs_per_user[u]/msgs_per_vote); } } } function atMessage(msg) { if(msg.m.startsWith("/")) { msg['X-Spam']=true; atCommand(msg.user, msg.m); return msg; } if(msg.user !== cb.room_slug) {new_msgs[msg.user] = (new_msgs[msg.user]||0)+1}; return msg; } init();
© Copyright Chaturbate 2011- 2026. All Rights Reserved.