NadekoBot/src/NadekoBot/Modules/Games/Commands/PollCommands.cs

185 lines
7.8 KiB
C#
Raw Normal View History

using Discord;
using Discord.Commands;
2016-12-17 00:16:14 +00:00
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games
{
public partial class Games
{
2016-12-16 19:45:46 +00:00
[Group]
public class PollCommands : ModuleBase
{
2016-12-16 19:45:46 +00:00
public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>();
2016-12-16 19:45:46 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task Poll([Remainder] string arg = null)
=> InternalStartPoll(arg, isPublic: false);
2016-12-16 19:45:46 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task PublicPoll([Remainder] string arg = null)
2016-12-16 19:45:46 +00:00
=> InternalStartPoll(arg, isPublic: true);
2016-12-16 19:45:46 +00:00
private async Task InternalStartPoll(string arg, bool isPublic = false)
{
2016-12-16 19:45:46 +00:00
var channel = (ITextChannel)Context.Channel;
2016-12-16 19:45:46 +00:00
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return;
var data = arg.Split(';');
if (data.Length < 3)
return;
2016-12-17 00:16:14 +00:00
var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic);
2016-12-16 19:45:46 +00:00
if (ActivePolls.TryAdd(channel.Guild, poll))
{
await poll.StartPoll().ConfigureAwait(false);
}
else
await channel.SendErrorAsync("Poll is already running on this server.").ConfigureAwait(false);
}
2016-12-16 19:45:46 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public async Task Pollend()
{
2016-12-16 19:45:46 +00:00
var channel = (ITextChannel)Context.Channel;
Poll poll;
ActivePolls.TryRemove(channel.Guild, out poll);
await poll.StopPoll().ConfigureAwait(false);
}
}
2016-12-16 19:45:46 +00:00
public class Poll
{
2016-12-16 19:45:46 +00:00
private readonly IUserMessage originalMessage;
private readonly IGuild guild;
private readonly string[] answers;
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
private readonly string question;
private DateTime started;
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
private readonly bool isPublic;
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{
this.originalMessage = umsg;
this.guild = ((ITextChannel)umsg.Channel).Guild;
this.question = question;
this.answers = enumerable as string[] ?? enumerable.ToArray();
this.isPublic = isPublic;
}
2016-09-04 15:23:58 +00:00
2016-12-16 19:45:46 +00:00
public async Task StartPoll()
{
started = DateTime.Now;
NadekoBot.Client.MessageReceived += Vote;
var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n";
var num = 1;
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!isPublic)
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
await originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
}
2016-12-16 19:45:46 +00:00
public async Task StopPoll()
{
2016-12-16 19:45:46 +00:00
NadekoBot.Client.MessageReceived -= Vote;
try
{
2016-12-16 19:45:46 +00:00
var results = participants.GroupBy(kvp => kvp.Value)
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value);
var totalVotesCast = results.Sum(kvp => kvp.Value);
if (totalVotesCast == 0)
{
2016-12-16 19:45:46 +00:00
await originalMessage.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false);
return;
}
2016-12-16 19:45:46 +00:00
var closeMessage = $"--------------**POLL CLOSED**--------------\n" +
$"📄 , here are the results:\n";
closeMessage = results.Aggregate(closeMessage, (current, kvp) => current + $"`{kvp.Key}.` **[{answers[kvp.Key - 1]}]**" +
$" has {kvp.Value} votes." +
$"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n");
await originalMessage.Channel.SendConfirmAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine($"Error in poll game {ex}");
}
}
private async void Vote(IMessage imsg)
2016-12-16 19:45:46 +00:00
{
try
{
// has to be a user message
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return;
// has to be an integer
int vote;
if (!int.TryParse(imsg.Content, out vote))
return;
if (vote < 1 || vote > answers.Length)
return;
IMessageChannel ch;
if (isPublic)
{
//if public, channel must be the same the poll started in
if (originalMessage.Channel.Id != imsg.Channel.Id)
return;
ch = imsg.Channel;
}
else
{
//if private, channel must be dm channel
if ((ch = msg.Channel as IDMChannel) == null)
return;
// user must be a member of the guild this poll is in
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
if (!guildUsers.Any(u => u.Id == imsg.Author.Id))
return;
}
//user can vote only once
if (participants.TryAdd(msg.Author.Id, vote))
{
if (!isPublic)
{
await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
}
else
{
var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
toDelete.DeleteAfter(5);
}
}
}
catch { }
2016-12-16 19:45:46 +00:00
}
}
}
}