2016-08-16 12:57:47 +00:00
|
|
|
|
using Discord;
|
|
|
|
|
using Discord.Commands;
|
2016-12-17 00:16:14 +00:00
|
|
|
|
using Discord.WebSocket;
|
2016-08-16 12:57:47 +00:00
|
|
|
|
using NadekoBot.Attributes;
|
2016-12-11 00:42:58 +00:00
|
|
|
|
using NadekoBot.Extensions;
|
2016-08-16 12:57:47 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
using System.Text;
|
2016-08-16 12:57:47 +00:00
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2016-08-20 20:35:27 +00:00
|
|
|
|
namespace NadekoBot.Modules.Games
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2016-09-08 20:46:38 +00:00
|
|
|
|
public partial class Games
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2016-12-16 19:45:46 +00:00
|
|
|
|
[Group]
|
2017-02-19 14:18:40 +00:00
|
|
|
|
public class PollCommands : NadekoSubmodule
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2017-01-08 17:08:57 +00:00
|
|
|
|
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
|
2016-08-16 12:57:47 +00:00
|
|
|
|
|
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-08-16 12:57:47 +00:00
|
|
|
|
|
2016-12-16 19:45:46 +00:00
|
|
|
|
[NadekoCommand, Usage, Description, Aliases]
|
|
|
|
|
[RequireUserPermission(GuildPermission.ManageMessages)]
|
|
|
|
|
[RequireContext(ContextType.Guild)]
|
2016-12-21 08:33:47 +00:00
|
|
|
|
public Task PublicPoll([Remainder] string arg = null)
|
2016-12-16 19:45:46 +00:00
|
|
|
|
=> InternalStartPoll(arg, isPublic: true);
|
2016-08-16 12:57:47 +00:00
|
|
|
|
|
2017-01-08 17:08:57 +00:00
|
|
|
|
[NadekoCommand, Usage, Description, Aliases]
|
|
|
|
|
[RequireUserPermission(GuildPermission.ManageMessages)]
|
|
|
|
|
[RequireContext(ContextType.Guild)]
|
|
|
|
|
public async Task PollStats()
|
|
|
|
|
{
|
|
|
|
|
Games.Poll poll;
|
|
|
|
|
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
await Context.Channel.EmbedAsync(poll.GetStats("Current Poll Results"));
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-16 19:45:46 +00:00
|
|
|
|
private async Task InternalStartPoll(string arg, bool isPublic = false)
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2016-12-16 19:45:46 +00:00
|
|
|
|
var channel = (ITextChannel)Context.Channel;
|
2016-08-16 12:57:47 +00:00
|
|
|
|
|
2016-12-16 19:45:46 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
|
|
|
|
|
return;
|
|
|
|
|
var data = arg.Split(';');
|
|
|
|
|
if (data.Length < 3)
|
2016-08-16 12:57:47 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2016-12-17 00:16:14 +00:00
|
|
|
|
var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic);
|
2017-01-08 17:08:57 +00:00
|
|
|
|
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
|
2016-12-16 19:45:46 +00:00
|
|
|
|
{
|
|
|
|
|
await poll.StartPoll().ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
await channel.SendErrorAsync("Poll is already running on this server.").ConfigureAwait(false);
|
2016-08-16 12:57:47 +00:00
|
|
|
|
}
|
2016-12-16 19:45:46 +00:00
|
|
|
|
|
|
|
|
|
[NadekoCommand, Usage, Description, Aliases]
|
|
|
|
|
[RequireUserPermission(GuildPermission.ManageMessages)]
|
|
|
|
|
[RequireContext(ContextType.Guild)]
|
|
|
|
|
public async Task Pollend()
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2016-12-16 19:45:46 +00:00
|
|
|
|
var channel = (ITextChannel)Context.Channel;
|
|
|
|
|
|
|
|
|
|
Poll poll;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
ActivePolls.TryRemove(channel.Guild.Id, out poll);
|
2016-12-16 19:45:46 +00:00
|
|
|
|
await poll.StopPoll().ConfigureAwait(false);
|
2016-08-16 12:57:47 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-16 19:45:46 +00:00
|
|
|
|
public class Poll
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2016-12-16 19:45:46 +00:00
|
|
|
|
private readonly IUserMessage originalMessage;
|
|
|
|
|
private readonly IGuild guild;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
private string[] Answers { get; }
|
2016-12-16 19:45:46 +00:00
|
|
|
|
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
|
|
|
|
|
private readonly string question;
|
|
|
|
|
private DateTime started;
|
|
|
|
|
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
|
2017-01-08 17:08:57 +00:00
|
|
|
|
public bool IsPublic { get; }
|
2016-12-16 19:45:46 +00:00
|
|
|
|
|
|
|
|
|
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
|
|
|
|
|
{
|
|
|
|
|
this.originalMessage = umsg;
|
|
|
|
|
this.guild = ((ITextChannel)umsg.Channel).Guild;
|
|
|
|
|
this.question = question;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
this.Answers = enumerable as string[] ?? enumerable.ToArray();
|
|
|
|
|
this.IsPublic = isPublic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public EmbedBuilder GetStats(string title)
|
|
|
|
|
{
|
|
|
|
|
var results = participants.GroupBy(kvp => kvp.Value)
|
|
|
|
|
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
|
|
|
|
|
.OrderByDescending(kvp => kvp.Value)
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
var eb = new EmbedBuilder().WithTitle(title);
|
|
|
|
|
|
|
|
|
|
var sb = new StringBuilder()
|
|
|
|
|
.AppendLine(Format.Bold(question))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
|
|
|
|
|
var totalVotesCast = 0;
|
|
|
|
|
if (results.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendLine("No votes cast.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < results.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var result = results[i];
|
|
|
|
|
sb.AppendLine($"`{i + 1}.` {Format.Bold(Answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes.");
|
|
|
|
|
totalVotesCast += result.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eb.WithDescription(sb.ToString())
|
|
|
|
|
.WithFooter(efb => efb.WithText(totalVotesCast + " total votes cast."));
|
|
|
|
|
|
|
|
|
|
return eb;
|
2016-12-16 19:45:46 +00:00
|
|
|
|
}
|
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;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
msgToSend = Answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
|
|
|
|
|
if (!IsPublic)
|
2016-12-16 19:45:46 +00:00
|
|
|
|
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-10-24 18:26:28 +00:00
|
|
|
|
|
2016-12-16 19:45:46 +00:00
|
|
|
|
public async Task StopPoll()
|
2016-08-16 12:57:47 +00:00
|
|
|
|
{
|
2016-12-16 19:45:46 +00:00
|
|
|
|
NadekoBot.Client.MessageReceived -= Vote;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
await originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
|
2016-12-16 19:45:46 +00:00
|
|
|
|
}
|
2016-10-24 18:26:28 +00:00
|
|
|
|
|
2017-01-15 01:28:33 +00:00
|
|
|
|
private async Task Vote(SocketMessage imsg)
|
2016-12-16 19:45:46 +00:00
|
|
|
|
{
|
2016-12-31 12:21:18 +00:00
|
|
|
|
try
|
2016-10-24 18:26:28 +00:00
|
|
|
|
{
|
2016-12-31 12:21:18 +00:00
|
|
|
|
// has to be a user message
|
2017-01-01 12:26:17 +00:00
|
|
|
|
var msg = imsg as SocketUserMessage;
|
2016-12-31 12:21:18 +00:00
|
|
|
|
if (msg == null || msg.Author.IsBot)
|
2016-12-28 05:31:39 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2016-12-31 12:21:18 +00:00
|
|
|
|
// has to be an integer
|
|
|
|
|
int vote;
|
|
|
|
|
if (!int.TryParse(imsg.Content, out vote))
|
|
|
|
|
return;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
if (vote < 1 || vote > Answers.Length)
|
2016-12-28 05:31:39 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2016-12-31 12:21:18 +00:00
|
|
|
|
IMessageChannel ch;
|
2017-01-08 17:08:57 +00:00
|
|
|
|
if (IsPublic)
|
2016-10-24 18:26:28 +00:00
|
|
|
|
{
|
2016-12-31 12:21:18 +00:00
|
|
|
|
//if public, channel must be the same the poll started in
|
|
|
|
|
if (originalMessage.Channel.Id != imsg.Channel.Id)
|
|
|
|
|
return;
|
|
|
|
|
ch = imsg.Channel;
|
2016-10-24 18:26:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-12-31 12:21:18 +00:00
|
|
|
|
//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))
|
|
|
|
|
{
|
2017-01-08 17:08:57 +00:00
|
|
|
|
if (!IsPublic)
|
2016-12-31 12:21:18 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2016-10-24 18:26:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-31 12:21:18 +00:00
|
|
|
|
catch { }
|
2016-12-16 19:45:46 +00:00
|
|
|
|
}
|
2016-08-16 12:57:47 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|