From 4eef1e9c2b5e7aebfaef5c50c5c326e06e5525e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A4r=20Halberkamp?= Date: Thu, 24 Sep 2015 23:35:11 +0200 Subject: [PATCH] Implement polls (serverside code) --- chat-plugins/poll.js | 163 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 chat-plugins/poll.js diff --git a/chat-plugins/poll.js b/chat-plugins/poll.js new file mode 100644 index 0000000000..76693b78e2 --- /dev/null +++ b/chat-plugins/poll.js @@ -0,0 +1,163 @@ +/* +* Poll chat plugin +* This plugin allows roomauth (default: driver and above) to run a poll in a room. +* Every room can have one poll, and every user can vote. The results are displayed for +* users that have voted, and are updated in real time. The poll can be closed with /endpoll. +* By bumbadadabum with (a lot of) help from Zarel. +*/ + +var permission = 'announce'; + +var Poll = (function () { + function Poll(room, question, options) { + if (room.pollNumber) { + room.pollNumber++; + } else { + room.pollNumber = 1; + } + this.room = room; + this.question = question; + this.voters = new Set(); + this.totalVotes = 0; + + this.options = new Map(); + for (var i = 0; i < options.length; i++) { + this.options.set(i + 1, {name: options[i], votes: 0}); + } + } + + Poll.prototype.vote = function (user, option) { + if (this.voters.has(user.latestIp)) { + return user.sendTo(this.room, "You have already voted for this poll."); + } else { + this.voters.add(user.latestIp); + } + + this.options.get(option).votes++; + this.totalVotes++; + + this.update(); + }; + + Poll.prototype.generateVotes = function () { + var output = '

' + Tools.escapeHTML(this.question) + '

'; + this.options.forEach(function (option, number) { + output += '
'; + }); + output += '
'; + + return output; + }; + + Poll.prototype.generateResults = function () { + var output = '

' + Tools.escapeHTML(this.question) + '

'; + var iter = this.options.entries(); + + var i = iter.next(); + while (!i.done) { + output += '' + i.value[0] + '. ' + Tools.escapeHTML(i.value[1].name) + '
' + i.value[1].votes + ' votes (' + Math.round((i.value[1].votes * 100) / this.totalVotes) + '% of total).
'; + i = iter.next(); + } + output += '
'; + + return output; + }; + + Poll.prototype.update = function () { + var results = this.generateResults(); + + // Update the poll results for everyone that has voted + for (var i in this.room.users) { + var user = this.room.users[i]; + if (this.voters.has(user.latestIp)) { + user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results); + } + } + }; + + Poll.prototype.display = function (user, broadcast) { + var votes = this.generateVotes(); + var results = this.generateResults(); + + var target = {}; + + if (broadcast) { + target = this.room.users; + } else { + target[0] = user; + } + + for (var i in target) { + var thisUser = target[i]; + if (this.voters.has(thisUser.latestIp)) { + thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results); + } else { + thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes); + } + } + }; + + Poll.prototype.end = function () { + var results = this.generateResults(); + + this.room.send('|uhtml|poll' + this.room.pollNumber + '|' + results); + this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|' + results); + }; + + return Poll; +})(); + +exports.commands = { + poll: { + new: function (target, room, user) { + var params = target.split(target.includes('|') ? '|' : ',').map(function (param) { return param.trim(); }); + + if (!this.can(permission, null, room)) return false; + if (room.poll) return this.errorReply("There is already a poll in progress in this room."); + + if (params.length < 3) { + return this.errorReply("Not enough arguments for /poll new."); + } + + var options = []; + + for (var i = 1; i < params.length; i++) { + options.push(params[i]); + } + + if (options.length > 8) { + return this.errorReply("Too many options for poll (maximum is 8)."); + } + + room.poll = new Poll(room, params[0], options); + room.poll.display(user, true); + }, + + vote: function (target, room, user) { + if (!room.poll) return this.errorReply("There is no poll running in this room."); + if (!target) return this.errorReply("Please specify an option."); + + var parsed = parseInt(target); + if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option."); + + if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll."); + + room.poll.vote(user, parsed); + }, + + end: function (target, room, user) { + if (!this.can(permission, null, room)) return false; + if (!room.poll) return this.errorReply("There is no poll running in this room."); + + room.poll.end(); + delete room.poll; + }, + + '': function (target, room, user) { + if (!room.poll) return this.errorReply("There is no poll running in this room."); + if (!this.canBroadcast()) return; + + room.poll.display(user, this.broadcasting); + } + } +};