2016-02-02 11:23:58 +00:00
using Discord ;
2016-10-12 21:41:31 +00:00
using Discord.Net ;
2017-01-01 12:26:17 +00:00
using Discord.WebSocket ;
2016-02-02 11:23:58 +00:00
using NadekoBot.Extensions ;
2017-01-12 19:19:56 +00:00
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 ;
2016-03-02 05:00:44 +00:00
using System.Text ;
2016-02-02 11:23:58 +00:00
using System.Threading ;
using System.Threading.Tasks ;
2016-08-20 20:35:27 +00:00
namespace NadekoBot.Modules.Games.Trivia
2016-03-26 02:25:03 +00:00
{
2016-08-16 13:29:48 +00:00
public class TriviaGame
2016-03-26 02:25:03 +00:00
{
2016-08-16 13:29:48 +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 ;
2016-12-25 12:09:22 +00:00
public bool ShowHints { get ; } = true ;
2016-02-02 11:23:58 +00:00
private CancellationTokenSource triviaCancelSource { get ; set ; }
public TriviaQuestion CurrentQuestion { get ; private set ; }
2016-03-03 17:24:58 +00:00
public HashSet < TriviaQuestion > oldQuestions { get ; } = new HashSet < TriviaQuestion > ( ) ;
2016-02-02 11:23:58 +00:00
2016-08-16 13:29:48 +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 ; }
2016-02-04 17:24:06 +00:00
public int WinRequirement { get ; } = 10 ;
2016-02-02 11:23:58 +00:00
2016-12-25 12:09:22 +00:00
public TriviaGame ( IGuild guild , ITextChannel channel , bool showHints , int winReq )
2016-03-26 02:25:03 +00:00
{
2016-12-25 12:09:22 +00:00
this . _log = LogManager . GetCurrentClassLogger ( ) ;
this . ShowHints = showHints ;
2016-08-16 13:29:48 +00:00
this . guild = guild ;
this . channel = channel ;
2016-12-25 12:09:22 +00:00
this . WinRequirement = winReq ;
2016-02-02 11:23:58 +00:00
}
2016-12-25 12:09:22 +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-12-25 12:09:22 +00:00
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
{
2016-12-25 12:09:22 +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
2016-12-25 12:09:22 +00:00
EmbedBuilder questionEmbed ;
IUserMessage questionMessage ;
try
2016-10-12 21:41:31 +00:00
{
2016-12-25 12:09:22 +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 ) ;
2016-12-25 12:09:22 +00:00
}
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 )
2016-12-25 12:09:22 +00:00
{
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
{
2016-12-25 12:09:22 +00:00
NadekoBot . Client . MessageReceived + = PotentialGuess ;
2016-02-02 11:23:58 +00:00
2016-12-25 12:09:22 +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 ( ) )
2016-12-25 12:09:22 +00:00
. 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 )
2016-12-25 12:09:22 +00:00
{
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
2016-12-25 12:09:22 +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 )
2016-12-25 12:09:22 +00:00
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
}
}
2016-12-25 12:09:22 +00:00
public async Task EnsureStopped ( )
2016-03-26 02:25:03 +00:00
{
2016-02-23 07:33:16 +00:00
ShouldStopGame = true ;
2016-12-25 12:09:22 +00:00
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 ( )
{
2016-12-25 12:09:22 +00:00
var old = ShouldStopGame ;
2016-02-02 11:23:58 +00:00
ShouldStopGame = true ;
2016-12-25 12:09:22 +00:00
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
{
2016-12-28 05:31:39 +00:00
try
2016-03-26 02:25:03 +00:00
{
2016-12-28 06:56:44 +00:00
if ( imsg . Author . IsBot )
return ;
2017-01-01 12:26:17 +00:00
var umsg = imsg as SocketUserMessage ;
2016-12-28 06:56:44 +00:00
if ( umsg = = null )
return ;
2016-12-28 05:31:39 +00:00
var textChannel = umsg . Channel as ITextChannel ;
if ( textChannel = = null | | textChannel . Guild ! = guild )
return ;
2016-09-04 15:23:58 +00:00
2016-12-28 05:31:39 +00:00
var guildUser = ( IGuildUser ) umsg . Author ;
2016-09-04 15:23:58 +00:00
2016-12-28 05:31:39 +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
{
2016-12-28 05:31:39 +00:00
Users . AddOrUpdate ( guildUser , 1 , ( gu , old ) = > + + old ) ;
guess = true ;
2016-02-26 08:46:50 +00:00
}
2016-12-28 05:31:39 +00:00
}
finally { _guessLock . Release ( ) ; }
if ( ! guess ) return ;
triviaCancelSource . Cancel ( ) ;
2016-12-25 12:09:22 +00:00
2016-12-28 05:31:39 +00:00
if ( Users [ guildUser ] = = WinRequirement )
{
ShouldStopGame = true ;
2017-01-12 19:19:56 +00:00
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 ) ;
2016-12-28 05:31:39 +00:00
return ;
2016-02-02 11:23:58 +00:00
}
2016-12-28 05:31:39 +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 ( )
{
2016-03-02 05:00:44 +00:00
if ( Users . Count = = 0 )
2016-12-25 12:09:22 +00:00
return "No results." ;
2016-02-02 11:23:58 +00:00
2016-03-02 05:00:44 +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
{
2016-08-16 13:29:48 +00:00
sb . AppendLine ( $"**{kvp.Key.Username}** has {kvp.Value} points" . ToString ( ) . SnPl ( kvp . Value ) ) ;
2016-02-02 11:23:58 +00:00
}
2016-03-02 05:00:44 +00:00
return sb . ToString ( ) ;
2016-02-02 11:23:58 +00:00
}
}
2016-12-25 12:09:22 +00:00
}