NadekoBot/NadekoBot.Core/Modules/Games/Common/Poll.cs

130 lines
4.5 KiB
C#

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.Core.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<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
private readonly string _question;
private readonly DiscordSocketClient _client;
private readonly NadekoStrings _strings;
private bool running = false;
public event Action<ulong> OnEnded = delegate { };
public Poll(DiscordSocketClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> 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<bool> 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);
}
}