Trivia improved quite a bit. Questions changed. More questions to come in the future via api. closes #180
This commit is contained in:
		@@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
 | 
			
		||||
        private int QuestionDurationMiliseconds { get; } = 30000;
 | 
			
		||||
        private int HintTimeoutMiliseconds { get; } = 6000;
 | 
			
		||||
        public bool ShowHints { get; set; } = true;
 | 
			
		||||
        public bool ShowHints { get; } = true;
 | 
			
		||||
        private CancellationTokenSource triviaCancelSource { get; set; }
 | 
			
		||||
 | 
			
		||||
        public TriviaQuestion CurrentQuestion { get; private set; }
 | 
			
		||||
@@ -35,95 +35,111 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
 | 
			
		||||
        public int WinRequirement { get; } = 10;
 | 
			
		||||
 | 
			
		||||
        public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq = 10)
 | 
			
		||||
        public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            ShowHints = showHints;
 | 
			
		||||
            this._log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
            this.ShowHints = showHints;
 | 
			
		||||
            this.guild = guild;
 | 
			
		||||
            this.channel = channel;
 | 
			
		||||
            WinRequirement = winReq;
 | 
			
		||||
            Task.Run(async () => { try { await StartGame().ConfigureAwait(false); } catch { } });
 | 
			
		||||
            this.WinRequirement = winReq;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task StartGame()
 | 
			
		||||
        public async Task StartGame()
 | 
			
		||||
        {
 | 
			
		||||
            while (!ShouldStopGame)
 | 
			
		||||
            {
 | 
			
		||||
                // reset the cancellation source
 | 
			
		||||
                triviaCancelSource = new CancellationTokenSource();
 | 
			
		||||
                var token = triviaCancelSource.Token;
 | 
			
		||||
 | 
			
		||||
                // load question
 | 
			
		||||
                CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
 | 
			
		||||
                if (CurrentQuestion == null)
 | 
			
		||||
                {
 | 
			
		||||
                    try { await channel.SendErrorAsync($":exclamation: Failed loading a trivia question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
                    await End().ConfigureAwait(false);
 | 
			
		||||
                    await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
 | 
			
		||||
                                                   //sendquestion
 | 
			
		||||
                try { await channel.SendConfirmAsync($":question: Question",$"**{CurrentQuestion.Question}**").ConfigureAwait(false); }
 | 
			
		||||
                catch (HttpException ex) when (ex.StatusCode  == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
 | 
			
		||||
                {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
 | 
			
		||||
                //receive messages
 | 
			
		||||
                NadekoBot.Client.MessageReceived += PotentialGuess;
 | 
			
		||||
 | 
			
		||||
                //allow people to guess
 | 
			
		||||
                GameActive = true;
 | 
			
		||||
 | 
			
		||||
                EmbedBuilder questionEmbed;
 | 
			
		||||
                IUserMessage questionMessage;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    //hint
 | 
			
		||||
                    await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
 | 
			
		||||
                    if (ShowHints)
 | 
			
		||||
                        try { await channel.SendConfirmAsync($":exclamation: Hint", CurrentQuestion.GetHint()).ConfigureAwait(false); }
 | 
			
		||||
                        catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
 | 
			
		||||
                    //timeout
 | 
			
		||||
                    await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
 | 
			
		||||
                    questionEmbed = new EmbedBuilder().WithOkColor()
 | 
			
		||||
                        .WithTitle("Trivia Game")
 | 
			
		||||
                        .AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category))
 | 
			
		||||
                        .AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question));
 | 
			
		||||
 | 
			
		||||
                    questionMessage = await channel.EmbedAsync(questionEmbed.Build()).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
 | 
			
		||||
                {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    _log.Warn(ex);
 | 
			
		||||
                    await Task.Delay(2000).ConfigureAwait(false);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                //receive messages
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    NadekoBot.Client.MessageReceived += PotentialGuess;
 | 
			
		||||
 | 
			
		||||
                    //allow people to guess
 | 
			
		||||
                    GameActive = true;
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        //hint
 | 
			
		||||
                        await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
 | 
			
		||||
                        if (ShowHints)
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
 | 
			
		||||
                                    .ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
 | 
			
		||||
                            {
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
 | 
			
		||||
                        //timeout
 | 
			
		||||
                        await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (TaskCanceledException) { } //means someone guessed the answer
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    GameActive = false;
 | 
			
		||||
                    NadekoBot.Client.MessageReceived -= PotentialGuess;
 | 
			
		||||
                }
 | 
			
		||||
                catch (TaskCanceledException) { } //means someone guessed the answer
 | 
			
		||||
                GameActive = false;
 | 
			
		||||
                if (!triviaCancelSource.IsCancellationRequested)
 | 
			
		||||
                    try { await channel.SendConfirmAsync($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
                NadekoBot.Client.MessageReceived -= PotentialGuess;
 | 
			
		||||
                // load next question if game is still running
 | 
			
		||||
                    try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
                await Task.Delay(2000).ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
            try { NadekoBot.Client.MessageReceived -= PotentialGuess; } catch { }
 | 
			
		||||
            GameActive = false;
 | 
			
		||||
            await End().ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task End()
 | 
			
		||||
        public async Task EnsureStopped()
 | 
			
		||||
        {
 | 
			
		||||
            ShouldStopGame = true;
 | 
			
		||||
            TriviaGame throwaway;
 | 
			
		||||
            Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                      .WithTitle("Leaderboard")
 | 
			
		||||
                      .WithDescription(GetLeaderboard())
 | 
			
		||||
                      .Build(), "Trivia game ended.").ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
            catch { }
 | 
			
		||||
 | 
			
		||||
            await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                    .WithAuthor(eab => eab.WithName("Trivia Game Ended"))
 | 
			
		||||
                    .WithTitle("Final Results")
 | 
			
		||||
                    .WithDescription(GetLeaderboard())
 | 
			
		||||
                    .Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task StopGame()
 | 
			
		||||
        {
 | 
			
		||||
            if (!ShouldStopGame)
 | 
			
		||||
                try { await channel.SendConfirmAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
            var old = ShouldStopGame;
 | 
			
		||||
            ShouldStopGame = true;
 | 
			
		||||
            if (!old)
 | 
			
		||||
                try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Task PotentialGuess(IMessage imsg)
 | 
			
		||||
@@ -137,11 +153,11 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return;
 | 
			
		||||
                    if ((umsg.Channel as ITextChannel).Guild != guild) return;
 | 
			
		||||
                    if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
 | 
			
		||||
                    var textChannel = umsg.Channel as ITextChannel;
 | 
			
		||||
                    if (textChannel == null || textChannel.Guild != guild)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    var guildUser = umsg.Author as IGuildUser;
 | 
			
		||||
                    var guildUser = (IGuildUser)umsg.Author;
 | 
			
		||||
 | 
			
		||||
                    var guess = false;
 | 
			
		||||
                    await _guessLock.WaitAsync().ConfigureAwait(false);
 | 
			
		||||
@@ -156,10 +172,15 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
                    finally { _guessLock.Release(); }
 | 
			
		||||
                    if (!guess) return;
 | 
			
		||||
                    triviaCancelSource.Cancel();
 | 
			
		||||
                    try { await channel.SendConfirmAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
                    if (Users[guildUser] != WinRequirement) return;
 | 
			
		||||
                    ShouldStopGame = true;
 | 
			
		||||
                    await channel.SendConfirmAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                    
 | 
			
		||||
                    if (Users[guildUser] == WinRequirement) {
 | 
			
		||||
                        ShouldStopGame = true;
 | 
			
		||||
                        await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
            });
 | 
			
		||||
@@ -169,7 +190,7 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
        public string GetLeaderboard()
 | 
			
		||||
        {
 | 
			
		||||
            if (Users.Count == 0)
 | 
			
		||||
                return "";
 | 
			
		||||
                return "No results.";
 | 
			
		||||
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
@@ -181,4 +202,4 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
        };
 | 
			
		||||
        public static int maxStringLength = 22;
 | 
			
		||||
 | 
			
		||||
        public string Category;
 | 
			
		||||
        public string Question;
 | 
			
		||||
        public string Answer;
 | 
			
		||||
        public string Category { get; set; }
 | 
			
		||||
        public string Question { get; set; }
 | 
			
		||||
        public string Answer { get; set; }
 | 
			
		||||
 | 
			
		||||
        public TriviaQuestion(string q, string a, string c)
 | 
			
		||||
        {
 | 
			
		||||
@@ -79,9 +79,6 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
            return str;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToString() =>
 | 
			
		||||
            "Question: **" + this.Question + "?**";
 | 
			
		||||
 | 
			
		||||
        private static string Scramble(string word)
 | 
			
		||||
        {
 | 
			
		||||
            var letters = word.ToArray();
 | 
			
		||||
@@ -101,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
                if (letters[i] != ' ')
 | 
			
		||||
                    letters[i] = '_';
 | 
			
		||||
            }
 | 
			
		||||
            return "`" + string.Join(" ", letters) + "`";
 | 
			
		||||
            return string.Join(" ", letters);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using Newtonsoft.Json.Linq;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
@@ -11,36 +12,31 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
{
 | 
			
		||||
    public class TriviaQuestionPool
 | 
			
		||||
    {
 | 
			
		||||
        public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
 | 
			
		||||
        public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>();
 | 
			
		||||
        private static TriviaQuestionPool _instance;
 | 
			
		||||
        public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
 | 
			
		||||
 | 
			
		||||
        private const string questionsFile = "data/trivia_questions.json";
 | 
			
		||||
 | 
			
		||||
        private Random rng { get; } = new NadekoRandom();
 | 
			
		||||
        
 | 
			
		||||
        private TriviaQuestion[] pool { get; }
 | 
			
		||||
 | 
			
		||||
        static TriviaQuestionPool() { }
 | 
			
		||||
 | 
			
		||||
        private TriviaQuestionPool()
 | 
			
		||||
        {
 | 
			
		||||
            Reload();
 | 
			
		||||
            pool = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TriviaQuestion GetRandomQuestion(IEnumerable<TriviaQuestion> exclude)
 | 
			
		||||
        public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude)
 | 
			
		||||
        {
 | 
			
		||||
            var list = pool.Except(exclude).ToList();
 | 
			
		||||
            var rand = rng.Next(0, list.Count);
 | 
			
		||||
            return list[rand];
 | 
			
		||||
        }
 | 
			
		||||
            if (pool.Length == 0)
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
        public void Reload()
 | 
			
		||||
        {
 | 
			
		||||
            var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
 | 
			
		||||
            TriviaQuestion randomQuestion;
 | 
			
		||||
            while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ;
 | 
			
		||||
 | 
			
		||||
            foreach (var item in arr)
 | 
			
		||||
            {
 | 
			
		||||
                var tq = new TriviaQuestion(item["Question"].ToString().SanitizeMentions(), item["Answer"].ToString().SanitizeMentions(), item["Category"]?.ToString());
 | 
			
		||||
                pool.Add(tq);
 | 
			
		||||
            }
 | 
			
		||||
            var r = new NadekoRandom();
 | 
			
		||||
            pool = new ConcurrentHashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
 | 
			
		||||
            return randomQuestion;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,27 +20,30 @@ namespace NadekoBot.Modules.Games
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            public async Task Trivia(IUserMessage umsg, params string[] args)
 | 
			
		||||
            public Task Trivia(IUserMessage umsg, [Remainder] string additionalArgs = "")
 | 
			
		||||
                => Trivia(umsg, 10, additionalArgs);
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            public async Task Trivia(IUserMessage umsg, int winReq = 10, [Remainder] string additionalArgs = "")
 | 
			
		||||
            {
 | 
			
		||||
                var channel = (ITextChannel)umsg.Channel;
 | 
			
		||||
 | 
			
		||||
                TriviaGame trivia;
 | 
			
		||||
                if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
 | 
			
		||||
                var showHints = !additionalArgs.Contains("nohint");
 | 
			
		||||
 | 
			
		||||
                TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
 | 
			
		||||
                if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
 | 
			
		||||
                {
 | 
			
		||||
                    var showHints = !args.Contains("nohint");
 | 
			
		||||
                    var number = args.Select(s =>
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        int num;
 | 
			
		||||
                        return new Tuple<bool, int>(int.TryParse(s, out num), num);
 | 
			
		||||
                    }).Where(t => t.Item1).Select(t => t.Item2).FirstOrDefault();
 | 
			
		||||
                    if (number < 0)
 | 
			
		||||
                        return;
 | 
			
		||||
                    var triviaGame = new TriviaGame(channel.Guild, (ITextChannel)umsg.Channel, showHints, number == 0 ? 10 : number);
 | 
			
		||||
                    if (RunningTrivias.TryAdd(channel.Guild.Id, triviaGame))
 | 
			
		||||
                        await channel.SendConfirmAsync($"**Trivia game started! {triviaGame.WinRequirement} points needed to win.**").ConfigureAwait(false);
 | 
			
		||||
                    else
 | 
			
		||||
                        await triviaGame.StopGame().ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                        await trivia.StartGame().ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    finally
 | 
			
		||||
                    {
 | 
			
		||||
                        RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
 | 
			
		||||
                        await trivia.EnsureStopped().ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    return;                    
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								src/NadekoBot/data/trivia_questions.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/NadekoBot/data/trivia_questions.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user