Typereaders finished, cleanup
This commit is contained in:
		| @@ -3,26 +3,26 @@ using System.Net.Sockets; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
|  | using NadekoBot.Services; | ||||||
|  | using StackExchange.Redis; | ||||||
|  |  | ||||||
| namespace NadekoBot.Common.ShardCom | namespace NadekoBot.Common.ShardCom | ||||||
| { | { | ||||||
|     public class ShardComClient |     public class ShardComClient | ||||||
|     { |     { | ||||||
|         private int port; |         private readonly IDataCache _cache; | ||||||
|  |  | ||||||
|         public ShardComClient(int port) |         public ShardComClient(IDataCache cache) | ||||||
|         { |         { | ||||||
|             this.port = port; |             _cache = cache; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public async Task Send(ShardComMessage data) |         public async Task Send(ShardComMessage data) | ||||||
|         { |         { | ||||||
|  |             var sub = _cache.Redis.GetSubscriber(); | ||||||
|             var msg = JsonConvert.SerializeObject(data); |             var msg = JsonConvert.SerializeObject(data); | ||||||
|             using (var client = new UdpClient()) |  | ||||||
|             { |             await sub.PublishAsync("shardcoord_send", msg).ConfigureAwait(false); | ||||||
|                 var bytes = Encoding.UTF8.GetBytes(msg); |  | ||||||
|                 await client.SendAsync(bytes, bytes.Length, IPAddress.Loopback.ToString(), port).ConfigureAwait(false); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,35 +4,26 @@ using System.Net.Sockets; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
|  | using NadekoBot.Services; | ||||||
|  |  | ||||||
| namespace NadekoBot.Common.ShardCom | namespace NadekoBot.Common.ShardCom | ||||||
| { | { | ||||||
|     public class ShardComServer : IDisposable |     public class ShardComServer | ||||||
|     { |     { | ||||||
|         private readonly UdpClient _client; |         private readonly IDataCache _cache; | ||||||
|  |  | ||||||
|         public ShardComServer(int port) |         public ShardComServer(IDataCache cache) | ||||||
|         { |         { | ||||||
|             _client = new UdpClient(port); |             _cache = cache; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void Start() |         public void Start() | ||||||
|         { |         { | ||||||
|             Task.Run(async () => |             var sub = _cache.Redis.GetSubscriber(); | ||||||
|  |             sub.SubscribeAsync("shardcoord_send", (ch, data) => | ||||||
|             { |             { | ||||||
|                 var ip = new IPEndPoint(IPAddress.Any, 0); |  | ||||||
|                 while (true) |  | ||||||
|                 { |  | ||||||
|                     var recv = await _client.ReceiveAsync(); |  | ||||||
|                     var data = Encoding.UTF8.GetString(recv.Buffer); |  | ||||||
|                 var _ = OnDataReceived(JsonConvert.DeserializeObject<ShardComMessage>(data)); |                 var _ = OnDataReceived(JsonConvert.DeserializeObject<ShardComMessage>(data)); | ||||||
|                 } |             }, StackExchange.Redis.CommandFlags.FireAndForget); | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Dispose() |  | ||||||
|         { |  | ||||||
|             _client.Dispose(); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public event Func<ShardComMessage, Task> OnDataReceived = delegate { return Task.CompletedTask; }; |         public event Func<ShardComMessage, Task> OnDataReceived = delegate { return Task.CompletedTask; }; | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ using Discord.WebSocket; | |||||||
|  |  | ||||||
| namespace NadekoBot.Common.TypeReaders | namespace NadekoBot.Common.TypeReaders | ||||||
| { | { | ||||||
|     public class CommandTypeReader : NadekoTypeReader |     public class CommandTypeReader : NadekoTypeReader<CommandInfo> | ||||||
|     { |     { | ||||||
|         public CommandTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) |         public CommandTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) | ||||||
|         { |         { | ||||||
| @@ -35,10 +35,14 @@ namespace NadekoBot.Common.TypeReaders | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class CommandOrCrTypeReader : CommandTypeReader |     public class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo> | ||||||
|     { |     { | ||||||
|  |         private readonly DiscordSocketClient _client; | ||||||
|  |         private readonly CommandService _cmds; | ||||||
|         public CommandOrCrTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) |         public CommandOrCrTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) | ||||||
|         { |         { | ||||||
|  |             _client = client; | ||||||
|  |             _cmds = cmds; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) |         public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) | ||||||
| @@ -63,7 +67,7 @@ namespace NadekoBot.Common.TypeReaders | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             var cmd = await base.Read(context, input, services); |             var cmd = await new CommandTypeReader(_client, _cmds).Read(context, input, services); | ||||||
|             if (cmd.IsSuccess) |             if (cmd.IsSuccess) | ||||||
|             { |             { | ||||||
|                 return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name)); |                 return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name)); | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ using Discord.WebSocket; | |||||||
|  |  | ||||||
| namespace NadekoBot.Common.TypeReaders | namespace NadekoBot.Common.TypeReaders | ||||||
| { | { | ||||||
|     public class GuildDateTimeTypeReader : NadekoTypeReader |     public class GuildDateTimeTypeReader : NadekoTypeReader<GuildDateTime> | ||||||
|     { |     { | ||||||
|         public GuildDateTimeTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) |         public GuildDateTimeTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -4,10 +4,11 @@ using System.Threading.Tasks; | |||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||||
| using NadekoBot.Core.Common.TypeReaders; | using NadekoBot.Core.Common.TypeReaders; | ||||||
|  | using Discord; | ||||||
|  |  | ||||||
| namespace NadekoBot.Common.TypeReaders | namespace NadekoBot.Common.TypeReaders | ||||||
| { | { | ||||||
|     public class GuildTypeReader : NadekoTypeReader |     public class GuildTypeReader : NadekoTypeReader<IGuild> | ||||||
|     { |     { | ||||||
|         private readonly DiscordSocketClient _client; |         private readonly DiscordSocketClient _client; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ using Discord.WebSocket; | |||||||
|  |  | ||||||
| namespace NadekoBot.Common.TypeReaders | namespace NadekoBot.Common.TypeReaders | ||||||
| { | { | ||||||
|     public class ModuleTypeReader : NadekoTypeReader |     public class ModuleTypeReader : NadekoTypeReader<ModuleInfo> | ||||||
|     { |     { | ||||||
|         private readonly CommandService _cmds; |         private readonly CommandService _cmds; | ||||||
|  |  | ||||||
| @@ -28,7 +28,7 @@ namespace NadekoBot.Common.TypeReaders | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class ModuleOrCrTypeReader : NadekoTypeReader |     public class ModuleOrCrTypeReader : NadekoTypeReader<ModuleOrCrInfo> | ||||||
|     { |     { | ||||||
|         private readonly CommandService _cmds; |         private readonly CommandService _cmds; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,8 @@ using Discord.WebSocket; | |||||||
|  |  | ||||||
| namespace NadekoBot.Core.Common.TypeReaders | namespace NadekoBot.Core.Common.TypeReaders | ||||||
| { | { | ||||||
|     public abstract class NadekoTypeReader : TypeReader |     public abstract class NadekoTypeReader<T> : TypeReader where | ||||||
|  |         T : class | ||||||
|     { |     { | ||||||
|         private readonly DiscordSocketClient _client; |         private readonly DiscordSocketClient _client; | ||||||
|         private readonly CommandService _cmds; |         private readonly CommandService _cmds; | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ namespace NadekoBot.Common.TypeReaders | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Used instead of bool for more flexible keywords for true/false only in the permission module |     /// Used instead of bool for more flexible keywords for true/false only in the permission module | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public class PermissionActionTypeReader : NadekoTypeReader |     public class PermissionActionTypeReader : NadekoTypeReader<PermissionAction> | ||||||
|     { |     { | ||||||
|         public PermissionActionTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) |         public PermissionActionTypeReader(DiscordSocketClient client, CommandService cmds) : base(client, cmds) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Administration.Services | |||||||
|                 _rep = new ReplacementBuilder() |                 _rep = new ReplacementBuilder() | ||||||
|                     .WithClient(client) |                     .WithClient(client) | ||||||
|                     .WithStats(client) |                     .WithStats(client) | ||||||
|                     //todo type readers |                     //todo how to add music to replacement builder? | ||||||
|                     //.WithMusic(music) |                     //.WithMusic(music) | ||||||
|                     .Build(); |                     .Build(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,19 +5,20 @@ namespace NadekoBot.Services.Impl | |||||||
| { | { | ||||||
|     public class RedisCache : IDataCache |     public class RedisCache : IDataCache | ||||||
|     { |     { | ||||||
|         private ulong _botid; |  | ||||||
|  |  | ||||||
|         public ConnectionMultiplexer Redis { get; } |         public ConnectionMultiplexer Redis { get; } | ||||||
|         private readonly IDatabase _db; |         private readonly IDatabase _db; | ||||||
|  |  | ||||||
|         public RedisCache(ulong botId) |         public RedisCache() | ||||||
|         { |         { | ||||||
|             _botid = botId; |  | ||||||
|             Redis = ConnectionMultiplexer.Connect("127.0.0.1"); |             Redis = ConnectionMultiplexer.Connect("127.0.0.1"); | ||||||
|             Redis.PreserveAsyncOrder = false; |             Redis.PreserveAsyncOrder = false; | ||||||
|             _db = Redis.GetDatabase(); |             _db = Redis.GetDatabase(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // things here so far don't need the bot id | ||||||
|  |         // because it's a good thing if different bots  | ||||||
|  |         // which are hosted on the same PC | ||||||
|  |         // can re-use the same image/anime data | ||||||
|         public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key) |         public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key) | ||||||
|         { |         { | ||||||
|             byte[] x = await _db.StringGetAsync("image_" + key); |             byte[] x = await _db.StringGetAsync("image_" + key); | ||||||
|   | |||||||
| @@ -17,14 +17,11 @@ using NadekoBot.Extensions; | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using NadekoBot.Common; | using NadekoBot.Common; | ||||||
| using NadekoBot.Common.ShardCom; | using NadekoBot.Common.ShardCom; | ||||||
| using NadekoBot.Common.TypeReaders; |  | ||||||
| using NadekoBot.Common.TypeReaders.Models; |  | ||||||
| using NadekoBot.Services.Database; | using NadekoBot.Services.Database; | ||||||
| using StackExchange.Redis; | using StackExchange.Redis; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using System.Runtime.Loader; |  | ||||||
| using NadekoBot.Core.Common.TypeReaders; |  | ||||||
|  |  | ||||||
|  | //todo Finish the script which autobuilds all projects if they're changed | ||||||
| namespace NadekoBot | namespace NadekoBot | ||||||
| { | { | ||||||
|     public class NadekoBot |     public class NadekoBot | ||||||
| @@ -57,8 +54,9 @@ namespace NadekoBot | |||||||
|         private readonly ShardComClient _comClient; |         private readonly ShardComClient _comClient; | ||||||
|  |  | ||||||
|         private readonly BotConfig _botConfig; |         private readonly BotConfig _botConfig; | ||||||
|  |         public IDataCache Cache { get; private set; } | ||||||
|  |  | ||||||
|         public NadekoBot(int shardId, int parentProcessId, int? port = null) |         public NadekoBot(int shardId, int parentProcessId) | ||||||
|         { |         { | ||||||
|             if (shardId < 0) |             if (shardId < 0) | ||||||
|                 throw new ArgumentOutOfRangeException(nameof(shardId)); |                 throw new ArgumentOutOfRangeException(nameof(shardId)); | ||||||
| @@ -67,6 +65,7 @@ namespace NadekoBot | |||||||
|             _log = LogManager.GetCurrentClassLogger(); |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|             TerribleElevatedPermissionCheck(); |             TerribleElevatedPermissionCheck(); | ||||||
|  |  | ||||||
|  |             Cache = new RedisCache(); | ||||||
|             Credentials = new BotCredentials(); |             Credentials = new BotCredentials(); | ||||||
|             _db = new DbService(Credentials); |             _db = new DbService(Credentials); | ||||||
|             Client = new DiscordSocketClient(new DiscordSocketConfig |             Client = new DiscordSocketClient(new DiscordSocketConfig | ||||||
| @@ -84,8 +83,7 @@ namespace NadekoBot | |||||||
|                 DefaultRunMode = RunMode.Sync, |                 DefaultRunMode = RunMode.Sync, | ||||||
|             }); |             }); | ||||||
|              |              | ||||||
|             port = port ?? Credentials.ShardRunPort; |             _comClient = new ShardComClient(Cache); | ||||||
|             _comClient = new ShardComClient(port.Value); |  | ||||||
|  |  | ||||||
|             using (var uow = _db.UnitOfWork) |             using (var uow = _db.UnitOfWork) | ||||||
|             { |             { | ||||||
| @@ -94,7 +92,7 @@ namespace NadekoBot | |||||||
|                 ErrorColor = new Color(Convert.ToUInt32(_botConfig.ErrorColor, 16)); |                 ErrorColor = new Color(Convert.ToUInt32(_botConfig.ErrorColor, 16)); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             SetupShard(parentProcessId, port.Value); |             SetupShard(parentProcessId); | ||||||
|  |  | ||||||
| #if GLOBAL_NADEKO | #if GLOBAL_NADEKO | ||||||
|             Client.Log += Client_Log; |             Client.Log += Client_Log; | ||||||
| @@ -144,7 +142,7 @@ namespace NadekoBot | |||||||
|                     .AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this |                     .AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this | ||||||
|                     .AddManual<NadekoBot>(this) |                     .AddManual<NadekoBot>(this) | ||||||
|                     .AddManual<IUnitOfWork>(uow) |                     .AddManual<IUnitOfWork>(uow) | ||||||
|                     .AddManual<IDataCache>(new RedisCache(Client.CurrentUser.Id)); |                     .AddManual<IDataCache>(Cache); | ||||||
|  |  | ||||||
|                 Services.LoadFrom(Assembly.GetAssembly(typeof(CommandHandler))); |                 Services.LoadFrom(Assembly.GetAssembly(typeof(CommandHandler))); | ||||||
|  |  | ||||||
| @@ -156,7 +154,7 @@ namespace NadekoBot | |||||||
|             Services.Unload(typeof(IUnitOfWork)); // unload it after the startup |             Services.Unload(typeof(IUnitOfWork)); // unload it after the startup | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private IEnumerable<NadekoTypeReader> LoadTypeReaders(Assembly assembly) |         private IEnumerable<object> LoadTypeReaders(Assembly assembly) | ||||||
|         { |         { | ||||||
|             Type[] allTypes; |             Type[] allTypes; | ||||||
|             try |             try | ||||||
| @@ -166,18 +164,30 @@ namespace NadekoBot | |||||||
|             catch (ReflectionTypeLoadException ex) |             catch (ReflectionTypeLoadException ex) | ||||||
|             { |             { | ||||||
|                 Console.WriteLine(ex.LoaderExceptions[0]); |                 Console.WriteLine(ex.LoaderExceptions[0]); | ||||||
|                 return Enumerable.Empty<NadekoTypeReader>(); |                 return Enumerable.Empty<object>(); | ||||||
|             } |             } | ||||||
|             var filteredTypes = allTypes |             var filteredTypes = allTypes | ||||||
|                 .Where(x => x.IsSubclassOf(typeof(NadekoTypeReader)) |                 .Where(x => x.IsSubclassOf(typeof(TypeReader))  | ||||||
|  |                     && x.BaseType.GetGenericArguments().Length > 0 | ||||||
|                     && !x.IsAbstract); |                     && !x.IsAbstract); | ||||||
|  |  | ||||||
|             var toReturn = new List<NadekoTypeReader>(); |             var toReturn = new List<object>(); | ||||||
|             foreach (var ft in filteredTypes) |             foreach (var ft in filteredTypes) | ||||||
|             { |             { | ||||||
|                 //:yayyy: |                 //:yayyy: | ||||||
|                 var x = (NadekoTypeReader)Activator.CreateInstance(ft, Client, CommandService); |                 var x = (TypeReader)Activator.CreateInstance(ft, Client, CommandService); | ||||||
|                 CommandService.AddTypeReader(x.GetType(), x); |                 //@.@ XD XDDD | ||||||
|  |                 var baseType = ft.BaseType; | ||||||
|  |                 var typeArgs = baseType.GetGenericArguments(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     CommandService.AddTypeReader(typeArgs[0], x); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     _log.Error(ex); | ||||||
|  |                     throw; | ||||||
|  |                 } | ||||||
|                 toReturn.Add(x); |                 toReturn.Add(x); | ||||||
|                 _log.Info("Loaded {0} typereader.", x.GetType().Name); |                 _log.Info("Loaded {0} typereader.", x.GetType().Name); | ||||||
|             } |             } | ||||||
| @@ -317,11 +327,11 @@ namespace NadekoBot | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void SetupShard(int parentProcessId, int port) |         private void SetupShard(int parentProcessId) | ||||||
|         { |         { | ||||||
|             if (Client.ShardId == 0) |             if (Client.ShardId == 0) | ||||||
|             { |             { | ||||||
|                 ShardCoord = new ShardsCoordinator(port); |                 ShardCoord = new ShardsCoordinator(Cache); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             new Thread(new ThreadStart(() => |             new Thread(new ThreadStart(() => | ||||||
|   | |||||||
| @@ -19,19 +19,28 @@ namespace NadekoBot.Services | |||||||
|  |  | ||||||
|         private readonly Logger _log; |         private readonly Logger _log; | ||||||
|         private readonly ShardComServer _comServer; |         private readonly ShardComServer _comServer; | ||||||
|         private readonly int _port; |  | ||||||
|         private readonly int _curProcessId; |         private readonly int _curProcessId; | ||||||
|  |  | ||||||
|         public ShardsCoordinator(int port) |         public ShardsCoordinator(IDataCache cache) | ||||||
|         { |         { | ||||||
|             LogSetup.SetupLogger(); |             LogSetup.SetupLogger(); | ||||||
|             _creds = new BotCredentials(); |             _creds = new BotCredentials(); | ||||||
|             _shardProcesses = new Process[_creds.TotalShards]; |             _shardProcesses = new Process[_creds.TotalShards]; | ||||||
|             Statuses = new ShardComMessage[_creds.TotalShards]; |             Statuses = new ShardComMessage[_creds.TotalShards]; | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |  | ||||||
|             _port = port; |  | ||||||
|  |  | ||||||
|             _comServer = new ShardComServer(port); |             for (int i = 0; i < Statuses.Length; i++) | ||||||
|  |             { | ||||||
|  |                 Statuses[i] = new ShardComMessage(); | ||||||
|  |                 var s = Statuses[i]; | ||||||
|  |                 s.ConnectionState = Discord.ConnectionState.Disconnected; | ||||||
|  |                 s.Guilds = 0; | ||||||
|  |                 s.ShardId = i; | ||||||
|  |                 s.Time = DateTime.Now - TimeSpan.FromMinutes(1); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |             _comServer = new ShardComServer(cache); | ||||||
|             _comServer.Start(); |             _comServer.Start(); | ||||||
|  |  | ||||||
|             _comServer.OnDataReceived += _comServer_OnDataReceived; |             _comServer.OnDataReceived += _comServer_OnDataReceived; | ||||||
| @@ -54,8 +63,10 @@ namespace NadekoBot.Services | |||||||
|                 var p = Process.Start(new ProcessStartInfo() |                 var p = Process.Start(new ProcessStartInfo() | ||||||
|                 { |                 { | ||||||
|                     FileName = _creds.ShardRunCommand, |                     FileName = _creds.ShardRunCommand, | ||||||
|                     Arguments = string.Format(_creds.ShardRunArguments, i, _curProcessId, _port) |                     Arguments = string.Format(_creds.ShardRunArguments, i, _curProcessId, "") | ||||||
|                 }); |                 }); | ||||||
|  |                 // last "" in format is for backwards compatibility | ||||||
|  |                 // because current startup commands have {2} in them probably | ||||||
|                 await Task.Delay(5000); |                 await Task.Delay(5000); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -21,6 +21,21 @@ namespace NadekoBot.Extensions | |||||||
| { | { | ||||||
|     public static class Extensions |     public static class Extensions | ||||||
|     { |     { | ||||||
|  |         //so ftw | ||||||
|  |         public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) | ||||||
|  |         { | ||||||
|  |             while (toCheck != null && toCheck != typeof(object)) | ||||||
|  |             { | ||||||
|  |                 var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; | ||||||
|  |                 if (generic == cur) | ||||||
|  |                 { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 toCheck = toCheck.BaseType; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public static async Task<string> ReplaceAsync(this Regex regex, string input, Func<Match, Task<string>> replacementFn) |         public static async Task<string> ReplaceAsync(this Regex regex, string input, Func<Match, Task<string>> replacementFn) | ||||||
|         { |         { | ||||||
|             var sb = new StringBuilder(); |             var sb = new StringBuilder(); | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.Commands; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Modules.Gambling.Common | ||||||
|  | { | ||||||
|  |     public abstract class CurrencyEvent | ||||||
|  |     { | ||||||
|  |         public abstract Task Stop(); | ||||||
|  |         public abstract Task Start(IUserMessage msg, ICommandContext channel); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,144 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.Commands; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Common.Collections; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Modules.Gambling.Common | ||||||
|  | { | ||||||
|  |     public class ReactionEvent : CurrencyEvent | ||||||
|  |     { | ||||||
|  |         private readonly ConcurrentHashSet<ulong> _reactionAwardedUsers = new ConcurrentHashSet<ulong>(); | ||||||
|  |         private readonly BotConfig _bc; | ||||||
|  |         private readonly Logger _log; | ||||||
|  |         private readonly DiscordSocketClient _client; | ||||||
|  |         private readonly CurrencyService _cs; | ||||||
|  |         private readonly SocketSelfUser _botUser; | ||||||
|  |  | ||||||
|  |         private IUserMessage StartingMessage { get; set; } | ||||||
|  |  | ||||||
|  |         private CancellationTokenSource Source { get; } | ||||||
|  |         private CancellationToken CancelToken { get; } | ||||||
|  |  | ||||||
|  |         private readonly ConcurrentQueue<ulong> _toGiveTo = new ConcurrentQueue<ulong>(); | ||||||
|  |         private readonly int _amount; | ||||||
|  |  | ||||||
|  |         public ReactionEvent(BotConfig bc, DiscordSocketClient client, CurrencyService cs, int amount) | ||||||
|  |         { | ||||||
|  |             _bc = bc; | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |             _client = client; | ||||||
|  |             _cs = cs; | ||||||
|  |             _botUser = client.CurrentUser; | ||||||
|  |             _amount = amount; | ||||||
|  |             Source = new CancellationTokenSource(); | ||||||
|  |             CancelToken = Source.Token; | ||||||
|  |  | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 var users = new List<ulong>(); | ||||||
|  |                 while (!CancelToken.IsCancellationRequested) | ||||||
|  |                 { | ||||||
|  |                     await Task.Delay(1000).ConfigureAwait(false); | ||||||
|  |                     while (_toGiveTo.TryDequeue(out var usrId)) | ||||||
|  |                     { | ||||||
|  |                         users.Add(usrId); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (users.Count > 0) | ||||||
|  |                     { | ||||||
|  |                         await _cs.AddToManyAsync("Reaction Event", _amount, users.ToArray()).ConfigureAwait(false); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     users.Clear(); | ||||||
|  |                 } | ||||||
|  |             }, CancelToken); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override async Task Stop() | ||||||
|  |         { | ||||||
|  |             if (StartingMessage != null) | ||||||
|  |                 await StartingMessage.DeleteAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |             if (!Source.IsCancellationRequested) | ||||||
|  |                 Source.Cancel(); | ||||||
|  |  | ||||||
|  |             _client.MessageDeleted -= MessageDeletedEventHandler; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task MessageDeletedEventHandler(Cacheable<IMessage, ulong> msg, ISocketMessageChannel channel) | ||||||
|  |         { | ||||||
|  |             if (StartingMessage?.Id == msg.Id) | ||||||
|  |             { | ||||||
|  |                 _log.Warn("Stopping flower reaction event because message is deleted."); | ||||||
|  |                 var __ = Task.Run(Stop); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override async Task Start(IUserMessage umsg, ICommandContext context) | ||||||
|  |         { | ||||||
|  |             StartingMessage = umsg; | ||||||
|  |             _client.MessageDeleted += MessageDeletedEventHandler; | ||||||
|  |  | ||||||
|  |             IEmote iemote; | ||||||
|  |             if (Emote.TryParse(_bc.CurrencySign, out var emote)) | ||||||
|  |             { | ||||||
|  |                 iemote = emote; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 iemote = new Emoji(_bc.CurrencySign); | ||||||
|  |             try { await StartingMessage.AddReactionAsync(iemote).ConfigureAwait(false); } | ||||||
|  |             catch | ||||||
|  |             { | ||||||
|  |                 try { await StartingMessage.AddReactionAsync(iemote).ConfigureAwait(false); } | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     try { await StartingMessage.DeleteAsync().ConfigureAwait(false); } | ||||||
|  |                     catch { return; } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             using (StartingMessage.OnReaction(_client, (r) => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     if (r.UserId == _botUser.Id) | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     if (r.Emote.Name == iemote.Name && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _reactionAwardedUsers.Add(r.User.Value.Id)) | ||||||
|  |                     { | ||||||
|  |                         _toGiveTo.Enqueue(r.UserId); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     // ignored | ||||||
|  |                 } | ||||||
|  |             })) | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     await Task.Delay(TimeSpan.FromHours(24), CancelToken).ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 catch (OperationCanceledException) | ||||||
|  |                 { | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 if (CancelToken.IsCancellationRequested) | ||||||
|  |                     return; | ||||||
|  |  | ||||||
|  |                 _log.Warn("Stopping flower reaction event because it expired."); | ||||||
|  |                 await Stop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,99 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.Commands; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Common; | ||||||
|  | using NadekoBot.Common.Collections; | ||||||
|  | using NadekoBot.Services; | ||||||
|  | using System; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Modules.Gambling.Common.CurrencyEvents | ||||||
|  | { | ||||||
|  |     public class SneakyEvent : CurrencyEvent | ||||||
|  |     { | ||||||
|  |         public event Action OnEnded; | ||||||
|  |         public string Code { get; private set; } = string.Empty; | ||||||
|  |         private readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>(); | ||||||
|  |  | ||||||
|  |         private readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10) | ||||||
|  |             .Concat(Enumerable.Range(65, 26)) | ||||||
|  |             .Concat(Enumerable.Range(97, 26)) | ||||||
|  |             .Select(x => (char)x) | ||||||
|  |             .ToArray(); | ||||||
|  |  | ||||||
|  |         private readonly CurrencyService _cs; | ||||||
|  |         private readonly DiscordSocketClient _client; | ||||||
|  |         private readonly IBotConfigProvider _bc; | ||||||
|  |         private readonly int _length; | ||||||
|  |  | ||||||
|  |         public SneakyEvent(CurrencyService cs, DiscordSocketClient client, | ||||||
|  |             IBotConfigProvider bc, int len) | ||||||
|  |         { | ||||||
|  |             _cs = cs; | ||||||
|  |             _client = client; | ||||||
|  |             _bc = bc; | ||||||
|  |             _length = len; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override async Task Start(IUserMessage msg, ICommandContext channel) | ||||||
|  |         { | ||||||
|  |             GenerateCode(); | ||||||
|  |  | ||||||
|  |             //start the event | ||||||
|  |             _client.MessageReceived += SneakyGameMessageReceivedEventHandler; | ||||||
|  |             await _client.SetGameAsync($"type {Code} for " + _bc.BotConfig.CurrencyPluralName) | ||||||
|  |                 .ConfigureAwait(false); | ||||||
|  |             await Task.Delay(_length * 1000).ConfigureAwait(false); | ||||||
|  |             await Stop().ConfigureAwait(false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void GenerateCode() | ||||||
|  |         { | ||||||
|  |             var rng = new NadekoRandom(); | ||||||
|  |  | ||||||
|  |             for (var i = 0; i < 5; i++) | ||||||
|  |             { | ||||||
|  |                 Code += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override async Task Stop() | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 _client.MessageReceived -= SneakyGameMessageReceivedEventHandler; | ||||||
|  |                 Code = string.Empty; | ||||||
|  |                 _sneakyGameAwardedUsers.Clear(); | ||||||
|  |                 await _client.SetGameAsync(null).ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |             catch { } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |  | ||||||
|  |                 OnEnded?.Invoke(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task SneakyGameMessageReceivedEventHandler(SocketMessage arg) | ||||||
|  |         { | ||||||
|  |             if (arg.Content == Code && | ||||||
|  |                 _sneakyGameAwardedUsers.Add(arg.Author.Id)) | ||||||
|  |             { | ||||||
|  |                 var _ = Task.Run(async () => | ||||||
|  |                 { | ||||||
|  |                     await _cs.AddAsync(arg.Author, "Sneaky Game Event", 100, false) | ||||||
|  |                         .ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |                     try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); } | ||||||
|  |                     catch | ||||||
|  |                     { | ||||||
|  |                         // ignored | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -2,42 +2,26 @@ | |||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using System; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||||
| using System.Threading; |  | ||||||
| using NadekoBot.Common; |  | ||||||
| using NadekoBot.Common.Attributes; | using NadekoBot.Common.Attributes; | ||||||
| using NadekoBot.Common.Collections; | using NadekoBot.Modules.Gambling.Common; | ||||||
| using NLog; | using NadekoBot.Modules.Gambling.Services; | ||||||
| using System.Collections.Concurrent; | using NadekoBot.Modules.Gambling.Common.CurrencyEvents; | ||||||
| using System.Collections.Generic; |  | ||||||
| using NadekoBot.Services.Database.Models; |  | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Gambling | namespace NadekoBot.Modules.Gambling | ||||||
| { | { | ||||||
|     //todo mess, needs unload thing too - refactor |  | ||||||
|     public partial class Gambling |     public partial class Gambling | ||||||
|     { |     { | ||||||
|         [Group] |         [Group] | ||||||
|         public class CurrencyEventsCommands : NadekoSubmodule |         public class CurrencyEventsCommands : NadekoSubmodule<CurrencyEventsService> | ||||||
|         { |         { | ||||||
|             public enum CurrencyEvent |             public enum CurrencyEvent | ||||||
|             { |             { | ||||||
|                 Reaction, |                 Reaction, | ||||||
|                 SneakyGameStatus |                 SneakyGameStatus | ||||||
|             } |             } | ||||||
|             //flower reaction event |  | ||||||
|             private static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>(); |  | ||||||
|  |  | ||||||
|             private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10) |  | ||||||
|                 .Concat(Enumerable.Range(65, 26)) |  | ||||||
|                 .Concat(Enumerable.Range(97, 26)) |  | ||||||
|                 .Select(x => (char)x) |  | ||||||
|                 .ToArray(); |  | ||||||
|  |  | ||||||
|             private string _secretCode = string.Empty; |  | ||||||
|             private readonly DiscordSocketClient _client; |             private readonly DiscordSocketClient _client; | ||||||
|             private readonly IBotConfigProvider _bc; |             private readonly IBotConfigProvider _bc; | ||||||
|             private readonly CurrencyService _cs; |             private readonly CurrencyService _cs; | ||||||
| @@ -65,68 +49,27 @@ namespace NadekoBot.Modules.Gambling | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             public async Task SneakyGameStatusEvent(ICommandContext context, int? arg) |             private async Task SneakyGameStatusEvent(ICommandContext context, int num) | ||||||
|             { |             { | ||||||
|                 int num; |                 if (num < 10 || num > 600) | ||||||
|                 if (arg == null || arg < 5) |  | ||||||
|                     num = 60; |                     num = 60; | ||||||
|                 else |  | ||||||
|                     num = arg.Value; |  | ||||||
|  |  | ||||||
|                 if (_secretCode != string.Empty) |                 var ev = new SneakyEvent(_cs, _client, _bc, num); | ||||||
|  |                 if (!await _service.StartSneakyEvent(ev, context.Message, context)) | ||||||
|                     return; |                     return; | ||||||
|                 var rng = new NadekoRandom(); |  | ||||||
|  |  | ||||||
|                 for (var i = 0; i < 5; i++) |  | ||||||
|                 { |  | ||||||
|                     _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 await _client.SetGameAsync($"type {_secretCode} for " + _bc.BotConfig.CurrencyPluralName) |  | ||||||
|                     .ConfigureAwait(false); |  | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     var title = GetText("sneakygamestatus_title"); |                     var title = GetText("sneakygamestatus_title"); | ||||||
|                     var desc = GetText("sneakygamestatus_desc", Format.Bold(100.ToString()) + _bc.BotConfig.CurrencySign, Format.Bold(num.ToString())); |                     var desc = GetText("sneakygamestatus_desc",  | ||||||
|                     await context.Channel.SendConfirmAsync(title, desc).ConfigureAwait(false); |                         Format.Bold(100.ToString()) + _bc.BotConfig.CurrencySign, | ||||||
|  |                         Format.Bold(num.ToString())); | ||||||
|  |                     await context.Channel.SendConfirmAsync(title, desc) | ||||||
|  |                         .ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
|                 { |                 { | ||||||
|                     // ignored |                     // ignored | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |  | ||||||
|                 _client.MessageReceived += SneakyGameMessageReceivedEventHandler; |  | ||||||
|                 await Task.Delay(num * 1000); |  | ||||||
|                 _client.MessageReceived -= SneakyGameMessageReceivedEventHandler; |  | ||||||
|  |  | ||||||
|                 var cnt = _sneakyGameAwardedUsers.Count; |  | ||||||
|                 _sneakyGameAwardedUsers.Clear(); |  | ||||||
|                 _secretCode = string.Empty; |  | ||||||
|  |  | ||||||
|                 await _client.SetGameAsync(GetText("sneakygamestatus_end", cnt)) |  | ||||||
|                     .ConfigureAwait(false); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private Task SneakyGameMessageReceivedEventHandler(SocketMessage arg) |  | ||||||
|             { |  | ||||||
|                 if (arg.Content == _secretCode && |  | ||||||
|                     _sneakyGameAwardedUsers.Add(arg.Author.Id)) |  | ||||||
|                 { |  | ||||||
|                     var _ = Task.Run(async () => |  | ||||||
|                     { |  | ||||||
|                         await _cs.AddAsync(arg.Author, "Sneaky Game Event", 100, false) |  | ||||||
|                             .ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                         try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); } |  | ||||||
|                         catch |  | ||||||
|                         { |  | ||||||
|                             // ignored |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 return Task.CompletedTask; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             public async Task ReactionEvent(ICommandContext context, int amount) |             public async Task ReactionEvent(ICommandContext context, int amount) | ||||||
| @@ -137,144 +80,11 @@ namespace NadekoBot.Modules.Gambling | |||||||
|                 var title = GetText("reaction_title"); |                 var title = GetText("reaction_title"); | ||||||
|                 var desc = GetText("reaction_desc", _bc.BotConfig.CurrencySign, Format.Bold(amount.ToString()) + _bc.BotConfig.CurrencySign); |                 var desc = GetText("reaction_desc", _bc.BotConfig.CurrencySign, Format.Bold(amount.ToString()) + _bc.BotConfig.CurrencySign); | ||||||
|                 var footer = GetText("reaction_footer", 24); |                 var footer = GetText("reaction_footer", 24); | ||||||
|  |                 var re = new ReactionEvent(_bc.BotConfig, _client, _cs, amount); | ||||||
|                 var msg = await context.Channel.SendConfirmAsync(title, |                 var msg = await context.Channel.SendConfirmAsync(title, | ||||||
|                         desc, footer: footer) |                         desc, footer: footer) | ||||||
|                     .ConfigureAwait(false); |                     .ConfigureAwait(false); | ||||||
|  |                 await re.Start(msg, context); | ||||||
|                 await new ReactionEvent(_bc.BotConfig, _client, _cs, amount).Start(msg, context); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public abstract class CurrencyEvent |  | ||||||
|     { |  | ||||||
|         public abstract Task Start(IUserMessage msg, ICommandContext channel); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public class ReactionEvent : CurrencyEvent |  | ||||||
|     { |  | ||||||
|         private readonly ConcurrentHashSet<ulong> _reactionAwardedUsers = new ConcurrentHashSet<ulong>(); |  | ||||||
|         private readonly BotConfig _bc; |  | ||||||
|         private readonly Logger _log; |  | ||||||
|         private readonly DiscordSocketClient _client; |  | ||||||
|         private readonly CurrencyService _cs; |  | ||||||
|         private readonly SocketSelfUser _botUser; |  | ||||||
|  |  | ||||||
|         private IUserMessage StartingMessage { get; set; } |  | ||||||
|  |  | ||||||
|         private CancellationTokenSource Source { get; } |  | ||||||
|         private CancellationToken CancelToken { get; } |  | ||||||
|  |  | ||||||
|         private readonly ConcurrentQueue<ulong> _toGiveTo = new ConcurrentQueue<ulong>(); |  | ||||||
|         private readonly int _amount; |  | ||||||
|  |  | ||||||
|         public ReactionEvent(BotConfig bc, DiscordSocketClient client, CurrencyService cs, int amount) |  | ||||||
|         { |  | ||||||
|             _bc = bc; |  | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |  | ||||||
|             _client = client; |  | ||||||
|             _cs = cs; |  | ||||||
|             _botUser = client.CurrentUser; |  | ||||||
|             _amount = amount; |  | ||||||
|             Source = new CancellationTokenSource(); |  | ||||||
|             CancelToken = Source.Token; |  | ||||||
|  |  | ||||||
|             var _ = Task.Run(async () => |  | ||||||
|             { |  | ||||||
|  |  | ||||||
|                 var users = new List<ulong>(); |  | ||||||
|                 while (!CancelToken.IsCancellationRequested) |  | ||||||
|                 { |  | ||||||
|                     await Task.Delay(1000).ConfigureAwait(false); |  | ||||||
|                     while (_toGiveTo.TryDequeue(out var usrId)) |  | ||||||
|                     { |  | ||||||
|                         users.Add(usrId); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     if (users.Count > 0) |  | ||||||
|                     { |  | ||||||
|                         await _cs.AddToManyAsync("Reaction Event", _amount, users.ToArray()).ConfigureAwait(false); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     users.Clear(); |  | ||||||
|                 } |  | ||||||
|             }, CancelToken); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private async Task End() |  | ||||||
|         { |  | ||||||
|             if(StartingMessage != null) |  | ||||||
|                 await StartingMessage.DeleteAsync().ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|             if(!Source.IsCancellationRequested) |  | ||||||
|                 Source.Cancel(); |  | ||||||
|  |  | ||||||
|             _client.MessageDeleted -= MessageDeletedEventHandler; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private Task MessageDeletedEventHandler(Cacheable<IMessage, ulong> msg, ISocketMessageChannel channel) { |  | ||||||
|             if (StartingMessage?.Id == msg.Id) |  | ||||||
|             { |  | ||||||
|                 _log.Warn("Stopping flower reaction event because message is deleted."); |  | ||||||
|                 var __ = Task.Run(End); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return Task.CompletedTask; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public override async Task Start(IUserMessage umsg, ICommandContext context) |  | ||||||
|         { |  | ||||||
|             StartingMessage = umsg; |  | ||||||
|             _client.MessageDeleted += MessageDeletedEventHandler; |  | ||||||
|  |  | ||||||
|             IEmote iemote; |  | ||||||
|             if (Emote.TryParse(_bc.CurrencySign, out var emote)) |  | ||||||
|             { |  | ||||||
|                 iemote = emote; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|                 iemote = new Emoji(_bc.CurrencySign); |  | ||||||
|             try { await StartingMessage.AddReactionAsync(iemote).ConfigureAwait(false); } |  | ||||||
|             catch |  | ||||||
|             { |  | ||||||
|                 try { await StartingMessage.AddReactionAsync(iemote).ConfigureAwait(false); } |  | ||||||
|                 catch |  | ||||||
|                 { |  | ||||||
|                     try { await StartingMessage.DeleteAsync().ConfigureAwait(false); } |  | ||||||
|                     catch { return; } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             using (StartingMessage.OnReaction(_client, (r) => |  | ||||||
|             { |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     if (r.UserId == _botUser.Id) |  | ||||||
|                         return; |  | ||||||
|  |  | ||||||
|                     if (r.Emote.Name == iemote.Name && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _reactionAwardedUsers.Add(r.User.Value.Id)) |  | ||||||
|                     { |  | ||||||
|                         _toGiveTo.Enqueue(r.UserId); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 catch |  | ||||||
|                 { |  | ||||||
|                     // ignored |  | ||||||
|                 } |  | ||||||
|             })) |  | ||||||
|             { |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     await Task.Delay(TimeSpan.FromHours(24), CancelToken).ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|                 catch (OperationCanceledException) |  | ||||||
|                 { |  | ||||||
|                      |  | ||||||
|                 } |  | ||||||
|                 if (CancelToken.IsCancellationRequested) |  | ||||||
|                     return; |  | ||||||
|  |  | ||||||
|                 _log.Warn("Stopping flower reaction event because it expired."); |  | ||||||
|                 await End(); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								NadekoBot.Modules.Gambling/Services/CurrencyEventsService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								NadekoBot.Modules.Gambling/Services/CurrencyEventsService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.Commands; | ||||||
|  | using NadekoBot.Modules.Gambling.Common; | ||||||
|  | using NadekoBot.Modules.Gambling.Common.CurrencyEvents; | ||||||
|  | using NadekoBot.Services; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Modules.Gambling.Services | ||||||
|  | { | ||||||
|  |     public class CurrencyEventsService : INService, IUnloadableService | ||||||
|  |     { | ||||||
|  |         public ConcurrentDictionary<ulong, List<ReactionEvent>> ReactionEvents { get; } | ||||||
|  |  | ||||||
|  |         public SneakyEvent SneakyEvent { get; private set; } = null; | ||||||
|  |         private SemaphoreSlim _sneakyLock = new SemaphoreSlim(1, 1); | ||||||
|  |  | ||||||
|  |         public CurrencyEventsService() | ||||||
|  |         { | ||||||
|  |             ReactionEvents = new ConcurrentDictionary<ulong, List<ReactionEvent>>(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task<bool> StartSneakyEvent(SneakyEvent ev, IUserMessage msg, ICommandContext ctx) | ||||||
|  |         { | ||||||
|  |             await _sneakyLock.WaitAsync().ConfigureAwait(false); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 if (SneakyEvent != null) | ||||||
|  |                     return false; | ||||||
|  |  | ||||||
|  |                 SneakyEvent = ev; | ||||||
|  |                 ev.OnEnded += () => SneakyEvent = null; | ||||||
|  |                 var _ = SneakyEvent.Start(msg, ctx).ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 _sneakyLock.Release(); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task Unload() | ||||||
|  |         { | ||||||
|  |             foreach (var kvp in ReactionEvents) | ||||||
|  |             { | ||||||
|  |                 foreach (var ev in kvp.Value) | ||||||
|  |                 { | ||||||
|  |                     try { await ev.Stop().ConfigureAwait(false); } catch { } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             ReactionEvents.Clear(); | ||||||
|  |  | ||||||
|  |             await _sneakyLock.WaitAsync().ConfigureAwait(false); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 await SneakyEvent.Stop().ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 _sneakyLock.Release(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -6,12 +6,9 @@ namespace NadekoBot | |||||||
|     { |     { | ||||||
|         public static Task Main(string[] args) |         public static Task Main(string[] args) | ||||||
|         { |         { | ||||||
|             if (args.Length == 3 && int.TryParse(args[0], out int shardId) && int.TryParse(args[1], out int parentProcessId)) |             if (args.Length == 2 && int.TryParse(args[0], out int shardId) && int.TryParse(args[1], out int parentProcessId)) | ||||||
|             { |             { | ||||||
|                 int? port = null; |                 return new NadekoBot(shardId, parentProcessId).RunAndBlockAsync(args); | ||||||
|                 if (int.TryParse(args[2], out var outPort)) |  | ||||||
|                     port = outPort; |  | ||||||
|                 return new NadekoBot(shardId, parentProcessId, outPort).RunAndBlockAsync(args); |  | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|                 return new NadekoBot(0, 0).RunAndBlockAsync(args); |                 return new NadekoBot(0, 0).RunAndBlockAsync(args); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user