using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Discord; using Discord.WebSocket; using NadekoBot.Extensions; using NadekoBot.Services.Impl; namespace NadekoBot.Modules.Games.Common { public class Poll { private readonly IUserMessage _originalMessage; private readonly IGuild _guild; private readonly string[] answers; private readonly ConcurrentDictionary _participants = new ConcurrentDictionary(); private readonly string _question; private readonly DiscordSocketClient _client; private readonly NadekoStrings _strings; private bool running = false; public event Action OnEnded = delegate { }; public Poll(DiscordSocketClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable enumerable) { _client = client; _strings = strings; _originalMessage = umsg; _guild = ((ITextChannel)umsg.Channel).Guild; _question = question; answers = enumerable as string[] ?? enumerable.ToArray(); } 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(GetText("no_votes_cast")); } else { for (int i = 0; i < results.Length; i++) { var result = results[i]; sb.AppendLine(GetText("poll_result", result.Key, Format.Bold(answers[result.Key - 1]), Format.Bold(result.Value.ToString()))); totalVotesCast += result.Value; } } eb.WithDescription(sb.ToString()) .WithFooter(efb => efb.WithText(GetText("x_votes_cast", totalVotesCast))); return eb; } public async Task StartPoll() { var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n"; var num = 1; msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); msgToSend += "\n" + Format.Bold(GetText("poll_vote_public")); await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false); running = true; } public async Task StopPoll() { running = false; OnEnded(_guild.Id); await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false); } public async Task TryVote(IUserMessage msg) { // has to be a user message if (msg == null || msg.Author.IsBot || !running) return false; // has to be an integer if (!int.TryParse(msg.Content, out int vote)) return false; if (vote < 1 || vote > answers.Length) return false; IMessageChannel ch; //if public, channel must be the same the poll started in if (_originalMessage.Channel.Id != msg.Channel.Id) return false; ch = msg.Channel; //user can vote only once if (_participants.TryAdd(msg.Author.Id, vote)) { var toDelete = await ch.SendConfirmAsync(GetText("poll_voted", Format.Bold(msg.Author.ToString()))).ConfigureAwait(false); toDelete.DeleteAfter(5); try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } return true; } return false; } private string GetText(string key, params object[] replacements) => _strings.GetText(key, _guild.Id, "Games".ToLowerInvariant(), replacements); } }