/* * Copyright (C) 2016-2020 phantombot.github.io/PhantomBot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * pollSystem.js * * This module enables the channel owner to start/manage polls * Start/stop polls is exported to $.poll for use in other scripts */ (function() { var poll = { pollId: 0, options: [], votes: [], voters: [], callback: function() {}, pollRunning: false, pollMaster: '', time: 0, question: '', minVotes: 0, result: '', hasTie: 0, counts: [], }, timeout; var objOBS = []; /** * @function hasKey * @param {Array} list * @param {*} value * @param {Number} [subIndex] * @returns {boolean} */ function hasKey(list, value, subIndex) { var i; if (subIndex > -1) { for (i in list) { if (list[i][subIndex].equalsIgnoreCase(value)) { return true; } } } else { for (i in list) { if (list[i].equalsIgnoreCase(value)) { return true; } } } return false; }; // Compile regular expressions. var rePollOpenFourOptions = new RegExp(/"([\w\W]+)"\s+"([\w\W]+)"\s+(\d+)\s+(\d+)/), rePollOpenThreeOptions = new RegExp(/"([\w\W]+)"\s+"([\w\W]+)"\s+(\d+)/), rePollOpenTwoOptions = new RegExp(/"([\w\W]+)"\s+"([\w\W]+)"/); /** * @function runPoll * @export $.poll * @param {string} question * @param {Array} options * @param {Number} time * @param {string} pollMaster * @param {Number} [minVotes] * @param {Function} callback * @param {string} [initialVote] * @returns {boolean} */ function runPoll(question, options, time, pollMaster, minVotes, callback) { var optionsStr = ""; if (poll.pollRunning) { return false } objOBS = []; poll.pollRunning = true; poll.pollMaster = pollMaster; poll.time = (parseInt(time) * 1000); poll.callback = callback; poll.question = question; poll.options = options; poll.minVotes = (minVotes ? minVotes : 1); poll.votes = []; poll.voters = []; poll.counts = []; poll.hasTie = 0; // Remove the old files. $.inidb.RemoveFile('pollPanel'); $.inidb.RemoveFile('pollVotes'); for (var i = 0; i < poll.options.length; i++) { optionsStr += (i + 1) + ") " + poll.options[i] + " "; $.inidb.set('pollVotes', poll.options[i], 0); objOBS.push({ 'label': poll.options[i], 'votes': 0 }); } if (poll.time > 0) { $.say($.lang.get('pollsystem.poll.started', $.resolveRank(pollMaster), time, poll.minVotes, poll.question, optionsStr)); timeout = setTimeout(function() { endPoll(); }, poll.time); } else { $.say($.lang.get('pollsystem.poll.started.nottime', $.resolveRank(pollMaster), poll.minVotes, poll.question, optionsStr)); } var msg = JSON.stringify({ 'start_poll': 'true', 'data': JSON.stringify(objOBS) }); $.alertspollssocket.sendJSONToAll(msg); $.inidb.set('pollPanel', 'title', question); $.inidb.set('pollPanel', 'options', options.join(',')); $.inidb.set('pollPanel', 'isActive', 'true'); return true; }; /** * @function vote * @param {string} sender * @param {string} voteText */ function vote(sender, voteText) { var optionIndex; if (!poll.pollRunning) { return; } if (hasKey(poll.voters, sender.toLowerCase())) { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.vote.already')); return; } optionIndex = parseInt(voteText); if (isNaN(optionIndex) || optionIndex < 1 || optionIndex > poll.options.length) { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.vote.invalid', voteText)); return; } optionIndex--; poll.voters.push(sender); poll.votes.push(optionIndex); for (var i = 0; i < objOBS.length; i++) { if (objOBS[i].label == poll.options[optionIndex]) objOBS[i].votes++; } var msg = JSON.stringify({ 'new_vote': 'true', 'data': JSON.stringify(objOBS) }); $.alertspollssocket.sendJSONToAll(msg); $.inidb.incr('pollVotes', poll.options[optionIndex], 1); }; /** * @function endPoll * @export $.poll */ function endPoll() { var mostVotes = -1, i; if (!poll.pollRunning) { return; } clearTimeout(timeout); $.inidb.set('pollPanel', 'isActive', 'false'); var msg = JSON.stringify({ 'end_poll': 'true' }); $.alertspollssocket.sendJSONToAll(msg); if (poll.minVotes > 0 && poll.votes.length < poll.minVotes) { poll.result = ''; poll.pollMaster = ''; poll.pollRunning = false; poll.callback(false); return; } for (i = 0; i < poll.options.length; poll.counts.push(0), i++); for (i = 0; i < poll.votes.length; poll.counts[poll.votes[i++]] += 1); for (i = 0; i < poll.counts.length; winner = ((poll.counts[i] > mostVotes) ? i : winner), mostVotes = ((poll.counts[i] > mostVotes) ? poll.counts[i] : mostVotes), i++); for (i = 0; i < poll.counts.length; (i != winner && poll.counts[i] == poll.counts[winner] ? poll.hasTie = 1 : 0), (poll.hasTie == 1 ? i = poll.counts.length : 0), i++); poll.result = poll.options[winner]; poll.pollMaster = ''; poll.pollRunning = false; // Store the results for the Panel to read. $.inidb.set('pollresults', 'question', poll.question); $.inidb.set('pollresults', 'result', poll.result); $.inidb.set('pollresults', 'votes', poll.votes.length); $.inidb.set('pollresults', 'options', poll.options.join(',')); $.inidb.set('pollresults', 'counts', poll.counts.join(',')); $.inidb.set('pollresults', 'istie', poll.hasTie); poll.callback(poll.result); }; /** * @event command */ $.bind('command', function(event) { var sender = event.getSender().toLowerCase(), command = event.getCommand(), argsString = event.getArguments().trim(), args = event.getArgs(), action = args[0]; if (command.equalsIgnoreCase('vote') && action !== undefined) { if (poll.pollRunning) { vote(sender, action); } } /** * @commandpath poll - Announce information about a poll, if one is running. */ if (command.equalsIgnoreCase('poll')) { if (!action) { if (poll.pollRunning) { var optionsStr = ""; for (var i = 0; i < poll.options.length; i++) { optionsStr += (i + 1) + ") " + poll.options[i] + (i == poll.options.length - 1 ? "" : " "); } $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.poll.running', poll.question, optionsStr)); } else { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.poll.usage')); } return; } /** * @commandpath poll results - Announce result information about the last run poll (Poll information is retained until shutdown) */ if (action.equalsIgnoreCase('results')) { if (poll.pollRunning) { $.say($.lang.get('pollsystem.results.running')); } else if (poll.result != '') { if (poll.hasTie) { $.say($.lang.get('pollsystem.results.lastpoll', poll.question, poll.votes.length, "Tie!", poll.options.join(', '), poll.counts.join(', '))); } else { $.say($.lang.get('pollsystem.results.lastpoll', poll.question, poll.votes.length, poll.result, poll.options.join(', '), poll.counts.join(', '))); } } else { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.results.404')); } } /** * @commandpath poll open ["poll question"] ["option1, option2, ..."] [seconds] [min votes] - Starts a poll with question and options. Optionally provide seconds and min votes. */ if (action.equalsIgnoreCase('open')) { var time = 0, question = '', options = [], minVotes = 1; argsString = argsString + ""; // Cast as a JavaScript string. if (argsString.match(rePollOpenFourOptions)) { question = argsString.match(rePollOpenFourOptions)[1]; options = argsString.match(rePollOpenFourOptions)[2].split(/,\s*/); time = parseInt(argsString.match(rePollOpenFourOptions)[3]); minVotes = parseInt(argsString.match(rePollOpenFourOptions)[4]); } else if (argsString.match(rePollOpenThreeOptions)) { question = argsString.match(rePollOpenThreeOptions)[1]; options = argsString.match(rePollOpenThreeOptions)[2].split(/,\s*/); time = parseInt(argsString.match(rePollOpenThreeOptions)[3]); } else if (argsString.match(rePollOpenTwoOptions)) { question = argsString.match(rePollOpenTwoOptions)[1]; options = argsString.match(rePollOpenTwoOptions)[2].split(/,\s*/); } else { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.open.usage')); return; } if (!question || !options || options.length === 0 || isNaN(minVotes) || minVotes < 1) { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.open.usage')); return; } if (options.length === 1) { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.open.moreoptions')); return; } if (runPoll(question, options, parseInt(time), sender, minVotes, function(winner) { if (winner === false) { $.say($.lang.get('pollsystem.runpoll.novotes', question)); return; } if (poll.hasTie) { $.say($.lang.get('pollsystem.runpoll.tie', question)); } else { $.say($.lang.get('pollsystem.runpoll.winner', question, winner)); } })) { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.runpoll.started')); } else { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.results.running')); } } /** * @commandpath poll close - Close the current poll and tally the votes */ if (action.equalsIgnoreCase('close')) { if (!poll.pollRunning) { $.say($.whisperPrefix(sender) + $.lang.get('pollsystem.close.nopoll')); return; } endPoll(); } } }); /** * @event initReady */ $.bind('initReady', function() { $.registerChatCommand('./systems/pollSystem.js', 'poll', 2); $.registerChatCommand('./systems/pollSystem.js', 'vote', 7); $.registerChatSubcommand('poll', 'results', 2); $.registerChatSubcommand('poll', 'open', 2); $.registerChatSubcommand('poll', 'close', 2); }); /** Export functions to API */ $.poll = { runPoll: runPoll, endPoll: endPoll }; })();