NadekoBot/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs

212 lines
8.4 KiB
C#
Raw Normal View History

2016-02-02 11:23:58 +00:00
using Discord;
2016-10-12 21:41:31 +00:00
using Discord.Net;
using Discord.WebSocket;
2016-02-02 11:23:58 +00:00
using NadekoBot.Extensions;
using NadekoBot.Services;
2016-10-05 05:01:19 +00:00
using NLog;
using System;
2016-02-02 11:23:58 +00:00
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
2016-02-02 11:23:58 +00:00
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Trivia
2016-03-26 02:25:03 +00:00
{
public class TriviaGame
2016-03-26 02:25:03 +00:00
{
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
2016-10-05 05:01:19 +00:00
private Logger _log { get; }
2016-02-02 11:23:58 +00:00
2016-10-03 02:19:14 +00:00
public IGuild guild { get; }
public ITextChannel channel { get; }
2016-02-02 11:23:58 +00:00
private int QuestionDurationMiliseconds { get; } = 30000;
private int HintTimeoutMiliseconds { get; } = 6000;
public bool ShowHints { get; } = true;
2016-02-02 11:23:58 +00:00
private CancellationTokenSource triviaCancelSource { get; set; }
public TriviaQuestion CurrentQuestion { get; private set; }
public HashSet<TriviaQuestion> oldQuestions { get; } = new HashSet<TriviaQuestion>();
2016-02-02 11:23:58 +00:00
public ConcurrentDictionary<IGuildUser, int> Users { get; } = new ConcurrentDictionary<IGuildUser, int>();
2016-02-02 11:23:58 +00:00
public bool GameActive { get; private set; } = false;
public bool ShouldStopGame { get; private set; }
public int WinRequirement { get; } = 10;
2016-02-02 11:23:58 +00:00
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
2016-03-26 02:25:03 +00:00
{
this._log = LogManager.GetCurrentClassLogger();
this.ShowHints = showHints;
this.guild = guild;
this.channel = channel;
this.WinRequirement = winReq;
2016-02-02 11:23:58 +00:00
}
public async Task StartGame()
2016-03-26 02:25:03 +00:00
{
while (!ShouldStopGame)
{
2016-02-02 11:23:58 +00:00
// reset the cancellation source
triviaCancelSource = new CancellationTokenSource();
2016-02-02 11:23:58 +00:00
// load question
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
2017-01-15 01:45:20 +00:00
if (CurrentQuestion == null ||
string.IsNullOrWhiteSpace(CurrentQuestion.Answer) ||
string.IsNullOrWhiteSpace(CurrentQuestion.Question))
2016-03-26 02:25:03 +00:00
{
await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
2016-02-02 11:23:58 +00:00
return;
}
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
EmbedBuilder questionEmbed;
IUserMessage questionMessage;
try
2016-10-12 21:41:31 +00:00
{
questionEmbed = new EmbedBuilder().WithOkColor()
.WithTitle("Trivia Game")
.AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category))
.AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question));
2016-12-31 16:34:21 +00:00
questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false);
}
2017-01-28 23:38:09 +00:00
catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound ||
ex.HttpCode == System.Net.HttpStatusCode.Forbidden ||
ex.HttpCode == System.Net.HttpStatusCode.BadRequest)
{
return;
}
catch (Exception ex)
{
_log.Warn(ex);
await Task.Delay(2000).ConfigureAwait(false);
continue;
2016-10-12 21:41:31 +00:00
}
2016-02-02 11:23:58 +00:00
//receive messages
2016-03-26 02:25:03 +00:00
try
{
NadekoBot.Client.MessageReceived += PotentialGuess;
2016-02-02 11:23:58 +00:00
//allow people to guess
GameActive = true;
try
{
//hint
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
if (ShowHints)
try
{
2017-01-03 15:02:02 +00:00
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
.ConfigureAwait(false);
}
2017-01-28 23:38:09 +00:00
catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound || ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
{
break;
}
catch (Exception ex) { _log.Warn(ex); }
//timeout
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
2016-02-02 11:23:58 +00:00
}
catch (TaskCanceledException) { } //means someone guessed the answer
}
finally
{
GameActive = false;
NadekoBot.Client.MessageReceived -= PotentialGuess;
2016-03-26 02:25:03 +00:00
}
2016-02-28 07:47:47 +00:00
if (!triviaCancelSource.IsCancellationRequested)
try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
2016-04-18 21:38:19 +00:00
await Task.Delay(2000).ConfigureAwait(false);
2016-02-02 11:23:58 +00:00
}
}
public async Task EnsureStopped()
2016-03-26 02:25:03 +00:00
{
ShouldStopGame = true;
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Trivia Game Ended"))
.WithTitle("Final Results")
2016-12-31 16:34:21 +00:00
.WithDescription(GetLeaderboard())).ConfigureAwait(false);
2016-02-02 11:23:58 +00:00
}
2016-03-26 02:25:03 +00:00
public async Task StopGame()
{
var old = ShouldStopGame;
2016-02-02 11:23:58 +00:00
ShouldStopGame = true;
if (!old)
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
2016-02-02 11:23:58 +00:00
}
2017-01-15 01:28:33 +00:00
private async Task PotentialGuess(SocketMessage imsg)
2016-03-26 02:25:03 +00:00
{
try
2016-03-26 02:25:03 +00:00
{
2016-12-28 06:56:44 +00:00
if (imsg.Author.IsBot)
return;
var umsg = imsg as SocketUserMessage;
2016-12-28 06:56:44 +00:00
if (umsg == null)
return;
var textChannel = umsg.Channel as ITextChannel;
if (textChannel == null || textChannel.Guild != guild)
return;
2016-09-04 15:23:58 +00:00
var guildUser = (IGuildUser)umsg.Author;
2016-09-04 15:23:58 +00:00
var guess = false;
await _guessLock.WaitAsync().ConfigureAwait(false);
try
{
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
2016-03-26 02:25:03 +00:00
{
Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
guess = true;
}
}
finally { _guessLock.Release(); }
if (!guess) return;
triviaCancelSource.Cancel();
if (Users[guildUser] == WinRequirement)
{
ShouldStopGame = true;
try { await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch { }
var reward = NadekoBot.BotConfig.TriviaCurrencyReward;
if (reward > 0)
2017-01-13 13:44:50 +00:00
await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
return;
2016-02-02 11:23:58 +00:00
}
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
2016-02-02 11:23:58 +00:00
}
2016-03-26 02:25:03 +00:00
public string GetLeaderboard()
{
if (Users.Count == 0)
return "No results.";
2016-02-02 11:23:58 +00:00
var sb = new StringBuilder();
2016-02-02 11:23:58 +00:00
2016-10-12 21:41:31 +00:00
foreach (var kvp in Users.OrderByDescending(kvp => kvp.Value))
2016-03-26 02:25:03 +00:00
{
sb.AppendLine($"**{kvp.Key.Username}** has {kvp.Value} points".ToString().SnPl(kvp.Value));
2016-02-02 11:23:58 +00:00
}
return sb.ToString();
2016-02-02 11:23:58 +00:00
}
}
}