Administration almost done, logcommands left
This commit is contained in:
		@@ -3,9 +3,7 @@ using Discord.Commands;
 | 
			
		||||
using NadekoBot.Attributes;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using NadekoBot.Services.Administration;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
@@ -16,31 +14,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class AutoAssignRoleCommands : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            //guildid/roleid
 | 
			
		||||
            private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
            private readonly AutoAssignRoleService _service;
 | 
			
		||||
 | 
			
		||||
            static AutoAssignRoleCommands()
 | 
			
		||||
            public AutoAssignRoleCommands(AutoAssignRoleService service, DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                var log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
                AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
 | 
			
		||||
                    .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
 | 
			
		||||
                NadekoBot.Client.UserJoined += async (user) =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        AutoAssignedRoles.TryGetValue(user.Guild.Id, out ulong roleId);
 | 
			
		||||
 | 
			
		||||
                        if (roleId == 0)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
 | 
			
		||||
 | 
			
		||||
                        if (role != null)
 | 
			
		||||
                            await user.AddRoleAsync(role).ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex) { log.Warn(ex); }
 | 
			
		||||
                };
 | 
			
		||||
                _db = db;
 | 
			
		||||
                _service = service;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -53,18 +33,18 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    if (Context.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
			
		||||
                    if (role == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        conf.AutoAssignRoleId = 0;
 | 
			
		||||
                        AutoAssignedRoles.TryRemove(Context.Guild.Id, out ulong throwaway);
 | 
			
		||||
                        _service.AutoAssignedRoles.TryRemove(Context.Guild.Id, out ulong throwaway);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        conf.AutoAssignRoleId = role.Id;
 | 
			
		||||
                        AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
 | 
			
		||||
                        _service.AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,9 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.Commands;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Attributes;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NLog;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services.Administration;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Administration
 | 
			
		||||
{
 | 
			
		||||
@@ -22,64 +12,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class GameChannelCommands : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            //private static readonly Timer _t;
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
            private readonly GameVoiceChannelService _service;
 | 
			
		||||
 | 
			
		||||
            private static readonly ConcurrentHashSet<ulong> gameVoiceChannels = new ConcurrentHashSet<ulong>();
 | 
			
		||||
 | 
			
		||||
            private static new readonly Logger _log;
 | 
			
		||||
 | 
			
		||||
            static GameChannelCommands()
 | 
			
		||||
            public GameChannelCommands(GameVoiceChannelService service, DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                //_t = new Timer(_ => {
 | 
			
		||||
 | 
			
		||||
                //}, null, );
 | 
			
		||||
 | 
			
		||||
                _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
                gameVoiceChannels = new ConcurrentHashSet<ulong>(
 | 
			
		||||
                    NadekoBot.AllGuildConfigs.Where(gc => gc.GameVoiceChannel != null)
 | 
			
		||||
                                             .Select(gc => gc.GameVoiceChannel.Value));
 | 
			
		||||
 | 
			
		||||
                NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            private static Task Client_UserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState)
 | 
			
		||||
            {
 | 
			
		||||
                var _ = Task.Run(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        var gUser = usr as SocketGuildUser;
 | 
			
		||||
                        if (gUser == null)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var game = gUser.Game?.Name.TrimTo(50).ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
                        if (oldState.VoiceChannel == newState.VoiceChannel ||
 | 
			
		||||
                            newState.VoiceChannel == null)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        if (!gameVoiceChannels.Contains(newState.VoiceChannel.Id) ||
 | 
			
		||||
                            string.IsNullOrWhiteSpace(game))
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var vch = gUser.Guild.VoiceChannels
 | 
			
		||||
                            .FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
 | 
			
		||||
 | 
			
		||||
                        if (vch == null)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        await Task.Delay(1000).ConfigureAwait(false);
 | 
			
		||||
                        await gUser.ModifyAsync(gu => gu.Channel = vch).ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        _log.Warn(ex);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
                _db = db;
 | 
			
		||||
                _service = service;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -96,20 +35,20 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                ulong? id;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
			
		||||
 | 
			
		||||
                    if (gc.GameVoiceChannel == vch.Id)
 | 
			
		||||
                    {
 | 
			
		||||
                        gameVoiceChannels.TryRemove(vch.Id);
 | 
			
		||||
                        _service.GameVoiceChannels.TryRemove(vch.Id);
 | 
			
		||||
                        id = gc.GameVoiceChannel = null;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if(gc.GameVoiceChannel != null)
 | 
			
		||||
                            gameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value);
 | 
			
		||||
                        gameVoiceChannels.Add(vch.Id);
 | 
			
		||||
                            _service.GameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value);
 | 
			
		||||
                        _service.GameVoiceChannels.Add(vch.Id);
 | 
			
		||||
                        id = gc.GameVoiceChannel = vch.Id;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@@ -122,7 +61,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    gameVoiceChannels.Add(vch.Id);
 | 
			
		||||
                    _service.GameVoiceChannels.Add(vch.Id);
 | 
			
		||||
                    await ReplyConfirmLocalized("gvc_enabled", Format.Bold(vch.Name)).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        {
 | 
			
		||||
            //Română, România
 | 
			
		||||
            //Bahasa Indonesia, Indonesia
 | 
			
		||||
            private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
 | 
			
		||||
            private static ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
 | 
			
		||||
            {
 | 
			
		||||
                //{"ar", "العربية" },
 | 
			
		||||
                {"zh-TW", "繁體中文, 台灣" },
 | 
			
		||||
@@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            public async Task LanguageSet()
 | 
			
		||||
            {
 | 
			
		||||
                var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild);
 | 
			
		||||
                var cul = _localization.GetCultureInfo(Context.Guild);
 | 
			
		||||
                await ReplyConfirmLocalized("lang_set_show", Format.Bold(cul.ToString()), Format.Bold(cul.NativeName))
 | 
			
		||||
                    .ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
@@ -61,13 +61,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    CultureInfo ci;
 | 
			
		||||
                    if (name.Trim().ToLowerInvariant() == "default")
 | 
			
		||||
                    {
 | 
			
		||||
                        NadekoBot.Localization.RemoveGuildCulture(Context.Guild);
 | 
			
		||||
                        ci = NadekoBot.Localization.DefaultCultureInfo;
 | 
			
		||||
                        _localization.RemoveGuildCulture(Context.Guild);
 | 
			
		||||
                        ci = _localization.DefaultCultureInfo;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        ci = new CultureInfo(name);
 | 
			
		||||
                        NadekoBot.Localization.SetGuildCulture(Context.Guild, ci);
 | 
			
		||||
                        _localization.SetGuildCulture(Context.Guild, ci);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    await ReplyConfirmLocalized("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
 | 
			
		||||
@@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
            public async Task LanguageSetDefault()
 | 
			
		||||
            {
 | 
			
		||||
                var cul = NadekoBot.Localization.DefaultCultureInfo;
 | 
			
		||||
                var cul = _localization.DefaultCultureInfo;
 | 
			
		||||
                await ReplyConfirmLocalized("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -94,13 +94,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    CultureInfo ci;
 | 
			
		||||
                    if (name.Trim().ToLowerInvariant() == "default")
 | 
			
		||||
                    {
 | 
			
		||||
                        NadekoBot.Localization.ResetDefaultCulture();
 | 
			
		||||
                        ci = NadekoBot.Localization.DefaultCultureInfo;
 | 
			
		||||
                        _localization.ResetDefaultCulture();
 | 
			
		||||
                        ci = _localization.DefaultCultureInfo;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        ci = new CultureInfo(name);
 | 
			
		||||
                        NadekoBot.Localization.SetDefaultCulture(ci);
 | 
			
		||||
                        _localization.SetDefaultCulture(ci);
 | 
			
		||||
                    }
 | 
			
		||||
                    await ReplyConfirmLocalized("lang_set_bot", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -35,11 +35,11 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
            static LogCommands()
 | 
			
		||||
            {
 | 
			
		||||
                Client = NadekoBot.Client;
 | 
			
		||||
                Client = _client;
 | 
			
		||||
                _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
                var sw = Stopwatch.StartNew();
 | 
			
		||||
 | 
			
		||||
                GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
 | 
			
		||||
                GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(gcs
 | 
			
		||||
                    .ToDictionary(g => g.GuildId, g => g.LogSetting));
 | 
			
		||||
 | 
			
		||||
                _timerReference = new Timer(async (state) =>
 | 
			
		||||
@@ -136,7 +136,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                    //var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList();
 | 
			
		||||
                    //var guildsMemberOf = _client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList();
 | 
			
		||||
                    //foreach (var g in guildsMemberOf)
 | 
			
		||||
                    //{
 | 
			
		||||
                    //    LogSetting logSetting;
 | 
			
		||||
@@ -840,7 +840,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
            private static void UnsetLogSetting(ulong guildId, LogType logChannelType)
 | 
			
		||||
            {
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var newLogSetting = uow.GuildConfigs.LogSettingsFor(guildId).LogSetting;
 | 
			
		||||
                    switch (logChannelType)
 | 
			
		||||
@@ -910,7 +910,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            {
 | 
			
		||||
                var channel = (ITextChannel)Context.Channel;
 | 
			
		||||
                LogSetting logSetting;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting;
 | 
			
		||||
                    GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
 | 
			
		||||
@@ -946,7 +946,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            {
 | 
			
		||||
                var channel = (ITextChannel)Context.Channel;
 | 
			
		||||
                int removed;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var config = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id);
 | 
			
		||||
                    LogSetting logSetting = GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting);
 | 
			
		||||
@@ -986,7 +986,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            {
 | 
			
		||||
                var channel = (ITextChannel)Context.Channel;
 | 
			
		||||
                ulong? channelId = null;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting;
 | 
			
		||||
                    GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ using NadekoBot.Attributes;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using NLog;
 | 
			
		||||
using NadekoBot.Modules.Administration.Commands.Migration;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
@@ -23,12 +22,11 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        public class Migration : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            private const int CURRENT_VERSION = 1;
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
 | 
			
		||||
            private new static readonly Logger _log;
 | 
			
		||||
 | 
			
		||||
            static Migration()
 | 
			
		||||
            public Migration(DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
                _db = db;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -36,7 +34,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            public async Task MigrateData()
 | 
			
		||||
            {
 | 
			
		||||
                var version = 0;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    version = uow.BotConfig.GetOrCreate().MigrationVersion;
 | 
			
		||||
                }
 | 
			
		||||
@@ -62,7 +60,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
            private void Migrate0_9To1_0()
 | 
			
		||||
            {
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var botConfig = uow.BotConfig.GetOrCreate();
 | 
			
		||||
                    MigrateConfig0_9(uow, botConfig);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,9 @@
 | 
			
		||||
using Discord.Commands;
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Attributes;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Services.Administration;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Administration
 | 
			
		||||
@@ -18,101 +13,31 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class PlayingRotateCommands : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            public static List<PlayingStatus> RotatingStatusMessages { get; }
 | 
			
		||||
            public static volatile bool RotatingStatuses;
 | 
			
		||||
            private readonly object _locker = new object();
 | 
			
		||||
            private new static Logger _log { get; }
 | 
			
		||||
            private static readonly Timer _t;
 | 
			
		||||
            private static readonly object _locker = new object();
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
            private readonly PlayingRotateService _service;
 | 
			
		||||
 | 
			
		||||
            private class TimerState
 | 
			
		||||
            public PlayingRotateCommands(PlayingRotateService service, DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                public int Index { get; set; }
 | 
			
		||||
                _db = db;
 | 
			
		||||
                _service = service;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            static PlayingRotateCommands()
 | 
			
		||||
            {
 | 
			
		||||
                _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
                RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
 | 
			
		||||
                RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
 | 
			
		||||
 | 
			
		||||
                _t = new Timer(async (objState) =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        var state = (TimerState)objState;
 | 
			
		||||
                        if (!RotatingStatuses)
 | 
			
		||||
                            return;
 | 
			
		||||
                        if (state.Index >= RotatingStatusMessages.Count)
 | 
			
		||||
                            state.Index = 0;
 | 
			
		||||
 | 
			
		||||
                        if (!RotatingStatusMessages.Any())
 | 
			
		||||
                            return;
 | 
			
		||||
                        var status = RotatingStatusMessages[state.Index++].Status;
 | 
			
		||||
                        if (string.IsNullOrWhiteSpace(status))
 | 
			
		||||
                            return;
 | 
			
		||||
                        PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
 | 
			
		||||
                        var shards = NadekoBot.Client.Shards;
 | 
			
		||||
                        for (int i = 0; i < shards.Count; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            var curShard = shards.ElementAt(i);
 | 
			
		||||
                            ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
 | 
			
		||||
                            try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
 | 
			
		||||
                            catch (Exception ex)
 | 
			
		||||
                            {
 | 
			
		||||
                                _log.Warn(ex);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        _log.Warn("Rotating playing status errored.\n" + ex);
 | 
			
		||||
                    }
 | 
			
		||||
                }, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
 | 
			
		||||
                new Dictionary<string, Func<string>> {
 | 
			
		||||
                    { "%servers%", () => NadekoBot.Client.Guilds.Count.ToString()},
 | 
			
		||||
                    { "%users%", () => NadekoBot.Client.Guilds.Sum(s => s.Users.Count).ToString()},
 | 
			
		||||
                    { "%playing%", () => {
 | 
			
		||||
                            var cnt = NadekoBot.MusicService.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
 | 
			
		||||
                            if (cnt != 1) return cnt.ToString();
 | 
			
		||||
                            try {
 | 
			
		||||
                                var mp = NadekoBot.MusicService.MusicPlayers.FirstOrDefault();
 | 
			
		||||
                                return mp.Value.CurrentSong.SongInfo.Title;
 | 
			
		||||
                            }
 | 
			
		||||
                            catch {
 | 
			
		||||
                                return "No songs";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    { "%queued%", () => NadekoBot.MusicService.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
 | 
			
		||||
                    { "%time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
 | 
			
		||||
                    { "%shardcount%", () => NadekoBot.Client.Shards.Count.ToString() },
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            public static Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
 | 
			
		||||
                new Dictionary<string, Func<DiscordSocketClient, string>> {
 | 
			
		||||
                    { "%shardid%", (client) => client.ShardId.ToString()},
 | 
			
		||||
                    { "%shardguilds%", (client) => client.Guilds.Count.ToString()},
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
            [OwnerOnly]
 | 
			
		||||
            public async Task RotatePlaying()
 | 
			
		||||
            {
 | 
			
		||||
                lock (_locker)
 | 
			
		||||
                {
 | 
			
		||||
                    using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                    using (var uow = _db.UnitOfWork)
 | 
			
		||||
                    {
 | 
			
		||||
                        var config = uow.BotConfig.GetOrCreate();
 | 
			
		||||
 | 
			
		||||
                        RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
 | 
			
		||||
                        _service.RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
 | 
			
		||||
                        uow.Complete();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (RotatingStatuses)
 | 
			
		||||
                if (_service.RotatingStatuses)
 | 
			
		||||
                    await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false);
 | 
			
		||||
                else
 | 
			
		||||
                    await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false);
 | 
			
		||||
@@ -122,12 +47,12 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            [OwnerOnly]
 | 
			
		||||
            public async Task AddPlaying([Remainder] string status)
 | 
			
		||||
            {
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
			
		||||
                    var toAdd = new PlayingStatus { Status = status };
 | 
			
		||||
                    config.RotatingStatusMessages.Add(toAdd);
 | 
			
		||||
                    RotatingStatusMessages.Add(toAdd);
 | 
			
		||||
                    _service.RotatingStatusMessages.Add(toAdd);
 | 
			
		||||
                    await uow.CompleteAsync();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -138,13 +63,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            [OwnerOnly]
 | 
			
		||||
            public async Task ListPlaying()
 | 
			
		||||
            {
 | 
			
		||||
                if (!RotatingStatusMessages.Any())
 | 
			
		||||
                if (!_service.RotatingStatusMessages.Any())
 | 
			
		||||
                    await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false);
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    var i = 1;
 | 
			
		||||
                    await ReplyConfirmLocalized("ropl_list",
 | 
			
		||||
                            string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
 | 
			
		||||
                            string.Join("\n\t", _service.RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
 | 
			
		||||
                        .ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +82,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                index -= 1;
 | 
			
		||||
 | 
			
		||||
                string msg;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
			
		||||
 | 
			
		||||
@@ -165,7 +90,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                        return;
 | 
			
		||||
                    msg = config.RotatingStatusMessages[index].Status;
 | 
			
		||||
                    config.RotatingStatusMessages.RemoveAt(index);
 | 
			
		||||
                    RotatingStatusMessages.RemoveAt(index);
 | 
			
		||||
                    _service.RotatingStatusMessages.RemoveAt(index);
 | 
			
		||||
                    await uow.CompleteAsync();
 | 
			
		||||
                }
 | 
			
		||||
                await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,246 +5,27 @@ using NadekoBot.Attributes;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using NadekoBot.Services.Administration;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Administration
 | 
			
		||||
{
 | 
			
		||||
    public partial class Administration
 | 
			
		||||
    {
 | 
			
		||||
        public enum ProtectionType
 | 
			
		||||
        {
 | 
			
		||||
            Raiding,
 | 
			
		||||
            Spamming,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public class AntiRaidStats
 | 
			
		||||
        {
 | 
			
		||||
            public AntiRaidSetting AntiRaidSettings { get; set; }
 | 
			
		||||
            public int UsersCount { get; set; }
 | 
			
		||||
            public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public class AntiSpamStats
 | 
			
		||||
        {
 | 
			
		||||
            public AntiSpamSetting AntiSpamSettings { get; set; }
 | 
			
		||||
            public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
 | 
			
		||||
                = new ConcurrentDictionary<ulong, UserSpamStats>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public class UserSpamStats : IDisposable
 | 
			
		||||
        {
 | 
			
		||||
            public int Count => timers.Count;
 | 
			
		||||
            public string LastMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
            private ConcurrentQueue<Timer> timers { get; }
 | 
			
		||||
 | 
			
		||||
            public UserSpamStats(IUserMessage msg)
 | 
			
		||||
            {
 | 
			
		||||
                LastMessage = msg.Content.ToUpperInvariant();
 | 
			
		||||
                timers = new ConcurrentQueue<Timer>();
 | 
			
		||||
 | 
			
		||||
                ApplyNextMessage(msg);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            private readonly object applyLock = new object();
 | 
			
		||||
            public void ApplyNextMessage(IUserMessage message)
 | 
			
		||||
            {
 | 
			
		||||
                lock(applyLock){
 | 
			
		||||
                    var upperMsg = message.Content.ToUpperInvariant();
 | 
			
		||||
                    if (upperMsg != LastMessage || (string.IsNullOrWhiteSpace(upperMsg) && message.Attachments.Any()))
 | 
			
		||||
                    {
 | 
			
		||||
                        LastMessage = upperMsg;
 | 
			
		||||
                        //todo c#7
 | 
			
		||||
                        Timer old;
 | 
			
		||||
                        while(timers.TryDequeue(out old))
 | 
			
		||||
                            old.Change(Timeout.Infinite, Timeout.Infinite);
 | 
			
		||||
                    }    
 | 
			
		||||
                    var t = new Timer((_) => {
 | 
			
		||||
                        //todo c#7
 | 
			
		||||
                        Timer __;
 | 
			
		||||
                        if(timers.TryDequeue(out __))
 | 
			
		||||
                            __.Change(Timeout.Infinite, Timeout.Infinite);
 | 
			
		||||
                    }, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
 | 
			
		||||
                    timers.Enqueue(t);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Dispose()
 | 
			
		||||
            {
 | 
			
		||||
                //todo c#7
 | 
			
		||||
                Timer old;
 | 
			
		||||
                while(timers.TryDequeue(out old))
 | 
			
		||||
                    old.Change(Timeout.Infinite, Timeout.Infinite);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class ProtectionCommands : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            private static readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds =
 | 
			
		||||
                    new ConcurrentDictionary<ulong, AntiRaidStats>();
 | 
			
		||||
            // guildId | (userId|messages)
 | 
			
		||||
            private static readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds =
 | 
			
		||||
                    new ConcurrentDictionary<ulong, AntiSpamStats>();
 | 
			
		||||
            private readonly ProtectionService _service;
 | 
			
		||||
            private readonly MuteService _mute;
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
 | 
			
		||||
            private new static readonly Logger _log;
 | 
			
		||||
 | 
			
		||||
            static ProtectionCommands()
 | 
			
		||||
            public ProtectionCommands(ProtectionService service, MuteService mute, DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
                foreach (var gc in NadekoBot.AllGuildConfigs)
 | 
			
		||||
                {
 | 
			
		||||
                    var raid = gc.AntiRaidSetting;
 | 
			
		||||
                    var spam = gc.AntiSpamSetting;
 | 
			
		||||
 | 
			
		||||
                    if (raid != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
 | 
			
		||||
                        _antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (spam != null)
 | 
			
		||||
                        _antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                NadekoBot.Client.MessageReceived += (imsg) =>
 | 
			
		||||
                {
 | 
			
		||||
                    var msg = imsg as IUserMessage;
 | 
			
		||||
                    if (msg == null || msg.Author.IsBot)
 | 
			
		||||
                        return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
                    var channel = msg.Channel as ITextChannel;
 | 
			
		||||
                    if (channel == null)
 | 
			
		||||
                        return Task.CompletedTask;
 | 
			
		||||
                    var _ = Task.Run(async () =>
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            AntiSpamStats spamSettings;
 | 
			
		||||
                            if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
 | 
			
		||||
                                spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
 | 
			
		||||
                                {
 | 
			
		||||
                                    ChannelId = channel.Id
 | 
			
		||||
                                }))
 | 
			
		||||
                                return;
 | 
			
		||||
 | 
			
		||||
                            var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, (id) => new UserSpamStats(msg),
 | 
			
		||||
                                (id, old) =>
 | 
			
		||||
                                {
 | 
			
		||||
                                    old.ApplyNextMessage(msg); return old;
 | 
			
		||||
                                });
 | 
			
		||||
 | 
			
		||||
                            if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
 | 
			
		||||
                                {
 | 
			
		||||
                                    stats.Dispose();
 | 
			
		||||
                                    await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
 | 
			
		||||
                                        .ConfigureAwait(false);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        catch
 | 
			
		||||
                        {
 | 
			
		||||
                            // ignored
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                NadekoBot.Client.UserJoined += (usr) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (usr.IsBot)
 | 
			
		||||
                        return Task.CompletedTask;
 | 
			
		||||
                    AntiRaidStats settings;
 | 
			
		||||
                    if (!_antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
 | 
			
		||||
                        return Task.CompletedTask;
 | 
			
		||||
                    if (!settings.RaidUsers.Add(usr))
 | 
			
		||||
                        return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
                    var _ = Task.Run(async () =>
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            ++settings.UsersCount;
 | 
			
		||||
 | 
			
		||||
                            if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold)
 | 
			
		||||
                            {
 | 
			
		||||
                                var users = settings.RaidUsers.ToArray();
 | 
			
		||||
                                settings.RaidUsers.Clear();
 | 
			
		||||
 | 
			
		||||
                                await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                            await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                            settings.RaidUsers.TryRemove(usr);
 | 
			
		||||
                            --settings.UsersCount;
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        catch
 | 
			
		||||
                        {
 | 
			
		||||
                            // ignored
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus)
 | 
			
		||||
            {
 | 
			
		||||
                _log.Info($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild");
 | 
			
		||||
                foreach (var gu in gus)
 | 
			
		||||
                {
 | 
			
		||||
                    switch (action)
 | 
			
		||||
                    {
 | 
			
		||||
                        case PunishmentAction.Mute:
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                await MuteCommands.MuteUser(gu).ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case PunishmentAction.Kick:
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                await gu.KickAsync().ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case PunishmentAction.Softban:
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
 | 
			
		||||
                                try
 | 
			
		||||
                                {
 | 
			
		||||
                                    await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
 | 
			
		||||
                                }
 | 
			
		||||
                                catch
 | 
			
		||||
                                {
 | 
			
		||||
                                    await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
 | 
			
		||||
                                    // try it twice, really don't want to ban user if 
 | 
			
		||||
                                    // only kick has been specified as the punishement
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case PunishmentAction.Ban:
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false);
 | 
			
		||||
                _service = service;
 | 
			
		||||
                _mute = mute;
 | 
			
		||||
                _db = db;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            private string GetAntiSpamString(AntiSpamStats stats)
 | 
			
		||||
@@ -282,9 +63,9 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                AntiRaidStats throwaway;
 | 
			
		||||
                if (_antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
			
		||||
                if (_service.AntiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
			
		||||
                {
 | 
			
		||||
                    using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                    using (var uow = _db.UnitOfWork)
 | 
			
		||||
                    {
 | 
			
		||||
                        var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting));
 | 
			
		||||
 | 
			
		||||
@@ -297,7 +78,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false);
 | 
			
		||||
                    await _mute.GetMuteRole(Context.Guild).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
@@ -316,9 +97,9 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                _antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
			
		||||
                _service.AntiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
			
		||||
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting));
 | 
			
		||||
 | 
			
		||||
@@ -339,10 +120,10 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                AntiSpamStats throwaway;
 | 
			
		||||
                if (_antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
			
		||||
                if (_service.AntiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
			
		||||
                {
 | 
			
		||||
                    throwaway.UserStats.ForEach(x => x.Value.Dispose());
 | 
			
		||||
                    using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                    using (var uow = _db.UnitOfWork)
 | 
			
		||||
                    {
 | 
			
		||||
                        var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting)
 | 
			
		||||
                            .ThenInclude(x => x.IgnoredChannels));
 | 
			
		||||
@@ -356,7 +137,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false);
 | 
			
		||||
                    await _mute.GetMuteRole(Context.Guild).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
@@ -374,9 +155,9 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                _antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
			
		||||
                _service.AntiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
			
		||||
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting));
 | 
			
		||||
 | 
			
		||||
@@ -398,7 +179,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    ChannelId = channel.Id
 | 
			
		||||
                };
 | 
			
		||||
                bool added;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
 | 
			
		||||
                    var spam = gc.AntiSpamSetting;
 | 
			
		||||
@@ -410,7 +191,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    if (spam.IgnoredChannels.Add(obj))
 | 
			
		||||
                    {
 | 
			
		||||
                        AntiSpamStats temp;
 | 
			
		||||
                        if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
			
		||||
                        if (_service.AntiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
			
		||||
                            temp.AntiSpamSettings.IgnoredChannels.Add(obj);
 | 
			
		||||
                        added = true;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -418,7 +199,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    {
 | 
			
		||||
                        spam.IgnoredChannels.Remove(obj);
 | 
			
		||||
                        AntiSpamStats temp;
 | 
			
		||||
                        if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
			
		||||
                        if (_service.AntiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
			
		||||
                            temp.AntiSpamSettings.IgnoredChannels.Remove(obj);
 | 
			
		||||
                        added = false;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -437,10 +218,10 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            public async Task AntiList()
 | 
			
		||||
            {
 | 
			
		||||
                AntiSpamStats spam;
 | 
			
		||||
                _antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
 | 
			
		||||
                _service.AntiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
 | 
			
		||||
 | 
			
		||||
                AntiRaidStats raid;
 | 
			
		||||
                _antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
 | 
			
		||||
                _service.AntiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
 | 
			
		||||
 | 
			
		||||
                if (spam == null && raid == null)
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Attributes;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Services.Administration;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
@@ -21,85 +22,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class RatelimitCommands : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
 | 
			
		||||
            public static ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>();
 | 
			
		||||
            public static ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
 | 
			
		||||
            private readonly RatelimitService _service;
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
 | 
			
		||||
            private new static readonly Logger _log;
 | 
			
		||||
 | 
			
		||||
            public class Ratelimiter
 | 
			
		||||
            public RatelimitCommands(RatelimitService service, DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                public class RatelimitedUser
 | 
			
		||||
                {
 | 
			
		||||
                    public ulong UserId { get; set; }
 | 
			
		||||
                    public int MessageCount { get; set; } = 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                public ulong ChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
                public int MaxMessages { get; set; }
 | 
			
		||||
                public int PerSeconds { get; set; }
 | 
			
		||||
 | 
			
		||||
                public CancellationTokenSource CancelSource { get; set; } = new CancellationTokenSource();
 | 
			
		||||
 | 
			
		||||
                public ConcurrentDictionary<ulong, RatelimitedUser> Users { get; set; } = new ConcurrentDictionary<ulong, RatelimitedUser>();
 | 
			
		||||
 | 
			
		||||
                public bool CheckUserRatelimit(ulong id, ulong guildId, SocketGuildUser optUser)
 | 
			
		||||
                {
 | 
			
		||||
                    if ((IgnoredUsers.TryGetValue(guildId, out HashSet<ulong> ignoreUsers) && ignoreUsers.Contains(id)) ||
 | 
			
		||||
                        (optUser != null && IgnoredRoles.TryGetValue(guildId, out HashSet<ulong> ignoreRoles) && optUser.Roles.Any(x => ignoreRoles.Contains(x.Id))))
 | 
			
		||||
                        return false;
 | 
			
		||||
 | 
			
		||||
                    var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
 | 
			
		||||
                    if (usr.MessageCount >= MaxMessages)
 | 
			
		||||
                    {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                    usr.MessageCount++;
 | 
			
		||||
                    var _ = Task.Run(async () =>
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await Task.Delay(PerSeconds * 1000, CancelSource.Token);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (OperationCanceledException) { }
 | 
			
		||||
                        usr.MessageCount--;
 | 
			
		||||
                    });
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            static RatelimitCommands()
 | 
			
		||||
            {
 | 
			
		||||
                _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
                IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
 | 
			
		||||
                    NadekoBot.AllGuildConfigs
 | 
			
		||||
                             .ToDictionary(x => x.GuildId,
 | 
			
		||||
                                           x => new HashSet<ulong>(x.SlowmodeIgnoredRoles.Select(y => y.RoleId))));
 | 
			
		||||
 | 
			
		||||
                IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(
 | 
			
		||||
                    NadekoBot.AllGuildConfigs
 | 
			
		||||
                             .ToDictionary(x => x.GuildId,
 | 
			
		||||
                                           x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId))));
 | 
			
		||||
 | 
			
		||||
                NadekoBot.Client.MessageReceived += async (umsg) =>
 | 
			
		||||
                 {
 | 
			
		||||
                     try
 | 
			
		||||
                     {
 | 
			
		||||
                         var usrMsg = umsg as SocketUserMessage;
 | 
			
		||||
                         var channel = usrMsg?.Channel as SocketTextChannel;
 | 
			
		||||
 | 
			
		||||
                         if (channel == null || usrMsg.IsAuthor())
 | 
			
		||||
                             return;
 | 
			
		||||
                         if (!RatelimitingChannels.TryGetValue(channel.Id, out Ratelimiter limiter))
 | 
			
		||||
                             return;
 | 
			
		||||
 | 
			
		||||
                         if (limiter.CheckUserRatelimit(usrMsg.Author.Id, channel.Guild.Id, usrMsg.Author as SocketGuildUser))
 | 
			
		||||
                             await usrMsg.DeleteAsync();
 | 
			
		||||
                     }
 | 
			
		||||
                     catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
                 };
 | 
			
		||||
                _service = service;
 | 
			
		||||
                _db = db;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -107,7 +36,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
			
		||||
            public async Task Slowmode()
 | 
			
		||||
            {
 | 
			
		||||
                if (RatelimitingChannels.TryRemove(Context.Channel.Id, out Ratelimiter throwaway))
 | 
			
		||||
                if (_service.RatelimitingChannels.TryRemove(Context.Channel.Id, out Ratelimiter throwaway))
 | 
			
		||||
                {
 | 
			
		||||
                    throwaway.CancelSource.Cancel();
 | 
			
		||||
                    await ReplyConfirmLocalized("slowmode_disabled").ConfigureAwait(false);
 | 
			
		||||
@@ -126,13 +55,13 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    await ReplyErrorLocalized("invalid_params").ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                var toAdd = new Ratelimiter()
 | 
			
		||||
                var toAdd = new Ratelimiter(_service)
 | 
			
		||||
                {
 | 
			
		||||
                    ChannelId = Context.Channel.Id,
 | 
			
		||||
                    MaxMessages = msg,
 | 
			
		||||
                    PerSeconds = perSec,
 | 
			
		||||
                };
 | 
			
		||||
                if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
 | 
			
		||||
                if(_service.RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
 | 
			
		||||
                {
 | 
			
		||||
                    await Context.Channel.SendConfirmAsync(GetText("slowmode_init"),
 | 
			
		||||
                            GetText("slowmode_desc", Format.Bold(toAdd.MaxMessages.ToString()), Format.Bold(toAdd.PerSeconds.ToString())))
 | 
			
		||||
@@ -153,7 +82,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                HashSet<SlowmodeIgnoredUser> usrs;
 | 
			
		||||
                bool removed;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    usrs = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.SlowmodeIgnoredUsers))
 | 
			
		||||
                        .SlowmodeIgnoredUsers;
 | 
			
		||||
@@ -164,7 +93,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                IgnoredUsers.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(usrs.Select(x => x.UserId)), (key, old) => new HashSet<ulong>(usrs.Select(x => x.UserId)));
 | 
			
		||||
                _service.IgnoredUsers.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(usrs.Select(x => x.UserId)), (key, old) => new HashSet<ulong>(usrs.Select(x => x.UserId)));
 | 
			
		||||
 | 
			
		||||
                if(removed)
 | 
			
		||||
                    await ReplyConfirmLocalized("slowmodewl_user_stop", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
			
		||||
@@ -185,7 +114,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                HashSet<SlowmodeIgnoredRole> roles;
 | 
			
		||||
                bool removed;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    roles = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.SlowmodeIgnoredRoles))
 | 
			
		||||
                        .SlowmodeIgnoredRoles;
 | 
			
		||||
@@ -196,7 +125,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                IgnoredRoles.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(roles.Select(x => x.RoleId)), (key, old) => new HashSet<ulong>(roles.Select(x => x.RoleId)));
 | 
			
		||||
                _service.IgnoredRoles.AddOrUpdate(Context.Guild.Id, new HashSet<ulong>(roles.Select(x => x.RoleId)), (key, old) => new HashSet<ulong>(roles.Select(x => x.RoleId)));
 | 
			
		||||
 | 
			
		||||
                if (removed)
 | 
			
		||||
                    await ReplyConfirmLocalized("slowmodewl_role_stop", Format.Bold(role.ToString())).ConfigureAwait(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,14 +18,20 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class SelfAssignedRolesCommands : NadekoSubmodule
 | 
			
		||||
        {
 | 
			
		||||
            
 | 
			
		||||
            private readonly DbHandler _db;
 | 
			
		||||
 | 
			
		||||
            public SelfAssignedRolesCommands(DbHandler db)
 | 
			
		||||
            {
 | 
			
		||||
                _db = db;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
			
		||||
            public async Task AdSarm()
 | 
			
		||||
            {
 | 
			
		||||
                bool newval;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
			
		||||
                    newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages;
 | 
			
		||||
@@ -49,7 +55,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                string msg;
 | 
			
		||||
                var error = false;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
			
		||||
                    if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
 | 
			
		||||
@@ -84,7 +90,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                bool success;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    success = uow.SelfAssignedRoles.DeleteByGuildAndRoleId(role.Guild.Id, role.Id);
 | 
			
		||||
                    await uow.CompleteAsync();
 | 
			
		||||
@@ -105,7 +111,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
                var removeMsg = new StringBuilder();
 | 
			
		||||
                var msg = new StringBuilder();
 | 
			
		||||
                var roleCnt = 0;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var roleModels = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id).ToList();
 | 
			
		||||
                    msg.AppendLine();
 | 
			
		||||
@@ -139,7 +145,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            public async Task Tesar()
 | 
			
		||||
            {
 | 
			
		||||
                bool areExclusive;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +166,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                GuildConfig conf;
 | 
			
		||||
                SelfAssignedRole[] roles;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
			
		||||
                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id).ToArray();
 | 
			
		||||
@@ -220,7 +226,7 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
 | 
			
		||||
                bool autoDeleteSelfAssignedRoleMessages;
 | 
			
		||||
                IEnumerable<SelfAssignedRole> roles;
 | 
			
		||||
                using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
                using (var uow = _db.UnitOfWork)
 | 
			
		||||
                {
 | 
			
		||||
                    autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
 | 
			
		||||
                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@ namespace NadekoBot.Modules.Administration
 | 
			
		||||
            public UserPunishCommands(DbHandler db, MuteService muteService)
 | 
			
		||||
            {
 | 
			
		||||
                _db = db;
 | 
			
		||||
 | 
			
		||||
                _muteService = muteService;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,38 +7,26 @@ using NadekoBot.Services.Database.Models;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Discord;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using NadekoBot.Services.Pokemon;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
{
 | 
			
		||||
    [NadekoModule("Pokemon", ">")]
 | 
			
		||||
    public class Pokemon : NadekoTopLevelModule
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>();
 | 
			
		||||
        private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>();
 | 
			
		||||
        
 | 
			
		||||
        public const string PokemonTypesFile = "data/pokemon_types.json";
 | 
			
		||||
        private readonly PokemonService _service;
 | 
			
		||||
        private readonly DbHandler _db;
 | 
			
		||||
        private readonly BotConfig _bc;
 | 
			
		||||
        private readonly CurrencyHandler _ch;
 | 
			
		||||
 | 
			
		||||
        private new static Logger _log { get; }
 | 
			
		||||
 | 
			
		||||
        static Pokemon()
 | 
			
		||||
        public Pokemon(PokemonService pokemonService, DbHandler db, BotConfig bc, CurrencyHandler ch)
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            if (File.Exists(PokemonTypesFile))
 | 
			
		||||
            {
 | 
			
		||||
                _pokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _log.Warn(PokemonTypesFile + " is missing. Pokemon types not loaded.");
 | 
			
		||||
            }
 | 
			
		||||
            _service = pokemonService;
 | 
			
		||||
            _db = db;
 | 
			
		||||
            _bc = bc;
 | 
			
		||||
            _ch = ch;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private int GetDamage(PokemonType usertype, PokemonType targetType)
 | 
			
		||||
        {
 | 
			
		||||
            var rng = new Random();
 | 
			
		||||
@@ -50,14 +38,13 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return damage;
 | 
			
		||||
        }
 | 
			
		||||
            
 | 
			
		||||
        }            
 | 
			
		||||
 | 
			
		||||
        private static PokemonType GetPokeType(ulong id)
 | 
			
		||||
        private PokemonType GetPokeType(ulong id)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            Dictionary<ulong, string> setTypes;
 | 
			
		||||
            using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
            using (var uow = _db.UnitOfWork)
 | 
			
		||||
            {
 | 
			
		||||
                setTypes = uow.PokeGame.GetAll().ToDictionary(x => x.UserId, y => y.type);
 | 
			
		||||
            }
 | 
			
		||||
@@ -66,17 +53,17 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
            {
 | 
			
		||||
                return StringToPokemonType(setTypes[id]);
 | 
			
		||||
            }
 | 
			
		||||
            var count = _pokemonTypes.Count;
 | 
			
		||||
            var count = _service.PokemonTypes.Count;
 | 
			
		||||
 | 
			
		||||
            var remainder = Math.Abs((int)(id % (ulong)count));
 | 
			
		||||
 | 
			
		||||
            return _pokemonTypes[remainder];
 | 
			
		||||
            return _service.PokemonTypes[remainder];
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private static PokemonType StringToPokemonType(string v)
 | 
			
		||||
        private PokemonType StringToPokemonType(string v)
 | 
			
		||||
        {
 | 
			
		||||
            var str = v?.ToUpperInvariant();
 | 
			
		||||
            var list = _pokemonTypes;
 | 
			
		||||
            var list = _service.PokemonTypes;
 | 
			
		||||
            foreach (var p in list)
 | 
			
		||||
            {
 | 
			
		||||
                if (str == p.Name)
 | 
			
		||||
@@ -111,7 +98,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
                   
 | 
			
		||||
            // Checking stats first, then move
 | 
			
		||||
            //Set up the userstats
 | 
			
		||||
            var userStats = _stats.GetOrAdd(user.Id, new PokeStats());
 | 
			
		||||
            var userStats = _service.Stats.GetOrAdd(user.Id, new PokeStats());
 | 
			
		||||
 | 
			
		||||
            //Check if able to move
 | 
			
		||||
            //User not able if HP < 0, has made more than 4 attacks
 | 
			
		||||
@@ -131,7 +118,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            //get target stats
 | 
			
		||||
            var targetStats = _stats.GetOrAdd(targetUser.Id, new PokeStats());
 | 
			
		||||
            var targetStats = _service.Stats.GetOrAdd(targetUser.Id, new PokeStats());
 | 
			
		||||
 | 
			
		||||
            //If target's HP is below 0, no use attacking
 | 
			
		||||
            if (targetStats.Hp <= 0)
 | 
			
		||||
@@ -195,8 +182,8 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
 | 
			
		||||
            //update dictionary
 | 
			
		||||
            //This can stay the same right?
 | 
			
		||||
            _stats[user.Id] = userStats;
 | 
			
		||||
            _stats[targetUser.Id] = targetStats;
 | 
			
		||||
            _service.Stats[user.Id] = userStats;
 | 
			
		||||
            _service.Stats[targetUser.Id] = targetStats;
 | 
			
		||||
 | 
			
		||||
            await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
@@ -228,9 +215,9 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (_stats.ContainsKey(targetUser.Id))
 | 
			
		||||
            if (_service.Stats.ContainsKey(targetUser.Id))
 | 
			
		||||
            {
 | 
			
		||||
                var targetStats = _stats[targetUser.Id];
 | 
			
		||||
                var targetStats = _service.Stats[targetUser.Id];
 | 
			
		||||
                if (targetStats.Hp == targetStats.MaxHp)
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false);
 | 
			
		||||
@@ -242,9 +229,9 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
                var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
 | 
			
		||||
                if (amount > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
 | 
			
		||||
                    if (!await _ch.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
 | 
			
		||||
                    {
 | 
			
		||||
                        await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                        await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -254,16 +241,16 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
                if (targetStats.Hp < 0)
 | 
			
		||||
                {
 | 
			
		||||
                    //Could heal only for half HP?
 | 
			
		||||
                    _stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
 | 
			
		||||
                    _service.Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
 | 
			
		||||
                    if (target == "yourself")
 | 
			
		||||
                    {
 | 
			
		||||
                        await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                        await ReplyConfirmLocalized("revive_yourself", _bc.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                    await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), _bc.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), _bc.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
@@ -291,7 +278,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
            var targetType = StringToPokemonType(typeTargeted);
 | 
			
		||||
            if (targetType == null)
 | 
			
		||||
            {
 | 
			
		||||
                await Context.Channel.EmbedAsync(_pokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), 
 | 
			
		||||
                await Context.Channel.EmbedAsync(_service.PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), 
 | 
			
		||||
                        (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
 | 
			
		||||
                                                          .WithValue(pt.Icon)
 | 
			
		||||
                                                          .WithIsInline(true)))
 | 
			
		||||
@@ -308,16 +295,16 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
            var amount = 1;
 | 
			
		||||
            if (amount > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
 | 
			
		||||
                if (!await _ch.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                    await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Actually changing the type here
 | 
			
		||||
 | 
			
		||||
            using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
            using (var uow = _db.UnitOfWork)
 | 
			
		||||
            {
 | 
			
		||||
                var pokeUsers = uow.PokeGame.GetAll().ToArray();
 | 
			
		||||
                var setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
 | 
			
		||||
@@ -344,7 +331,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
            //Now for the response
 | 
			
		||||
            await ReplyConfirmLocalized("settype_success", 
 | 
			
		||||
                targetType, 
 | 
			
		||||
                NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
                _bc.CurrencySign).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -97,8 +97,12 @@ namespace NadekoBot
 | 
			
		||||
            var soundcloud = new SoundCloudApiService(credentials);
 | 
			
		||||
 | 
			
		||||
            //module services
 | 
			
		||||
            //todo 90 - Make this automatic
 | 
			
		||||
            var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db);
 | 
			
		||||
#region Searches
 | 
			
		||||
            var searchesService = new SearchesService(Client, google, db);
 | 
			
		||||
            var streamNotificationService = new StreamNotificationService(db, Client, strings);
 | 
			
		||||
#endregion
 | 
			
		||||
            var clashService = new ClashOfClansService(Client, db, localization, strings);
 | 
			
		||||
            var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials);
 | 
			
		||||
            var crService = new CustomReactionsService(db, Client);
 | 
			
		||||
@@ -108,11 +112,16 @@ namespace NadekoBot
 | 
			
		||||
            var selfService = new SelfService(this, commandHandler, db, BotConfig);
 | 
			
		||||
            var vcRoleService = new VcRoleService(Client, AllGuildConfigs);
 | 
			
		||||
            var vPlusTService = new VplusTService(Client, AllGuildConfigs, strings, db);
 | 
			
		||||
            var muteService = new MuteService(Client, AllGuildConfigs, db);
 | 
			
		||||
            var ratelimitService = new RatelimitService(Client, AllGuildConfigs);
 | 
			
		||||
            var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService);
 | 
			
		||||
            var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService);
 | 
			
		||||
            var gameVcService = new GameVoiceChannelService(Client, db, AllGuildConfigs);
 | 
			
		||||
            var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs);
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //initialize Services
 | 
			
		||||
            Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
 | 
			
		||||
            Services = new NServiceProvider.ServiceProviderBuilder()
 | 
			
		||||
                .Add<ILocalization>(localization)
 | 
			
		||||
                .Add<IStatsService>(stats)
 | 
			
		||||
                .Add<IImagesService>(images)
 | 
			
		||||
@@ -129,14 +138,22 @@ namespace NadekoBot
 | 
			
		||||
                //modules
 | 
			
		||||
                .Add<UtilityService>(utilityService)
 | 
			
		||||
                .Add<SearchesService>(searchesService)
 | 
			
		||||
                    .Add(streamNotificationService)
 | 
			
		||||
                .Add<ClashOfClansService>(clashService)
 | 
			
		||||
                .Add<MusicService>(musicService)
 | 
			
		||||
                .Add<GreetSettingsService>(greetSettingsService)
 | 
			
		||||
                .Add<CustomReactionsService>(crService)
 | 
			
		||||
                .Add<GamesService>(gamesService)
 | 
			
		||||
                .Add(selfService)
 | 
			
		||||
                .Add(vcRoleService)
 | 
			
		||||
                .Add(vPlusTService)
 | 
			
		||||
                .Add<AdministrationService>(administrationService)
 | 
			
		||||
                    .Add(selfService)
 | 
			
		||||
                    .Add(vcRoleService)
 | 
			
		||||
                    .Add(vPlusTService)
 | 
			
		||||
                    .Add(muteService)
 | 
			
		||||
                    .Add(ratelimitService)
 | 
			
		||||
                    .Add(playingRotateService)
 | 
			
		||||
                    .Add(gameVcService)
 | 
			
		||||
                    .Add(autoAssignRoleService)
 | 
			
		||||
                    .Add(protectionService)
 | 
			
		||||
                .Build();
 | 
			
		||||
 | 
			
		||||
            commandHandler.AddServices(Services);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,17 @@
 | 
			
		||||
    <None Remove="Modules\Permissions\**" />
 | 
			
		||||
    <Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Administration.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\AutoAssignRoleCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\GameChannelCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\LocalizationCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\Migration.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\Migration\0_9..cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\Migration\MigrationException.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\MuteCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\PlayingRotateCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\ProtectionCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\RatelimitCommand.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\SelfAssignedRolesCommand.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\SelfCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\ServerGreetCommands.cs" />
 | 
			
		||||
    <Compile Include="Modules\Administration\Commands\UserPunishCommands.cs" />
 | 
			
		||||
@@ -61,7 +71,7 @@
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="AngleSharp" Version="0.9.9" />
 | 
			
		||||
    <PackageReference Include="Discord.Net" Version="1.0.0-rc3-00743" />
 | 
			
		||||
    <PackageReference Include="Discord.Net" Version="1.0.0-rc3-00746" />
 | 
			
		||||
    <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" />
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,46 @@
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Administration
 | 
			
		||||
{
 | 
			
		||||
    public class AutoAssignRoleService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
        private readonly DiscordShardedClient _client;
 | 
			
		||||
 | 
			
		||||
        //guildid/roleid
 | 
			
		||||
        public ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
 | 
			
		||||
 | 
			
		||||
        public AutoAssignRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            _client = client;
 | 
			
		||||
 | 
			
		||||
            AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(
 | 
			
		||||
                gcs.Where(x => x.AutoAssignRoleId != 0)
 | 
			
		||||
                    .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
 | 
			
		||||
 | 
			
		||||
            _client.UserJoined += async (user) =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    AutoAssignedRoles.TryGetValue(user.Guild.Id, out ulong roleId);
 | 
			
		||||
 | 
			
		||||
                    if (roleId == 0)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
 | 
			
		||||
 | 
			
		||||
                    if (role != null)
 | 
			
		||||
                        await user.AddRoleAsync(role).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,73 @@
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Administration
 | 
			
		||||
{
 | 
			
		||||
    public class GameVoiceChannelService
 | 
			
		||||
    {
 | 
			
		||||
        public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>();
 | 
			
		||||
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
        private readonly DbHandler _db;
 | 
			
		||||
        private readonly DiscordShardedClient _client;
 | 
			
		||||
 | 
			
		||||
        public GameVoiceChannelService(DiscordShardedClient client, DbHandler db, IEnumerable<GuildConfig> gcs)
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            _db = db;
 | 
			
		||||
            _client = client;
 | 
			
		||||
 | 
			
		||||
            GameVoiceChannels = new ConcurrentHashSet<ulong>(
 | 
			
		||||
                gcs.Where(gc => gc.GameVoiceChannel != null)
 | 
			
		||||
                                         .Select(gc => gc.GameVoiceChannel.Value));
 | 
			
		||||
 | 
			
		||||
            _client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Task Client_UserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState)
 | 
			
		||||
        {
 | 
			
		||||
            var _ = Task.Run(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var gUser = usr as SocketGuildUser;
 | 
			
		||||
                    if (gUser == null)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    var game = gUser.Game?.Name.TrimTo(50).ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
                    if (oldState.VoiceChannel == newState.VoiceChannel ||
 | 
			
		||||
                        newState.VoiceChannel == null)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    if (!GameVoiceChannels.Contains(newState.VoiceChannel.Id) ||
 | 
			
		||||
                        string.IsNullOrWhiteSpace(game))
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    var vch = gUser.Guild.VoiceChannels
 | 
			
		||||
                        .FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
 | 
			
		||||
 | 
			
		||||
                    if (vch == null)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    await Task.Delay(1000).ConfigureAwait(false);
 | 
			
		||||
                    await gUser.ModifyAsync(gu => gu.Channel = vch).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    _log.Warn(ex);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								src/NadekoBot/Services/Administration/PlayingRotateService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/NadekoBot/Services/Administration/PlayingRotateService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NadekoBot.Services.Music;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Administration
 | 
			
		||||
{
 | 
			
		||||
    //todo 99 - Could make a placeholder service, which can work for any module
 | 
			
		||||
    //and have replacements which are dependent on the types provided in the constructor
 | 
			
		||||
    public class PlayingRotateService
 | 
			
		||||
    {
 | 
			
		||||
        public List<PlayingStatus> RotatingStatusMessages { get; }
 | 
			
		||||
        public volatile bool RotatingStatuses;
 | 
			
		||||
        private readonly Timer _t;
 | 
			
		||||
        private readonly DiscordShardedClient _client;
 | 
			
		||||
        private readonly BotConfig _bc;
 | 
			
		||||
        private readonly MusicService _music;
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
 | 
			
		||||
        private class TimerState
 | 
			
		||||
        {
 | 
			
		||||
            public int Index { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public PlayingRotateService(DiscordShardedClient client, BotConfig bc, MusicService music)
 | 
			
		||||
        {
 | 
			
		||||
            _client = client;
 | 
			
		||||
            _bc = bc;
 | 
			
		||||
            _music = music;
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
            RotatingStatusMessages = _bc.RotatingStatusMessages;
 | 
			
		||||
            RotatingStatuses = _bc.RotatingStatuses;
 | 
			
		||||
 | 
			
		||||
            _t = new Timer(async (objState) =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var state = (TimerState)objState;
 | 
			
		||||
                    if (!RotatingStatuses)
 | 
			
		||||
                        return;
 | 
			
		||||
                    if (state.Index >= RotatingStatusMessages.Count)
 | 
			
		||||
                        state.Index = 0;
 | 
			
		||||
 | 
			
		||||
                    if (!RotatingStatusMessages.Any())
 | 
			
		||||
                        return;
 | 
			
		||||
                    var status = RotatingStatusMessages[state.Index++].Status;
 | 
			
		||||
                    if (string.IsNullOrWhiteSpace(status))
 | 
			
		||||
                        return;
 | 
			
		||||
                    PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(_client,_music)));
 | 
			
		||||
                    var shards = _client.Shards;
 | 
			
		||||
                    for (int i = 0; i < shards.Count; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        var curShard = shards.ElementAt(i);
 | 
			
		||||
                        ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
 | 
			
		||||
                        try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
 | 
			
		||||
                        catch (Exception ex)
 | 
			
		||||
                        {
 | 
			
		||||
                            _log.Warn(ex);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    _log.Warn("Rotating playing status errored.\n" + ex);
 | 
			
		||||
                }
 | 
			
		||||
            }, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Dictionary<string, Func<DiscordShardedClient, MusicService, string>> PlayingPlaceholders { get; } =
 | 
			
		||||
            new Dictionary<string, Func<DiscordShardedClient, MusicService, string>> {
 | 
			
		||||
                    { "%servers%", (c, ms) => c.Guilds.Count.ToString()},
 | 
			
		||||
                    { "%users%", (c, ms) => c.Guilds.Sum(s => s.Users.Count).ToString()},
 | 
			
		||||
                    { "%playing%", (c, ms) => {
 | 
			
		||||
                            var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
 | 
			
		||||
                            if (cnt != 1) return cnt.ToString();
 | 
			
		||||
                            try {
 | 
			
		||||
                                var mp = ms.MusicPlayers.FirstOrDefault();
 | 
			
		||||
                                return mp.Value.CurrentSong.SongInfo.Title;
 | 
			
		||||
                            }
 | 
			
		||||
                            catch {
 | 
			
		||||
                                return "No songs";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    { "%queued%", (c, ms) => ms.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
 | 
			
		||||
                    { "%time%", (c, ms) => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
 | 
			
		||||
                    { "%shardcount%", (c, ms) => c.Shards.Count.ToString() },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
 | 
			
		||||
            new Dictionary<string, Func<DiscordSocketClient, string>> {
 | 
			
		||||
                    { "%shardid%", (client) => client.ShardId.ToString()},
 | 
			
		||||
                    { "%shardguilds%", (client) => client.Guilds.Count.ToString()},
 | 
			
		||||
            };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										182
									
								
								src/NadekoBot/Services/Administration/ProtectionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/NadekoBot/Services/Administration/ProtectionService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Administration
 | 
			
		||||
{
 | 
			
		||||
    public class ProtectionService
 | 
			
		||||
    {
 | 
			
		||||
        public readonly ConcurrentDictionary<ulong, AntiRaidStats> AntiRaidGuilds =
 | 
			
		||||
                new ConcurrentDictionary<ulong, AntiRaidStats>();
 | 
			
		||||
        // guildId | (userId|messages)
 | 
			
		||||
        public readonly ConcurrentDictionary<ulong, AntiSpamStats> AntiSpamGuilds =
 | 
			
		||||
                new ConcurrentDictionary<ulong, AntiSpamStats>();
 | 
			
		||||
 | 
			
		||||
        //todo sub LogCommands to this
 | 
			
		||||
        public event Func<IEnumerable<IGuildUser>, PunishmentAction, ProtectionType, Task> OnAntiProtectionTriggered = delegate { return Task.CompletedTask; };
 | 
			
		||||
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
        private readonly DiscordShardedClient _client;
 | 
			
		||||
        private readonly MuteService _mute;
 | 
			
		||||
 | 
			
		||||
        public ProtectionService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, MuteService mute)
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            _client = client;
 | 
			
		||||
            _mute = mute;
 | 
			
		||||
 | 
			
		||||
            foreach (var gc in gcs)
 | 
			
		||||
            {
 | 
			
		||||
                var raid = gc.AntiRaidSetting;
 | 
			
		||||
                var spam = gc.AntiSpamSetting;
 | 
			
		||||
 | 
			
		||||
                if (raid != null)
 | 
			
		||||
                {
 | 
			
		||||
                    var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
 | 
			
		||||
                    AntiRaidGuilds.TryAdd(gc.GuildId, raidStats);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (spam != null)
 | 
			
		||||
                    AntiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _client.MessageReceived += (imsg) =>
 | 
			
		||||
            {
 | 
			
		||||
                var msg = imsg as IUserMessage;
 | 
			
		||||
                if (msg == null || msg.Author.IsBot)
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
                var channel = msg.Channel as ITextChannel;
 | 
			
		||||
                if (channel == null)
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
                var _ = Task.Run(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!AntiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings) ||
 | 
			
		||||
                            spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
 | 
			
		||||
                            {
 | 
			
		||||
                                ChannelId = channel.Id
 | 
			
		||||
                            }))
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, (id) => new UserSpamStats(msg),
 | 
			
		||||
                            (id, old) =>
 | 
			
		||||
                            {
 | 
			
		||||
                                old.ApplyNextMessage(msg); return old;
 | 
			
		||||
                            });
 | 
			
		||||
 | 
			
		||||
                        if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
 | 
			
		||||
                            {
 | 
			
		||||
                                stats.Dispose();
 | 
			
		||||
                                await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
 | 
			
		||||
                                    .ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                        // ignored
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            _client.UserJoined += (usr) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (usr.IsBot)
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
                if (!AntiRaidGuilds.TryGetValue(usr.Guild.Id, out var settings))
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
                if (!settings.RaidUsers.Add(usr))
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
                var _ = Task.Run(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        ++settings.UsersCount;
 | 
			
		||||
 | 
			
		||||
                        if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold)
 | 
			
		||||
                        {
 | 
			
		||||
                            var users = settings.RaidUsers.ToArray();
 | 
			
		||||
                            settings.RaidUsers.Clear();
 | 
			
		||||
 | 
			
		||||
                            await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
 | 
			
		||||
                        }
 | 
			
		||||
                        await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                        settings.RaidUsers.TryRemove(usr);
 | 
			
		||||
                        --settings.UsersCount;
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                        // ignored
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus)
 | 
			
		||||
        {
 | 
			
		||||
            _log.Info($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild");
 | 
			
		||||
            foreach (var gu in gus)
 | 
			
		||||
            {
 | 
			
		||||
                switch (action)
 | 
			
		||||
                {
 | 
			
		||||
                    case PunishmentAction.Mute:
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await _mute.MuteUser(gu).ConfigureAwait(false);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PunishmentAction.Kick:
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await gu.KickAsync().ConfigureAwait(false);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PunishmentAction.Softban:
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                            catch
 | 
			
		||||
                            {
 | 
			
		||||
                                await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
 | 
			
		||||
                                // try it twice, really don't want to ban user if 
 | 
			
		||||
                                // only kick has been specified as the punishement
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PunishmentAction.Ban:
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            await OnAntiProtectionTriggered(gus, action, pt).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/NadekoBot/Services/Administration/ProtectionStats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/NadekoBot/Services/Administration/ProtectionStats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Administration
 | 
			
		||||
{
 | 
			
		||||
    public enum ProtectionType
 | 
			
		||||
    {
 | 
			
		||||
        Raiding,
 | 
			
		||||
        Spamming,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class AntiRaidStats
 | 
			
		||||
    {
 | 
			
		||||
        public AntiRaidSetting AntiRaidSettings { get; set; }
 | 
			
		||||
        public int UsersCount { get; set; }
 | 
			
		||||
        public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class AntiSpamStats
 | 
			
		||||
    {
 | 
			
		||||
        public AntiSpamSetting AntiSpamSettings { get; set; }
 | 
			
		||||
        public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
 | 
			
		||||
            = new ConcurrentDictionary<ulong, UserSpamStats>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								src/NadekoBot/Services/Administration/RatelimitService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/NadekoBot/Services/Administration/RatelimitService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Administration
 | 
			
		||||
{
 | 
			
		||||
    public class RatelimitService
 | 
			
		||||
    {
 | 
			
		||||
        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>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
 | 
			
		||||
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
        private readonly DiscordShardedClient _client;
 | 
			
		||||
 | 
			
		||||
        public RatelimitService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            _client = client;
 | 
			
		||||
 | 
			
		||||
            IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
 | 
			
		||||
                gcs.ToDictionary(x => x.GuildId,
 | 
			
		||||
                                 x => new HashSet<ulong>(x.SlowmodeIgnoredRoles.Select(y => y.RoleId))));
 | 
			
		||||
 | 
			
		||||
            IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(
 | 
			
		||||
                gcs.ToDictionary(x => x.GuildId,
 | 
			
		||||
                                 x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId))));
 | 
			
		||||
 | 
			
		||||
            _client.MessageReceived += async (umsg) =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var usrMsg = umsg as SocketUserMessage;
 | 
			
		||||
                    var channel = usrMsg?.Channel as SocketTextChannel;
 | 
			
		||||
 | 
			
		||||
                    if (channel == null || usrMsg == null || usrMsg.IsAuthor(client))
 | 
			
		||||
                        return;
 | 
			
		||||
                    if (!RatelimitingChannels.TryGetValue(channel.Id, out Ratelimiter limiter))
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    if (limiter.CheckUserRatelimit(usrMsg.Author.Id, channel.Guild.Id, usrMsg.Author as SocketGuildUser))
 | 
			
		||||
                        await usrMsg.DeleteAsync();
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex) { _log.Warn(ex); }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								src/NadekoBot/Services/Administration/Ratelimiter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/NadekoBot/Services/Administration/Ratelimiter.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
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.Administration
 | 
			
		||||
{
 | 
			
		||||
    public class Ratelimiter
 | 
			
		||||
    {
 | 
			
		||||
        private readonly RatelimitService _svc;
 | 
			
		||||
 | 
			
		||||
        public class RatelimitedUser
 | 
			
		||||
        {
 | 
			
		||||
            public ulong UserId { get; set; }
 | 
			
		||||
            public int MessageCount { get; set; } = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ulong ChannelId { get; set; }
 | 
			
		||||
        public int MaxMessages { get; set; }
 | 
			
		||||
        public int PerSeconds { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Ratelimiter(RatelimitService svc)
 | 
			
		||||
        {
 | 
			
		||||
            _svc = svc;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CancellationTokenSource CancelSource { get; set; } = new CancellationTokenSource();
 | 
			
		||||
 | 
			
		||||
        public ConcurrentDictionary<ulong, RatelimitedUser> Users { get; set; } = new ConcurrentDictionary<ulong, RatelimitedUser>();
 | 
			
		||||
 | 
			
		||||
        public bool CheckUserRatelimit(ulong id, ulong guildId, SocketGuildUser optUser)
 | 
			
		||||
        {
 | 
			
		||||
            if ((_svc.IgnoredUsers.TryGetValue(guildId, out HashSet<ulong> ignoreUsers) && ignoreUsers.Contains(id)) ||
 | 
			
		||||
                (optUser != null && _svc.IgnoredRoles.TryGetValue(guildId, out HashSet<ulong> ignoreRoles) && optUser.Roles.Any(x => ignoreRoles.Contains(x.Id))))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
 | 
			
		||||
            if (usr.MessageCount >= MaxMessages)
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            usr.MessageCount++;
 | 
			
		||||
            var _ = Task.Run(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await Task.Delay(PerSeconds * 1000, CancelSource.Token);
 | 
			
		||||
                }
 | 
			
		||||
                catch (OperationCanceledException) { }
 | 
			
		||||
                usr.MessageCount--;
 | 
			
		||||
            });
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,8 +12,7 @@ namespace NadekoBot.Services.Administration
 | 
			
		||||
    {
 | 
			
		||||
        public volatile bool ForwardDMs;
 | 
			
		||||
        public volatile bool ForwardDMsToAllOwners;
 | 
			
		||||
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
        
 | 
			
		||||
        private readonly NadekoBot _bot;
 | 
			
		||||
        private readonly CommandHandler _cmdHandler;
 | 
			
		||||
        private readonly DbHandler _db;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								src/NadekoBot/Services/Administration/UserSpamStats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/NadekoBot/Services/Administration/UserSpamStats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
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.Administration
 | 
			
		||||
{
 | 
			
		||||
    public class UserSpamStats : IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        public int Count => timers.Count;
 | 
			
		||||
        public string LastMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
        private ConcurrentQueue<Timer> timers { get; }
 | 
			
		||||
 | 
			
		||||
        public UserSpamStats(IUserMessage msg)
 | 
			
		||||
        {
 | 
			
		||||
            LastMessage = msg.Content.ToUpperInvariant();
 | 
			
		||||
            timers = new ConcurrentQueue<Timer>();
 | 
			
		||||
 | 
			
		||||
            ApplyNextMessage(msg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly object applyLock = new object();
 | 
			
		||||
        public void ApplyNextMessage(IUserMessage message)
 | 
			
		||||
        {
 | 
			
		||||
            lock (applyLock)
 | 
			
		||||
            {
 | 
			
		||||
                var upperMsg = message.Content.ToUpperInvariant();
 | 
			
		||||
                if (upperMsg != LastMessage || (string.IsNullOrWhiteSpace(upperMsg) && message.Attachments.Any()))
 | 
			
		||||
                {
 | 
			
		||||
                    LastMessage = upperMsg;
 | 
			
		||||
                    while (timers.TryDequeue(out var old))
 | 
			
		||||
                        old.Change(Timeout.Infinite, Timeout.Infinite);
 | 
			
		||||
                }
 | 
			
		||||
                var t = new Timer((_) => {
 | 
			
		||||
                    if (timers.TryDequeue(out var old))
 | 
			
		||||
                        old.Change(Timeout.Infinite, Timeout.Infinite);
 | 
			
		||||
                }, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
 | 
			
		||||
                timers.Enqueue(t);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            while (timers.TryDequeue(out var old))
 | 
			
		||||
                old.Change(Timeout.Infinite, Timeout.Infinite);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
namespace NadekoBot.Services.Pokemon
 | 
			
		||||
{
 | 
			
		||||
    class PokeStats
 | 
			
		||||
    public class PokeStats
 | 
			
		||||
    {
 | 
			
		||||
        //Health left
 | 
			
		||||
        public int Hp { get; set; } = 500;
 | 
			
		||||
							
								
								
									
										32
									
								
								src/NadekoBot/Services/Pokemon/PokemonService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/NadekoBot/Services/Pokemon/PokemonService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using NLog;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Pokemon
 | 
			
		||||
{
 | 
			
		||||
    public class PokemonService
 | 
			
		||||
    {
 | 
			
		||||
        public readonly List<PokemonType> PokemonTypes = new List<PokemonType>();
 | 
			
		||||
        public readonly ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>();
 | 
			
		||||
 | 
			
		||||
        public const string PokemonTypesFile = "data/pokemon_types.json";
 | 
			
		||||
 | 
			
		||||
        private Logger _log { get; }
 | 
			
		||||
 | 
			
		||||
        public PokemonService()
 | 
			
		||||
        {
 | 
			
		||||
            _log = LogManager.GetCurrentClassLogger();
 | 
			
		||||
            if (File.Exists(PokemonTypesFile))
 | 
			
		||||
            {
 | 
			
		||||
                PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                PokemonTypes = new List<PokemonType>();
 | 
			
		||||
                _log.Warn(PokemonTypesFile + " is missing. Pokemon types not loaded.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
namespace NadekoBot.Services.Pokemon
 | 
			
		||||
{
 | 
			
		||||
    public class PokemonType
 | 
			
		||||
    {
 | 
			
		||||
@@ -18,6 +18,9 @@ namespace NadekoBot.Extensions
 | 
			
		||||
{
 | 
			
		||||
    public static class Extensions
 | 
			
		||||
    {
 | 
			
		||||
        public static bool IsAuthor(this IMessage msg, IDiscordClient client) =>
 | 
			
		||||
            msg.Author?.Id == client.CurrentUser.Id;
 | 
			
		||||
 | 
			
		||||
        private static readonly IEmote arrow_left = Emote.Parse("⬅");
 | 
			
		||||
        private static readonly IEmote arrow_right = Emote.Parse("➡");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user