Huge cleanup,
rewrite of the NadekoBot.cs, way services are loaded has changed. Updated discord.net.
This commit is contained in:
		| @@ -9,17 +9,10 @@ namespace NadekoBot.TypeReaders | ||||
| { | ||||
|     public class CommandTypeReader : TypeReader | ||||
|     { | ||||
|         private readonly CommandService _cmds; | ||||
|         private readonly CommandHandler _cmdHandler; | ||||
|  | ||||
|         public CommandTypeReader(CommandService cmds, CommandHandler cmdHandler) | ||||
|         { | ||||
|             _cmds = cmds; | ||||
|             _cmdHandler = cmdHandler; | ||||
|         } | ||||
|  | ||||
|         public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _) | ||||
|         public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) | ||||
|         { | ||||
|             var _cmds = ((INServiceProvider)services).GetService<CommandService>(); | ||||
|             var _cmdHandler = ((INServiceProvider)services).GetService<CommandHandler>(); | ||||
|             input = input.ToUpperInvariant(); | ||||
|             var prefix = _cmdHandler.GetPrefix(context.Guild); | ||||
|             if (!input.StartsWith(prefix.ToUpperInvariant())) | ||||
| @@ -38,17 +31,12 @@ namespace NadekoBot.TypeReaders | ||||
|  | ||||
|     public class CommandOrCrTypeReader : CommandTypeReader | ||||
|     { | ||||
|         private readonly CustomReactionsService _crs; | ||||
|  | ||||
|         public CommandOrCrTypeReader(CustomReactionsService crs, CommandService cmds, CommandHandler cmdHandler) : base(cmds, cmdHandler) | ||||
|         { | ||||
|             _crs = crs; | ||||
|         } | ||||
|  | ||||
|         public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _) | ||||
|         public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) | ||||
|         { | ||||
|             input = input.ToUpperInvariant(); | ||||
|  | ||||
|             var _crs = ((INServiceProvider)services).GetService<CustomReactionsService>(); | ||||
|  | ||||
|             if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input)) | ||||
|             { | ||||
|                 return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input)); | ||||
| @@ -65,7 +53,7 @@ namespace NadekoBot.TypeReaders | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var cmd = await base.Read(context, input, _); | ||||
|             var cmd = await base.Read(context, input, services); | ||||
|             if (cmd.IsSuccess) | ||||
|             { | ||||
|                 return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name)); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Services; | ||||
| using NadekoBot.Services.Administration; | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| @@ -7,15 +8,9 @@ namespace NadekoBot.TypeReaders | ||||
| { | ||||
|     public class GuildDateTimeTypeReader : TypeReader | ||||
|     { | ||||
|         private readonly GuildTimezoneService _gts; | ||||
|  | ||||
|         public GuildDateTimeTypeReader(GuildTimezoneService gts) | ||||
|         { | ||||
|             _gts = gts; | ||||
|         } | ||||
|  | ||||
|         public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _) | ||||
|         public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services) | ||||
|         { | ||||
|             var _gts = (GuildTimezoneService)services.GetService(typeof(GuildTimezoneService)); | ||||
|             if (!DateTime.TryParse(input, out var dt)) | ||||
|                 return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format.")); | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageRoles)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task SetMuteRole([Remainder] string name) | ||||
|             { | ||||
|                 name = name.Trim(); | ||||
| @@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageRoles)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public Task SetMuteRole([Remainder] IRole role) | ||||
|                 => SetMuteRole(role.Name); | ||||
|  | ||||
| @@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageRoles)] | ||||
|             [RequireUserPermission(GuildPermission.MuteMembers)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Mute(IGuildUser user) | ||||
|             { | ||||
|                 try | ||||
| @@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageRoles)] | ||||
|             [RequireUserPermission(GuildPermission.MuteMembers)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Mute(int minutes, IGuildUser user) | ||||
|             { | ||||
|                 if (minutes < 1 || minutes > 1440) | ||||
|   | ||||
| @@ -11,7 +11,7 @@ namespace NadekoBot.Modules.Administration | ||||
|         public class PrefixCommands : NadekoSubmodule | ||||
|         { | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public new async Task Prefix() | ||||
|             { | ||||
|                 await ReplyConfirmLocalized("prefix_current", Format.Code(_cmdHandler.GetPrefix(Context.Guild))).ConfigureAwait(false); | ||||
| @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.Administrator)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public new async Task Prefix([Remainder]string prefix) | ||||
|             { | ||||
|                 if (string.IsNullOrWhiteSpace(prefix)) | ||||
|   | ||||
| @@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(ChannelPermission.ManageMessages)] | ||||
|             [RequireBotPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Prune(int count) | ||||
|             { | ||||
|                 count++; | ||||
| @@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(ChannelPermission.ManageMessages)] | ||||
|             [RequireBotPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Prune(IGuildUser user, int count = 100) | ||||
|             { | ||||
|                 if (user.Id == Context.User.Id) | ||||
|   | ||||
| @@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task SlowmodeWhitelist(IGuildUser user) | ||||
|             { | ||||
|                 var siu = new SlowmodeIgnoredUser | ||||
| @@ -99,7 +99,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task SlowmodeWhitelist(IRole role) | ||||
|             { | ||||
|                 var sir = new SlowmodeIgnoredRole | ||||
|   | ||||
| @@ -289,7 +289,7 @@ namespace NadekoBot.Modules.Administration | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireUserPermission(GuildPermission.ManageNicknames)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task SetNick([Remainder] string newNick = null) | ||||
|             { | ||||
|                 if (string.IsNullOrWhiteSpace(newNick)) | ||||
| @@ -303,7 +303,7 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireBotPermission(GuildPermission.ManageNicknames)] | ||||
|             [RequireUserPermission(GuildPermission.ManageNicknames)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task SetNick(IGuildUser gu, [Remainder] string newNick = null) | ||||
|             { | ||||
|                 await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false); | ||||
|   | ||||
| @@ -132,27 +132,27 @@ namespace NadekoBot.Modules.Administration | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.BanMembers)] | ||||
|             [Priority(1)] | ||||
|             [Priority(2)] | ||||
|             public Task Warnlog(int page, IGuildUser user) | ||||
|                 => Warnlog(page, user.Id); | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(0)] | ||||
|             [Priority(3)] | ||||
|             public Task Warnlog(IGuildUser user) | ||||
|                 => Context.User.Id == user.Id || ((IGuildUser)Context.User).GuildPermissions.BanMembers ? Warnlog(user.Id) : Task.CompletedTask; | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.BanMembers)] | ||||
|             [Priority(3)] | ||||
|             [Priority(0)] | ||||
|             public Task Warnlog(int page, ulong userId) | ||||
|                 => InternalWarnlog(userId, page - 1); | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.BanMembers)] | ||||
|             [Priority(2)] | ||||
|             [Priority(1)] | ||||
|             public Task Warnlog(ulong userId) | ||||
|                 => InternalWarnlog(userId, 0); | ||||
|  | ||||
|   | ||||
| @@ -1,239 +0,0 @@ | ||||
| using Discord.Commands; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using Discord; | ||||
| using NadekoBot.Attributes; | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Services.ClashOfClans; | ||||
|  | ||||
| namespace NadekoBot.Modules.ClashOfClans | ||||
| { | ||||
|     public class ClashOfClans : NadekoTopLevelModule | ||||
|     { | ||||
|         private readonly ClashOfClansService _service; | ||||
|  | ||||
|         public ClashOfClans(ClashOfClansService service) | ||||
|         { | ||||
|             _service = service; | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|         public async Task CreateWar(int size, [Remainder] string enemyClan = null) | ||||
|         { | ||||
|             if (string.IsNullOrWhiteSpace(enemyClan)) | ||||
|                 return; | ||||
|  | ||||
|             if (size < 10 || size > 50 || size % 5 != 0) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("invalid_size").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             List<ClashWar> wars; | ||||
|             if (!_service.ClashWars.TryGetValue(Context.Guild.Id, out wars)) | ||||
|             { | ||||
|                 wars = new List<ClashWar>(); | ||||
|                 if (!_service.ClashWars.TryAdd(Context.Guild.Id, wars)) | ||||
|                     return; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             var cw = await _service.CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id); | ||||
|  | ||||
|             wars.Add(cw); | ||||
|             await ReplyErrorLocalized("war_created", _service.ShortPrint(cw)).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task StartWar([Remainder] string number = null) | ||||
|         { | ||||
|             int num = 0; | ||||
|             int.TryParse(number, out num); | ||||
|  | ||||
|             var warsInfo = _service.GetWarInfo(Context.Guild, num); | ||||
|             if (warsInfo == null) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var war = warsInfo.Item1[warsInfo.Item2]; | ||||
|             try | ||||
|             { | ||||
|                 war.Start(); | ||||
|                 await ReplyConfirmLocalized("war_started", _service.ShortPrint(war)).ConfigureAwait(false); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_already_started", _service.ShortPrint(war)).ConfigureAwait(false); | ||||
|             } | ||||
|             _service.SaveWar(war); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task ListWar([Remainder] string number = null) | ||||
|         { | ||||
|  | ||||
|             // if number is null, print all wars in a short way | ||||
|             if (string.IsNullOrWhiteSpace(number)) | ||||
|             { | ||||
|                 //check if there are any wars | ||||
|                 List<ClashWar> wars = null; | ||||
|                 _service.ClashWars.TryGetValue(Context.Guild.Id, out wars); | ||||
|                 if (wars == null || wars.Count == 0) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 var sb = new StringBuilder(); | ||||
|                 sb.AppendLine("**-------------------------**"); | ||||
|                 for (var i = 0; i < wars.Count; i++) | ||||
|                 { | ||||
|                     sb.AppendLine($"**#{i + 1}.**  `{GetText("enemy")}:` **{wars[i].EnemyClan}**"); | ||||
|                     sb.AppendLine($"\t\t`{GetText("size")}:` **{wars[i].Size} v {wars[i].Size}**"); | ||||
|                     sb.AppendLine("**-------------------------**"); | ||||
|                 } | ||||
|                 await Context.Channel.SendConfirmAsync(GetText("list_active_wars"), sb.ToString()).ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var num = 0; | ||||
|             int.TryParse(number, out num); | ||||
|             //if number is not null, print the war needed | ||||
|             var warsInfo = _service.GetWarInfo(Context.Guild, num); | ||||
|             if (warsInfo == null) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var war = warsInfo.Item1[warsInfo.Item2]; | ||||
|             await Context.Channel.SendConfirmAsync(_service.Localize(war, "info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), _service.ToPrettyString(war)).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task BaseCall(int number, int baseNumber, [Remainder] string other_name = null) | ||||
|         { | ||||
|             var warsInfo = _service.GetWarInfo(Context.Guild, number); | ||||
|             if (warsInfo == null || warsInfo.Item1.Count == 0) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var usr = | ||||
|                 string.IsNullOrWhiteSpace(other_name) ? | ||||
|                 Context.User.Username : | ||||
|                 other_name; | ||||
|             try | ||||
|             { | ||||
|                 var war = warsInfo.Item1[warsInfo.Item2]; | ||||
|                 _service.Call(war, usr, baseNumber - 1); | ||||
|                 _service.SaveWar(war); | ||||
|                 await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, _service.ShortPrint(war)).ConfigureAwait(false); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task CallFinish1(int number, int baseNumber = 0) | ||||
|         { | ||||
|             await FinishClaim(number, baseNumber - 1, 1); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task CallFinish2(int number, int baseNumber = 0) | ||||
|         { | ||||
|             await FinishClaim(number, baseNumber - 1, 2); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task CallFinish(int number, int baseNumber = 0) | ||||
|         { | ||||
|             await FinishClaim(number, baseNumber - 1); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task EndWar(int number) | ||||
|         { | ||||
|             var warsInfo = _service.GetWarInfo(Context.Guild, number); | ||||
|             if (warsInfo == null) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var war = warsInfo.Item1[warsInfo.Item2]; | ||||
|             war.End(); | ||||
|             _service.SaveWar(war); | ||||
|             await ReplyConfirmLocalized("war_ended", _service.ShortPrint(warsInfo.Item1[warsInfo.Item2])).ConfigureAwait(false); | ||||
|  | ||||
|             warsInfo.Item1.RemoveAt(warsInfo.Item2); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task Unclaim(int number, [Remainder] string otherName = null) | ||||
|         { | ||||
|             var warsInfo = _service.GetWarInfo(Context.Guild, number); | ||||
|             if (warsInfo == null || warsInfo.Item1.Count == 0) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var usr = | ||||
|                 string.IsNullOrWhiteSpace(otherName) ? | ||||
|                 Context.User.Username : | ||||
|                 otherName; | ||||
|             try | ||||
|             { | ||||
|                 var war = warsInfo.Item1[warsInfo.Item2]; | ||||
|                 var baseNumber = _service.Uncall(war, usr); | ||||
|                 _service.SaveWar(war); | ||||
|                 await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private async Task FinishClaim(int number, int baseNumber, int stars = 3) | ||||
|         { | ||||
|             var warInfo = _service.GetWarInfo(Context.Guild, number); | ||||
|             if (warInfo == null || warInfo.Item1.Count == 0) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var war = warInfo.Item1[warInfo.Item2]; | ||||
|             try | ||||
|             { | ||||
|                 if (baseNumber == -1) | ||||
|                 { | ||||
|                     baseNumber = _service.FinishClaim(war, Context.User.Username, stars); | ||||
|                     _service.SaveWar(war); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     _service.FinishClaim(war, baseNumber, stars); | ||||
|                 } | ||||
|                 await ReplyConfirmLocalized("base_destroyed", baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -84,7 +84,7 @@ namespace NadekoBot.Modules.CustomReactions | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(0)] | ||||
|         [Priority(1)] | ||||
|         public async Task ListCustReact(int page = 1) | ||||
|         { | ||||
|             if (--page < 0 || page > 999) | ||||
| @@ -130,7 +130,7 @@ namespace NadekoBot.Modules.CustomReactions | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(1)] | ||||
|         [Priority(0)] | ||||
|         public async Task ListCustReact(All x) | ||||
|         { | ||||
|             CustomReaction[] customReactions; | ||||
|   | ||||
| @@ -58,7 +58,7 @@ namespace NadekoBot.Modules.Gambling | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Roll(int num) | ||||
|             { | ||||
|                 await InternalRoll(num, true).ConfigureAwait(false); | ||||
| @@ -66,21 +66,21 @@ namespace NadekoBot.Modules.Gambling | ||||
|  | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Rolluo(int num = 1) | ||||
|             { | ||||
|                 await InternalRoll(num, false).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Roll(string arg) | ||||
|             { | ||||
|                 await InternallDndRoll(arg, true).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Rolluo(string arg) | ||||
|             { | ||||
|                 await InternallDndRoll(arg, false).ConfigureAwait(false); | ||||
|   | ||||
| @@ -197,12 +197,12 @@ namespace NadekoBot.Modules.Gambling | ||||
|             private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6); | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public Task Divorce([Remainder]IGuildUser target) => Divorce(target.Id); | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Divorce([Remainder]ulong targetId) | ||||
|             { | ||||
|                 if (targetId == Context.User.Id) | ||||
|   | ||||
| @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Gambling | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(0)] | ||||
|         [Priority(1)] | ||||
|         public async Task Cash([Remainder] IUser user = null) | ||||
|         { | ||||
|             if(user == null) | ||||
| @@ -62,7 +62,7 @@ namespace NadekoBot.Modules.Gambling | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(1)] | ||||
|         [Priority(0)] | ||||
|         public async Task Cash(ulong userId) | ||||
|         { | ||||
|             await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); | ||||
| @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Gambling | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [OwnerOnly] | ||||
|         [Priority(2)] | ||||
|         [Priority(0)] | ||||
|         public Task Award(int amount, [Remainder] IGuildUser usr) => | ||||
|             Award(amount, usr.Id); | ||||
|  | ||||
| @@ -107,7 +107,7 @@ namespace NadekoBot.Modules.Gambling | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [OwnerOnly] | ||||
|         [Priority(0)] | ||||
|         [Priority(2)] | ||||
|         public async Task Award(int amount, [Remainder] IRole role) | ||||
|         { | ||||
|             var users = (await Context.Guild.GetUsersAsync()) | ||||
|   | ||||
| @@ -82,14 +82,14 @@ namespace NadekoBot.Modules.Help | ||||
|             await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false); | ||||
|         } | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(1)] | ||||
|         [Priority(0)] | ||||
|         public async Task H([Remainder] string fail) | ||||
|         { | ||||
|             await ReplyErrorLocalized("command_not_found").ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(0)] | ||||
|         [Priority(1)] | ||||
|         public async Task H([Remainder] CommandInfo com = null) | ||||
|         { | ||||
|             var channel = Context.Channel; | ||||
|   | ||||
| @@ -377,7 +377,7 @@ namespace NadekoBot.Modules.Music | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [Priority(0)] | ||||
|         [Priority(1)] | ||||
|         public async Task SongRemove(int index) | ||||
|         { | ||||
|             if (index < 1) | ||||
| @@ -406,7 +406,7 @@ namespace NadekoBot.Modules.Music | ||||
|         public enum All { All } | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [Priority(1)] | ||||
|         [Priority(0)] | ||||
|         public async Task SongRemove(All all) | ||||
|         { | ||||
|             var mp = _music.GetPlayerOrDefault(Context.Guild.Id); | ||||
| @@ -853,41 +853,6 @@ namespace NadekoBot.Modules.Music | ||||
|             else | ||||
|                 await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false); | ||||
|         } | ||||
|         //todo readd goto | ||||
|         //[NadekoCommand, Usage, Description, Aliases] | ||||
|         //[RequireContext(ContextType.Guild)] | ||||
|         //public async Task Goto(int time) | ||||
|         //{ | ||||
|         //    MusicPlayer musicPlayer; | ||||
|         //    if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null) | ||||
|         //        return; | ||||
|         //    if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) | ||||
|         //        return; | ||||
|  | ||||
|         //    if (time < 0) | ||||
|         //        return; | ||||
|  | ||||
|         //    var currentSong = musicPlayer.CurrentSong; | ||||
|  | ||||
|         //    if (currentSong == null) | ||||
|         //        return; | ||||
|  | ||||
|         //    //currentSong.PrintStatusMessage = false; | ||||
|         //    var gotoSong = currentSong.Clone(); | ||||
|         //    gotoSong.SkipTo = time; | ||||
|         //    musicPlayer.AddSong(gotoSong, 0); | ||||
|         //    musicPlayer.Next(); | ||||
|  | ||||
|         //    var minutes = (time / 60).ToString(); | ||||
|         //    var seconds = (time % 60).ToString(); | ||||
|  | ||||
|         //    if (minutes.Length == 1) | ||||
|         //        minutes = "0" + minutes; | ||||
|         //    if (seconds.Length == 1) | ||||
|         //        seconds = "0" + seconds; | ||||
|  | ||||
|         //    await ReplyConfirmLocalized("skipped_to", minutes, seconds).ConfigureAwait(false); | ||||
|         //} | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|   | ||||
| @@ -192,10 +192,18 @@ namespace NadekoBot.Modules.NSFW | ||||
|             if (imgObj == null) | ||||
|                 await ReplyErrorLocalized("not_found").ConfigureAwait(false); | ||||
|             else | ||||
|                 await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() | ||||
|             { | ||||
|                 var embed = new EmbedBuilder().WithOkColor() | ||||
|                     .WithDescription($"{Context.User} [{tag ?? "url"}]({imgObj}) ") | ||||
|                     .WithImageUrl(imgObj.FileUrl) | ||||
|                     .WithFooter(efb => efb.WithText(type.ToString()))).ConfigureAwait(false); | ||||
|                     .WithFooter(efb => efb.WithText(type.ToString())); | ||||
|  | ||||
|                 if (Uri.IsWellFormedUriString(imgObj.FileUrl, UriKind.Absolute)) | ||||
|                     embed.WithImageUrl(imgObj.FileUrl); | ||||
|                 else | ||||
|                     _log.Error($"Image link from {type} is not a proper Url: {imgObj.FileUrl}"); | ||||
|  | ||||
|                 await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Mal([Remainder] string name) | ||||
|             { | ||||
|                 if (string.IsNullOrWhiteSpace(name)) | ||||
| @@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Searches | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public Task Mal(IGuildUser usr) => Mal(usr.Username); | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|   | ||||
| @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches | ||||
|             private const string _xkcdUrl = "https://xkcd.com"; | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Xkcd(string arg = null) | ||||
|             { | ||||
|                 if (arg?.ToLowerInvariant().Trim() == "latest") | ||||
| @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Searches | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Xkcd(int num) | ||||
|             { | ||||
|                 if (num < 1) | ||||
|   | ||||
| @@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Utility | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Remind(MeOrHere meorhere, string timeStr, [Remainder] string message) | ||||
|             { | ||||
|                 ulong target; | ||||
| @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Utility | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Remind(ITextChannel channel, string timeStr, [Remainder] string message) | ||||
|             { | ||||
|                 var perms = ((IGuildUser)Context.User).GetPermissions((ITextChannel)channel); | ||||
|   | ||||
| @@ -63,7 +63,6 @@ namespace NadekoBot.Modules.Utility | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(0)] | ||||
|             public async Task RepeatRemove(int index) | ||||
|             { | ||||
|                 if (!_service.RepeaterReady) | ||||
| @@ -103,7 +102,7 @@ namespace NadekoBot.Modules.Utility | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(1)] | ||||
|             [Priority(0)] | ||||
|             public async Task Repeat(int minutes, [Remainder] string message) | ||||
|             { | ||||
|                 if (!_service.RepeaterReady) | ||||
| @@ -152,7 +151,7 @@ namespace NadekoBot.Modules.Utility | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||
|             [Priority(0)] | ||||
|             [Priority(1)] | ||||
|             public async Task Repeat(GuildDateTime gt, [Remainder] string message) | ||||
|             { | ||||
|                 if (!_service.RepeaterReady) | ||||
|   | ||||
| @@ -14,20 +14,12 @@ using System.Collections.Immutable; | ||||
| using System.Diagnostics; | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using System.Threading; | ||||
| using NadekoBot.Services.Searches; | ||||
| using NadekoBot.Services.ClashOfClans; | ||||
| using NadekoBot.Services.Music; | ||||
| using NadekoBot.Services.CustomReactions; | ||||
| using NadekoBot.Services.Games; | ||||
| using NadekoBot.Services.Administration; | ||||
| using NadekoBot.Services.Permissions; | ||||
| using NadekoBot.Services.Utility; | ||||
| using NadekoBot.Services.Help; | ||||
| using System.IO; | ||||
| using NadekoBot.Services.Pokemon; | ||||
| using NadekoBot.DataStructures.ShardCom; | ||||
| using NadekoBot.DataStructures; | ||||
| using NadekoBot.Extensions; | ||||
| using System.Collections.Generic; | ||||
| using NadekoBot.Services.Database; | ||||
|  | ||||
| namespace NadekoBot | ||||
| { | ||||
| @@ -35,6 +27,15 @@ namespace NadekoBot | ||||
|     { | ||||
|         private Logger _log; | ||||
|  | ||||
|         public BotCredentials Credentials { get; } | ||||
|  | ||||
|         public DiscordSocketClient Client { get; } | ||||
|         public CommandService CommandService { get; } | ||||
|  | ||||
|         public DbService Db { get; } | ||||
|         public BotConfig BotConfig { get; } | ||||
|         public ImmutableArray<GuildConfig> AllGuildConfigs { get; private set; } | ||||
|  | ||||
|         /* I don't know how to make this not be static | ||||
|          * and keep the convenience of .WithOkColor | ||||
|          * and .WithErrorColor extensions methods. | ||||
| @@ -44,23 +45,9 @@ namespace NadekoBot | ||||
|         public static Color OkColor { get; private set; } | ||||
|         public static Color ErrorColor { get; private set; } | ||||
|  | ||||
|         public ImmutableArray<GuildConfig> AllGuildConfigs { get; private set; } | ||||
|         public BotConfig BotConfig { get; } | ||||
|         public DbService Db { get; } | ||||
|         public CommandService CommandService { get; } | ||||
|         public CommandHandler CommandHandler { get; private set; } | ||||
|         public Localization Localization { get; private set; } | ||||
|         public NadekoStrings Strings { get; private set; } | ||||
|         public StatsService Stats { get; private set; } | ||||
|         public ImagesService Images { get; } | ||||
|         public CurrencyService Currency { get; } | ||||
|         public GoogleApiService GoogleApi { get; } | ||||
|  | ||||
|         public DiscordSocketClient Client { get; } | ||||
|         public bool Ready { get; private set; } | ||||
|         public TaskCompletionSource<bool> Ready { get; private set; } = new TaskCompletionSource<bool>(); | ||||
|  | ||||
|         public INServiceProvider Services { get; private set; } | ||||
|         public BotCredentials Credentials { get; } | ||||
|  | ||||
|         public int ShardId { get; } | ||||
|         public ShardsCoordinator ShardCoord { get; private set; } | ||||
| @@ -72,26 +59,14 @@ namespace NadekoBot | ||||
|             if (shardId < 0) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(shardId)); | ||||
|  | ||||
|             ShardId = shardId; | ||||
|  | ||||
|             LogSetup.SetupLogger(); | ||||
|             _log = LogManager.GetCurrentClassLogger(); | ||||
|             TerribleElevatedPermissionCheck(); | ||||
|  | ||||
|             ShardId = shardId; | ||||
|  | ||||
|             Credentials = new BotCredentials(); | ||||
|  | ||||
|             port = port ?? Credentials.ShardRunPort; | ||||
|             _comClient = new ShardComClient(port.Value); | ||||
|  | ||||
|             Db = new DbService(Credentials); | ||||
|  | ||||
|             using (var uow = Db.UnitOfWork) | ||||
|             { | ||||
|                 BotConfig = uow.BotConfig.GetOrCreate(); | ||||
|                 OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); | ||||
|                 ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); | ||||
|             } | ||||
|              | ||||
|             Client = new DiscordSocketClient(new DiscordSocketConfig | ||||
|             { | ||||
|                 MessageCacheSize = 10, | ||||
| @@ -101,16 +76,21 @@ namespace NadekoBot | ||||
|                 ShardId = shardId, | ||||
|                 AlwaysDownloadUsers = false, | ||||
|             }); | ||||
|  | ||||
|             CommandService = new CommandService(new CommandServiceConfig() | ||||
|             { | ||||
|                 CaseSensitiveCommands = false, | ||||
|                 DefaultRunMode = RunMode.Sync, | ||||
|             }); | ||||
|  | ||||
|             Images = new ImagesService(); | ||||
|             Currency = new CurrencyService(BotConfig, Db); | ||||
|             GoogleApi = new GoogleApiService(Credentials); | ||||
|             port = port ?? Credentials.ShardRunPort; | ||||
|             _comClient = new ShardComClient(port.Value); | ||||
|  | ||||
|             using (var uow = Db.UnitOfWork) | ||||
|             { | ||||
|                 BotConfig = uow.BotConfig.GetOrCreate(); | ||||
|                 OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); | ||||
|                 ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); | ||||
|             } | ||||
|  | ||||
|             SetupShard(shardId, parentProcessId, port.Value); | ||||
|  | ||||
| @@ -146,140 +126,34 @@ namespace NadekoBot | ||||
|             { | ||||
|                 AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray(); | ||||
|  | ||||
|                 Localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db); | ||||
|                 Strings = new NadekoStrings(Localization); | ||||
|                 CommandHandler = new CommandHandler(Client, Db, BotConfig, AllGuildConfigs, CommandService, Credentials, this); | ||||
|                 Stats = new StatsService(Client, CommandHandler, Credentials, ShardCoord); | ||||
|  | ||||
|                 var soundcloudApiService = new SoundCloudApiService(Credentials); | ||||
|  | ||||
|                 #region help | ||||
|                 var helpService = new HelpService(BotConfig, CommandHandler, Strings); | ||||
|                 #endregion | ||||
|  | ||||
|                 //module services | ||||
|                 //todo 90 - autodiscover, DI, and add instead of manual like this | ||||
|                 #region utility | ||||
|                 var remindService = new RemindService(Client, BotConfig, Db, startingGuildIdList, uow); | ||||
|                 var repeaterService = new MessageRepeaterService(this, Client, AllGuildConfigs); | ||||
|                 var converterService = new ConverterService(Client, Db); | ||||
|                 var commandMapService = new CommandMapService(AllGuildConfigs); | ||||
|                 var patreonRewardsService = new PatreonRewardsService(Credentials, Db, Currency, Client); | ||||
|                 var verboseErrorsService = new VerboseErrorsService(AllGuildConfigs, Db, CommandHandler, helpService); | ||||
|                 var pruneService = new PruneService(); | ||||
|                 var streamRoleService = new StreamRoleService(Client, Db, AllGuildConfigs); | ||||
|                 #endregion | ||||
|  | ||||
|                 #region permissions | ||||
|                 var permissionsService = new PermissionService(Client, Db, BotConfig, CommandHandler, Strings); | ||||
|                 var blacklistService = new BlacklistService(BotConfig); | ||||
|                 var cmdcdsService = new CmdCdService(AllGuildConfigs); | ||||
|                 var filterService = new FilterService(Client, AllGuildConfigs); | ||||
|                 var globalPermsService = new GlobalPermissionService(BotConfig); | ||||
|                 #endregion | ||||
|  | ||||
|                 #region Searches | ||||
|                 var searchesService = new SearchesService(Client, GoogleApi, Db); | ||||
|                 var streamNotificationService = new StreamNotificationService(Db, Client, Strings); | ||||
|                 var animeSearchService = new AnimeSearchService(); | ||||
|                 #endregion | ||||
|  | ||||
|                 var clashService = new ClashOfClansService(Client, Db, Localization, Strings, uow, startingGuildIdList); | ||||
|                 var musicService = new MusicService(Client, GoogleApi, Strings, Localization, Db, soundcloudApiService, Credentials, AllGuildConfigs); | ||||
|                 var crService = new CustomReactionsService(permissionsService, Db, Strings, Client, CommandHandler, BotConfig, uow); | ||||
|  | ||||
|                 #region Games | ||||
|                 var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, Strings, Images, CommandHandler); | ||||
|                 var chatterBotService = new ChatterBotService(Client, permissionsService, AllGuildConfigs, CommandHandler, Strings); | ||||
|                 var pollService = new PollService(Client, Strings); | ||||
|                 #endregion | ||||
|  | ||||
|                 #region administration | ||||
|                 var administrationService = new AdministrationService(AllGuildConfigs, CommandHandler); | ||||
|                 var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, Db); | ||||
|                 var selfService = new SelfService(Client, this, CommandHandler, Db, BotConfig, Localization, Strings, Credentials); | ||||
|                 var vcRoleService = new VcRoleService(Client, AllGuildConfigs, Db); | ||||
|                 var vPlusTService = new VplusTService(Client, AllGuildConfigs, Strings, Db); | ||||
|                 var muteService = new MuteService(Client, AllGuildConfigs, Db); | ||||
|                 var ratelimitService = new SlowmodeService(Client, AllGuildConfigs); | ||||
|                 var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService); | ||||
|                 var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService, Db); | ||||
|                 var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs); | ||||
|                 var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs); | ||||
|                 var guildTimezoneService = new GuildTimezoneService(Client, AllGuildConfigs, Db); | ||||
|                 var logCommandService = new LogCommandService(Client, Strings, AllGuildConfigs, Db, muteService, protectionService, guildTimezoneService); | ||||
|                 #endregion | ||||
|  | ||||
|                 #region pokemon  | ||||
|                 var pokemonService = new PokemonService(); | ||||
|                 #endregion | ||||
|                 var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db); | ||||
|  | ||||
|                 //initialize Services | ||||
|                 Services = new NServiceProvider.ServiceProviderBuilder() | ||||
|                     .Add<ILocalization>(Localization) | ||||
|                     .Add<IStatsService>(Stats) | ||||
|                     .Add<IImagesService>(Images) | ||||
|                     .Add<IGoogleApiService>(GoogleApi) | ||||
|                     .Add<IStatsService>(Stats) | ||||
|                     .Add<IBotCredentials>(Credentials) | ||||
|                     .Add<CommandService>(CommandService) | ||||
|                     .Add<NadekoStrings>(Strings) | ||||
|                     .Add<DiscordSocketClient>(Client) | ||||
|                     .Add<BotConfig>(BotConfig) | ||||
|                     .Add<CurrencyService>(Currency) | ||||
|                     .Add<CommandHandler>(CommandHandler) | ||||
|                     .Add<DbService>(Db) | ||||
|                         //modules | ||||
|                         .Add(commandMapService) | ||||
|                         .Add(remindService) | ||||
|                         .Add(repeaterService) | ||||
|                         .Add(converterService) | ||||
|                         .Add(verboseErrorsService) | ||||
|                         .Add(patreonRewardsService) | ||||
|                         .Add(pruneService) | ||||
|                         .Add(streamRoleService) | ||||
|                     .Add<SearchesService>(searchesService) | ||||
|                         .Add(streamNotificationService) | ||||
|                         .Add(animeSearchService) | ||||
|                     .Add<ClashOfClansService>(clashService) | ||||
|                     .Add<MusicService>(musicService) | ||||
|                     .Add<GreetSettingsService>(greetSettingsService) | ||||
|                     .Add<CustomReactionsService>(crService) | ||||
|                     .Add<HelpService>(helpService) | ||||
|                     .Add<GamesService>(gamesService) | ||||
|                         .Add(chatterBotService) | ||||
|                         .Add(pollService) | ||||
|                     .Add<AdministrationService>(administrationService) | ||||
|                         .Add(selfService) | ||||
|                         .Add(vcRoleService) | ||||
|                         .Add(vPlusTService) | ||||
|                         .Add(muteService) | ||||
|                         .Add(ratelimitService) | ||||
|                         .Add(playingRotateService) | ||||
|                         .Add(gameVcService) | ||||
|                         .Add(autoAssignRoleService) | ||||
|                         .Add(protectionService) | ||||
|                         .Add(logCommandService) | ||||
|                         .Add(guildTimezoneService) | ||||
|                     .Add<PermissionService>(permissionsService) | ||||
|                         .Add(blacklistService) | ||||
|                         .Add(cmdcdsService) | ||||
|                         .Add(filterService) | ||||
|                         .Add(globalPermsService) | ||||
|                     .Add<PokemonService>(pokemonService) | ||||
|                     .Add<NadekoBot>(this) | ||||
|                     .AddManual<IBotCredentials>(Credentials) | ||||
|                     .AddManual(Db) | ||||
|                     .AddManual(BotConfig) | ||||
|                     .AddManual(Client) | ||||
|                     .AddManual(CommandService) | ||||
|                     .AddManual<ILocalization>(localization) | ||||
|                     .AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this | ||||
|                     .AddManual<NadekoBot>(this) | ||||
|                     .AddManual<IUnitOfWork>(uow) | ||||
|                     .AddManual(ShardCoord) | ||||
|                     .LoadFrom(Assembly.GetEntryAssembly()) | ||||
|                     .Build(); | ||||
|  | ||||
|                 CommandHandler.AddServices(Services); | ||||
|                 var commandHandler = Services.GetService<CommandHandler>(); | ||||
|                 commandHandler.AddServices(Services); | ||||
|  | ||||
|                 //setup typereaders | ||||
|                 CommandService.AddTypeReader<PermissionAction>(new PermissionActionTypeReader()); | ||||
|                 CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader(CommandService, CommandHandler)); | ||||
|                 CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader(crService, CommandService, CommandHandler)); | ||||
|                 CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader()); | ||||
|                 CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader()); | ||||
|                 CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader(CommandService)); | ||||
|                 CommandService.AddTypeReader<ModuleOrCrInfo>(new ModuleOrCrTypeReader(CommandService)); | ||||
|                 CommandService.AddTypeReader<IGuild>(new GuildTypeReader(Client)); | ||||
|                 CommandService.AddTypeReader<GuildDateTime>(new GuildDateTimeTypeReader(guildTimezoneService)); | ||||
|                 CommandService.AddTypeReader<GuildDateTime>(new GuildDateTimeTypeReader()); | ||||
|  | ||||
|             } | ||||
|         } | ||||
| @@ -382,7 +256,7 @@ namespace NadekoBot | ||||
|                     .Where(x => x.Preconditions.Any(y => y.GetType() == typeof(NoPublicBot))) | ||||
|                     .ForEach(x => CommandService.RemoveModuleAsync(x)); | ||||
|  | ||||
|             Ready = true; | ||||
|             Ready.TrySetResult(true); | ||||
|             _log.Info($"Shard {ShardId} ready."); | ||||
|             //_log.Info(await stats.Print().ConfigureAwait(false)); | ||||
|         } | ||||
|   | ||||
| @@ -55,7 +55,7 @@ | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="AngleSharp" Version="0.9.9" /> | ||||
|     <PackageReference Include="Discord.Net" Version="1.0.1-build-00785" /> | ||||
|     <PackageReference Include="Discord.Net" Version="1.0.2-build-00797" /> | ||||
|     <PackageReference Include="libvideo" Version="1.0.1" /> | ||||
|     <PackageReference Include="CoreCLR-NCalc" Version="2.1.2" /> | ||||
|     <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" /> | ||||
|   | ||||
| @@ -11,7 +11,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class AdministrationService | ||||
|     public class AdministrationService : INService | ||||
|     { | ||||
|         public readonly ConcurrentHashSet<ulong> DeleteMessagesOnCommand; | ||||
|         private readonly Logger _log; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class AutoAssignRoleService | ||||
|     public class AutoAssignRoleService : INService | ||||
|     { | ||||
|         private readonly Logger _log; | ||||
|         private readonly DiscordSocketClient _client; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class GameVoiceChannelService | ||||
|     public class GameVoiceChannelService : INService | ||||
|     { | ||||
|         public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>(); | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ using Discord.WebSocket; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class GuildTimezoneService | ||||
|     public class GuildTimezoneService : INService | ||||
|     { | ||||
|         //hack >.> | ||||
|         public static ConcurrentDictionary<ulong, GuildTimezoneService> AllServices { get; } = new ConcurrentDictionary<ulong, GuildTimezoneService>(); | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class LogCommandService | ||||
|     public class LogCommandService : INService | ||||
|     { | ||||
|  | ||||
|         private readonly DiscordSocketClient _client; | ||||
|   | ||||
| @@ -20,7 +20,7 @@ namespace NadekoBot.Services.Administration | ||||
|         All | ||||
|     } | ||||
|  | ||||
|     public class MuteService | ||||
|     public class MuteService : INService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } | ||||
|         public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ using System.Threading; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class PlayingRotateService | ||||
|     public class PlayingRotateService : INService | ||||
|     { | ||||
|         private readonly Timer _t; | ||||
|         private readonly DiscordSocketClient _client; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class ProtectionService | ||||
|     public class ProtectionService : INService | ||||
|     { | ||||
|         public readonly ConcurrentDictionary<ulong, AntiRaidStats> AntiRaidGuilds = | ||||
|                 new ConcurrentDictionary<ulong, AntiRaidStats>(); | ||||
|   | ||||
| @@ -9,7 +9,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class PruneService | ||||
|     public class PruneService : INService | ||||
|     { | ||||
|         //channelids where prunes are currently occuring | ||||
|         private ConcurrentHashSet<ulong> _pruningGuilds = new ConcurrentHashSet<ulong>(); | ||||
|   | ||||
| @@ -12,7 +12,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class SlowmodeService : IEarlyBlocker | ||||
|     public class SlowmodeService : IEarlyBlocker, INService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>(); | ||||
|         public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(); | ||||
|   | ||||
| @@ -12,7 +12,7 @@ using System.Collections.Generic; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class SelfService : ILateExecutor | ||||
|     public class SelfService : ILateExecutor, INService | ||||
|     { | ||||
|         public volatile bool ForwardDMs; | ||||
|         public volatile bool ForwardDMsToAllOwners; | ||||
| @@ -44,8 +44,7 @@ namespace NadekoBot.Services.Administration | ||||
|  | ||||
|             var _ = Task.Run(async () => | ||||
|             { | ||||
|                 while (!bot.Ready) | ||||
|                     await Task.Delay(1000); | ||||
|                 await bot.Ready.Task.ConfigureAwait(false); | ||||
|  | ||||
|                 foreach (var cmd in bc.StartupCommands) | ||||
|                 { | ||||
| @@ -56,8 +55,7 @@ namespace NadekoBot.Services.Administration | ||||
|  | ||||
|             var ___ = Task.Run(async () => | ||||
|             { | ||||
|                 while (!bot.Ready) | ||||
|                     await Task.Delay(1000); | ||||
|                 await bot.Ready.Task.ConfigureAwait(false); | ||||
|  | ||||
|                 await Task.Delay(5000); | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class VcRoleService | ||||
|     public class VcRoleService : INService | ||||
|     { | ||||
|         private readonly Logger _log; | ||||
|         private readonly DbService _db; | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Administration | ||||
| { | ||||
|     public class VplusTService | ||||
|     public class VplusTService : INService | ||||
|     { | ||||
|         private readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); | ||||
|  | ||||
|   | ||||
| @@ -1,262 +0,0 @@ | ||||
| using Discord; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Services.Database; | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.ClashOfClans | ||||
| { | ||||
|     // todo 99 rewrite, just made this compile, it's a complete mess. A lot of the things here should actually be in the actual module.  | ||||
|     // service should just handle the state, module should print out what happened, so anything that has to do with strings | ||||
|     // shouldn't be here | ||||
|     public class ClashOfClansService | ||||
|     { | ||||
|         private readonly DiscordSocketClient _client; | ||||
|         private readonly DbService _db; | ||||
|         private readonly ILocalization _localization; | ||||
|         private readonly NadekoStrings _strings; | ||||
|         private readonly Timer checkWarTimer; | ||||
|  | ||||
|         public ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } | ||||
|  | ||||
|         public ClashOfClansService(DiscordSocketClient client, DbService db,  | ||||
|             ILocalization localization, NadekoStrings strings, IUnitOfWork uow,  | ||||
|             List<long> guilds) | ||||
|         { | ||||
|             _client = client; | ||||
|             _db = db; | ||||
|             _localization = localization; | ||||
|             _strings = strings; | ||||
|  | ||||
|             ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>( | ||||
|                 uow.ClashOfClans | ||||
|                     .GetAllWars(guilds) | ||||
|                     .Select(cw => | ||||
|                     { | ||||
|                         cw.Channel = _client.GetGuild(cw.GuildId)? | ||||
|                                                         .GetTextChannel(cw.ChannelId); | ||||
|                         return cw; | ||||
|                     }) | ||||
|                     .Where(cw => cw.Channel != null) | ||||
|                     .GroupBy(cw => cw.GuildId) | ||||
|                     .ToDictionary(g => g.Key, g => g.ToList())); | ||||
|  | ||||
|             checkWarTimer = new Timer(async _ => | ||||
|             { | ||||
|                 foreach (var kvp in ClashWars) | ||||
|                 { | ||||
|                     foreach (var war in kvp.Value) | ||||
|                     { | ||||
|                         try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { } | ||||
|                     } | ||||
|                 } | ||||
|             }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); | ||||
|         } | ||||
|  | ||||
|         private async Task CheckWar(TimeSpan callExpire, ClashWar war) | ||||
|         { | ||||
|             var Bases = war.Bases; | ||||
|             for (var i = 0; i < Bases.Count; i++) | ||||
|             { | ||||
|                 var callUser = Bases[i].CallUser; | ||||
|                 if (callUser == null) continue; | ||||
|                 if ((!Bases[i].BaseDestroyed) && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire) | ||||
|                 { | ||||
|                     if (Bases[i].Stars != 3) | ||||
|                         Bases[i].BaseDestroyed = true; | ||||
|                     else | ||||
|                         Bases[i] = null; | ||||
|                     try | ||||
|                     { | ||||
|                         SaveWar(war); | ||||
|                         await war.Channel.SendErrorAsync(_strings.GetText("claim_expired", | ||||
|                                     _localization.GetCultureInfo(war.Channel.GuildId), | ||||
|                                     typeof(ClashOfClansService).Name.ToLowerInvariant(), | ||||
|                                     Format.Bold(Bases[i].CallUser), | ||||
|                                     ShortPrint(war))); | ||||
|                     } | ||||
|                     catch { } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public Tuple<List<ClashWar>, int> GetWarInfo(IGuild guild, int num) | ||||
|         { | ||||
|             List<ClashWar> wars = null; | ||||
|             ClashWars.TryGetValue(guild.Id, out wars); | ||||
|             if (wars == null || wars.Count == 0) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             // get the number of the war | ||||
|             else if (num < 1 || num > wars.Count) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             num -= 1; | ||||
|             //get the actual war | ||||
|             return new Tuple<List<ClashWar>, int>(wars, num); | ||||
|         } | ||||
|  | ||||
|         public async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId) | ||||
|         { | ||||
|             var channel = _client.GetGuild(serverId)?.GetTextChannel(channelId); | ||||
|             using (var uow = _db.UnitOfWork) | ||||
|             { | ||||
|                 var cw = new ClashWar | ||||
|                 { | ||||
|                     EnemyClan = enemyClan, | ||||
|                     Size = size, | ||||
|                     Bases = new List<ClashCaller>(size), | ||||
|                     GuildId = serverId, | ||||
|                     ChannelId = channelId, | ||||
|                     Channel = channel, | ||||
|                 }; | ||||
|                 cw.Bases.Capacity = size; | ||||
|                 for (int i = 0; i < size; i++) | ||||
|                 { | ||||
|                     cw.Bases.Add(new ClashCaller() | ||||
|                     { | ||||
|                         CallUser = null, | ||||
|                         SequenceNumber = i, | ||||
|                     }); | ||||
|                 } | ||||
|                 uow.ClashOfClans.Add(cw); | ||||
|                 await uow.CompleteAsync(); | ||||
|                 return cw; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void SaveWar(ClashWar cw) | ||||
|         { | ||||
|             if (cw.WarState == StateOfWar.Ended) | ||||
|             { | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     uow.ClashOfClans.Remove(cw); | ||||
|                     uow.CompleteAsync(); | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             using (var uow = _db.UnitOfWork) | ||||
|             { | ||||
|                 uow.ClashOfClans.Update(cw); | ||||
|                 uow.CompleteAsync(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Call(ClashWar cw, string u, int baseNumber) | ||||
|         { | ||||
|             if (baseNumber < 0 || baseNumber >= cw.Bases.Count) | ||||
|                 throw new ArgumentException(Localize(cw, "invalid_base_number")); | ||||
|             if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3) | ||||
|                 throw new ArgumentException(Localize(cw, "base_already_claimed")); | ||||
|             for (var i = 0; i < cw.Bases.Count; i++) | ||||
|             { | ||||
|                 if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u) | ||||
|                     throw new ArgumentException(Localize(cw, "claimed_other", u, i + 1)); | ||||
|             } | ||||
|  | ||||
|             var cc = cw.Bases[baseNumber]; | ||||
|             cc.CallUser = u.Trim(); | ||||
|             cc.TimeAdded = DateTime.UtcNow; | ||||
|             cc.BaseDestroyed = false; | ||||
|         } | ||||
|  | ||||
|         public int FinishClaim(ClashWar cw, string user, int stars = 3) | ||||
|         { | ||||
|             user = user.Trim(); | ||||
|             for (var i = 0; i < cw.Bases.Count; i++) | ||||
|             { | ||||
|                 if (cw.Bases[i]?.BaseDestroyed != false || cw.Bases[i]?.CallUser != user) continue; | ||||
|                 cw.Bases[i].BaseDestroyed = true; | ||||
|                 cw.Bases[i].Stars = stars; | ||||
|                 return i; | ||||
|             } | ||||
|             throw new InvalidOperationException(Localize(cw, "not_partic_or_destroyed", user)); | ||||
|         } | ||||
|  | ||||
|         public void FinishClaim(ClashWar cw, int index, int stars = 3) | ||||
|         { | ||||
|             if (index < 0 || index > cw.Bases.Count) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(index)); | ||||
|             var toFinish = cw.Bases[index]; | ||||
|             if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(Localize(cw, "base_already_destroyed")); | ||||
|             if (toFinish.CallUser == null) throw new InvalidOperationException(Localize(cw, "base_already_unclaimed")); | ||||
|             toFinish.BaseDestroyed = true; | ||||
|             toFinish.Stars = stars; | ||||
|         } | ||||
|  | ||||
|         public int Uncall(ClashWar cw, string user) | ||||
|         { | ||||
|             user = user.Trim(); | ||||
|             for (var i = 0; i < cw.Bases.Count; i++) | ||||
|             { | ||||
|                 if (cw.Bases[i]?.CallUser != user) continue; | ||||
|                 cw.Bases[i].CallUser = null; | ||||
|                 return i; | ||||
|             } | ||||
|             throw new InvalidOperationException(Localize(cw, "not_partic")); | ||||
|         } | ||||
|  | ||||
|         public string ShortPrint(ClashWar cw) => | ||||
|             $"`{cw.EnemyClan}` ({cw.Size} v {cw.Size})"; | ||||
|  | ||||
|         public string ToPrettyString(ClashWar cw) | ||||
|         { | ||||
|             var sb = new StringBuilder(); | ||||
|  | ||||
|             if (cw.WarState == StateOfWar.Created) | ||||
|                 sb.AppendLine("`not started`"); | ||||
|             var twoHours = new TimeSpan(2, 0, 0); | ||||
|             for (var i = 0; i < cw.Bases.Count; i++) | ||||
|             { | ||||
|                 if (cw.Bases[i].CallUser == null) | ||||
|                 { | ||||
|                     sb.AppendLine($"`{i + 1}.` ❌*{Localize(cw, "not_claimed")}*"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (cw.Bases[i].BaseDestroyed) | ||||
|                     { | ||||
|                         sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {new string('⭐', cw.Bases[i].Stars)}"); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours; | ||||
|                         if (cw.Bases[i].Stars == 3) | ||||
|                         { | ||||
|                             sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left {new string('⭐', cw.Bases[i].Stars)} {string.Concat(Enumerable.Repeat("🔸", 3 - cw.Bases[i].Stars))}"); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             return sb.ToString(); | ||||
|         } | ||||
|  | ||||
|         public string Localize(ClashWar cw, string key, params object[] replacements) | ||||
|         { | ||||
|             return string.Format(Localize(cw, key), replacements); | ||||
|         } | ||||
|  | ||||
|         public string Localize(ClashWar cw, string key) | ||||
|         { | ||||
|             return _strings.GetText(key, | ||||
|                 _localization.GetCultureInfo(cw.Channel?.GuildId), | ||||
|                 "ClashOfClans".ToLowerInvariant()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using System; | ||||
| using System.Linq; | ||||
|  | ||||
| namespace NadekoBot.Services.ClashOfClans | ||||
| { | ||||
|     public static class Extensions | ||||
|     { | ||||
|         public static void ResetTime(this ClashCaller c) | ||||
|         { | ||||
|             c.TimeAdded = DateTime.UtcNow; | ||||
|         } | ||||
|  | ||||
|         public static void Destroy(this ClashCaller c) | ||||
|         { | ||||
|             c.BaseDestroyed = true; | ||||
|         } | ||||
|  | ||||
|         public static void End(this ClashWar cw) | ||||
|         { | ||||
|             //Ended = true; | ||||
|             cw.WarState = StateOfWar.Ended; | ||||
|         } | ||||
|  | ||||
|         public static void Start(this ClashWar cw) | ||||
|         { | ||||
|             if (cw.WarState == StateOfWar.Started) | ||||
|                 throw new InvalidOperationException("war_already_started"); | ||||
|             //if (Started) | ||||
|             //    throw new InvalidOperationException(); | ||||
|             //Started = true; | ||||
|             cw.WarState = StateOfWar.Started; | ||||
|             cw.StartedAt = DateTime.UtcNow; | ||||
|             foreach (var b in cw.Bases.Where(b => b.CallUser != null)) | ||||
|             { | ||||
|                 b.ResetTime(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -24,7 +24,8 @@ namespace NadekoBot.Services | ||||
|  | ||||
|         public int GetHashCode(IGuildUser obj) => obj.Id.GetHashCode(); | ||||
|     } | ||||
|     public class CommandHandler | ||||
|  | ||||
|     public class CommandHandler : INService | ||||
|     { | ||||
|         public const int GlobalCommandsCooldown = 750; | ||||
|  | ||||
| @@ -189,7 +190,7 @@ namespace NadekoBot.Services | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (msg.Author.IsBot || !_bot.Ready) //no bots, wait until bot connected and initialized | ||||
|                 if (msg.Author.IsBot || !_bot.Ready.Task.IsCompleted) //no bots, wait until bot connected and initialized | ||||
|                     return; | ||||
|  | ||||
|                 if (!(msg is SocketUserMessage usrMsg)) | ||||
| @@ -296,52 +297,93 @@ namespace NadekoBot.Services | ||||
|             => ExecuteCommand(context, input.Substring(argPos), serviceProvider, multiMatchHandling); | ||||
|  | ||||
|  | ||||
|         public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand(CommandContext context, string input, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | ||||
|         public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand(CommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | ||||
|         { | ||||
|             var searchResult = _commandService.Search(context, input); | ||||
|             if (!searchResult.IsSuccess) | ||||
|                 return (false, null, null); | ||||
|  | ||||
|             var commands = searchResult.Commands; | ||||
|             for (int i = commands.Count - 1; i >= 0; i--) | ||||
|             var preconditionResults = new Dictionary<CommandMatch, PreconditionResult>(); | ||||
|  | ||||
|             foreach (var match in commands) | ||||
|             { | ||||
|                 var preconditionResult = await commands[i].CheckPreconditionsAsync(context, serviceProvider).ConfigureAwait(false); | ||||
|                 if (!preconditionResult.IsSuccess) | ||||
|                 { | ||||
|                     return (false, preconditionResult.ErrorReason, commands[i].Command); | ||||
|                 preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, services).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|                 var parseResult = await commands[i].ParseAsync(context, searchResult, preconditionResult).ConfigureAwait(false); | ||||
|                 if (!parseResult.IsSuccess) | ||||
|             var successfulPreconditions = preconditionResults | ||||
|                 .Where(x => x.Value.IsSuccess) | ||||
|                 .ToArray(); | ||||
|  | ||||
|             if (successfulPreconditions.Length == 0) | ||||
|             { | ||||
|                 //All preconditions failed, return the one from the highest priority command | ||||
|                 var bestCandidate = preconditionResults | ||||
|                     .OrderByDescending(x => x.Key.Command.Priority) | ||||
|                     .FirstOrDefault(x => !x.Value.IsSuccess); | ||||
|                 return (false, bestCandidate.Value.ErrorReason, commands[0].Command); | ||||
|             } | ||||
|  | ||||
|             var parseResultsDict = new Dictionary<CommandMatch, ParseResult>(); | ||||
|             foreach (var pair in successfulPreconditions) | ||||
|             { | ||||
|                 var parseResult = await pair.Key.ParseAsync(context, searchResult, pair.Value, services).ConfigureAwait(false); | ||||
|  | ||||
|                 if (parseResult.Error == CommandError.MultipleMatches) | ||||
|                 { | ||||
|                         TypeReaderValue[] argList, paramList; | ||||
|                     IReadOnlyList<TypeReaderValue> argList, paramList; | ||||
|                     switch (multiMatchHandling) | ||||
|                     { | ||||
|                         case MultiMatchHandling.Best: | ||||
|                                 argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToArray(); | ||||
|                                 paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToArray(); | ||||
|                             argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray(); | ||||
|                             paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray(); | ||||
|                             parseResult = ParseResult.FromSuccess(argList, paramList); | ||||
|                             break; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                     if (!parseResult.IsSuccess) | ||||
|                     { | ||||
|                         if (commands.Count == 1) | ||||
|                             return (false, parseResult.ErrorReason, commands[i].Command); | ||||
|                         else | ||||
|                             continue; | ||||
|                 parseResultsDict[pair.Key] = parseResult; | ||||
|             } | ||||
|             // Calculates the 'score' of a command given a parse result | ||||
|             float CalculateScore(CommandMatch match, ParseResult parseResult) | ||||
|             { | ||||
|                 float argValuesScore = 0, paramValuesScore = 0; | ||||
|  | ||||
|                 if (match.Command.Parameters.Count > 0) | ||||
|                 { | ||||
|                     var argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | ||||
|                     var paramValuesSum = parseResult.ParamValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | ||||
|  | ||||
|                     argValuesScore = argValuesSum / match.Command.Parameters.Count; | ||||
|                     paramValuesScore = paramValuesSum / match.Command.Parameters.Count; | ||||
|                 } | ||||
|  | ||||
|                 var cmd = commands[i].Command; | ||||
|                 var totalArgsScore = (argValuesScore + paramValuesScore) / 2; | ||||
|                 return match.Command.Priority + totalArgsScore * 0.99f; | ||||
|             } | ||||
|  | ||||
|             //Order the parse results by their score so that we choose the most likely result to execute | ||||
|             var parseResults = parseResultsDict | ||||
|                 .OrderByDescending(x => CalculateScore(x.Key, x.Value)); | ||||
|  | ||||
|             var successfulParses = parseResults | ||||
|                 .Where(x => x.Value.IsSuccess) | ||||
|                 .ToArray(); | ||||
|  | ||||
|             if (successfulParses.Length == 0) | ||||
|             { | ||||
|                 //All parses failed, return the one from the highest priority command, using score as a tie breaker | ||||
|                 var bestMatch = parseResults | ||||
|                     .FirstOrDefault(x => !x.Value.IsSuccess); | ||||
|                 return (false, bestMatch.Value.ErrorReason, commands[0].Command); | ||||
|             } | ||||
|  | ||||
|             var cmd = successfulParses[0].Key.Command; | ||||
|  | ||||
|             // Bot will ignore commands which are ran more often than what specified by | ||||
|             // GlobalCommandsCooldown constant (miliseconds) | ||||
|             if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) | ||||
|                     return (false, null, commands[i].Command); | ||||
|                 return (false, null, cmd); | ||||
|             //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); | ||||
|  | ||||
|             var commandName = cmd.Aliases.First(); | ||||
| @@ -351,11 +393,14 @@ namespace NadekoBot.Services | ||||
|                     await exec.TryBlockLate(_client, context.Message, context.Guild, context.Channel, context.User, cmd.Module.GetTopLevelModule().Name, commandName).ConfigureAwait(false)) | ||||
|                 { | ||||
|                     _log.Info("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, svc.GetType().Name); | ||||
|                         return (false, null, commands[i].Command); | ||||
|                     return (false, null, cmd); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|                 var execResult = (ExecuteResult)(await commands[i].ExecuteAsync(context, parseResult, serviceProvider)); | ||||
|             //If we get this far, at least one parse was successful. Execute the most likely overload. | ||||
|             var chosenOverload = successfulParses[0]; | ||||
|             var execResult = (ExecuteResult)await chosenOverload.Key.ExecuteAsync(context, chosenOverload.Value, services).ConfigureAwait(false); | ||||
|  | ||||
|             if (execResult.Exception != null && (!(execResult.Exception is HttpException he) || he.DiscordCode != 50013)) | ||||
|             { | ||||
|                 lock (errorLogLock) | ||||
| @@ -368,11 +413,8 @@ namespace NadekoBot.Services | ||||
|                     _log.Warn(execResult.Exception); | ||||
|                 } | ||||
|             } | ||||
|                 return (true, null, commands[i].Command); | ||||
|             } | ||||
|  | ||||
|             return (false, null, null); | ||||
|             //return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); | ||||
|             return (true, null, cmd); | ||||
|         } | ||||
|  | ||||
|         private readonly object errorLogLock = new object(); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ using NadekoBot.Services.Database; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     public class CurrencyService | ||||
|     public class CurrencyService : INService | ||||
|     { | ||||
|         private readonly BotConfig _config; | ||||
|         private readonly DbService _db; | ||||
|   | ||||
| @@ -14,7 +14,7 @@ using NadekoBot.Services.Database; | ||||
|  | ||||
| namespace NadekoBot.Services.CustomReactions | ||||
| { | ||||
|     public class CustomReactionsService : IEarlyBlockingExecutor | ||||
|     public class CustomReactionsService : IEarlyBlockingExecutor, INService | ||||
|     { | ||||
|         public CustomReaction[] GlobalReactions = new CustomReaction[] { }; | ||||
|         public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>(); | ||||
|   | ||||
| @@ -65,7 +65,7 @@ Nadeko Support Server: https://discord.gg/nadekobot"; | ||||
|         public List<StartupCommand> StartupCommands { get; set; } | ||||
|         public HashSet<BlockedCmdOrMdl> BlockedCommands { get; set; } | ||||
|         public HashSet<BlockedCmdOrMdl> BlockedModules { get; set; } | ||||
|         public int PermissionVersion { get; set; } = 1; | ||||
|         public int PermissionVersion { get; set; } | ||||
|         public string DefaultPrefix { get; set; } = "."; | ||||
|         public bool CustomReactionsStartWith { get; set; } = false; | ||||
|     } | ||||
|   | ||||
| @@ -6,6 +6,6 @@ namespace NadekoBot.Services.Database.Repositories | ||||
| { | ||||
|     public interface IReminderRepository : IRepository<Reminder> | ||||
|     { | ||||
|         IEnumerable<Reminder> GetIncludedReminders(List<long> guildIds); | ||||
|         IEnumerable<Reminder> GetIncludedReminders(IEnumerable<long> guildIds); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         public IEnumerable<Reminder> GetIncludedReminders(List<long> guildIds) | ||||
|         public IEnumerable<Reminder> GetIncludedReminders(IEnumerable<long> guildIds) | ||||
|         { | ||||
|             return _set.Where(x => guildIds.Contains((long)x.ServerId)).ToList(); | ||||
|         } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Games | ||||
| { | ||||
|     public class ChatterBotService : IEarlyBlockingExecutor | ||||
|     public class ChatterBotService : IEarlyBlockingExecutor, INService | ||||
|     { | ||||
|         private readonly DiscordSocketClient _client; | ||||
|         private readonly Logger _log; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Games | ||||
| { | ||||
|     public class GamesService | ||||
|     public class GamesService : INService | ||||
|     { | ||||
|         private readonly BotConfig _bc; | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ using NLog; | ||||
|  | ||||
| namespace NadekoBot.Services.Games | ||||
| { | ||||
|     public class PollService : IEarlyBlockingExecutor | ||||
|     public class PollService : IEarlyBlockingExecutor, INService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>(); | ||||
|         private readonly Logger _log; | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     public class GreetSettingsService | ||||
|     public class GreetSettingsService : INService | ||||
|     { | ||||
|         private readonly DbService _db; | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ using NadekoBot.Attributes; | ||||
|  | ||||
| namespace NadekoBot.Services.Help | ||||
| { | ||||
|     public class HelpService : ILateExecutor | ||||
|     public class HelpService : ILateExecutor, INService | ||||
|     { | ||||
|         private readonly BotConfig _bc; | ||||
|         private readonly CommandHandler _ch; | ||||
|   | ||||
| @@ -5,7 +5,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     public interface IGoogleApiService | ||||
|     public interface IGoogleApiService : INService | ||||
|     { | ||||
|         IEnumerable<string> Languages { get; } | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,7 @@ using System.Collections.Immutable; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     public interface IImagesService | ||||
|     public interface IImagesService : INService | ||||
|     { | ||||
|         ImmutableArray<byte> Heads { get; } | ||||
|         ImmutableArray<byte> Tails { get; } | ||||
|   | ||||
							
								
								
									
										16
									
								
								src/NadekoBot/Services/INService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/NadekoBot/Services/INService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     /// <summary> | ||||
|     /// All services must implement this interface in order to be auto-discovered by the DI system | ||||
|     /// </summary> | ||||
|     public interface INService | ||||
|     { | ||||
|          | ||||
|     } | ||||
| } | ||||
| @@ -3,7 +3,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     public interface IStatsService | ||||
|     public interface IStatsService : INService | ||||
|     { | ||||
|         string Author { get; } | ||||
|         long CommandsRan { get; } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ using System.Text.RegularExpressions; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
|     public class NadekoStrings | ||||
|     public class NadekoStrings : INService | ||||
|     { | ||||
|         public const string stringsPath = @"_strings/"; | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Impl | ||||
| { | ||||
|     public class SoundCloudApiService | ||||
|     public class SoundCloudApiService : INService | ||||
|     { | ||||
|         private readonly IBotCredentials _creds; | ||||
|  | ||||
|   | ||||
							
								
								
									
										24
									
								
								src/NadekoBot/Services/Impl/StartingGuildsListService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/NadekoBot/Services/Impl/StartingGuildsListService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| using Discord.WebSocket; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using System.Collections; | ||||
|  | ||||
| namespace NadekoBot.Services.Impl | ||||
| { | ||||
|     public class StartingGuildsService : IEnumerable<long>, INService | ||||
|     { | ||||
|         private readonly ImmutableList<long> _guilds; | ||||
|  | ||||
|         public StartingGuildsService(DiscordSocketClient client) | ||||
|         { | ||||
|             this._guilds = client.Guilds.Select(x => (long)x.Id).ToImmutableList(); | ||||
|         } | ||||
|  | ||||
|         public IEnumerator<long> GetEnumerator() => | ||||
|             _guilds.GetEnumerator(); | ||||
|  | ||||
|         IEnumerator IEnumerable.GetEnumerator() => | ||||
|             _guilds.GetEnumerator(); | ||||
|     } | ||||
| } | ||||
| @@ -15,7 +15,7 @@ using NadekoBot.Services.Impl; | ||||
|  | ||||
| namespace NadekoBot.Services.Music | ||||
| { | ||||
|     public class MusicService | ||||
|     public class MusicService : INService | ||||
|     { | ||||
|         public const string MusicDataPath = "data/musicdata"; | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Permissions | ||||
| { | ||||
|     public class BlacklistService : IEarlyBlocker | ||||
|     public class BlacklistService : IEarlyBlocker, INService | ||||
|     { | ||||
|         public ConcurrentHashSet<ulong> BlacklistedUsers { get; } | ||||
|         public ConcurrentHashSet<ulong> BlacklistedGuilds { get; } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Permissions | ||||
| { | ||||
|     public class CmdCdService : ILateBlocker | ||||
|     public class CmdCdService : ILateBlocker, INService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; } | ||||
|         public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>(); | ||||
|   | ||||
| @@ -12,7 +12,7 @@ using NLog; | ||||
|  | ||||
| namespace NadekoBot.Services.Permissions | ||||
| { | ||||
|     public class FilterService : IEarlyBlocker | ||||
|     public class FilterService : IEarlyBlocker, INService | ||||
|     { | ||||
|         private readonly Logger _log; | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Permissions | ||||
| { | ||||
|     public class GlobalPermissionService : ILateBlocker | ||||
|     public class GlobalPermissionService : ILateBlocker, INService | ||||
|     { | ||||
|         public readonly ConcurrentHashSet<string> BlockedModules; | ||||
|         public readonly ConcurrentHashSet<string> BlockedCommands; | ||||
|   | ||||
| @@ -11,14 +11,14 @@ using System.Threading.Tasks; | ||||
| using Discord; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Services.Database; | ||||
| using NadekoBot.Services; | ||||
|  | ||||
| namespace NadekoBot.Services.Permissions | ||||
| { | ||||
|     public class PermissionService : ILateBlocker | ||||
|     public class PermissionService : ILateBlocker, INService | ||||
|     { | ||||
|         private readonly DbService _db; | ||||
|         private readonly Logger _log; | ||||
|         private readonly CommandHandler _cmd; | ||||
|         private readonly NadekoStrings _strings; | ||||
|  | ||||
| @@ -26,16 +26,15 @@ namespace NadekoBot.Services.Permissions | ||||
|         public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } = | ||||
|             new ConcurrentDictionary<ulong, PermissionCache>(); | ||||
|  | ||||
|         public PermissionService(DiscordSocketClient client, DbService db, BotConfig bc, CommandHandler cmd, NadekoStrings strings) | ||||
|         public PermissionService(DiscordSocketClient client, DbService db, CommandHandler cmd, NadekoStrings strings) | ||||
|         { | ||||
|             _log = LogManager.GetCurrentClassLogger(); | ||||
|             _db = db; | ||||
|             _cmd = cmd; | ||||
|             _strings = strings; | ||||
|  | ||||
|             var sw = Stopwatch.StartNew(); | ||||
|             if (client.ShardId == 0) | ||||
|                 TryMigratePermissions(bc); | ||||
|                 TryMigratePermissions(); | ||||
|  | ||||
|             using (var uow = _db.UnitOfWork) | ||||
|             { | ||||
| @@ -49,9 +48,6 @@ namespace NadekoBot.Services.Permissions | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             sw.Stop(); | ||||
|             _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); | ||||
|         } | ||||
|  | ||||
|         public PermissionCache GetCache(ulong guildId) | ||||
| @@ -71,12 +67,13 @@ namespace NadekoBot.Services.Permissions | ||||
|             return pc; | ||||
|         } | ||||
|  | ||||
|         private void TryMigratePermissions(BotConfig bc) | ||||
|         { | ||||
|             var log = LogManager.GetCurrentClassLogger(); | ||||
|             if (bc.PermissionVersion <= 1) | ||||
|         private void TryMigratePermissions() | ||||
|         { | ||||
|             using (var uow = _db.UnitOfWork) | ||||
|             { | ||||
|                 var bc = uow.BotConfig.GetOrCreate(); | ||||
|                 var log = LogManager.GetCurrentClassLogger(); | ||||
|                 if (bc.PermissionVersion <= 1) | ||||
|                 { | ||||
|                     log.Info("Permission version is 1, upgrading to 2."); | ||||
|                     var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs | ||||
| @@ -134,10 +131,7 @@ namespace NadekoBot.Services.Permissions | ||||
|                     bc.PermissionVersion = 2; | ||||
|                     uow.Complete(); | ||||
|                 } | ||||
|             } | ||||
|                 if (bc.PermissionVersion <= 2) | ||||
|             { | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" }; | ||||
|                     uow._context.Database.ExecuteSqlCommand( | ||||
|   | ||||
| @@ -6,7 +6,7 @@ using System.IO; | ||||
|  | ||||
| namespace NadekoBot.Services.Pokemon | ||||
| { | ||||
|     public class PokemonService | ||||
|     public class PokemonService : INService | ||||
|     { | ||||
|         public readonly List<PokemonType> PokemonTypes = new List<PokemonType>(); | ||||
|         public readonly ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>(); | ||||
|   | ||||
| @@ -6,7 +6,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Searches | ||||
| { | ||||
|     public class AnimeSearchService | ||||
|     public class AnimeSearchService : INService | ||||
|     { | ||||
|         private readonly Logger _log; | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ using System.Xml; | ||||
|  | ||||
| namespace NadekoBot.Services.Searches | ||||
| { | ||||
|     public class SearchesService | ||||
|     public class SearchesService : INService | ||||
|     { | ||||
|         private readonly DiscordSocketClient _client; | ||||
|         private readonly IGoogleApiService _google; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Searches | ||||
| { | ||||
|     public class StreamNotificationService | ||||
|     public class StreamNotificationService : INService | ||||
|     { | ||||
|         private readonly Timer _streamCheckTimer; | ||||
|         private bool firstStreamNotifPass { get; set; } = true; | ||||
|   | ||||
| @@ -3,6 +3,15 @@ using System.Collections; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Reflection; | ||||
| using Discord.Commands; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using NadekoBot.Services.Impl; | ||||
| using System.Linq; | ||||
| using NadekoBot.Extensions; | ||||
| using System.Diagnostics; | ||||
| using NLog; | ||||
|  | ||||
| namespace NadekoBot.Services | ||||
| { | ||||
| @@ -16,8 +25,14 @@ namespace NadekoBot.Services | ||||
|         public class ServiceProviderBuilder | ||||
|         { | ||||
|             private ConcurrentDictionary<Type, object> _dict = new ConcurrentDictionary<Type, object>(); | ||||
|             private readonly Logger _log; | ||||
|  | ||||
|             public ServiceProviderBuilder Add<T>(T obj) | ||||
|             public ServiceProviderBuilder() | ||||
|             { | ||||
|                 _log = LogManager.GetCurrentClassLogger(); | ||||
|             } | ||||
|  | ||||
|             public ServiceProviderBuilder AddManual<T>(T obj) | ||||
|             { | ||||
|                 _dict.TryAdd(typeof(T), obj); | ||||
|                 return this; | ||||
| @@ -27,6 +42,61 @@ namespace NadekoBot.Services | ||||
|             { | ||||
|                 return new NServiceProvider(_dict); | ||||
|             } | ||||
|  | ||||
|             public ServiceProviderBuilder LoadFrom(Assembly assembly) | ||||
|             { | ||||
|                 var allTypes = assembly.GetTypes(); | ||||
|                 var services = new Queue<Type>(allTypes | ||||
|                         .Where(x => x.GetInterfaces().Contains(typeof(INService)) && !x.GetTypeInfo().IsInterface && !x.GetTypeInfo().IsAbstract) | ||||
|                         .ToArray()); | ||||
|  | ||||
|                 var interfaces = new HashSet<Type>(allTypes | ||||
|                         .Where(x => x.GetInterfaces().Contains(typeof(INService)) && x.GetTypeInfo().IsInterface)); | ||||
|  | ||||
|                 var sw = Stopwatch.StartNew(); | ||||
|                 var swInstance = new Stopwatch(); | ||||
|                 while (services.Count > 0) | ||||
|                 { | ||||
|                     var type = services.Dequeue(); //get a type i need to make an instance of | ||||
|  | ||||
|                     if (_dict.TryGetValue(type, out _)) // if that type is already instantiated, skip | ||||
|                         continue; | ||||
|  | ||||
|                     var ctor = type.GetConstructors()[0]; | ||||
|                     var argTypes = ctor | ||||
|                         .GetParameters() | ||||
|                         .Select(x => x.ParameterType) | ||||
|                         .ToArray(); // get constructor argument types i need to pass in | ||||
|  | ||||
|                     var args = new List<object>(argTypes.Length); | ||||
|                     foreach (var arg in argTypes) //get constructor arguments from the dictionary of already instantiated types | ||||
|                     { | ||||
|                         if (_dict.TryGetValue(arg, out var argObj)) //if i got current one, add it to the list of instances and move on | ||||
|                             args.Add(argObj); | ||||
|                         else //if i failed getting it, add it to the end, and break | ||||
|                         { | ||||
|                             services.Enqueue(type); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     if (args.Count != argTypes.Length) | ||||
|                         continue; | ||||
|                     swInstance.Restart(); | ||||
|                     var instance = ctor.Invoke(args.ToArray()); | ||||
|                     swInstance.Stop(); | ||||
|                     if (swInstance.Elapsed.TotalSeconds > 5) | ||||
|                         _log.Info($"{type.Name} took {swInstance.Elapsed.TotalSeconds:F2}s to load."); | ||||
|                     var interfaceType = interfaces.FirstOrDefault(x => instance.GetType().GetInterfaces().Contains(x)); | ||||
|                     if (interfaceType != null) | ||||
|                         _dict.TryAdd(interfaceType, instance); | ||||
|  | ||||
|                     _dict.TryAdd(type, instance); | ||||
|                 } | ||||
|                 sw.Stop(); | ||||
|                 _log.Info($"All services loaded in {sw.Elapsed.TotalSeconds:F2}s"); | ||||
|  | ||||
|                 return this; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private readonly ImmutableDictionary<Type, object> _services; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ using NadekoBot.Extensions; | ||||
|  | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     public class CommandMapService : IInputTransformer | ||||
|     public class CommandMapService : IInputTransformer, INService | ||||
|     { | ||||
|         private readonly Logger _log; | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     public class ConverterService | ||||
|     public class ConverterService : INService | ||||
|     { | ||||
|         public List<ConvertUnit> Units { get; } = new List<ConvertUnit>(); | ||||
|         private readonly Logger _log; | ||||
|   | ||||
| @@ -8,7 +8,7 @@ using System.Threading.Tasks; | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     //todo 50 rewrite | ||||
|     public class MessageRepeaterService | ||||
|     public class MessageRepeaterService : INService | ||||
|     { | ||||
|         //messagerepeater | ||||
|         //guildid/RepeatRunners | ||||
| @@ -19,8 +19,7 @@ namespace NadekoBot.Services.Utility | ||||
|         { | ||||
|             var _ = Task.Run(async () => | ||||
|             { | ||||
|                 while (!bot.Ready) | ||||
|                     await Task.Delay(1000); | ||||
|                 await bot.Ready.Task.ConfigureAwait(false); | ||||
|  | ||||
|                 Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(gcs | ||||
|                     .ToDictionary(gc => gc.GuildId, | ||||
|   | ||||
| @@ -9,13 +9,12 @@ using System.Collections.Immutable; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Net.Http; | ||||
| using System.Text; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     public class PatreonRewardsService | ||||
|     public class PatreonRewardsService : INService | ||||
|     { | ||||
|         private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1); | ||||
|  | ||||
|   | ||||
| @@ -4,9 +4,9 @@ using NadekoBot.DataStructures.Replacements; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Services.Database; | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using NadekoBot.Services.Impl; | ||||
| using NLog; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text.RegularExpressions; | ||||
| using System.Threading; | ||||
| @@ -14,7 +14,7 @@ using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     public class RemindService | ||||
|     public class RemindService : INService | ||||
|     { | ||||
|         public readonly Regex Regex = new Regex(@"^(?:(?<months>\d)mo)?(?:(?<weeks>\d)w)?(?:(?<days>\d{1,2})d)?(?:(?<hours>\d{1,2})h)?(?:(?<minutes>\d{1,2})m)?$", | ||||
|                                 RegexOptions.Compiled | RegexOptions.Multiline); | ||||
| @@ -29,7 +29,7 @@ namespace NadekoBot.Services.Utility | ||||
|         private readonly DbService _db; | ||||
|  | ||||
|         public RemindService(DiscordSocketClient client, BotConfig config, DbService db, | ||||
|             List<long> guilds, IUnitOfWork uow) | ||||
|              StartingGuildsService guilds, IUnitOfWork uow) | ||||
|         { | ||||
|             _config = config; | ||||
|             _client = client; | ||||
|   | ||||
| @@ -12,7 +12,7 @@ using NLog; | ||||
|  | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     public class StreamRoleService | ||||
|     public class StreamRoleService : INService | ||||
|     { | ||||
|         private readonly DbService _db; | ||||
|         private readonly ConcurrentDictionary<ulong, StreamRoleSettings> guildSettings; | ||||
| @@ -26,6 +26,7 @@ namespace NadekoBot.Services.Utility | ||||
|             this._log = LogManager.GetCurrentClassLogger(); | ||||
|  | ||||
|             guildSettings = gcs.ToDictionary(x => x.GuildId, x => x.StreamRole) | ||||
|                 .Where(x => x.Value.FromRoleId != 0 && x.Value.AddRoleId != 0) | ||||
|                .ToConcurrent(); | ||||
|  | ||||
|             client.GuildMemberUpdated += Client_GuildMemberUpdated; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ using System.Linq; | ||||
|  | ||||
| namespace NadekoBot.Services.Utility | ||||
| { | ||||
|     public class VerboseErrorsService | ||||
|     public class VerboseErrorsService : INService | ||||
|     { | ||||
|         private readonly ConcurrentHashSet<ulong> guildsEnabled; | ||||
|         private readonly DbService _db; | ||||
|   | ||||
| @@ -3,7 +3,6 @@ using NadekoBot.Services; | ||||
| using NadekoBot.Services.Impl; | ||||
| using NLog; | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user