Huge cleanup,

rewrite of the NadekoBot.cs, way services are loaded has changed. Updated discord.net.
This commit is contained in:
Master Kwoth 2017-07-15 05:04:16 +02:00
parent f239c46e20
commit 4eca5be1d4
74 changed files with 395 additions and 965 deletions

View File

@ -9,17 +9,10 @@ namespace NadekoBot.TypeReaders
{ {
public class CommandTypeReader : TypeReader public class CommandTypeReader : TypeReader
{ {
private readonly CommandService _cmds; public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
private readonly CommandHandler _cmdHandler;
public CommandTypeReader(CommandService cmds, CommandHandler cmdHandler)
{
_cmds = cmds;
_cmdHandler = cmdHandler;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{ {
var _cmds = ((INServiceProvider)services).GetService<CommandService>();
var _cmdHandler = ((INServiceProvider)services).GetService<CommandHandler>();
input = input.ToUpperInvariant(); input = input.ToUpperInvariant();
var prefix = _cmdHandler.GetPrefix(context.Guild); var prefix = _cmdHandler.GetPrefix(context.Guild);
if (!input.StartsWith(prefix.ToUpperInvariant())) if (!input.StartsWith(prefix.ToUpperInvariant()))
@ -38,17 +31,12 @@ namespace NadekoBot.TypeReaders
public class CommandOrCrTypeReader : CommandTypeReader public class CommandOrCrTypeReader : CommandTypeReader
{ {
private readonly CustomReactionsService _crs; public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
public CommandOrCrTypeReader(CustomReactionsService crs, CommandService cmds, CommandHandler cmdHandler) : base(cmds, cmdHandler)
{
_crs = crs;
}
public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{ {
input = input.ToUpperInvariant(); input = input.ToUpperInvariant();
var _crs = ((INServiceProvider)services).GetService<CustomReactionsService>();
if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input)) if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input))
{ {
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(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) if (cmd.IsSuccess)
{ {
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name)); return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name));

View File

@ -1,4 +1,5 @@
using Discord.Commands; using Discord.Commands;
using NadekoBot.Services;
using NadekoBot.Services.Administration; using NadekoBot.Services.Administration;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -7,15 +8,9 @@ namespace NadekoBot.TypeReaders
{ {
public class GuildDateTimeTypeReader : TypeReader public class GuildDateTimeTypeReader : TypeReader
{ {
private readonly GuildTimezoneService _gts; public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
public GuildDateTimeTypeReader(GuildTimezoneService gts)
{
_gts = gts;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{ {
var _gts = (GuildTimezoneService)services.GetService(typeof(GuildTimezoneService));
if (!DateTime.TryParse(input, out var dt)) if (!DateTime.TryParse(input, out var dt))
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format.")); return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format."));

View File

@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[Priority(1)] [Priority(0)]
public async Task SetMuteRole([Remainder] string name) public async Task SetMuteRole([Remainder] string name)
{ {
name = name.Trim(); name = name.Trim();
@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[Priority(0)] [Priority(1)]
public Task SetMuteRole([Remainder] IRole role) public Task SetMuteRole([Remainder] IRole role)
=> SetMuteRole(role.Name); => SetMuteRole(role.Name);
@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.MuteMembers)] [RequireUserPermission(GuildPermission.MuteMembers)]
[Priority(1)] [Priority(0)]
public async Task Mute(IGuildUser user) public async Task Mute(IGuildUser user)
{ {
try try
@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.MuteMembers)] [RequireUserPermission(GuildPermission.MuteMembers)]
[Priority(0)] [Priority(1)]
public async Task Mute(int minutes, IGuildUser user) public async Task Mute(int minutes, IGuildUser user)
{ {
if (minutes < 1 || minutes > 1440) if (minutes < 1 || minutes > 1440)

View File

@ -11,7 +11,7 @@ namespace NadekoBot.Modules.Administration
public class PrefixCommands : NadekoSubmodule public class PrefixCommands : NadekoSubmodule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public new async Task Prefix() public new async Task Prefix()
{ {
await ReplyConfirmLocalized("prefix_current", Format.Code(_cmdHandler.GetPrefix(Context.Guild))).ConfigureAwait(false); await ReplyConfirmLocalized("prefix_current", Format.Code(_cmdHandler.GetPrefix(Context.Guild))).ConfigureAwait(false);
@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.Administrator)] [RequireUserPermission(GuildPermission.Administrator)]
[Priority(1)] [Priority(0)]
public new async Task Prefix([Remainder]string prefix) public new async Task Prefix([Remainder]string prefix)
{ {
if (string.IsNullOrWhiteSpace(prefix)) if (string.IsNullOrWhiteSpace(prefix))

View File

@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(ChannelPermission.ManageMessages)] [RequireUserPermission(ChannelPermission.ManageMessages)]
[RequireBotPermission(GuildPermission.ManageMessages)] [RequireBotPermission(GuildPermission.ManageMessages)]
[Priority(0)] [Priority(1)]
public async Task Prune(int count) public async Task Prune(int count)
{ {
count++; count++;
@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(ChannelPermission.ManageMessages)] [RequireUserPermission(ChannelPermission.ManageMessages)]
[RequireBotPermission(GuildPermission.ManageMessages)] [RequireBotPermission(GuildPermission.ManageMessages)]
[Priority(1)] [Priority(0)]
public async Task Prune(IGuildUser user, int count = 100) public async Task Prune(IGuildUser user, int count = 100)
{ {
if (user.Id == Context.User.Id) if (user.Id == Context.User.Id)

View File

@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[Priority(1)] [Priority(0)]
public async Task SlowmodeWhitelist(IGuildUser user) public async Task SlowmodeWhitelist(IGuildUser user)
{ {
var siu = new SlowmodeIgnoredUser var siu = new SlowmodeIgnoredUser
@ -99,7 +99,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[Priority(0)] [Priority(1)]
public async Task SlowmodeWhitelist(IRole role) public async Task SlowmodeWhitelist(IRole role)
{ {
var sir = new SlowmodeIgnoredRole var sir = new SlowmodeIgnoredRole

View File

@ -289,7 +289,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageNicknames)] [RequireUserPermission(GuildPermission.ManageNicknames)]
[Priority(1)] [Priority(0)]
public async Task SetNick([Remainder] string newNick = null) public async Task SetNick([Remainder] string newNick = null)
{ {
if (string.IsNullOrWhiteSpace(newNick)) if (string.IsNullOrWhiteSpace(newNick))
@ -303,7 +303,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireBotPermission(GuildPermission.ManageNicknames)] [RequireBotPermission(GuildPermission.ManageNicknames)]
[RequireUserPermission(GuildPermission.ManageNicknames)] [RequireUserPermission(GuildPermission.ManageNicknames)]
[Priority(0)] [Priority(1)]
public async Task SetNick(IGuildUser gu, [Remainder] string newNick = null) public async Task SetNick(IGuildUser gu, [Remainder] string newNick = null)
{ {
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false); await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);

View File

@ -132,27 +132,27 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.BanMembers)] [RequireUserPermission(GuildPermission.BanMembers)]
[Priority(1)] [Priority(2)]
public Task Warnlog(int page, IGuildUser user) public Task Warnlog(int page, IGuildUser user)
=> Warnlog(page, user.Id); => Warnlog(page, user.Id);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(0)] [Priority(3)]
public Task Warnlog(IGuildUser user) public Task Warnlog(IGuildUser user)
=> Context.User.Id == user.Id || ((IGuildUser)Context.User).GuildPermissions.BanMembers ? Warnlog(user.Id) : Task.CompletedTask; => Context.User.Id == user.Id || ((IGuildUser)Context.User).GuildPermissions.BanMembers ? Warnlog(user.Id) : Task.CompletedTask;
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.BanMembers)] [RequireUserPermission(GuildPermission.BanMembers)]
[Priority(3)] [Priority(0)]
public Task Warnlog(int page, ulong userId) public Task Warnlog(int page, ulong userId)
=> InternalWarnlog(userId, page - 1); => InternalWarnlog(userId, page - 1);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.BanMembers)] [RequireUserPermission(GuildPermission.BanMembers)]
[Priority(2)] [Priority(1)]
public Task Warnlog(ulong userId) public Task Warnlog(ulong userId)
=> InternalWarnlog(userId, 0); => InternalWarnlog(userId, 0);

View File

@ -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);
}
}
}
}

View File

@ -84,7 +84,7 @@ namespace NadekoBot.Modules.CustomReactions
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public async Task ListCustReact(int page = 1) public async Task ListCustReact(int page = 1)
{ {
if (--page < 0 || page > 999) if (--page < 0 || page > 999)
@ -130,7 +130,7 @@ namespace NadekoBot.Modules.CustomReactions
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task ListCustReact(All x) public async Task ListCustReact(All x)
{ {
CustomReaction[] customReactions; CustomReaction[] customReactions;

View File

@ -58,7 +58,7 @@ namespace NadekoBot.Modules.Gambling
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public async Task Roll(int num) public async Task Roll(int num)
{ {
await InternalRoll(num, true).ConfigureAwait(false); await InternalRoll(num, true).ConfigureAwait(false);
@ -66,21 +66,21 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public async Task Rolluo(int num = 1) public async Task Rolluo(int num = 1)
{ {
await InternalRoll(num, false).ConfigureAwait(false); await InternalRoll(num, false).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task Roll(string arg) public async Task Roll(string arg)
{ {
await InternallDndRoll(arg, true).ConfigureAwait(false); await InternallDndRoll(arg, true).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task Rolluo(string arg) public async Task Rolluo(string arg)
{ {
await InternallDndRoll(arg, false).ConfigureAwait(false); await InternallDndRoll(arg, false).ConfigureAwait(false);

View File

@ -197,12 +197,12 @@ namespace NadekoBot.Modules.Gambling
private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6); private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(1)] [Priority(0)]
public Task Divorce([Remainder]IGuildUser target) => Divorce(target.Id); public Task Divorce([Remainder]IGuildUser target) => Divorce(target.Id);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(0)] [Priority(1)]
public async Task Divorce([Remainder]ulong targetId) public async Task Divorce([Remainder]ulong targetId)
{ {
if (targetId == Context.User.Id) if (targetId == Context.User.Id)

View File

@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Gambling
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public async Task Cash([Remainder] IUser user = null) public async Task Cash([Remainder] IUser user = null)
{ {
if(user == null) if(user == null)
@ -62,7 +62,7 @@ namespace NadekoBot.Modules.Gambling
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task Cash(ulong userId) public async Task Cash(ulong userId)
{ {
await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
[Priority(2)] [Priority(0)]
public Task Award(int amount, [Remainder] IGuildUser usr) => public Task Award(int amount, [Remainder] IGuildUser usr) =>
Award(amount, usr.Id); Award(amount, usr.Id);
@ -107,7 +107,7 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
[Priority(0)] [Priority(2)]
public async Task Award(int amount, [Remainder] IRole role) public async Task Award(int amount, [Remainder] IRole role)
{ {
var users = (await Context.Guild.GetUsersAsync()) var users = (await Context.Guild.GetUsersAsync())

View File

@ -82,14 +82,14 @@ namespace NadekoBot.Modules.Help
await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false); await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task H([Remainder] string fail) public async Task H([Remainder] string fail)
{ {
await ReplyErrorLocalized("command_not_found").ConfigureAwait(false); await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public async Task H([Remainder] CommandInfo com = null) public async Task H([Remainder] CommandInfo com = null)
{ {
var channel = Context.Channel; var channel = Context.Channel;

View File

@ -377,7 +377,7 @@ namespace NadekoBot.Modules.Music
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(0)] [Priority(1)]
public async Task SongRemove(int index) public async Task SongRemove(int index)
{ {
if (index < 1) if (index < 1)
@ -406,7 +406,7 @@ namespace NadekoBot.Modules.Music
public enum All { All } public enum All { All }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(1)] [Priority(0)]
public async Task SongRemove(All all) public async Task SongRemove(All all)
{ {
var mp = _music.GetPlayerOrDefault(Context.Guild.Id); var mp = _music.GetPlayerOrDefault(Context.Guild.Id);
@ -853,41 +853,6 @@ namespace NadekoBot.Modules.Music
else else
await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]

View File

@ -192,10 +192,18 @@ namespace NadekoBot.Modules.NSFW
if (imgObj == null) if (imgObj == null)
await ReplyErrorLocalized("not_found").ConfigureAwait(false); await ReplyErrorLocalized("not_found").ConfigureAwait(false);
else else
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() {
var embed = new EmbedBuilder().WithOkColor()
.WithDescription($"{Context.User} [{tag ?? "url"}]({imgObj}) ") .WithDescription($"{Context.User} [{tag ?? "url"}]({imgObj}) ")
.WithImageUrl(imgObj.FileUrl) .WithFooter(efb => efb.WithText(type.ToString()));
.WithFooter(efb => efb.WithText(type.ToString()))).ConfigureAwait(false);
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);
}
} }
} }
} }

View File

@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task Mal([Remainder] string name) public async Task Mal([Remainder] string name)
{ {
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(0)] [Priority(1)]
public Task Mal(IGuildUser usr) => Mal(usr.Username); public Task Mal(IGuildUser usr) => Mal(usr.Username);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches
private const string _xkcdUrl = "https://xkcd.com"; private const string _xkcdUrl = "https://xkcd.com";
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(0)]
public async Task Xkcd(string arg = null) public async Task Xkcd(string arg = null)
{ {
if (arg?.ToLowerInvariant().Trim() == "latest") if (arg?.ToLowerInvariant().Trim() == "latest")
@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Searches
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(0)] [Priority(1)]
public async Task Xkcd(int num) public async Task Xkcd(int num)
{ {
if (num < 1) if (num < 1)

View File

@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(0)] [Priority(1)]
public async Task Remind(MeOrHere meorhere, string timeStr, [Remainder] string message) public async Task Remind(MeOrHere meorhere, string timeStr, [Remainder] string message)
{ {
ulong target; ulong target;
@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[Priority(1)] [Priority(0)]
public async Task Remind(ITextChannel channel, string timeStr, [Remainder] string message) public async Task Remind(ITextChannel channel, string timeStr, [Remainder] string message)
{ {
var perms = ((IGuildUser)Context.User).GetPermissions((ITextChannel)channel); var perms = ((IGuildUser)Context.User).GetPermissions((ITextChannel)channel);

View File

@ -63,7 +63,6 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[Priority(0)]
public async Task RepeatRemove(int index) public async Task RepeatRemove(int index)
{ {
if (!_service.RepeaterReady) if (!_service.RepeaterReady)
@ -103,7 +102,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[Priority(1)] [Priority(0)]
public async Task Repeat(int minutes, [Remainder] string message) public async Task Repeat(int minutes, [Remainder] string message)
{ {
if (!_service.RepeaterReady) if (!_service.RepeaterReady)
@ -152,7 +151,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[Priority(0)] [Priority(1)]
public async Task Repeat(GuildDateTime gt, [Remainder] string message) public async Task Repeat(GuildDateTime gt, [Remainder] string message)
{ {
if (!_service.RepeaterReady) if (!_service.RepeaterReady)

View File

@ -14,20 +14,12 @@ using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Threading; 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 System.IO;
using NadekoBot.Services.Pokemon;
using NadekoBot.DataStructures.ShardCom; using NadekoBot.DataStructures.ShardCom;
using NadekoBot.DataStructures; using NadekoBot.DataStructures;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Collections.Generic;
using NadekoBot.Services.Database;
namespace NadekoBot namespace NadekoBot
{ {
@ -35,6 +27,15 @@ namespace NadekoBot
{ {
private Logger _log; 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 /* I don't know how to make this not be static
* and keep the convenience of .WithOkColor * and keep the convenience of .WithOkColor
* and .WithErrorColor extensions methods. * and .WithErrorColor extensions methods.
@ -44,23 +45,9 @@ namespace NadekoBot
public static Color OkColor { get; private set; } public static Color OkColor { get; private set; }
public static Color ErrorColor { get; private set; } public static Color ErrorColor { get; private set; }
public ImmutableArray<GuildConfig> AllGuildConfigs { get; private set; } public TaskCompletionSource<bool> Ready { get; private set; } = new TaskCompletionSource<bool>();
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 INServiceProvider Services { get; private set; } public INServiceProvider Services { get; private set; }
public BotCredentials Credentials { get; }
public int ShardId { get; } public int ShardId { get; }
public ShardsCoordinator ShardCoord { get; private set; } public ShardsCoordinator ShardCoord { get; private set; }
@ -72,26 +59,14 @@ namespace NadekoBot
if (shardId < 0) if (shardId < 0)
throw new ArgumentOutOfRangeException(nameof(shardId)); throw new ArgumentOutOfRangeException(nameof(shardId));
ShardId = shardId;
LogSetup.SetupLogger(); LogSetup.SetupLogger();
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
TerribleElevatedPermissionCheck(); TerribleElevatedPermissionCheck();
ShardId = shardId;
Credentials = new BotCredentials(); Credentials = new BotCredentials();
port = port ?? Credentials.ShardRunPort;
_comClient = new ShardComClient(port.Value);
Db = new DbService(Credentials); 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 Client = new DiscordSocketClient(new DiscordSocketConfig
{ {
MessageCacheSize = 10, MessageCacheSize = 10,
@ -101,16 +76,21 @@ namespace NadekoBot
ShardId = shardId, ShardId = shardId,
AlwaysDownloadUsers = false, AlwaysDownloadUsers = false,
}); });
CommandService = new CommandService(new CommandServiceConfig() CommandService = new CommandService(new CommandServiceConfig()
{ {
CaseSensitiveCommands = false, CaseSensitiveCommands = false,
DefaultRunMode = RunMode.Sync, DefaultRunMode = RunMode.Sync,
}); });
Images = new ImagesService(); port = port ?? Credentials.ShardRunPort;
Currency = new CurrencyService(BotConfig, Db); _comClient = new ShardComClient(port.Value);
GoogleApi = new GoogleApiService(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));
}
SetupShard(shardId, parentProcessId, port.Value); SetupShard(shardId, parentProcessId, port.Value);
@ -146,140 +126,34 @@ namespace NadekoBot
{ {
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray(); AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
Localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db); var 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
//initialize Services //initialize Services
Services = new NServiceProvider.ServiceProviderBuilder() Services = new NServiceProvider.ServiceProviderBuilder()
.Add<ILocalization>(Localization) .AddManual<IBotCredentials>(Credentials)
.Add<IStatsService>(Stats) .AddManual(Db)
.Add<IImagesService>(Images) .AddManual(BotConfig)
.Add<IGoogleApiService>(GoogleApi) .AddManual(Client)
.Add<IStatsService>(Stats) .AddManual(CommandService)
.Add<IBotCredentials>(Credentials) .AddManual<ILocalization>(localization)
.Add<CommandService>(CommandService) .AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this
.Add<NadekoStrings>(Strings) .AddManual<NadekoBot>(this)
.Add<DiscordSocketClient>(Client) .AddManual<IUnitOfWork>(uow)
.Add<BotConfig>(BotConfig) .AddManual(ShardCoord)
.Add<CurrencyService>(Currency) .LoadFrom(Assembly.GetEntryAssembly())
.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)
.Build(); .Build();
CommandHandler.AddServices(Services); var commandHandler = Services.GetService<CommandHandler>();
commandHandler.AddServices(Services);
//setup typereaders //setup typereaders
CommandService.AddTypeReader<PermissionAction>(new PermissionActionTypeReader()); CommandService.AddTypeReader<PermissionAction>(new PermissionActionTypeReader());
CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader(CommandService, CommandHandler)); CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader());
CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader(crService, CommandService, CommandHandler)); CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader());
CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader(CommandService)); CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader(CommandService));
CommandService.AddTypeReader<ModuleOrCrInfo>(new ModuleOrCrTypeReader(CommandService)); CommandService.AddTypeReader<ModuleOrCrInfo>(new ModuleOrCrTypeReader(CommandService));
CommandService.AddTypeReader<IGuild>(new GuildTypeReader(Client)); 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))) .Where(x => x.Preconditions.Any(y => y.GetType() == typeof(NoPublicBot)))
.ForEach(x => CommandService.RemoveModuleAsync(x)); .ForEach(x => CommandService.RemoveModuleAsync(x));
Ready = true; Ready.TrySetResult(true);
_log.Info($"Shard {ShardId} ready."); _log.Info($"Shard {ShardId} ready.");
//_log.Info(await stats.Print().ConfigureAwait(false)); //_log.Info(await stats.Print().ConfigureAwait(false));
} }

View File

@ -55,7 +55,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AngleSharp" Version="0.9.9" /> <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="libvideo" Version="1.0.1" />
<PackageReference Include="CoreCLR-NCalc" Version="2.1.2" /> <PackageReference Include="CoreCLR-NCalc" Version="2.1.2" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" /> <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" />

View File

@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class AdministrationService public class AdministrationService : INService
{ {
public readonly ConcurrentHashSet<ulong> DeleteMessagesOnCommand; public readonly ConcurrentHashSet<ulong> DeleteMessagesOnCommand;
private readonly Logger _log; private readonly Logger _log;

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class AutoAssignRoleService public class AutoAssignRoleService : INService
{ {
private readonly Logger _log; private readonly Logger _log;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;

View File

@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class GameVoiceChannelService public class GameVoiceChannelService : INService
{ {
public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>(); public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>();

View File

@ -9,7 +9,7 @@ using Discord.WebSocket;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class GuildTimezoneService public class GuildTimezoneService : INService
{ {
//hack >.> //hack >.>
public static ConcurrentDictionary<ulong, GuildTimezoneService> AllServices { get; } = new ConcurrentDictionary<ulong, GuildTimezoneService>(); public static ConcurrentDictionary<ulong, GuildTimezoneService> AllServices { get; } = new ConcurrentDictionary<ulong, GuildTimezoneService>();

View File

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class LogCommandService public class LogCommandService : INService
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Services.Administration
All All
} }
public class MuteService public class MuteService : INService
{ {
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }

View File

@ -9,7 +9,7 @@ using System.Threading;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class PlayingRotateService public class PlayingRotateService : INService
{ {
private readonly Timer _t; private readonly Timer _t;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;

View File

@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class ProtectionService public class ProtectionService : INService
{ {
public readonly ConcurrentDictionary<ulong, AntiRaidStats> AntiRaidGuilds = public readonly ConcurrentDictionary<ulong, AntiRaidStats> AntiRaidGuilds =
new ConcurrentDictionary<ulong, AntiRaidStats>(); new ConcurrentDictionary<ulong, AntiRaidStats>();

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class PruneService public class PruneService : INService
{ {
//channelids where prunes are currently occuring //channelids where prunes are currently occuring
private ConcurrentHashSet<ulong> _pruningGuilds = new ConcurrentHashSet<ulong>(); private ConcurrentHashSet<ulong> _pruningGuilds = new ConcurrentHashSet<ulong>();

View File

@ -12,7 +12,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration 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, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(); public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>();

View File

@ -12,7 +12,7 @@ using System.Collections.Generic;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class SelfService : ILateExecutor public class SelfService : ILateExecutor, INService
{ {
public volatile bool ForwardDMs; public volatile bool ForwardDMs;
public volatile bool ForwardDMsToAllOwners; public volatile bool ForwardDMsToAllOwners;
@ -44,8 +44,7 @@ namespace NadekoBot.Services.Administration
var _ = Task.Run(async () => var _ = Task.Run(async () =>
{ {
while (!bot.Ready) await bot.Ready.Task.ConfigureAwait(false);
await Task.Delay(1000);
foreach (var cmd in bc.StartupCommands) foreach (var cmd in bc.StartupCommands)
{ {
@ -56,8 +55,7 @@ namespace NadekoBot.Services.Administration
var ___ = Task.Run(async () => var ___ = Task.Run(async () =>
{ {
while (!bot.Ready) await bot.Ready.Task.ConfigureAwait(false);
await Task.Delay(1000);
await Task.Delay(5000); await Task.Delay(5000);

View File

@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class VcRoleService public class VcRoleService : INService
{ {
private readonly Logger _log; private readonly Logger _log;
private readonly DbService _db; private readonly DbService _db;

View File

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class VplusTService public class VplusTService : INService
{ {
private readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); private readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);

View File

@ -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());
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -24,7 +24,8 @@ namespace NadekoBot.Services
public int GetHashCode(IGuildUser obj) => obj.Id.GetHashCode(); public int GetHashCode(IGuildUser obj) => obj.Id.GetHashCode();
} }
public class CommandHandler
public class CommandHandler : INService
{ {
public const int GlobalCommandsCooldown = 750; public const int GlobalCommandsCooldown = 750;
@ -189,7 +190,7 @@ namespace NadekoBot.Services
{ {
try 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; return;
if (!(msg is SocketUserMessage usrMsg)) if (!(msg is SocketUserMessage usrMsg))
@ -296,52 +297,93 @@ namespace NadekoBot.Services
=> ExecuteCommand(context, input.Substring(argPos), serviceProvider, multiMatchHandling); => 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); var searchResult = _commandService.Search(context, input);
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return (false, null, null); return (false, null, null);
var commands = searchResult.Commands; 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); preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, services).ConfigureAwait(false);
if (!preconditionResult.IsSuccess)
{
return (false, preconditionResult.ErrorReason, commands[i].Command);
} }
var parseResult = await commands[i].ParseAsync(context, searchResult, preconditionResult).ConfigureAwait(false); var successfulPreconditions = preconditionResults
if (!parseResult.IsSuccess) .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) if (parseResult.Error == CommandError.MultipleMatches)
{ {
TypeReaderValue[] argList, paramList; IReadOnlyList<TypeReaderValue> argList, paramList;
switch (multiMatchHandling) switch (multiMatchHandling)
{ {
case MultiMatchHandling.Best: case MultiMatchHandling.Best:
argList = parseResult.ArgValues.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()).ToArray(); paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray();
parseResult = ParseResult.FromSuccess(argList, paramList); parseResult = ParseResult.FromSuccess(argList, paramList);
break; break;
} }
} }
if (!parseResult.IsSuccess) parseResultsDict[pair.Key] = parseResult;
{
if (commands.Count == 1)
return (false, parseResult.ErrorReason, commands[i].Command);
else
continue;
} }
// 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 // Bot will ignore commands which are ran more often than what specified by
// GlobalCommandsCooldown constant (miliseconds) // GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) 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."); //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.");
var commandName = cmd.Aliases.First(); 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)) 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); _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)) if (execResult.Exception != null && (!(execResult.Exception is HttpException he) || he.DiscordCode != 50013))
{ {
lock (errorLogLock) lock (errorLogLock)
@ -368,11 +413,8 @@ namespace NadekoBot.Services
_log.Warn(execResult.Exception); _log.Warn(execResult.Exception);
} }
} }
return (true, null, commands[i].Command);
}
return (false, null, null); return (true, null, cmd);
//return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload."));
} }
private readonly object errorLogLock = new object(); private readonly object errorLogLock = new object();

View File

@ -7,7 +7,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class CurrencyService public class CurrencyService : INService
{ {
private readonly BotConfig _config; private readonly BotConfig _config;
private readonly DbService _db; private readonly DbService _db;

View File

@ -14,7 +14,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Services.CustomReactions namespace NadekoBot.Services.CustomReactions
{ {
public class CustomReactionsService : IEarlyBlockingExecutor public class CustomReactionsService : IEarlyBlockingExecutor, INService
{ {
public CustomReaction[] GlobalReactions = new CustomReaction[] { }; public CustomReaction[] GlobalReactions = new CustomReaction[] { };
public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>(); public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();

View File

@ -65,7 +65,7 @@ Nadeko Support Server: https://discord.gg/nadekobot";
public List<StartupCommand> StartupCommands { get; set; } public List<StartupCommand> StartupCommands { get; set; }
public HashSet<BlockedCmdOrMdl> BlockedCommands { get; set; } public HashSet<BlockedCmdOrMdl> BlockedCommands { get; set; }
public HashSet<BlockedCmdOrMdl> BlockedModules { 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 string DefaultPrefix { get; set; } = ".";
public bool CustomReactionsStartWith { get; set; } = false; public bool CustomReactionsStartWith { get; set; } = false;
} }

View File

@ -6,6 +6,6 @@ namespace NadekoBot.Services.Database.Repositories
{ {
public interface IReminderRepository : IRepository<Reminder> public interface IReminderRepository : IRepository<Reminder>
{ {
IEnumerable<Reminder> GetIncludedReminders(List<long> guildIds); IEnumerable<Reminder> GetIncludedReminders(IEnumerable<long> guildIds);
} }
} }

View File

@ -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(); return _set.Where(x => guildIds.Contains((long)x.ServerId)).ToList();
} }

View File

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Games namespace NadekoBot.Services.Games
{ {
public class ChatterBotService : IEarlyBlockingExecutor public class ChatterBotService : IEarlyBlockingExecutor, INService
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly Logger _log; private readonly Logger _log;

View File

@ -15,7 +15,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Games namespace NadekoBot.Services.Games
{ {
public class GamesService public class GamesService : INService
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;

View File

@ -9,7 +9,7 @@ using NLog;
namespace NadekoBot.Services.Games namespace NadekoBot.Services.Games
{ {
public class PollService : IEarlyBlockingExecutor public class PollService : IEarlyBlockingExecutor, INService
{ {
public ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>(); public ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
private readonly Logger _log; private readonly Logger _log;

View File

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class GreetSettingsService public class GreetSettingsService : INService
{ {
private readonly DbService _db; private readonly DbService _db;

View File

@ -11,7 +11,7 @@ using NadekoBot.Attributes;
namespace NadekoBot.Services.Help namespace NadekoBot.Services.Help
{ {
public class HelpService : ILateExecutor public class HelpService : ILateExecutor, INService
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CommandHandler _ch; private readonly CommandHandler _ch;

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public interface IGoogleApiService public interface IGoogleApiService : INService
{ {
IEnumerable<string> Languages { get; } IEnumerable<string> Languages { get; }

View File

@ -3,7 +3,7 @@ using System.Collections.Immutable;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public interface IImagesService public interface IImagesService : INService
{ {
ImmutableArray<byte> Heads { get; } ImmutableArray<byte> Heads { get; }
ImmutableArray<byte> Tails { get; } ImmutableArray<byte> Tails { get; }

View 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
{
}
}

View File

@ -3,7 +3,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public interface IStatsService public interface IStatsService : INService
{ {
string Author { get; } string Author { get; }
long CommandsRan { get; } long CommandsRan { get; }

View File

@ -11,7 +11,7 @@ using System.Text.RegularExpressions;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class NadekoStrings public class NadekoStrings : INService
{ {
public const string stringsPath = @"_strings/"; public const string stringsPath = @"_strings/";

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Impl namespace NadekoBot.Services.Impl
{ {
public class SoundCloudApiService public class SoundCloudApiService : INService
{ {
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;

View 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();
}
}

View File

@ -15,7 +15,7 @@ using NadekoBot.Services.Impl;
namespace NadekoBot.Services.Music namespace NadekoBot.Services.Music
{ {
public class MusicService public class MusicService : INService
{ {
public const string MusicDataPath = "data/musicdata"; public const string MusicDataPath = "data/musicdata";

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class BlacklistService : IEarlyBlocker public class BlacklistService : IEarlyBlocker, INService
{ {
public ConcurrentHashSet<ulong> BlacklistedUsers { get; } public ConcurrentHashSet<ulong> BlacklistedUsers { get; }
public ConcurrentHashSet<ulong> BlacklistedGuilds { get; } public ConcurrentHashSet<ulong> BlacklistedGuilds { get; }

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class CmdCdService : ILateBlocker public class CmdCdService : ILateBlocker, INService
{ {
public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; } public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>(); public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();

View File

@ -12,7 +12,7 @@ using NLog;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class FilterService : IEarlyBlocker public class FilterService : IEarlyBlocker, INService
{ {
private readonly Logger _log; private readonly Logger _log;

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class GlobalPermissionService : ILateBlocker public class GlobalPermissionService : ILateBlocker, INService
{ {
public readonly ConcurrentHashSet<string> BlockedModules; public readonly ConcurrentHashSet<string> BlockedModules;
public readonly ConcurrentHashSet<string> BlockedCommands; public readonly ConcurrentHashSet<string> BlockedCommands;

View File

@ -11,14 +11,14 @@ using System.Threading.Tasks;
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services.Database;
using NadekoBot.Services; using NadekoBot.Services;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class PermissionService : ILateBlocker public class PermissionService : ILateBlocker, INService
{ {
private readonly DbService _db; private readonly DbService _db;
private readonly Logger _log;
private readonly CommandHandler _cmd; private readonly CommandHandler _cmd;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
@ -26,16 +26,15 @@ namespace NadekoBot.Services.Permissions
public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } = public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
new ConcurrentDictionary<ulong, PermissionCache>(); 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; _db = db;
_cmd = cmd; _cmd = cmd;
_strings = strings; _strings = strings;
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
if (client.ShardId == 0) if (client.ShardId == 0)
TryMigratePermissions(bc); TryMigratePermissions();
using (var uow = _db.UnitOfWork) 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) public PermissionCache GetCache(ulong guildId)
@ -71,12 +67,13 @@ namespace NadekoBot.Services.Permissions
return pc; return pc;
} }
private void TryMigratePermissions(BotConfig bc) private void TryMigratePermissions()
{
var log = LogManager.GetCurrentClassLogger();
if (bc.PermissionVersion <= 1)
{ {
using (var uow = _db.UnitOfWork) 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."); log.Info("Permission version is 1, upgrading to 2.");
var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
@ -134,10 +131,7 @@ namespace NadekoBot.Services.Permissions
bc.PermissionVersion = 2; bc.PermissionVersion = 2;
uow.Complete(); uow.Complete();
} }
}
if (bc.PermissionVersion <= 2) if (bc.PermissionVersion <= 2)
{
using (var uow = _db.UnitOfWork)
{ {
var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" }; var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" };
uow._context.Database.ExecuteSqlCommand( uow._context.Database.ExecuteSqlCommand(

View File

@ -6,7 +6,7 @@ using System.IO;
namespace NadekoBot.Services.Pokemon namespace NadekoBot.Services.Pokemon
{ {
public class PokemonService public class PokemonService : INService
{ {
public readonly List<PokemonType> PokemonTypes = new List<PokemonType>(); public readonly List<PokemonType> PokemonTypes = new List<PokemonType>();
public readonly ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>(); public readonly ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>();

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Searches namespace NadekoBot.Services.Searches
{ {
public class AnimeSearchService public class AnimeSearchService : INService
{ {
private readonly Logger _log; private readonly Logger _log;

View File

@ -15,7 +15,7 @@ using System.Xml;
namespace NadekoBot.Services.Searches namespace NadekoBot.Services.Searches
{ {
public class SearchesService public class SearchesService : INService
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;

View File

@ -15,7 +15,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Searches namespace NadekoBot.Services.Searches
{ {
public class StreamNotificationService public class StreamNotificationService : INService
{ {
private readonly Timer _streamCheckTimer; private readonly Timer _streamCheckTimer;
private bool firstStreamNotifPass { get; set; } = true; private bool firstStreamNotifPass { get; set; } = true;

View File

@ -3,6 +3,15 @@ using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; 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 namespace NadekoBot.Services
{ {
@ -16,8 +25,14 @@ namespace NadekoBot.Services
public class ServiceProviderBuilder public class ServiceProviderBuilder
{ {
private ConcurrentDictionary<Type, object> _dict = new ConcurrentDictionary<Type, object>(); 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); _dict.TryAdd(typeof(T), obj);
return this; return this;
@ -27,6 +42,61 @@ namespace NadekoBot.Services
{ {
return new NServiceProvider(_dict); 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; private readonly ImmutableDictionary<Type, object> _services;

View File

@ -10,7 +10,7 @@ using NadekoBot.Extensions;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
public class CommandMapService : IInputTransformer public class CommandMapService : IInputTransformer, INService
{ {
private readonly Logger _log; private readonly Logger _log;

View File

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
public class ConverterService public class ConverterService : INService
{ {
public List<ConvertUnit> Units { get; } = new List<ConvertUnit>(); public List<ConvertUnit> Units { get; } = new List<ConvertUnit>();
private readonly Logger _log; private readonly Logger _log;

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
//todo 50 rewrite //todo 50 rewrite
public class MessageRepeaterService public class MessageRepeaterService : INService
{ {
//messagerepeater //messagerepeater
//guildid/RepeatRunners //guildid/RepeatRunners
@ -19,8 +19,7 @@ namespace NadekoBot.Services.Utility
{ {
var _ = Task.Run(async () => var _ = Task.Run(async () =>
{ {
while (!bot.Ready) await bot.Ready.Task.ConfigureAwait(false);
await Task.Delay(1000);
Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(gcs Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(gcs
.ToDictionary(gc => gc.GuildId, .ToDictionary(gc => gc.GuildId,

View File

@ -9,13 +9,12 @@ using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
public class PatreonRewardsService public class PatreonRewardsService : INService
{ {
private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1);

View File

@ -4,9 +4,9 @@ using NadekoBot.DataStructures.Replacements;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Impl;
using NLog; using NLog;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Utility 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)?$", 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); RegexOptions.Compiled | RegexOptions.Multiline);
@ -29,7 +29,7 @@ namespace NadekoBot.Services.Utility
private readonly DbService _db; private readonly DbService _db;
public RemindService(DiscordSocketClient client, BotConfig config, DbService db, public RemindService(DiscordSocketClient client, BotConfig config, DbService db,
List<long> guilds, IUnitOfWork uow) StartingGuildsService guilds, IUnitOfWork uow)
{ {
_config = config; _config = config;
_client = client; _client = client;

View File

@ -12,7 +12,7 @@ using NLog;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
public class StreamRoleService public class StreamRoleService : INService
{ {
private readonly DbService _db; private readonly DbService _db;
private readonly ConcurrentDictionary<ulong, StreamRoleSettings> guildSettings; private readonly ConcurrentDictionary<ulong, StreamRoleSettings> guildSettings;
@ -26,6 +26,7 @@ namespace NadekoBot.Services.Utility
this._log = LogManager.GetCurrentClassLogger(); this._log = LogManager.GetCurrentClassLogger();
guildSettings = gcs.ToDictionary(x => x.GuildId, x => x.StreamRole) guildSettings = gcs.ToDictionary(x => x.GuildId, x => x.StreamRole)
.Where(x => x.Value.FromRoleId != 0 && x.Value.AddRoleId != 0)
.ToConcurrent(); .ToConcurrent();
client.GuildMemberUpdated += Client_GuildMemberUpdated; client.GuildMemberUpdated += Client_GuildMemberUpdated;

View File

@ -10,7 +10,7 @@ using System.Linq;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
public class VerboseErrorsService public class VerboseErrorsService : INService
{ {
private readonly ConcurrentHashSet<ulong> guildsEnabled; private readonly ConcurrentHashSet<ulong> guildsEnabled;
private readonly DbService _db; private readonly DbService _db;

View File

@ -3,7 +3,6 @@ using NadekoBot.Services;
using NadekoBot.Services.Impl; using NadekoBot.Services.Impl;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;