2017-10-15 07:39:46 +00:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Collections.Immutable ;
using System.IO ;
using System.Linq ;
using System.Threading ;
using System.Threading.Tasks ;
using Discord ;
using Discord.WebSocket ;
using NadekoBot.Common ;
using NadekoBot.Common.Collections ;
using NadekoBot.Extensions ;
using NadekoBot.Modules.Games.Common ;
using NadekoBot.Core.Services ;
using NadekoBot.Core.Services.Impl ;
using Newtonsoft.Json ;
using NLog ;
using NadekoBot.Modules.Games.Common.Acrophobia ;
using NadekoBot.Modules.Games.Common.Connect4 ;
using NadekoBot.Modules.Games.Common.Hangman ;
using NadekoBot.Modules.Games.Common.Trivia ;
using NadekoBot.Modules.Games.Common.Nunchi ;
namespace NadekoBot.Modules.Games.Services
{
public class GamesService : INService , IUnloadableService
{
private readonly IBotConfigProvider _bc ;
public readonly ConcurrentDictionary < ulong , GirlRating > GirlRatings = new ConcurrentDictionary < ulong , GirlRating > ( ) ;
2017-10-31 08:52:46 +00:00
2017-10-15 07:39:46 +00:00
public readonly ImmutableArray < string > EightBallResponses ;
private readonly Timer _t ;
private readonly CommandHandler _cmd ;
private readonly NadekoStrings _strings ;
private readonly IImagesService _images ;
private readonly Logger _log ;
2017-10-31 08:52:46 +00:00
private readonly NadekoRandom _rng ;
private readonly CurrencyService _cs ;
2017-10-15 07:39:46 +00:00
public readonly string TypingArticlesPath = "data/typing_articles2.json" ;
private readonly CommandHandler _cmdHandler ;
public List < TypingArticle > TypingArticles { get ; } = new List < TypingArticle > ( ) ;
//channelId, game
public ConcurrentDictionary < ulong , Acrophobia > AcrophobiaGames { get ; } = new ConcurrentDictionary < ulong , Acrophobia > ( ) ;
public ConcurrentDictionary < ulong , Connect4Game > Connect4Games { get ; } = new ConcurrentDictionary < ulong , Connect4Game > ( ) ;
public ConcurrentDictionary < ulong , Hangman > HangmanGames { get ; } = new ConcurrentDictionary < ulong , Hangman > ( ) ;
public TermPool TermPool { get ; } = new TermPool ( ) ;
public ConcurrentDictionary < ulong , TriviaGame > RunningTrivias { get ; } = new ConcurrentDictionary < ulong , TriviaGame > ( ) ;
public Dictionary < ulong , TicTacToe > TicTacToeGames { get ; } = new Dictionary < ulong , TicTacToe > ( ) ;
public ConcurrentDictionary < ulong , TypingGame > RunningContests { get ; } = new ConcurrentDictionary < ulong , TypingGame > ( ) ;
public ConcurrentDictionary < ulong , Nunchi > NunchiGames { get ; } = new ConcurrentDictionary < ulong , Common . Nunchi . Nunchi > ( ) ;
public GamesService ( CommandHandler cmd , IBotConfigProvider bc , NadekoBot bot ,
2017-10-31 08:52:46 +00:00
NadekoStrings strings , IImagesService images , CommandHandler cmdHandler ,
CurrencyService cs )
2017-10-15 07:39:46 +00:00
{
_bc = bc ;
_cmd = cmd ;
_strings = strings ;
_images = images ;
_cmdHandler = cmdHandler ;
_log = LogManager . GetCurrentClassLogger ( ) ;
2017-10-31 08:52:46 +00:00
_rng = new NadekoRandom ( ) ;
_cs = cs ;
2017-10-15 07:39:46 +00:00
//8ball
EightBallResponses = _bc . BotConfig . EightBallResponses . Select ( ebr = > ebr . Text ) . ToImmutableArray ( ) ;
//girl ratings
_t = new Timer ( ( _ ) = >
{
GirlRatings . Clear ( ) ;
} , null , TimeSpan . FromDays ( 1 ) , TimeSpan . FromDays ( 1 ) ) ;
//plantpick
_cmd . OnMessageNoTrigger + = PotentialFlowerGeneration ;
GenerationChannels = new ConcurrentHashSet < ulong > ( bot
. AllGuildConfigs
. SelectMany ( c = > c . GenerateCurrencyChannelIds . Select ( obj = > obj . ChannelId ) ) ) ;
try
{
TypingArticles = JsonConvert . DeserializeObject < List < TypingArticle > > ( File . ReadAllText ( TypingArticlesPath ) ) ;
}
catch ( Exception ex )
{
_log . Warn ( "Error while loading typing articles {0}" , ex . ToString ( ) ) ;
TypingArticles = new List < TypingArticle > ( ) ;
}
}
public async Task Unload ( )
{
_t . Change ( Timeout . Infinite , Timeout . Infinite ) ;
_cmd . OnMessageNoTrigger - = PotentialFlowerGeneration ;
AcrophobiaGames . ForEach ( x = > x . Value . Dispose ( ) ) ;
AcrophobiaGames . Clear ( ) ;
Connect4Games . ForEach ( x = > x . Value . Dispose ( ) ) ;
Connect4Games . Clear ( ) ;
HangmanGames . ForEach ( x = > x . Value . Dispose ( ) ) ;
HangmanGames . Clear ( ) ;
await Task . WhenAll ( RunningTrivias . Select ( x = > x . Value . StopGame ( ) ) ) ;
RunningTrivias . Clear ( ) ;
TicTacToeGames . Clear ( ) ;
await Task . WhenAll ( RunningContests . Select ( x = > x . Value . Stop ( ) ) )
. ConfigureAwait ( false ) ;
RunningContests . Clear ( ) ;
NunchiGames . ForEach ( x = > x . Value . Dispose ( ) ) ;
NunchiGames . Clear ( ) ;
}
private void DisposeElems ( IEnumerable < IDisposable > xs )
{
xs . ForEach ( x = > x . Dispose ( ) ) ;
}
public void AddTypingArticle ( IUser user , string text )
{
TypingArticles . Add ( new TypingArticle
{
Title = $"Text added on {DateTime.UtcNow} by {user}" ,
Text = text . SanitizeMentions ( ) ,
} ) ;
File . WriteAllText ( TypingArticlesPath , JsonConvert . SerializeObject ( TypingArticles ) ) ;
}
public ConcurrentHashSet < ulong > GenerationChannels { get ; }
//channelid/message
public ConcurrentDictionary < ulong , List < IUserMessage > > PlantedFlowers { get ; } = new ConcurrentDictionary < ulong , List < IUserMessage > > ( ) ;
//channelId/last generation
public ConcurrentDictionary < ulong , DateTime > LastGenerations { get ; } = new ConcurrentDictionary < ulong , DateTime > ( ) ;
private ConcurrentDictionary < ulong , object > _locks { get ; } = new ConcurrentDictionary < ulong , object > ( ) ;
2017-10-31 08:52:46 +00:00
public ConcurrentHashSet < ulong > HalloweenAwardedUsers { get ; } = new ConcurrentHashSet < ulong > ( ) ;
2017-10-15 07:39:46 +00:00
public ( string Name , ImmutableArray < byte > Data ) GetRandomCurrencyImage ( )
{
var rng = new NadekoRandom ( ) ;
return _images . Currency [ rng . Next ( 0 , _images . Currency . Length ) ] ;
}
private string GetText ( ITextChannel ch , string key , params object [ ] rep )
= > _strings . GetText ( key , ch . GuildId , "Games" . ToLowerInvariant ( ) , rep ) ;
private Task PotentialFlowerGeneration ( IUserMessage imsg )
{
var msg = imsg as SocketUserMessage ;
if ( msg = = null | | msg . Author . IsBot )
return Task . CompletedTask ;
var channel = imsg . Channel as ITextChannel ;
if ( channel = = null )
return Task . CompletedTask ;
if ( ! GenerationChannels . Contains ( channel . Id ) )
return Task . CompletedTask ;
var _ = Task . Run ( async ( ) = >
{
try
{
var lastGeneration = LastGenerations . GetOrAdd ( channel . Id , DateTime . MinValue ) ;
var rng = new NadekoRandom ( ) ;
if ( DateTime . UtcNow - TimeSpan . FromSeconds ( _bc . BotConfig . CurrencyGenerationCooldown ) < lastGeneration ) //recently generated in this channel, don't generate again
return ;
var num = rng . Next ( 1 , 101 ) + _bc . BotConfig . CurrencyGenerationChance * 100 ;
if ( num > 100 & & LastGenerations . TryUpdate ( channel . Id , DateTime . UtcNow , lastGeneration ) )
{
var dropAmount = _bc . BotConfig . CurrencyDropAmount ;
var dropAmountMax = _bc . BotConfig . CurrencyDropAmountMax ;
if ( dropAmountMax ! = null & & dropAmountMax > dropAmount )
dropAmount = new NadekoRandom ( ) . Next ( dropAmount , dropAmountMax . Value + 1 ) ;
if ( dropAmount > 0 )
{
var msgs = new IUserMessage [ dropAmount ] ;
var prefix = _cmdHandler . GetPrefix ( channel . Guild . Id ) ;
var toSend = dropAmount = = 1
? GetText ( channel , "curgen_sn" , _bc . BotConfig . CurrencySign )
+ " " + GetText ( channel , "pick_sn" , prefix )
: GetText ( channel , "curgen_pl" , dropAmount , _bc . BotConfig . CurrencySign )
+ " " + GetText ( channel , "pick_pl" , prefix ) ;
var file = GetRandomCurrencyImage ( ) ;
using ( var fileStream = file . Data . ToStream ( ) )
{
var sent = await channel . SendFileAsync (
fileStream ,
file . Name ,
toSend ) . ConfigureAwait ( false ) ;
msgs [ 0 ] = sent ;
}
PlantedFlowers . AddOrUpdate ( channel . Id , msgs . ToList ( ) , ( id , old ) = > { old . AddRange ( msgs ) ; return old ; } ) ;
}
}
}
catch ( Exception ex )
{
LogManager . GetCurrentClassLogger ( ) . Warn ( ex ) ;
}
} ) ;
return Task . CompletedTask ;
}
2017-10-31 08:52:46 +00:00
public async Task < bool > GetTreat ( ulong userId )
{
if ( _rng . Next ( 0 , 10 ) ! = 0 )
{
await _cs . AddAsync ( userId , "Halloween 2017 Treat" , 10 )
. ConfigureAwait ( false ) ;
return true ;
}
return false ;
}
2017-10-15 07:39:46 +00:00
}
}