Merge remote-tracking branch 'Kwoth/dev' into dev
# Conflicts: # src/NadekoBot/Resources/CommandStrings.resx
This commit is contained in:
		
							
								
								
									
										1095
									
								
								src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1095
									
								
								src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public partial class guildtimezoneandlocale : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<string>(
 | 
				
			||||||
 | 
					                name: "Locale",
 | 
				
			||||||
 | 
					                table: "GuildConfigs",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                defaultValue: null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<string>(
 | 
				
			||||||
 | 
					                name: "TimeZoneId",
 | 
				
			||||||
 | 
					                table: "GuildConfigs",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                defaultValue: null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<string>(
 | 
				
			||||||
 | 
					                name: "Locale",
 | 
				
			||||||
 | 
					                table: "BotConfig",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                defaultValue: null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "Locale",
 | 
				
			||||||
 | 
					                table: "GuildConfigs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "TimeZoneId",
 | 
				
			||||||
 | 
					                table: "GuildConfigs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "Locale",
 | 
				
			||||||
 | 
					                table: "BotConfig");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -128,6 +128,8 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.Property<string>("HelpString");
 | 
					                    b.Property<string>("HelpString");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Locale");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("MigrationVersion");
 | 
					                    b.Property<int>("MigrationVersion");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("MinimumBetAmount");
 | 
					                    b.Property<int>("MinimumBetAmount");
 | 
				
			||||||
@@ -469,6 +471,8 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.Property<ulong>("GuildId");
 | 
					                    b.Property<ulong>("GuildId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("Locale");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int?>("LogSettingId");
 | 
					                    b.Property<int?>("LogSettingId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<string>("MuteRoleName");
 | 
					                    b.Property<string>("MuteRoleName");
 | 
				
			||||||
@@ -483,6 +487,8 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.Property<bool>("SendDmGreetMessage");
 | 
					                    b.Property<bool>("SendDmGreetMessage");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Property<string>("TimeZoneId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<bool>("VerbosePermissions");
 | 
					                    b.Property<bool>("VerbosePermissions");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<bool>("VoicePlusTextEnabled");
 | 
					                    b.Property<bool>("VoicePlusTextEnabled");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,14 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.Commands;
 | 
					using Discord.Commands;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using System.Net.Http;
 | 
					 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using static NadekoBot.Modules.Permissions.Permissions;
 | 
					using static NadekoBot.Modules.Permissions.Permissions;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
@@ -20,21 +16,18 @@ using NLog;
 | 
				
			|||||||
namespace NadekoBot.Modules.Administration
 | 
					namespace NadekoBot.Modules.Administration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Administration", ".")]
 | 
					    [NadekoModule("Administration", ".")]
 | 
				
			||||||
    public partial class Administration : DiscordModule
 | 
					    public partial class Administration : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private static ConcurrentHashSet<ulong> deleteMessagesOnCommand { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>();
 | 
					        private new static readonly Logger _log;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; } = new ConcurrentHashSet<ulong>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private new static Logger _log { get; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        static Administration()
 | 
					        static Administration()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
 | 
					            NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
 | 
					            deleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,7 +40,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    var channel = msg.Channel as SocketTextChannel;
 | 
					                    var channel = msg.Channel as SocketTextChannel;
 | 
				
			||||||
                    if (channel == null)
 | 
					                    if (channel == null)
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
 | 
					                    if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
 | 
				
			||||||
                        await msg.DeleteAsync().ConfigureAwait(false);
 | 
					                        await msg.DeleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
@@ -74,17 +67,17 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    PermRole = config.PermissionRole,
 | 
					                    PermRole = config.PermissionRole,
 | 
				
			||||||
                    Verbose = config.VerbosePermissions,
 | 
					                    Verbose = config.VerbosePermissions,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id,
 | 
					                Cache.AddOrUpdate(channel.Guild.Id,
 | 
				
			||||||
                    toAdd, (id, old) => toAdd);
 | 
					                    toAdd, (id, old) => toAdd);
 | 
				
			||||||
                await uow.CompleteAsync();
 | 
					                await uow.CompleteAsync();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false);
 | 
				
			||||||
            await channel.SendConfirmAsync($"{Context.Message.Author.Mention} 🆗 **Permissions for this server are reset.**");
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.Administrator)]
 | 
					        [RequireUserPermission(GuildPermission.Administrator)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
        public async Task Delmsgoncmd()
 | 
					        public async Task Delmsgoncmd()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            bool enabled;
 | 
					            bool enabled;
 | 
				
			||||||
@@ -97,29 +90,31 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            if (enabled)
 | 
					            if (enabled)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DeleteMessagesOnCommand.Add(Context.Guild.Id);
 | 
					                deleteMessagesOnCommand.Add(Context.Guild.Id);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DeleteMessagesOnCommand.TryRemove(Context.Guild.Id);
 | 
					                deleteMessagesOnCommand.TryRemove(Context.Guild.Id);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("❗**Stopped automatic deletion of successful command invokations.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
        public async Task Setrole(IGuildUser usr, [Remainder] IRole role)
 | 
					        public async Task Setrole(IGuildUser usr, [Remainder] IRole role)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await usr.AddRolesAsync(role).ConfigureAwait(false);
 | 
					                await usr.AddRolesAsync(role).ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"ℹ️ Successfully added role **{role.Name}** to user **{usr.Username}**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("setrole", Format.Bold(role.Name), Format.Bold(usr.ToString()))
 | 
				
			||||||
 | 
					                    .ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Failed to add role. **Bot has insufficient permissions.**\n").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("setrole_err").ConfigureAwait(false);
 | 
				
			||||||
                Console.WriteLine(ex.ToString());
 | 
					                Console.WriteLine(ex.ToString());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -127,95 +122,94 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
        public async Task Removerole(IGuildUser usr, [Remainder] IRole role)
 | 
					        public async Task Removerole(IGuildUser usr, [Remainder] IRole role)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await usr.RemoveRolesAsync(role).ConfigureAwait(false);
 | 
					                await usr.RemoveRolesAsync(role).ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"ℹ️ Successfully removed role **{role.Name}** from user **{usr.Username}**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("remrole", Format.Bold(role.Name), Format.Bold(usr.ToString())).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch
 | 
					            catch
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Failed to remove role. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("remrole_err").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
        public async Task RenameRole(IRole roleToEdit, string newname)
 | 
					        public async Task RenameRole(IRole roleToEdit, string newname)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (roleToEdit.Position > (await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position))
 | 
					                if (roleToEdit.Position > (await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("🚫 You can't edit roles higher than your highest role.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("renrole_perms").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false);
 | 
					                await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("✅ Role renamed.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("renrole").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception)
 | 
					            catch (Exception)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Failed to rename role. Probably **insufficient permissions.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("renrole_err").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
        public async Task RemoveAllRoles([Remainder] IGuildUser user)
 | 
					        public async Task RemoveAllRoles([Remainder] IGuildUser user)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await user.RemoveRolesAsync(user.GetRoles()).ConfigureAwait(false);
 | 
					                await user.RemoveRolesAsync(user.GetRoles()).ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"🗑 Successfully removed **all** roles from user **{user.Username}**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch
 | 
					            catch
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Failed to remove roles. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("rar_err").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
        public async Task CreateRole([Remainder] string roleName = null)
 | 
					        public async Task CreateRole([Remainder] string roleName = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(roleName))
 | 
					            if (string.IsNullOrWhiteSpace(roleName))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            try
 | 
					
 | 
				
			||||||
            {
 | 
					            var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false);
 | 
				
			||||||
                var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("cr", Format.Bold(r.Name)).ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"✅ Successfully created role **{r.Name}**.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Unspecified error.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
        public async Task RoleColor(params string[] args)
 | 
					        public async Task RoleColor(params string[] args)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (args.Count() != 2 && args.Count() != 4)
 | 
					            if (args.Length != 2 && args.Length != 4)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("❌ The parameters specified are **invalid.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("rc_params").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var roleName = args[0].ToUpperInvariant();
 | 
					            var roleName = args[0].ToUpperInvariant();
 | 
				
			||||||
            var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault();
 | 
					            var role = Context.Guild.Roles.FirstOrDefault(r => r.Name.ToUpperInvariant() == roleName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (role == null)
 | 
					            if (role == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🚫 That role **does not exist.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("rc_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var rgb = args.Count() == 4;
 | 
					                var rgb = args.Length == 4;
 | 
				
			||||||
                var arg1 = args[1].Replace("#", "");
 | 
					                var arg1 = args[1].Replace("#", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16));
 | 
					                var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16));
 | 
				
			||||||
@@ -223,64 +217,57 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16));
 | 
					                var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false);
 | 
					                await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("rc", Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception)
 | 
					            catch (Exception)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Error occured, most likely **invalid parameters** or **insufficient permissions.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("rc_perms").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.BanMembers)]
 | 
					        [RequireUserPermission(GuildPermission.BanMembers)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.BanMembers)]
 | 
				
			||||||
        public async Task Ban(IGuildUser user, [Remainder] string msg = null)
 | 
					        public async Task Ban(IGuildUser user, [Remainder] string msg = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(msg))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                msg = "❗️No reason provided.";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()))
 | 
					            if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(msg))
 | 
					            if (!string.IsNullOrWhiteSpace(msg))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{Context.Guild.Name}` server.**\n" +
 | 
					                    await user.SendErrorAsync(GetText("bandm", Format.Bold(Context.Guild.Name), msg));
 | 
				
			||||||
                                            $"⚖ *Reason:* {msg}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    await Task.Delay(2000).ConfigureAwait(false);
 | 
					                    await Task.Delay(2000).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("⛔️ **Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
 | 
					            await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
            catch
 | 
					                    .WithTitle("⛔️ " + GetText("banned_user"))
 | 
				
			||||||
            {
 | 
					                    .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ **Error.** Most likely I don't have sufficient permissions.").ConfigureAwait(false);
 | 
					                    .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
 | 
				
			||||||
            }
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.KickMembers)]
 | 
					        [RequireUserPermission(GuildPermission.KickMembers)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
					        [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.BanMembers)]
 | 
				
			||||||
        public async Task Softban(IGuildUser user, [Remainder] string msg = null)
 | 
					        public async Task Softban(IGuildUser user, [Remainder] string msg = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(msg))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                msg = "❗️No reason provided.";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (Context.User.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
 | 
					            if (Context.User.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
 | 
					                await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -288,161 +275,162 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{Context.Guild.Name}` server.**\n" +
 | 
					                    await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg));
 | 
				
			||||||
                  $"⚖ *Reason:* {msg}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    await Task.Delay(2000).ConfigureAwait(false);
 | 
					                    await Task.Delay(2000).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try
 | 
					            await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
 | 
				
			||||||
            {
 | 
					            try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
 | 
				
			||||||
                await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
 | 
					            catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
 | 
				
			||||||
                try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
 | 
					 | 
				
			||||||
                catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("☣ **Soft-Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
 | 
					            await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
            }
 | 
					                    .WithTitle("☣ " + GetText("sb_user"))
 | 
				
			||||||
            catch
 | 
					                    .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
 | 
				
			||||||
            {
 | 
					                    .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.KickMembers)]
 | 
					        [RequireUserPermission(GuildPermission.KickMembers)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.KickMembers)]
 | 
				
			||||||
        public async Task Kick(IGuildUser user, [Remainder] string msg = null)
 | 
					        public async Task Kick(IGuildUser user, [Remainder] string msg = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (user == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await Context.Channel.SendErrorAsync("❗️User not found.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (Context.Message.Author.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
 | 
					            if (Context.Message.Author.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
 | 
					                await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(msg))
 | 
					            if (!string.IsNullOrWhiteSpace(msg))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await user.SendErrorAsync($"‼️**You have been KICKED from `{Context.Guild.Name}` server.**\n" +
 | 
					                    await user.SendErrorAsync(GetText("kickdm", Format.Bold(Context.Guild.Name), msg));
 | 
				
			||||||
                                    $"⚖ *Reason:* {msg}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    await Task.Delay(2000).ConfigureAwait(false);
 | 
					                    await Task.Delay(2000).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch { }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            try
 | 
					
 | 
				
			||||||
            {
 | 
					            await user.KickAsync().ConfigureAwait(false);
 | 
				
			||||||
                await user.KickAsync().ConfigureAwait(false);
 | 
					            await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("‼️**Kicked** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
 | 
					                    .WithTitle(GetText("kicked_user"))
 | 
				
			||||||
            }
 | 
					                    .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
 | 
				
			||||||
            catch
 | 
					                    .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
 | 
				
			||||||
            {
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.DeafenMembers)]
 | 
					        [RequireUserPermission(GuildPermission.DeafenMembers)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.DeafenMembers)]
 | 
				
			||||||
        public async Task Deafen(params IGuildUser[] users)
 | 
					        public async Task Deafen(params IGuildUser[] users)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!users.Any())
 | 
					            if (!users.Any())
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            try
 | 
					            foreach (var u in users)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var u in users)
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
 | 
					                    await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false);
 | 
					                catch
 | 
				
			||||||
            }
 | 
					                {
 | 
				
			||||||
            catch
 | 
					                    // ignored
 | 
				
			||||||
            {
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            await ReplyConfirmLocalized("deafen").ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.DeafenMembers)]
 | 
					        [RequireUserPermission(GuildPermission.DeafenMembers)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.DeafenMembers)]
 | 
				
			||||||
        public async Task UnDeafen(params IGuildUser[] users)
 | 
					        public async Task UnDeafen(params IGuildUser[] users)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!users.Any())
 | 
					            if (!users.Any())
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            try
 | 
					
 | 
				
			||||||
 | 
					            foreach (var u in users)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var u in users)
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
 | 
					                    await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false);
 | 
					                catch
 | 
				
			||||||
            }
 | 
					                {
 | 
				
			||||||
            catch
 | 
					                    // ignored
 | 
				
			||||||
            {
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            await ReplyConfirmLocalized("undeafen").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
					        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
        public async Task DelVoiChanl([Remainder] IVoiceChannel voiceChannel)
 | 
					        public async Task DelVoiChanl([Remainder] IVoiceChannel voiceChannel)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await voiceChannel.DeleteAsync().ConfigureAwait(false);
 | 
					            await voiceChannel.DeleteAsync().ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"🗑 Removed voice channel **{voiceChannel.Name}** successfully.").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("delvoich", Format.Bold(voiceChannel.Name)).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
					        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
        public async Task CreatVoiChanl([Remainder] string channelName)
 | 
					        public async Task CreatVoiChanl([Remainder] string channelName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var ch = await Context.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false);
 | 
					            var ch = await Context.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"✅ Created voice channel **{ch.Name}**. ID: `{ch.Id}`").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("createvoich",Format.Bold(ch.Name)).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
					        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
        public async Task DelTxtChanl([Remainder] ITextChannel toDelete)
 | 
					        public async Task DelTxtChanl([Remainder] ITextChannel toDelete)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await toDelete.DeleteAsync().ConfigureAwait(false);
 | 
					            await toDelete.DeleteAsync().ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"🗑 Removed text channel **{toDelete.Name}**. ID: `{toDelete.Id}`").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("deltextchan", Format.Bold(toDelete.Name)).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
					        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
        public async Task CreaTxtChanl([Remainder] string channelName)
 | 
					        public async Task CreaTxtChanl([Remainder] string channelName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var txtCh = await Context.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false);
 | 
					            var txtCh = await Context.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"✅ Added text channel **{txtCh.Name}**. ID: `{txtCh.Id}`").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("createtextchan", Format.Bold(txtCh.Name)).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
					        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
        public async Task SetTopic([Remainder] string topic = null)
 | 
					        public async Task SetTopic([Remainder] string topic = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var channel = (ITextChannel)Context.Channel;
 | 
					            var channel = (ITextChannel)Context.Channel;
 | 
				
			||||||
            topic = topic ?? "";
 | 
					            topic = topic ?? "";
 | 
				
			||||||
            await channel.ModifyAsync(c => c.Topic = topic);
 | 
					            await channel.ModifyAsync(c => c.Topic = topic);
 | 
				
			||||||
            await channel.SendConfirmAsync("🆗 **New channel topic set.**").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("set_topic").ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
					        [RequireUserPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageChannels)]
 | 
				
			||||||
        public async Task SetChanlName([Remainder] string name)
 | 
					        public async Task SetChanlName([Remainder] string name)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var channel = (ITextChannel)Context.Channel;
 | 
					            var channel = (ITextChannel)Context.Channel;
 | 
				
			||||||
            await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false);
 | 
					            await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false);
 | 
				
			||||||
            await channel.SendConfirmAsync("🆗 **New channel name set.**").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("set_channel_name").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -462,6 +450,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(ChannelPermission.ManageMessages)]
 | 
					        [RequireUserPermission(ChannelPermission.ManageMessages)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
        public async Task Prune(int count)
 | 
					        public async Task Prune(int count)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (count < 1)
 | 
					            if (count < 1)
 | 
				
			||||||
@@ -477,6 +466,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(ChannelPermission.ManageMessages)]
 | 
					        [RequireUserPermission(ChannelPermission.ManageMessages)]
 | 
				
			||||||
 | 
					        [RequireBotPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
        public async Task Prune(IGuildUser user, int count = 100)
 | 
					        public async Task Prune(IGuildUser user, int count = 100)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (count < 1)
 | 
					            if (count < 1)
 | 
				
			||||||
@@ -495,11 +485,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        [RequireUserPermission(GuildPermission.MentionEveryone)]
 | 
					        [RequireUserPermission(GuildPermission.MentionEveryone)]
 | 
				
			||||||
        public async Task MentionRole(params IRole[] roles)
 | 
					        public async Task MentionRole(params IRole[] roles)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string send = $"❕{Context.User.Mention} has invoked a mention on the following roles ❕";
 | 
					            string send = "❕" +GetText("menrole",Context.User.Mention);
 | 
				
			||||||
            foreach (var role in roles)
 | 
					            foreach (var role in roles)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                send += $"\n**{role.Name}**\n";
 | 
					                send += $"\n**{role.Name}**\n";
 | 
				
			||||||
                send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Distinct().Select(u => u.Mention));
 | 
					                send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Take(50).Select(u => u.Mention));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            while (send.Length > 2000)
 | 
					            while (send.Length > 2000)
 | 
				
			||||||
@@ -513,7 +503,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            await Context.Channel.SendMessageAsync(send).ConfigureAwait(false);
 | 
					            await Context.Channel.SendMessageAsync(send).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        IGuild nadekoSupportServer;
 | 
					        private IGuild _nadekoSupportServer;
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        public async Task Donators()
 | 
					        public async Task Donators()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -523,18 +513,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                donatorsOrdered = uow.Donators.GetDonatorsOrdered();
 | 
					                donatorsOrdered = uow.Donators.GetDonatorsOrdered();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
 | 
					            await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
 | 
					            _nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (nadekoSupportServer == null)
 | 
					            var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585);
 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var patreonRole = nadekoSupportServer.GetRole(236667642088259585);
 | 
					 | 
				
			||||||
            if (patreonRole == null)
 | 
					            if (patreonRole == null)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var usrs = (await nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u));
 | 
					            var usrs = (await _nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u));
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false);
 | 
					            await Context.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -549,8 +536,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount);
 | 
					                don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount);
 | 
				
			||||||
                await uow.CompleteAsync();
 | 
					                await uow.CompleteAsync();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            await ReplyConfirmLocalized("donadd", don.Amount).ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //[NadekoCommand, Usage, Description, Aliases]
 | 
					        //[NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,10 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.Commands;
 | 
					using Discord.Commands;
 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.Diagnostics;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,24 +13,23 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class AutoAssignRoleCommands : ModuleBase
 | 
					        public class AutoAssignRoleCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					 | 
				
			||||||
            //guildid/roleid
 | 
					            //guildid/roleid
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
 | 
					            private static ConcurrentDictionary<ulong, ulong> autoAssignedRoles { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static AutoAssignRoleCommands()
 | 
					            static AutoAssignRoleCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					                var log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
 | 
					                autoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
 | 
				
			||||||
                    .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
 | 
					                    .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
 | 
				
			||||||
                NadekoBot.Client.UserJoined += async (user) =>
 | 
					                NadekoBot.Client.UserJoined += async (user) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        ulong roleId = 0;
 | 
					                        ulong roleId;
 | 
				
			||||||
                        AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
 | 
					                        autoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (roleId == 0)
 | 
					                        if (roleId == 0)
 | 
				
			||||||
                            return;
 | 
					                            return;
 | 
				
			||||||
@@ -43,7 +39,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        if (role != null)
 | 
					                        if (role != null)
 | 
				
			||||||
                            await user.AddRolesAsync(role).ConfigureAwait(false);
 | 
					                            await user.AddRolesAsync(role).ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch (Exception ex) { _log.Warn(ex); }
 | 
					                    catch (Exception ex) { log.Warn(ex); }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,20 +48,19 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					            [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
            public async Task AutoAssignRole([Remainder] IRole role = null)
 | 
					            public async Task AutoAssignRole([Remainder] IRole role = null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                GuildConfig conf;
 | 
					 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
					                    var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
				
			||||||
                    if (role == null)
 | 
					                    if (role == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        conf.AutoAssignRoleId = 0;
 | 
					                        conf.AutoAssignRoleId = 0;
 | 
				
			||||||
                        ulong throwaway;
 | 
					                        ulong throwaway;
 | 
				
			||||||
                        AutoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway);
 | 
					                        autoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        conf.AutoAssignRoleId = role.Id;
 | 
					                        conf.AutoAssignRoleId = role.Id;
 | 
				
			||||||
                        AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
 | 
					                        autoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
@@ -73,11 +68,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (role == null)
 | 
					                if (role == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("🆗 **Auto assign role** on user join is now **disabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("aar_disabled").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("aar_enabled").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,90 +0,0 @@
 | 
				
			|||||||
using Discord;
 | 
					 | 
				
			||||||
using Discord.Commands;
 | 
					 | 
				
			||||||
using Discord.WebSocket;
 | 
					 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					 | 
				
			||||||
using NadekoBot.Services;
 | 
					 | 
				
			||||||
using NLog;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Diagnostics;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace NadekoBot.Modules.Administration
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public partial class Administration
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        [Group]
 | 
					 | 
				
			||||||
        public class DMForwardCommands : ModuleBase
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            private static bool ForwardDMs { get; set; }
 | 
					 | 
				
			||||||
            private static bool ForwardDMsToAllOwners { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            private static readonly Logger _log;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            static DMForwardCommands()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
					 | 
				
			||||||
                    ForwardDMs = config.ForwardMessages;
 | 
					 | 
				
			||||||
                    ForwardDMsToAllOwners = config.ForwardToAllOwners;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
            [OwnerOnly]
 | 
					 | 
				
			||||||
            public async Task ForwardMessages()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
					 | 
				
			||||||
                    ForwardDMs = config.ForwardMessages = !config.ForwardMessages;
 | 
					 | 
				
			||||||
                    uow.Complete();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (ForwardDMs)
 | 
					 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
            [OwnerOnly]
 | 
					 | 
				
			||||||
            public async Task ForwardToAll()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
					 | 
				
			||||||
                    ForwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
 | 
					 | 
				
			||||||
                    uow.Complete();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (ForwardDMsToAllOwners)
 | 
					 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ **I will forward DMs to all owners.**").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ **I will forward DMs only to the first owner.**").ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public static async Task HandleDMForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (ForwardDMs && ownerChannels.Any())
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var title = $"DM from [{msg.Author}]({msg.Author.Id})";
 | 
					 | 
				
			||||||
                    if (ForwardDMsToAllOwners)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        var msgs = await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
 | 
					 | 
				
			||||||
                                                                   .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        var firstOwnerChannel = ownerChannels.First();
 | 
					 | 
				
			||||||
                        if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
 | 
					 | 
				
			||||||
                            try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } catch { }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,234 @@
 | 
				
			|||||||
 | 
					using Discord;
 | 
				
			||||||
 | 
					using Discord.Commands;
 | 
				
			||||||
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.Immutable;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Modules.Administration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public partial class Administration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [Group]
 | 
				
			||||||
 | 
					        public class LocalizationCommands : NadekoSubmodule
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                {"en-US", "English, United States" },
 | 
				
			||||||
 | 
					                {"sr-cyrl-rs", "Serbian, Cyrillic" }
 | 
				
			||||||
 | 
					            }.ToImmutableDictionary();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
 | 
					            public async Task LanguageSet()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild);
 | 
				
			||||||
 | 
					                await ReplyConfirmLocalized("lang_set_show", Format.Bold(cul.ToString()), Format.Bold(cul.NativeName))
 | 
				
			||||||
 | 
					                    .ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
 | 
					            [RequireUserPermission(GuildPermission.Administrator)]
 | 
				
			||||||
 | 
					            public async Task LanguageSet(string name)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    CultureInfo ci;
 | 
				
			||||||
 | 
					                    if (name.Trim().ToLowerInvariant() == "default")
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        NadekoBot.Localization.RemoveGuildCulture(Context.Guild);
 | 
				
			||||||
 | 
					                        ci = NadekoBot.Localization.DefaultCultureInfo;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ci = new CultureInfo(name);
 | 
				
			||||||
 | 
					                        NadekoBot.Localization.SetGuildCulture(Context.Guild, ci);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch(Exception)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            public async Task LanguageSetDefault()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var cul = NadekoBot.Localization.DefaultCultureInfo;
 | 
				
			||||||
 | 
					                await ReplyConfirmLocalized("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            [OwnerOnly]
 | 
				
			||||||
 | 
					            public async Task LanguageSetDefault(string name)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    CultureInfo ci;
 | 
				
			||||||
 | 
					                    if (name.Trim().ToLowerInvariant() == "default")
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        NadekoBot.Localization.ResetDefaultCulture();
 | 
				
			||||||
 | 
					                        ci = NadekoBot.Localization.DefaultCultureInfo;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ci = new CultureInfo(name);
 | 
				
			||||||
 | 
					                        NadekoBot.Localization.SetDefaultCulture(ci);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("lang_set_bot", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            [OwnerOnly]
 | 
				
			||||||
 | 
					            public async Task LanguagesList()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyConfirmLocalized("lang_list",
 | 
				
			||||||
 | 
					                        string.Join("\n", supportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}")))
 | 
				
			||||||
 | 
					                    .ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/* list of language codes for reference. 
 | 
				
			||||||
 | 
					 * taken from https://github.com/dotnet/coreclr/blob/ee5862c6a257e60e263537d975ab6c513179d47f/src/mscorlib/src/System/Globalization/CultureData.cs#L192
 | 
				
			||||||
 | 
					            { "029", "en-029" },
 | 
				
			||||||
 | 
					            { "AE",  "ar-AE" },
 | 
				
			||||||
 | 
					            { "AF",  "prs-AF" },
 | 
				
			||||||
 | 
					            { "AL",  "sq-AL" },
 | 
				
			||||||
 | 
					            { "AM",  "hy-AM" },
 | 
				
			||||||
 | 
					            { "AR",  "es-AR" },
 | 
				
			||||||
 | 
					            { "AT",  "de-AT" },
 | 
				
			||||||
 | 
					            { "AU",  "en-AU" },
 | 
				
			||||||
 | 
					            { "AZ",  "az-Cyrl-AZ" },
 | 
				
			||||||
 | 
					            { "BA",  "bs-Latn-BA" },
 | 
				
			||||||
 | 
					            { "BD",  "bn-BD" },
 | 
				
			||||||
 | 
					            { "BE",  "nl-BE" },
 | 
				
			||||||
 | 
					            { "BG",  "bg-BG" },
 | 
				
			||||||
 | 
					            { "BH",  "ar-BH" },
 | 
				
			||||||
 | 
					            { "BN",  "ms-BN" },
 | 
				
			||||||
 | 
					            { "BO",  "es-BO" },
 | 
				
			||||||
 | 
					            { "BR",  "pt-BR" },
 | 
				
			||||||
 | 
					            { "BY",  "be-BY" },
 | 
				
			||||||
 | 
					            { "BZ",  "en-BZ" },
 | 
				
			||||||
 | 
					            { "CA",  "en-CA" },
 | 
				
			||||||
 | 
					            { "CH",  "it-CH" },
 | 
				
			||||||
 | 
					            { "CL",  "es-CL" },
 | 
				
			||||||
 | 
					            { "CN",  "zh-CN" },
 | 
				
			||||||
 | 
					            { "CO",  "es-CO" },
 | 
				
			||||||
 | 
					            { "CR",  "es-CR" },
 | 
				
			||||||
 | 
					            { "CS",  "sr-Cyrl-CS" },
 | 
				
			||||||
 | 
					            { "CZ",  "cs-CZ" },
 | 
				
			||||||
 | 
					            { "DE",  "de-DE" },
 | 
				
			||||||
 | 
					            { "DK",  "da-DK" },
 | 
				
			||||||
 | 
					            { "DO",  "es-DO" },
 | 
				
			||||||
 | 
					            { "DZ",  "ar-DZ" },
 | 
				
			||||||
 | 
					            { "EC",  "es-EC" },
 | 
				
			||||||
 | 
					            { "EE",  "et-EE" },
 | 
				
			||||||
 | 
					            { "EG",  "ar-EG" },
 | 
				
			||||||
 | 
					            { "ES",  "es-ES" },
 | 
				
			||||||
 | 
					            { "ET",  "am-ET" },
 | 
				
			||||||
 | 
					            { "FI",  "fi-FI" },
 | 
				
			||||||
 | 
					            { "FO",  "fo-FO" },
 | 
				
			||||||
 | 
					            { "FR",  "fr-FR" },
 | 
				
			||||||
 | 
					            { "GB",  "en-GB" },
 | 
				
			||||||
 | 
					            { "GE",  "ka-GE" },
 | 
				
			||||||
 | 
					            { "GL",  "kl-GL" },
 | 
				
			||||||
 | 
					            { "GR",  "el-GR" },
 | 
				
			||||||
 | 
					            { "GT",  "es-GT" },
 | 
				
			||||||
 | 
					            { "HK",  "zh-HK" },
 | 
				
			||||||
 | 
					            { "HN",  "es-HN" },
 | 
				
			||||||
 | 
					            { "HR",  "hr-HR" },
 | 
				
			||||||
 | 
					            { "HU",  "hu-HU" },
 | 
				
			||||||
 | 
					            { "ID",  "id-ID" },
 | 
				
			||||||
 | 
					            { "IE",  "en-IE" },
 | 
				
			||||||
 | 
					            { "IL",  "he-IL" },
 | 
				
			||||||
 | 
					            { "IN",  "hi-IN" },
 | 
				
			||||||
 | 
					            { "IQ",  "ar-IQ" },
 | 
				
			||||||
 | 
					            { "IR",  "fa-IR" },
 | 
				
			||||||
 | 
					            { "IS",  "is-IS" },
 | 
				
			||||||
 | 
					            { "IT",  "it-IT" },
 | 
				
			||||||
 | 
					            { "IV",  "" },
 | 
				
			||||||
 | 
					            { "JM",  "en-JM" },
 | 
				
			||||||
 | 
					            { "JO",  "ar-JO" },
 | 
				
			||||||
 | 
					            { "JP",  "ja-JP" },
 | 
				
			||||||
 | 
					            { "KE",  "sw-KE" },
 | 
				
			||||||
 | 
					            { "KG",  "ky-KG" },
 | 
				
			||||||
 | 
					            { "KH",  "km-KH" },
 | 
				
			||||||
 | 
					            { "KR",  "ko-KR" },
 | 
				
			||||||
 | 
					            { "KW",  "ar-KW" },
 | 
				
			||||||
 | 
					            { "KZ",  "kk-KZ" },
 | 
				
			||||||
 | 
					            { "LA",  "lo-LA" },
 | 
				
			||||||
 | 
					            { "LB",  "ar-LB" },
 | 
				
			||||||
 | 
					            { "LI",  "de-LI" },
 | 
				
			||||||
 | 
					            { "LK",  "si-LK" },
 | 
				
			||||||
 | 
					            { "LT",  "lt-LT" },
 | 
				
			||||||
 | 
					            { "LU",  "lb-LU" },
 | 
				
			||||||
 | 
					            { "LV",  "lv-LV" },
 | 
				
			||||||
 | 
					            { "LY",  "ar-LY" },
 | 
				
			||||||
 | 
					            { "MA",  "ar-MA" },
 | 
				
			||||||
 | 
					            { "MC",  "fr-MC" },
 | 
				
			||||||
 | 
					            { "ME",  "sr-Latn-ME" },
 | 
				
			||||||
 | 
					            { "MK",  "mk-MK" },
 | 
				
			||||||
 | 
					            { "MN",  "mn-MN" },
 | 
				
			||||||
 | 
					            { "MO",  "zh-MO" },
 | 
				
			||||||
 | 
					            { "MT",  "mt-MT" },
 | 
				
			||||||
 | 
					            { "MV",  "dv-MV" },
 | 
				
			||||||
 | 
					            { "MX",  "es-MX" },
 | 
				
			||||||
 | 
					            { "MY",  "ms-MY" },
 | 
				
			||||||
 | 
					            { "NG",  "ig-NG" },
 | 
				
			||||||
 | 
					            { "NI",  "es-NI" },
 | 
				
			||||||
 | 
					            { "NL",  "nl-NL" },
 | 
				
			||||||
 | 
					            { "NO",  "nn-NO" },
 | 
				
			||||||
 | 
					            { "NP",  "ne-NP" },
 | 
				
			||||||
 | 
					            { "NZ",  "en-NZ" },
 | 
				
			||||||
 | 
					            { "OM",  "ar-OM" },
 | 
				
			||||||
 | 
					            { "PA",  "es-PA" },
 | 
				
			||||||
 | 
					            { "PE",  "es-PE" },
 | 
				
			||||||
 | 
					            { "PH",  "en-PH" },
 | 
				
			||||||
 | 
					            { "PK",  "ur-PK" },
 | 
				
			||||||
 | 
					            { "PL",  "pl-PL" },
 | 
				
			||||||
 | 
					            { "PR",  "es-PR" },
 | 
				
			||||||
 | 
					            { "PT",  "pt-PT" },
 | 
				
			||||||
 | 
					            { "PY",  "es-PY" },
 | 
				
			||||||
 | 
					            { "QA",  "ar-QA" },
 | 
				
			||||||
 | 
					            { "RO",  "ro-RO" },
 | 
				
			||||||
 | 
					            { "RS",  "sr-Latn-RS" },
 | 
				
			||||||
 | 
					            { "RU",  "ru-RU" },
 | 
				
			||||||
 | 
					            { "RW",  "rw-RW" },
 | 
				
			||||||
 | 
					            { "SA",  "ar-SA" },
 | 
				
			||||||
 | 
					            { "SE",  "sv-SE" },
 | 
				
			||||||
 | 
					            { "SG",  "zh-SG" },
 | 
				
			||||||
 | 
					            { "SI",  "sl-SI" },
 | 
				
			||||||
 | 
					            { "SK",  "sk-SK" },
 | 
				
			||||||
 | 
					            { "SN",  "wo-SN" },
 | 
				
			||||||
 | 
					            { "SV",  "es-SV" },
 | 
				
			||||||
 | 
					            { "SY",  "ar-SY" },
 | 
				
			||||||
 | 
					            { "TH",  "th-TH" },
 | 
				
			||||||
 | 
					            { "TJ",  "tg-Cyrl-TJ" },
 | 
				
			||||||
 | 
					            { "TM",  "tk-TM" },
 | 
				
			||||||
 | 
					            { "TN",  "ar-TN" },
 | 
				
			||||||
 | 
					            { "TR",  "tr-TR" },
 | 
				
			||||||
 | 
					            { "TT",  "en-TT" },
 | 
				
			||||||
 | 
					            { "TW",  "zh-TW" },
 | 
				
			||||||
 | 
					            { "UA",  "uk-UA" },
 | 
				
			||||||
 | 
					            { "US",  "en-US" },
 | 
				
			||||||
 | 
					            { "UY",  "es-UY" },
 | 
				
			||||||
 | 
					            { "UZ",  "uz-Cyrl-UZ" },
 | 
				
			||||||
 | 
					            { "VE",  "es-VE" },
 | 
				
			||||||
 | 
					            { "VN",  "vi-VN" },
 | 
				
			||||||
 | 
					            { "YE",  "ar-YE" },
 | 
				
			||||||
 | 
					            { "ZA",  "af-ZA" },
 | 
				
			||||||
 | 
					            { "ZW",  "en-ZW" }
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.Commands;
 | 
					using Discord.Commands;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using NadekoBot.Modules.Permissions;
 | 
					using NadekoBot.Modules.Permissions;
 | 
				
			||||||
@@ -21,45 +20,43 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class LogCommands : ModuleBase
 | 
					        public class LogCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private const string clockEmojiUrl = "https://cdn.discordapp.com/attachments/155726317222887425/258309524966866945/clock.png";
 | 
					            private static DiscordShardedClient client { get; }
 | 
				
			||||||
 | 
					            private new static Logger _log { get; }
 | 
				
			||||||
            private static DiscordShardedClient _client { get; }
 | 
					 | 
				
			||||||
            private static Logger _log { get; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
 | 
					            private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
 | 
				
			||||||
            private static string currentTime => $"{DateTime.Now:HH:mm:ss}";
 | 
					            private static string currentTime => $"{DateTime.Now:HH:mm:ss}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }
 | 
					            public static ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static ConcurrentDictionary<ITextChannel, List<string>> PresenceUpdates { get; } = new ConcurrentDictionary<ITextChannel, List<string>>();
 | 
					            private static ConcurrentDictionary<ITextChannel, List<string>> presenceUpdates { get; } = new ConcurrentDictionary<ITextChannel, List<string>>();
 | 
				
			||||||
            private static Timer timerReference { get; }
 | 
					            private static readonly Timer _timerReference;
 | 
				
			||||||
            private IGoogleApiService _google { get; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static LogCommands()
 | 
					            static LogCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = NadekoBot.Client;
 | 
					                client = NadekoBot.Client;
 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					                _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
                var sw = Stopwatch.StartNew();
 | 
					                var sw = Stopwatch.StartNew();
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
 | 
				
			||||||
                {
 | 
					                    .ToDictionary(g => g.GuildId, g => g.LogSetting));
 | 
				
			||||||
                    GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
 | 
					 | 
				
			||||||
                                                                                      .ToDictionary(g => g.GuildId, g => g.LogSetting));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                timerReference = new Timer(async (state) =>
 | 
					                _timerReference = new Timer(async (state) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var keys = PresenceUpdates.Keys.ToList();
 | 
					                        var keys = presenceUpdates.Keys.ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        await Task.WhenAll(keys.Select(async key =>
 | 
					                        await Task.WhenAll(keys.Select(async key =>
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            List<string> messages;
 | 
					                            List<string> messages;
 | 
				
			||||||
                            if (PresenceUpdates.TryRemove(key, out messages))
 | 
					                            if (presenceUpdates.TryRemove(key, out messages))
 | 
				
			||||||
                                try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } catch { }
 | 
					                                try { await key.SendConfirmAsync(key.Guild.GetLogText("presence_updates"), string.Join(Environment.NewLine, messages)); }
 | 
				
			||||||
 | 
					                                catch
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    // ignored
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                        }));
 | 
					                        }));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch (Exception ex)
 | 
					                    catch (Exception ex)
 | 
				
			||||||
@@ -72,23 +69,22 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
 | 
					                _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                //_client.MessageReceived += _client_MessageReceived;
 | 
					                //_client.MessageReceived += _client_MessageReceived;
 | 
				
			||||||
                _client.MessageUpdated += _client_MessageUpdated;
 | 
					                client.MessageUpdated += _client_MessageUpdated;
 | 
				
			||||||
                _client.MessageDeleted += _client_MessageDeleted;
 | 
					                client.MessageDeleted += _client_MessageDeleted;
 | 
				
			||||||
                _client.UserBanned += _client_UserBanned;
 | 
					                client.UserBanned += _client_UserBanned;
 | 
				
			||||||
                _client.UserUnbanned += _client_UserUnbanned;
 | 
					                client.UserUnbanned += _client_UserUnbanned;
 | 
				
			||||||
                _client.UserJoined += _client_UserJoined;
 | 
					                client.UserJoined += _client_UserJoined;
 | 
				
			||||||
                _client.UserLeft += _client_UserLeft;
 | 
					                client.UserLeft += _client_UserLeft;
 | 
				
			||||||
                _client.UserPresenceUpdated += _client_UserPresenceUpdated;
 | 
					                client.UserPresenceUpdated += _client_UserPresenceUpdated;
 | 
				
			||||||
                _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
 | 
					                client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
 | 
				
			||||||
                _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
 | 
					                client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
 | 
				
			||||||
                _client.GuildMemberUpdated += _client_GuildUserUpdated;
 | 
					                client.GuildMemberUpdated += _client_GuildUserUpdated;
 | 
				
			||||||
#if !GLOBAL_NADEKO
 | 
					#if !GLOBAL_NADEKO
 | 
				
			||||||
                _client.UserUpdated += _client_UserUpdated;
 | 
					                client.UserUpdated += _client_UserUpdated;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					                client.ChannelCreated += _client_ChannelCreated;
 | 
				
			||||||
                _client.ChannelCreated += _client_ChannelCreated;
 | 
					                client.ChannelDestroyed += _client_ChannelDestroyed;
 | 
				
			||||||
                _client.ChannelDestroyed += _client_ChannelDestroyed;
 | 
					                client.ChannelUpdated += _client_ChannelUpdated;
 | 
				
			||||||
                _client.ChannelUpdated += _client_ChannelUpdated;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                MuteCommands.UserMuted += MuteCommands_UserMuted;
 | 
					                MuteCommands.UserMuted += MuteCommands_UserMuted;
 | 
				
			||||||
                MuteCommands.UserUnmuted += MuteCommands_UserUnmuted;
 | 
					                MuteCommands.UserUnmuted += MuteCommands_UserUnmuted;
 | 
				
			||||||
@@ -119,7 +115,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if (before.Username != after.Username)
 | 
					                    if (before.Username != after.Username)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        embed.WithTitle("👥 Username Changed")
 | 
					                        embed.WithTitle("👥 " + g.GetLogText("username_changed"))
 | 
				
			||||||
                            .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
 | 
					                            .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
 | 
				
			||||||
                            .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true))
 | 
					                            .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true))
 | 
				
			||||||
                            .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true))
 | 
					                            .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true))
 | 
				
			||||||
@@ -128,7 +124,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (before.AvatarUrl != after.AvatarUrl)
 | 
					                    else if (before.AvatarUrl != after.AvatarUrl)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        embed.WithTitle("👥 Avatar Changed")
 | 
					                        embed.WithTitle("👥" + g.GetLogText("avatar_changed"))
 | 
				
			||||||
                            .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
 | 
					                            .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
 | 
				
			||||||
                            .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}")
 | 
					                            .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}")
 | 
				
			||||||
                            .WithThumbnailUrl(before.AvatarUrl)
 | 
					                            .WithThumbnailUrl(before.AvatarUrl)
 | 
				
			||||||
@@ -159,7 +155,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    //}
 | 
					                    //}
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                { }
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after)
 | 
					            private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after)
 | 
				
			||||||
@@ -188,20 +186,23 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    var str = "";
 | 
					                    var str = "";
 | 
				
			||||||
                    if (beforeVch?.Guild == afterVch?.Guild)
 | 
					                    if (beforeVch?.Guild == afterVch?.Guild)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        str = $"{usr.Username} moved from {beforeVch.Name} to {afterVch.Name}";
 | 
					                        str = logChannel.Guild.GetLogText("moved", usr.Username, beforeVch?.Name, afterVch?.Name);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (beforeVch == null)
 | 
					                    else if (beforeVch == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        str = $"{usr.Username} has joined {afterVch.Name}";
 | 
					                        str = logChannel.Guild.GetLogText("joined", usr.Username, afterVch.Name);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (afterVch == null)
 | 
					                    else if (afterVch == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        str = $"{usr.Username} has left {beforeVch.Name}";
 | 
					                        str = logChannel.Guild.GetLogText("left", usr.Username, beforeVch.Name);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false);
 | 
					                    var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false);
 | 
				
			||||||
                    toDelete.DeleteAfter(5);
 | 
					                    toDelete.DeleteAfter(5);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType)
 | 
					            private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType)
 | 
				
			||||||
@@ -216,28 +217,32 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    ITextChannel logChannel;
 | 
					                    ITextChannel logChannel;
 | 
				
			||||||
                    if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
 | 
					                    if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    string mutes = "";
 | 
					                    var mutes = "";
 | 
				
			||||||
 | 
					                    var mutedLocalized = logChannel.Guild.GetLogText("muted_sn");
 | 
				
			||||||
                    switch (muteType)
 | 
					                    switch (muteType)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        case MuteCommands.MuteType.Voice:
 | 
					                        case MuteCommands.MuteType.Voice:
 | 
				
			||||||
                            mutes = "voice chat";
 | 
					                            mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_voice", mutedLocalized);
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        case MuteCommands.MuteType.Chat:
 | 
					                        case MuteCommands.MuteType.Chat:
 | 
				
			||||||
                            mutes = "text chat";
 | 
					                            mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text", mutedLocalized);
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        case MuteCommands.MuteType.All:
 | 
					                        case MuteCommands.MuteType.All:
 | 
				
			||||||
                            mutes = "text and voice chat";
 | 
					                            mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", mutedLocalized);
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔇 User Muted from " + mutes))
 | 
					                    var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes))
 | 
				
			||||||
                            .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
 | 
					                            .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
 | 
				
			||||||
                            .WithFooter(fb => fb.WithText(currentTime))
 | 
					                            .WithFooter(fb => fb.WithText(currentTime))
 | 
				
			||||||
                            .WithOkColor();
 | 
					                            .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType)
 | 
					            private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType)
 | 
				
			||||||
@@ -253,28 +258,32 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
 | 
					                    if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    string mutes = "";
 | 
					                    var mutes = "";
 | 
				
			||||||
 | 
					                    var unmutedLocalized = logChannel.Guild.GetLogText("unmuted_sn");
 | 
				
			||||||
                    switch (muteType)
 | 
					                    switch (muteType)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        case MuteCommands.MuteType.Voice:
 | 
					                        case MuteCommands.MuteType.Voice:
 | 
				
			||||||
                            mutes = "voice chat";
 | 
					                            mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_voice", unmutedLocalized);
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        case MuteCommands.MuteType.Chat:
 | 
					                        case MuteCommands.MuteType.Chat:
 | 
				
			||||||
                            mutes = "text chat";
 | 
					                            mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text", unmutedLocalized);
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        case MuteCommands.MuteType.All:
 | 
					                        case MuteCommands.MuteType.All:
 | 
				
			||||||
                            mutes = "text and voice chat";
 | 
					                            mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", unmutedLocalized);
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔊 User Unmuted from " + mutes))
 | 
					                    var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes))
 | 
				
			||||||
                            .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
 | 
					                            .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
 | 
				
			||||||
                            .WithFooter(fb => fb.WithText($"{currentTime}"))
 | 
					                            .WithFooter(fb => fb.WithText($"{currentTime}"))
 | 
				
			||||||
                            .WithOkColor();
 | 
					                            .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection)
 | 
					            public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection)
 | 
				
			||||||
@@ -293,28 +302,31 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var punishment = "";
 | 
					                    var punishment = "";
 | 
				
			||||||
                    if (action == PunishmentAction.Mute)
 | 
					                    switch (action)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        punishment = "🔇 MUTED";
 | 
					                        case PunishmentAction.Mute:
 | 
				
			||||||
                    }
 | 
					                            punishment = "🔇 " + logChannel.Guild.GetLogText("muted_pl").ToUpperInvariant();
 | 
				
			||||||
                    else if (action == PunishmentAction.Kick)
 | 
					                            break;
 | 
				
			||||||
                    {
 | 
					                        case PunishmentAction.Kick:
 | 
				
			||||||
                        punishment = "☣ SOFT-BANNED (KICKED)";
 | 
					                            punishment = "☣ " + logChannel.Guild.GetLogText("soft_banned_pl").ToUpperInvariant();
 | 
				
			||||||
                    }
 | 
					                            break;
 | 
				
			||||||
                    else if (action == PunishmentAction.Ban)
 | 
					                        case PunishmentAction.Ban:
 | 
				
			||||||
                    {
 | 
					                            punishment = "⛔️ " + logChannel.Guild.GetLogText("banned_pl").ToUpperInvariant();
 | 
				
			||||||
                        punishment = "⛔️ BANNED";
 | 
					                            break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}"))
 | 
					                    var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}"))
 | 
				
			||||||
                            .WithTitle($"Users " + punishment)
 | 
					                            .WithTitle(logChannel.Guild.GetLogText("users") + " " + punishment)
 | 
				
			||||||
                            .WithDescription(String.Join("\n", users.Select(u => u.ToString())))
 | 
					                            .WithDescription(string.Join("\n", users.Select(u => u.ToString())))
 | 
				
			||||||
                            .WithFooter(fb => fb.WithText($"{currentTime}"))
 | 
					                            .WithFooter(fb => fb.WithText($"{currentTime}"))
 | 
				
			||||||
                            .WithOkColor();
 | 
					                            .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after)
 | 
					            private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after)
 | 
				
			||||||
@@ -333,23 +345,23 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
 | 
					                        .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
 | 
				
			||||||
                    if (before.Nickname != after.Nickname)
 | 
					                    if (before.Nickname != after.Nickname)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        embed.WithAuthor(eab => eab.WithName("👥 Nickname Changed"))
 | 
					                        embed.WithAuthor(eab => eab.WithName("👥 " + logChannel.Guild.GetLogText("nick_change")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            .AddField(efb => efb.WithName("Old Nickname").WithValue($"{before.Nickname}#{before.Discriminator}"))
 | 
					                            .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_nick")).WithValue($"{before.Nickname}#{before.Discriminator}"))
 | 
				
			||||||
                            .AddField(efb => efb.WithName("New Nickname").WithValue($"{after.Nickname}#{after.Discriminator}"));
 | 
					                            .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_nick")).WithValue($"{after.Nickname}#{after.Discriminator}"));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (!before.RoleIds.SequenceEqual(after.RoleIds))
 | 
					                    else if (!before.RoleIds.SequenceEqual(after.RoleIds))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (before.RoleIds.Count < after.RoleIds.Count)
 | 
					                        if (before.RoleIds.Count < after.RoleIds.Count)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
 | 
					                            var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
 | 
				
			||||||
                            embed.WithAuthor(eab => eab.WithName("⚔ User's Role Added"))
 | 
					                            embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_add")))
 | 
				
			||||||
                                .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
 | 
					                                .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else if (before.RoleIds.Count > after.RoleIds.Count)
 | 
					                        else if (before.RoleIds.Count > after.RoleIds.Count)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
 | 
					                            var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
 | 
				
			||||||
                            embed.WithAuthor(eab => eab.WithName("⚔ User's Role Removed"))
 | 
					                            embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_rem")))
 | 
				
			||||||
                                .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
 | 
					                                .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -357,7 +369,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter)
 | 
					            private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter)
 | 
				
			||||||
@@ -386,23 +401,26 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if (before.Name != after.Name)
 | 
					                    if (before.Name != after.Name)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        embed.WithTitle("ℹ️ Channel Name Changed")
 | 
					                        embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_name_change"))
 | 
				
			||||||
                            .WithDescription($"{after} | {after.Id}")
 | 
					                            .WithDescription($"{after} | {after.Id}")
 | 
				
			||||||
                            .AddField(efb => efb.WithName("Old Name").WithValue(before.Name));
 | 
					                            .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("ch_old_name")).WithValue(before.Name));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
 | 
					                    else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        embed.WithTitle("ℹ️ Channel Topic Changed")
 | 
					                        embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_topic_change"))
 | 
				
			||||||
                            .WithDescription($"{after} | {after.Id}")
 | 
					                            .WithDescription($"{after} | {after.Id}")
 | 
				
			||||||
                            .AddField(efb => efb.WithName("Old Topic").WithValue(beforeTextChannel.Topic))
 | 
					                            .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_topic")).WithValue(beforeTextChannel?.Topic ?? "-"))
 | 
				
			||||||
                            .AddField(efb => efb.WithName("New Topic").WithValue(afterTextChannel.Topic));
 | 
					                            .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_topic")).WithValue(afterTextChannel?.Topic ?? "-"));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_ChannelDestroyed(IChannel ich)
 | 
					            private static async Task _client_ChannelDestroyed(IChannel ich)
 | 
				
			||||||
@@ -422,14 +440,23 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    ITextChannel logChannel;
 | 
					                    ITextChannel logChannel;
 | 
				
			||||||
                    if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null)
 | 
					                    if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null)
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
 | 
					                    string title;
 | 
				
			||||||
 | 
					                    if (ch is IVoiceChannel)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        title = logChannel.Guild.GetLogText("voice_chan_destroyed");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        title = logChannel.Guild.GetLogText("text_chan_destroyed");
 | 
				
			||||||
                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
					                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed")
 | 
					                        .WithTitle("🆕 " + title)
 | 
				
			||||||
                        .WithDescription($"{ch.Name} | {ch.Id}")
 | 
					                        .WithDescription($"{ch.Name} | {ch.Id}")
 | 
				
			||||||
                        .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
 | 
					                        .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_ChannelCreated(IChannel ich)
 | 
					            private static async Task _client_ChannelCreated(IChannel ich)
 | 
				
			||||||
@@ -448,10 +475,16 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    ITextChannel logChannel;
 | 
					                    ITextChannel logChannel;
 | 
				
			||||||
                    if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null)
 | 
					                    if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null)
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
 | 
					                    string title;
 | 
				
			||||||
 | 
					                    if (ch is IVoiceChannel)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        title = logChannel.Guild.GetLogText("voice_chan_created");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        title = logChannel.Guild.GetLogText("text_chan_created");
 | 
				
			||||||
                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
					                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created")
 | 
					                        .WithTitle("🆕 " + title)
 | 
				
			||||||
                        .WithDescription($"{ch.Name} | {ch.Id}")
 | 
					                        .WithDescription($"{ch.Name} | {ch.Id}")
 | 
				
			||||||
                        .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
 | 
					                        .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -484,20 +517,29 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    string str = null;
 | 
					                    string str = null;
 | 
				
			||||||
                    if (beforeVch?.Guild == afterVch?.Guild)
 | 
					                    if (beforeVch?.Guild == afterVch?.Guild)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel.";
 | 
					                        str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vmoved",
 | 
				
			||||||
 | 
					                                "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
 | 
				
			||||||
 | 
					                                Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? ""));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (beforeVch == null)
 | 
					                    else if (beforeVch == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has joined **{afterVch.Name}** voice channel.";
 | 
					                        str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vjoined",
 | 
				
			||||||
 | 
					                                "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
 | 
				
			||||||
 | 
					                                Format.Bold(afterVch.Name ?? ""));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else if (afterVch == null)
 | 
					                    else if (afterVch == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel.";
 | 
					                        str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft",
 | 
				
			||||||
 | 
					                                "👤" + Format.Code(prettyCurrentTime), "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
 | 
				
			||||||
 | 
					                                Format.Bold(beforeVch.Name ?? ""));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    if (str != null)
 | 
					                    if (str != null)
 | 
				
			||||||
                        PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
 | 
					                        presenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
 | 
					            private static async Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
 | 
				
			||||||
@@ -520,7 +562,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    string str = "";
 | 
					                    string str = "";
 | 
				
			||||||
                    if (before.Status != after.Status)
 | 
					                    if (before.Status != after.Status)
 | 
				
			||||||
                        str = $"🎭`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**.";
 | 
					                        str = "🎭" + Format.Code(prettyCurrentTime) +
 | 
				
			||||||
 | 
					                              logChannel.Guild.GetLogText("user_status_change",
 | 
				
			||||||
 | 
					                                    "👤" + Format.Bold(usr.Username),
 | 
				
			||||||
 | 
					                                    Format.Bold(after.Status.ToString()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    //if (before.Game?.Name != after.Game?.Name)
 | 
					                    //if (before.Game?.Name != after.Game?.Name)
 | 
				
			||||||
                    //{
 | 
					                    //{
 | 
				
			||||||
@@ -529,9 +574,12 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    //    str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**.";
 | 
					                    //    str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**.";
 | 
				
			||||||
                    //}
 | 
					                    //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
 | 
					                    presenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_UserLeft(IGuildUser usr)
 | 
					            private static async Task _client_UserLeft(IGuildUser usr)
 | 
				
			||||||
@@ -549,13 +597,16 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
					                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle("❌ User Left")
 | 
					                        .WithTitle("❌ " + logChannel.Guild.GetLogText("user_left"))
 | 
				
			||||||
                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
					                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
				
			||||||
                        .WithDescription(usr.ToString())
 | 
					                        .WithDescription(usr.ToString())
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
					                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
				
			||||||
                        .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
 | 
					                        .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_UserJoined(IGuildUser usr)
 | 
					            private static async Task _client_UserJoined(IGuildUser usr)
 | 
				
			||||||
@@ -573,7 +624,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
					                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle("✅ User Joined")
 | 
					                        .WithTitle("✅ " + logChannel.Guild.GetLogText("user_joined"))
 | 
				
			||||||
                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
					                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
				
			||||||
                        .WithDescription($"{usr}")
 | 
					                        .WithDescription($"{usr}")
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
					                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
				
			||||||
@@ -597,7 +648,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
					                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle("♻️ User Unbanned")
 | 
					                        .WithTitle("♻️ " + logChannel.Guild.GetLogText("user_unbanned"))
 | 
				
			||||||
                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
					                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
				
			||||||
                        .WithDescription(usr.ToString())
 | 
					                        .WithDescription(usr.ToString())
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
					                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
				
			||||||
@@ -620,7 +671,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
					                    await logChannel.EmbedAsync(new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle("🚫 User Banned")
 | 
					                        .WithTitle("🚫 " + logChannel.Guild.GetLogText("user_banned"))
 | 
				
			||||||
                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
					                        .WithThumbnailUrl(usr.AvatarUrl)
 | 
				
			||||||
                        .WithDescription(usr.ToString())
 | 
					                        .WithDescription(usr.ToString())
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
					                        .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
 | 
				
			||||||
@@ -653,17 +704,20 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    var embed = new EmbedBuilder()
 | 
					                    var embed = new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle($"🗑 Message Deleted in #{((ITextChannel)msg.Channel).Name}")
 | 
					                        .WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name))
 | 
				
			||||||
                        .WithDescription($"{msg.Author}")
 | 
					                        .WithDescription($"{msg.Author}")
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
 | 
					                        .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false))
 | 
					                        .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false))
 | 
				
			||||||
                        .WithFooter(efb => efb.WithText(currentTime));
 | 
					                        .WithFooter(efb => efb.WithText(currentTime));
 | 
				
			||||||
                    if (msg.Attachments.Any())
 | 
					                    if (msg.Attachments.Any())
 | 
				
			||||||
                        embed.AddField(efb => efb.WithName("Attachments").WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false));
 | 
					                        embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task _client_MessageUpdated(Optional<SocketMessage> optmsg, SocketMessage imsg2)
 | 
					            private static async Task _client_MessageUpdated(Optional<SocketMessage> optmsg, SocketMessage imsg2)
 | 
				
			||||||
@@ -697,16 +751,19 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    var embed = new EmbedBuilder()
 | 
					                    var embed = new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle($"📝 Message Updated in #{((ITextChannel)after.Channel).Name}")
 | 
					                        .WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name))
 | 
				
			||||||
                        .WithDescription(after.Author.ToString())
 | 
					                        .WithDescription(after.Author.ToString())
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
 | 
					                        .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
 | 
				
			||||||
                        .AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
 | 
					                        .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
 | 
				
			||||||
                        .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false))
 | 
					                        .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false))
 | 
				
			||||||
                        .WithFooter(efb => efb.WithText(currentTime));
 | 
					                        .WithFooter(efb => efb.WithText(currentTime));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                    await logChannel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public enum LogType
 | 
					            public enum LogType
 | 
				
			||||||
@@ -778,8 +835,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    case LogType.UserMuted:
 | 
					                    case LogType.UserMuted:
 | 
				
			||||||
                        id = logSetting.UserMutedId;
 | 
					                        id = logSetting.UserMutedId;
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    default:
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!id.HasValue)
 | 
					                if (!id.HasValue)
 | 
				
			||||||
@@ -850,8 +905,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        case LogType.VoicePresenceTTS:
 | 
					                        case LogType.VoicePresenceTTS:
 | 
				
			||||||
                            newLogSetting.LogVoicePresenceTTSId = null;
 | 
					                            newLogSetting.LogVoicePresenceTTSId = null;
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        default:
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting);
 | 
					                    GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting);
 | 
				
			||||||
                    uow.Complete();
 | 
					                    uow.Complete();
 | 
				
			||||||
@@ -895,9 +948,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (action.Value)
 | 
					                if (action.Value)
 | 
				
			||||||
                    await channel.SendConfirmAsync("Logging all events in this channel.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("log_all").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await channel.SendConfirmAsync("Logging disabled.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("log_disabled").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -924,9 +977,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (removed == 0)
 | 
					                if (removed == 0)
 | 
				
			||||||
                    await channel.SendConfirmAsync($"Logging will IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("log_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await channel.SendConfirmAsync($"Logging will NOT IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("log_not_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -935,7 +988,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task LogEvents()
 | 
					            public async Task LogEvents()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("Log events you can subscribe to:", String.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>()));
 | 
					                await ReplyConfirmLocalized("log_events", string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>())).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -1003,10 +1056,19 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (channelId != null)
 | 
					                if (channelId != null)
 | 
				
			||||||
                    await channel.SendConfirmAsync($"Logging **{type}** event in this channel.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("log", Format.Bold(type.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await channel.SendConfirmAsync($"Stopped logging **{type}** event.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("log_stop", Format.Bold(type.ToString())).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class GuildExtensions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static string GetLogText(this IGuild guild, string key, params object[] replacements)
 | 
				
			||||||
 | 
					            => NadekoModule.GetTextStatic(key,
 | 
				
			||||||
 | 
					                NadekoBot.Localization.GetCultureInfo(guild),
 | 
				
			||||||
 | 
					                typeof(Administration).Name.ToLowerInvariant(),
 | 
				
			||||||
 | 
					                replacements);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -20,11 +20,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class Migration : ModuleBase
 | 
					        public class Migration : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private const int CURRENT_VERSION = 1;
 | 
					            private const int CURRENT_VERSION = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					            private new static readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static Migration()
 | 
					            static Migration()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -51,12 +51,12 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                                break;
 | 
					                                break;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await Context.Channel.SendMessageAsync("🆙 **Migration done.**").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("migration_done").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _log.Error(ex);
 | 
					                    _log.Error(ex);
 | 
				
			||||||
                    await Context.Channel.SendMessageAsync("⚠️ **Error while migrating, check `logs` for more informations.**").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("migration_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -103,11 +103,8 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        var greetChannel = (ulong)(long)reader["GreetChannelId"];
 | 
					                        var greetChannel = (ulong)(long)reader["GreetChannelId"];
 | 
				
			||||||
                        var greetMsg = (string)reader["GreetText"];
 | 
					                        var greetMsg = (string)reader["GreetText"];
 | 
				
			||||||
                        var bye = (long)reader["Bye"] == 1;
 | 
					                        var bye = (long)reader["Bye"] == 1;
 | 
				
			||||||
                        var byeDM = (long)reader["ByePM"] == 1;
 | 
					 | 
				
			||||||
                        var byeChannel = (ulong)(long)reader["ByeChannelId"];
 | 
					                        var byeChannel = (ulong)(long)reader["ByeChannelId"];
 | 
				
			||||||
                        var byeMsg = (string)reader["ByeText"];
 | 
					                        var byeMsg = (string)reader["ByeText"];
 | 
				
			||||||
                        var grdel = false;
 | 
					 | 
				
			||||||
                        var byedel = grdel;
 | 
					 | 
				
			||||||
                        var gc = uow.GuildConfigs.For(gid, set => set);
 | 
					                        var gc = uow.GuildConfigs.For(gid, set => set);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (greetDM)
 | 
					                        if (greetDM)
 | 
				
			||||||
@@ -121,7 +118,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        gc.ByeMessageChannelId = byeChannel;
 | 
					                        gc.ByeMessageChannelId = byeChannel;
 | 
				
			||||||
                        gc.ChannelByeMessageText = byeMsg;
 | 
					                        gc.ChannelByeMessageText = byeMsg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        gc.AutoDeleteGreetMessagesTimer = gc.AutoDeleteByeMessagesTimer = grdel ? 30 : 0;
 | 
					 | 
				
			||||||
                        _log.Info(++i);
 | 
					                        _log.Info(++i);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -129,12 +125,12 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    _log.Warn("Greet/bye messages won't be migrated");
 | 
					                    _log.Warn("Greet/bye messages won't be migrated");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var com2 = db.CreateCommand();
 | 
					                var com2 = db.CreateCommand();
 | 
				
			||||||
                com.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId";
 | 
					                com2.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                i = 0;
 | 
					                i = 0;
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var reader2 = com.ExecuteReader();
 | 
					                    var reader2 = com2.ExecuteReader();
 | 
				
			||||||
                    while (reader2.Read())
 | 
					                    while (reader2.Read())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        _log.Info(++i);
 | 
					                        _log.Info(++i);
 | 
				
			||||||
@@ -203,7 +199,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles;
 | 
					                            guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles;
 | 
				
			||||||
                            guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
 | 
					                            guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
 | 
				
			||||||
                            selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
 | 
					                            selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
 | 
				
			||||||
                            var logSetting = guildConfig.LogSetting;
 | 
					 | 
				
			||||||
                            guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
 | 
					                            guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
 | 
					                            guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
 | 
				
			||||||
@@ -249,7 +244,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            private void MigratePermissions0_9(IUnitOfWork uow)
 | 
					            private void MigratePermissions0_9(IUnitOfWork uow)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var PermissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>();
 | 
					                var permissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>();
 | 
				
			||||||
                if (!Directory.Exists("data/permissions/"))
 | 
					                if (!Directory.Exists("data/permissions/"))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _log.Warn("No data from permissions will be migrated.");
 | 
					                    _log.Warn("No data from permissions will be migrated.");
 | 
				
			||||||
@@ -263,12 +258,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        if (string.IsNullOrWhiteSpace(strippedFileName)) continue;
 | 
					                        if (string.IsNullOrWhiteSpace(strippedFileName)) continue;
 | 
				
			||||||
                        var id = ulong.Parse(strippedFileName);
 | 
					                        var id = ulong.Parse(strippedFileName);
 | 
				
			||||||
                        var data = JsonConvert.DeserializeObject<ServerPermissions0_9>(File.ReadAllText(file));
 | 
					                        var data = JsonConvert.DeserializeObject<ServerPermissions0_9>(File.ReadAllText(file));
 | 
				
			||||||
                        PermissionsDict.TryAdd(id, data);
 | 
					                        permissionsDict.TryAdd(id, data);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // ignored
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch { }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var i = 0;
 | 
					                var i = 0;
 | 
				
			||||||
                PermissionsDict
 | 
					                permissionsDict
 | 
				
			||||||
                    .Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) })
 | 
					                    .Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) })
 | 
				
			||||||
                    .AsParallel()
 | 
					                    .AsParallel()
 | 
				
			||||||
                    .ForAll(perms =>
 | 
					                    .ForAll(perms =>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,6 @@ using NadekoBot.Services.Database.Models;
 | 
				
			|||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Diagnostics;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,11 +16,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class MuteCommands : ModuleBase
 | 
					        public class MuteCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>();
 | 
					            private static ConcurrentDictionary<ulong, string> guildMuteRoles { get; }
 | 
				
			||||||
 | 
					            private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> mutedUsers { get; }
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
 | 
					            public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
 | 
				
			||||||
            public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
 | 
					            public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
 | 
				
			||||||
@@ -36,14 +33,12 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            static MuteCommands()
 | 
					            static MuteCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var _log = LogManager.GetCurrentClassLogger();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var configs = NadekoBot.AllGuildConfigs;
 | 
					                var configs = NadekoBot.AllGuildConfigs;
 | 
				
			||||||
                GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
 | 
					                guildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
 | 
				
			||||||
                        .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
 | 
					                        .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
 | 
				
			||||||
                        .ToDictionary(c => c.GuildId, c => c.MuteRoleName));
 | 
					                        .ToDictionary(c => c.GuildId, c => c.MuteRoleName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
 | 
					                mutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
 | 
				
			||||||
                    k => k.GuildId,
 | 
					                    k => k.GuildId,
 | 
				
			||||||
                    v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
 | 
					                    v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
 | 
				
			||||||
                ));
 | 
					                ));
 | 
				
			||||||
@@ -56,16 +51,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    ConcurrentHashSet<ulong> muted;
 | 
					                    ConcurrentHashSet<ulong> muted;
 | 
				
			||||||
                    MutedUsers.TryGetValue(usr.Guild.Id, out muted);
 | 
					                    mutedUsers.TryGetValue(usr.Guild.Id, out muted);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (muted == null || !muted.Contains(usr.Id))
 | 
					                    if (muted == null || !muted.Contains(usr.Id))
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    else
 | 
					                    await MuteUser(usr).ConfigureAwait(false);
 | 
				
			||||||
                        await MuteUser(usr).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _log.Warn(ex);
 | 
					                    LogManager.GetCurrentClassLogger().Warn(ex);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -82,7 +76,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        UserId = usr.Id
 | 
					                        UserId = usr.Id
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    ConcurrentHashSet<ulong> muted;
 | 
					                    ConcurrentHashSet<ulong> muted;
 | 
				
			||||||
                    if (MutedUsers.TryGetValue(usr.Guild.Id, out muted))
 | 
					                    if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
 | 
				
			||||||
                        muted.Add(usr.Id);
 | 
					                        muted.Add(usr.Id);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
@@ -102,18 +96,18 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        UserId = usr.Id
 | 
					                        UserId = usr.Id
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    ConcurrentHashSet<ulong> muted;
 | 
					                    ConcurrentHashSet<ulong> muted;
 | 
				
			||||||
                    if (MutedUsers.TryGetValue(usr.Guild.Id, out muted))
 | 
					                    if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
 | 
				
			||||||
                        muted.TryRemove(usr.Id);
 | 
					                        muted.TryRemove(usr.Id);
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                UserUnmuted(usr, MuteType.All);
 | 
					                UserUnmuted(usr, MuteType.All);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static async Task<IRole> GetMuteRole(IGuild guild)
 | 
					            public static async Task<IRole>GetMuteRole(IGuild guild)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                const string defaultMuteRoleName = "nadeko-mute";
 | 
					                const string defaultMuteRoleName = "nadeko-mute";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
 | 
					                var muteRoleName = guildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
 | 
					                var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
 | 
				
			||||||
                if (muteRole == null)
 | 
					                if (muteRole == null)
 | 
				
			||||||
@@ -135,7 +129,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny))
 | 
					                            await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny))
 | 
				
			||||||
                                    .ConfigureAwait(false);
 | 
					                                    .ConfigureAwait(false);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        catch { }
 | 
					                        catch
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // ignored
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        await Task.Delay(200).ConfigureAwait(false);
 | 
					                        await Task.Delay(200).ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -148,7 +145,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [Priority(1)]
 | 
					            [Priority(1)]
 | 
				
			||||||
            public async Task SetMuteRole([Remainder] string name)
 | 
					            public async Task SetMuteRole([Remainder] string name)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
                name = name.Trim();
 | 
					                name = name.Trim();
 | 
				
			||||||
                if (string.IsNullOrWhiteSpace(name))
 | 
					                if (string.IsNullOrWhiteSpace(name))
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
@@ -157,10 +153,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
					                    var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
				
			||||||
                    config.MuteRoleName = name;
 | 
					                    config.MuteRoleName = name;
 | 
				
			||||||
                    GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
 | 
					                    guildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -179,11 +175,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await MuteUser(user).ConfigureAwait(false);
 | 
					                    await MuteUser(user).ConfigureAwait(false);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"🔇 **{user}** has been **muted** from text and voice chat.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -196,11 +192,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await UnmuteUser(user).ConfigureAwait(false);
 | 
					                    await UnmuteUser(user).ConfigureAwait(false);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"🔉 **{user}** has been **unmuted** from text and voice chat.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -213,11 +209,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
 | 
					                    await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
 | 
				
			||||||
                    UserMuted(user, MuteType.Chat);
 | 
					                    UserMuted(user, MuteType.Chat);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -230,11 +226,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
 | 
					                    await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
 | 
				
			||||||
                    UserUnmuted(user, MuteType.Chat);
 | 
					                    UserUnmuted(user, MuteType.Chat);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -247,11 +243,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
 | 
					                    await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
 | 
				
			||||||
                    UserMuted(user, MuteType.Voice);
 | 
					                    UserMuted(user, MuteType.Voice);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -264,11 +260,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
 | 
					                    await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
 | 
				
			||||||
                    UserUnmuted(user, MuteType.Voice);
 | 
					                    UserUnmuted(user, MuteType.Voice);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch
 | 
					                catch
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord.Commands;
 | 
				
			||||||
using Discord.Commands;
 | 
					 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
@@ -8,7 +7,6 @@ using NadekoBot.Services.Database.Models;
 | 
				
			|||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Diagnostics;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
@@ -18,16 +16,17 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class PlayingRotateCommands : ModuleBase
 | 
					        public class PlayingRotateCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					 | 
				
			||||||
            public static List<PlayingStatus> RotatingStatusMessages { get; }
 | 
					            public static List<PlayingStatus> RotatingStatusMessages { get; }
 | 
				
			||||||
            public static bool RotatingStatuses { get; private set; } = false;
 | 
					            public static volatile bool RotatingStatuses;
 | 
				
			||||||
            private static Timer _t { get; }
 | 
					            private readonly object _locker = new object();
 | 
				
			||||||
 | 
					            private new static Logger _log { get; }
 | 
				
			||||||
 | 
					            private static readonly Timer _t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private class TimerState
 | 
					            private class TimerState
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                public int Index { get; set; } = 0;
 | 
					                public int Index { get; set; }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static PlayingRotateCommands()
 | 
					            static PlayingRotateCommands()
 | 
				
			||||||
@@ -37,8 +36,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
 | 
					                RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
 | 
				
			||||||
                RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
 | 
					                RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _t = new Timer(async (objState) =>
 | 
					                _t = new Timer(async (objState) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
@@ -46,26 +43,24 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        var state = (TimerState)objState;
 | 
					                        var state = (TimerState)objState;
 | 
				
			||||||
                        if (!RotatingStatuses)
 | 
					                        if (!RotatingStatuses)
 | 
				
			||||||
                            return;
 | 
					                            return;
 | 
				
			||||||
                        else
 | 
					                        if (state.Index >= RotatingStatusMessages.Count)
 | 
				
			||||||
                        {
 | 
					                            state.Index = 0;
 | 
				
			||||||
                            if (state.Index >= RotatingStatusMessages.Count)
 | 
					 | 
				
			||||||
                                state.Index = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            if (!RotatingStatusMessages.Any())
 | 
					                        if (!RotatingStatusMessages.Any())
 | 
				
			||||||
                                return;
 | 
					                            return;
 | 
				
			||||||
                            var status = RotatingStatusMessages[state.Index++].Status;
 | 
					                        var status = RotatingStatusMessages[state.Index++].Status;
 | 
				
			||||||
                            if (string.IsNullOrWhiteSpace(status))
 | 
					                        if (string.IsNullOrWhiteSpace(status))
 | 
				
			||||||
                                return;
 | 
					                            return;
 | 
				
			||||||
                            PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
 | 
					                        PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
 | 
				
			||||||
                            var shards = NadekoBot.Client.Shards;
 | 
					                        var shards = NadekoBot.Client.Shards;
 | 
				
			||||||
                            for (int i = 0; i < shards.Count; i++)
 | 
					                        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)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(shards.ElementAt(i))));
 | 
					                                _log.Warn(ex);
 | 
				
			||||||
                                try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
 | 
					 | 
				
			||||||
                                catch (Exception ex)
 | 
					 | 
				
			||||||
                                {
 | 
					 | 
				
			||||||
                                    _log.Warn(ex);
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -107,17 +102,20 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task RotatePlaying()
 | 
					            public async Task RotatePlaying()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                lock (_locker)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
					                    using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var config = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
 | 
					                        RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
 | 
				
			||||||
                    await uow.CompleteAsync();
 | 
					                        uow.Complete();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (RotatingStatuses)
 | 
					                if (RotatingStatuses)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ **Rotating playing status disabled.**").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -133,7 +131,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await uow.CompleteAsync();
 | 
					                    await uow.CompleteAsync();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("ropl_added").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -141,11 +139,13 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            public async Task ListPlaying()
 | 
					            public async Task ListPlaying()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!RotatingStatusMessages.Any())
 | 
					                if (!RotatingStatusMessages.Any())
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("❎ **No rotating playing statuses set.**");
 | 
					                    await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var i = 1;
 | 
					                    var i = 1;
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"ℹ️ {Context.User.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")));
 | 
					                    await ReplyConfirmLocalized("ropl_list",
 | 
				
			||||||
 | 
					                            string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
 | 
				
			||||||
 | 
					                        .ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                index -= 1;
 | 
					                index -= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                string msg = "";
 | 
					                string msg;
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var config = uow.BotConfig.GetOrCreate();
 | 
					                    var config = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
@@ -168,7 +168,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    RotatingStatusMessages.RemoveAt(index);
 | 
					                    RotatingStatusMessages.RemoveAt(index);
 | 
				
			||||||
                    await uow.CompleteAsync();
 | 
					                    await uow.CompleteAsync();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,10 @@ using Microsoft.EntityFrameworkCore;
 | 
				
			|||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using NadekoBot.Services.Database;
 | 
					 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,12 +24,8 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        public class AntiRaidStats
 | 
					        public class AntiRaidStats
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public AntiRaidSetting AntiRaidSettings { get; set; }
 | 
					            public AntiRaidSetting AntiRaidSettings { get; set; }
 | 
				
			||||||
            public int UsersCount { get; set; } = 0;
 | 
					            public int UsersCount { get; set; }
 | 
				
			||||||
            public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
 | 
					            public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
            public override string ToString() =>
 | 
					 | 
				
			||||||
                $"If **{AntiRaidSettings.UserThreshold}** or more users join within **{AntiRaidSettings.Seconds}** seconds," +
 | 
					 | 
				
			||||||
                $" I will **{AntiRaidSettings.Action}** them.";
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public class AntiSpamStats
 | 
					        public class AntiSpamStats
 | 
				
			||||||
@@ -39,16 +33,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            public AntiSpamSetting AntiSpamSettings { get; set; }
 | 
					            public AntiSpamSetting AntiSpamSettings { get; set; }
 | 
				
			||||||
            public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
 | 
					            public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
 | 
				
			||||||
                = new ConcurrentDictionary<ulong, UserSpamStats>();
 | 
					                = new ConcurrentDictionary<ulong, UserSpamStats>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
            public override string ToString()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var ignoredString = string.Join(", ", AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (string.IsNullOrWhiteSpace(ignoredString))
 | 
					 | 
				
			||||||
                    ignoredString = "none";
 | 
					 | 
				
			||||||
                return $"If a user posts **{AntiSpamSettings.MessageThreshold}** same messages in a row, I will **{AntiSpamSettings.Action}** them."
 | 
					 | 
				
			||||||
                + $"\n\t__IgnoredChannels__: {ignoredString}";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public class UserSpamStats
 | 
					        public class UserSpamStats
 | 
				
			||||||
@@ -76,15 +60,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class ProtectionCommands : ModuleBase
 | 
					        public class ProtectionCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, AntiRaidStats> antiRaidGuilds =
 | 
					            private static readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds =
 | 
				
			||||||
                    new ConcurrentDictionary<ulong, AntiRaidStats>();
 | 
					                    new ConcurrentDictionary<ulong, AntiRaidStats>();
 | 
				
			||||||
            // guildId | (userId|messages)
 | 
					            // guildId | (userId|messages)
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, AntiSpamStats> antiSpamGuilds =
 | 
					            private static readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds =
 | 
				
			||||||
                    new ConcurrentDictionary<ulong, AntiSpamStats>();
 | 
					                    new ConcurrentDictionary<ulong, AntiSpamStats>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					            private new static readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static ProtectionCommands()
 | 
					            static ProtectionCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -98,11 +82,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    if (raid != null)
 | 
					                    if (raid != null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
 | 
					                        var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
 | 
				
			||||||
                        antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
 | 
					                        _antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (spam != null)
 | 
					                    if (spam != null)
 | 
				
			||||||
                        antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
 | 
					                        _antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                NadekoBot.Client.MessageReceived += (imsg) =>
 | 
					                NadekoBot.Client.MessageReceived += (imsg) =>
 | 
				
			||||||
@@ -119,7 +103,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        try
 | 
					                        try
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            AntiSpamStats spamSettings;
 | 
					                            AntiSpamStats spamSettings;
 | 
				
			||||||
                            if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
 | 
					                            if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
 | 
				
			||||||
                                spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
 | 
					                                spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
 | 
				
			||||||
                                {
 | 
					                                {
 | 
				
			||||||
                                    ChannelId = channel.Id
 | 
					                                    ChannelId = channel.Id
 | 
				
			||||||
@@ -141,7 +125,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        catch { }
 | 
					                        catch
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // ignored
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    return Task.CompletedTask;
 | 
					                    return Task.CompletedTask;
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
@@ -151,7 +138,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    if (usr.IsBot)
 | 
					                    if (usr.IsBot)
 | 
				
			||||||
                        return Task.CompletedTask;
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
                    AntiRaidStats settings;
 | 
					                    AntiRaidStats settings;
 | 
				
			||||||
                    if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
 | 
					                    if (!_antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
 | 
				
			||||||
                        return Task.CompletedTask;
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
                    if (!settings.RaidUsers.Add(usr))
 | 
					                    if (!settings.RaidUsers.Add(usr))
 | 
				
			||||||
                        return Task.CompletedTask;
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
@@ -175,7 +162,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            --settings.UsersCount;
 | 
					                            --settings.UsersCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        catch { }
 | 
					                        catch
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // ignored
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    return Task.CompletedTask;
 | 
					                    return Task.CompletedTask;
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
@@ -219,13 +209,27 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
 | 
					                            catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        default:
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false);
 | 
					                await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private string GetAntiSpamString(AntiSpamStats stats)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var ignoredString = string.Join(", ", stats.AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (string.IsNullOrWhiteSpace(ignoredString))
 | 
				
			||||||
 | 
					                    ignoredString = "none";
 | 
				
			||||||
 | 
					                return GetText("spam_stats",
 | 
				
			||||||
 | 
					                        Format.Bold(stats.AntiSpamSettings.MessageThreshold.ToString()), 
 | 
				
			||||||
 | 
					                        Format.Bold(stats.AntiSpamSettings.Action.ToString()), 
 | 
				
			||||||
 | 
					                        ignoredString);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private string GetAntiRaidString(AntiRaidStats stats) => GetText("raid_stats",
 | 
				
			||||||
 | 
					                Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()),
 | 
				
			||||||
 | 
					                Format.Bold(stats.AntiRaidSettings.Seconds.ToString()),
 | 
				
			||||||
 | 
					                Format.Bold(stats.AntiRaidSettings.Action.ToString()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
@@ -234,18 +238,18 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (userThreshold < 2 || userThreshold > 30)
 | 
					                if (userThreshold < 2 || userThreshold > 30)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("❗️User threshold must be between **2** and **30**.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("raid_cnt", 2, 30).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (seconds < 2 || seconds > 300)
 | 
					                if (seconds < 2 || seconds > 300)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("❗️Time must be between **2** and **300** seconds.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("raid_time", 2, 300).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AntiRaidStats throwaway;
 | 
					                AntiRaidStats throwaway;
 | 
				
			||||||
                if (antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
					                if (_antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    using (var uow = DbHandler.UnitOfWork())
 | 
					                    using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@@ -254,7 +258,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        gc.AntiRaidSetting = null;
 | 
					                        gc.AntiRaidSetting = null;
 | 
				
			||||||
                        await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                        await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("**Anti-Raid** feature has been **disabled** on this server.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("prot_disable", "Anti-Raid").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -264,10 +268,8 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
 | 
					 | 
				
			||||||
                        "or create 'nadeko-mute' role with disabled SendMessages and try again.")
 | 
					 | 
				
			||||||
                            .ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    _log.Warn(ex);
 | 
					                    _log.Warn(ex);
 | 
				
			||||||
 | 
					                    await ReplyErrorLocalized("prot_error").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -281,7 +283,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
					                _antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -291,7 +293,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("Anti-Raid Enabled", $"{Context.User.Mention} {stats.ToString()}")
 | 
					                await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Raid"), $"{Context.User.Mention} {GetAntiRaidString(stats)}")
 | 
				
			||||||
                        .ConfigureAwait(false);
 | 
					                        .ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -304,7 +306,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AntiSpamStats throwaway;
 | 
					                AntiSpamStats throwaway;
 | 
				
			||||||
                if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
					                if (_antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    using (var uow = DbHandler.UnitOfWork())
 | 
					                    using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@@ -314,7 +316,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                        gc.AntiSpamSetting = null;
 | 
					                        gc.AntiSpamSetting = null;
 | 
				
			||||||
                        await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                        await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("**Anti-Spam** has been **disabled** on this server.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("prot_disable", "Anti-Spam").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -324,10 +326,8 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
 | 
					 | 
				
			||||||
                        "or create 'nadeko-mute' role with disabled SendMessages and try again.")
 | 
					 | 
				
			||||||
                            .ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    _log.Warn(ex);
 | 
					                    _log.Warn(ex);
 | 
				
			||||||
 | 
					                    await ReplyErrorLocalized("prot_error").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -340,7 +340,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
					                _antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -350,7 +350,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("Anti-Spam Enabled", $"{Context.User.Mention} {stats.ToString()}").ConfigureAwait(false);
 | 
					                await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Spam"), $"{Context.User.Mention} {GetAntiSpamString(stats)}").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -376,7 +376,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    if (spam.IgnoredChannels.Add(obj))
 | 
					                    if (spam.IgnoredChannels.Add(obj))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        AntiSpamStats temp;
 | 
					                        AntiSpamStats temp;
 | 
				
			||||||
                        if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
					                        if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
				
			||||||
                            temp.AntiSpamSettings.IgnoredChannels.Add(obj);
 | 
					                            temp.AntiSpamSettings.IgnoredChannels.Add(obj);
 | 
				
			||||||
                        added = true;
 | 
					                        added = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -384,7 +384,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        spam.IgnoredChannels.Remove(obj);
 | 
					                        spam.IgnoredChannels.Remove(obj);
 | 
				
			||||||
                        AntiSpamStats temp;
 | 
					                        AntiSpamStats temp;
 | 
				
			||||||
                        if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
					                        if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
 | 
				
			||||||
                            temp.AntiSpamSettings.IgnoredChannels.Remove(obj);
 | 
					                            temp.AntiSpamSettings.IgnoredChannels.Remove(obj);
 | 
				
			||||||
                        added = false;
 | 
					                        added = false;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -392,9 +392,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (added)
 | 
					                if (added)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("Anti-Spam will ignore this channel.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("spam_ignore", "Anti-Spam").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("Anti-Spam will no longer ignore this channel.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("spam_not_ignore", "Anti-Spam").ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -402,31 +402,29 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task AntiList()
 | 
					            public async Task AntiList()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                AntiSpamStats spam;
 | 
					                AntiSpamStats spam;
 | 
				
			||||||
                antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
 | 
					                _antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AntiRaidStats raid;
 | 
					                AntiRaidStats raid;
 | 
				
			||||||
                antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
 | 
					                _antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (spam == null && raid == null)
 | 
					                if (spam == null && raid == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("No protections enabled.");
 | 
					                    await ReplyConfirmLocalized("prot_none").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var embed = new EmbedBuilder().WithOkColor()
 | 
					                var embed = new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                    .WithTitle("Protections Enabled");
 | 
					                    .WithTitle(GetText("prot_active"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (spam != null)
 | 
					                if (spam != null)
 | 
				
			||||||
                    embed.AddField(efb => efb.WithName("Anti-Spam")
 | 
					                    embed.AddField(efb => efb.WithName("Anti-Spam")
 | 
				
			||||||
                        .WithValue(spam.ToString())
 | 
					                        .WithValue(GetAntiSpamString(spam))
 | 
				
			||||||
                        .WithIsInline(true));
 | 
					                        .WithIsInline(true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (raid != null)
 | 
					                if (raid != null)
 | 
				
			||||||
                    embed.AddField(efb => efb.WithName("Anti-Raid")
 | 
					                    embed.AddField(efb => efb.WithName("Anti-Raid")
 | 
				
			||||||
                        .WithValue(raid.ToString())
 | 
					                        .WithValue(GetAntiRaidString(raid))
 | 
				
			||||||
                        .WithIsInline(true));
 | 
					                        .WithIsInline(true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class RatelimitCommand : ModuleBase
 | 
					        public class RatelimitCommand : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
 | 
					            public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					            private new static readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public class Ratelimiter
 | 
					            public class Ratelimiter
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -37,26 +37,22 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                public bool CheckUserRatelimit(ulong id)
 | 
					                public bool CheckUserRatelimit(ulong id)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    RatelimitedUser usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
 | 
					                    var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
 | 
				
			||||||
                    if (usr.MessageCount == MaxMessages)
 | 
					                    if (usr.MessageCount == MaxMessages)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return true;
 | 
					                        return true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    usr.MessageCount++;
 | 
				
			||||||
 | 
					                    var _ = Task.Run(async () =>
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        usr.MessageCount++;
 | 
					                        try
 | 
				
			||||||
                        var t = Task.Run(async () =>
 | 
					 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            try
 | 
					                            await Task.Delay(PerSeconds * 1000, cancelSource.Token);
 | 
				
			||||||
                            {
 | 
					                        }
 | 
				
			||||||
                                await Task.Delay(PerSeconds * 1000, cancelSource.Token);
 | 
					                        catch (OperationCanceledException) { }
 | 
				
			||||||
                            }
 | 
					                        usr.MessageCount--;
 | 
				
			||||||
                            catch (OperationCanceledException) { }
 | 
					                    });
 | 
				
			||||||
                            usr.MessageCount--;
 | 
					                    return false;
 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                        return false;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,9 +65,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                     try
 | 
					                     try
 | 
				
			||||||
                     {
 | 
					                     {
 | 
				
			||||||
                         var usrMsg = umsg as IUserMessage;
 | 
					                         var usrMsg = umsg as IUserMessage;
 | 
				
			||||||
                         if (usrMsg == null)
 | 
					                         var channel = usrMsg?.Channel as ITextChannel;
 | 
				
			||||||
                             return;
 | 
					 | 
				
			||||||
                         var channel = usrMsg.Channel as ITextChannel;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                         if (channel == null || usrMsg.IsAuthor())
 | 
					                         if (channel == null || usrMsg.IsAuthor())
 | 
				
			||||||
                             return;
 | 
					                             return;
 | 
				
			||||||
@@ -95,8 +89,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway))
 | 
					                if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    throwaway.cancelSource.Cancel();
 | 
					                    throwaway.cancelSource.Cancel();
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Slow mode disabled.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("slowmode_disabled").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,7 +102,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                
 | 
					                
 | 
				
			||||||
                if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
 | 
					                if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ Invalid parameters.");
 | 
					                    await ReplyErrorLocalized("invalid_params").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var toAdd = new Ratelimiter()
 | 
					                var toAdd = new Ratelimiter()
 | 
				
			||||||
@@ -120,8 +113,8 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                };
 | 
					                };
 | 
				
			||||||
                if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
 | 
					                if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("Slow mode initiated",
 | 
					                    await Context.Channel.SendConfirmAsync(GetText("slowmode_init"),
 | 
				
			||||||
                                                $"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.")
 | 
					                            GetText("slowmode_desc", Format.Bold(toAdd.MaxMessages.ToString()), Format.Bold(toAdd.PerSeconds.ToString())))
 | 
				
			||||||
                                                .ConfigureAwait(false);
 | 
					                                                .ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class SelfAssignedRolesCommands : ModuleBase
 | 
					        public class SelfAssignedRolesCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -44,25 +44,30 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                IEnumerable<SelfAssignedRole> roles;
 | 
					                IEnumerable<SelfAssignedRole> roles;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                string msg;
 | 
					                string msg;
 | 
				
			||||||
 | 
					                var error = false;
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
					                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
				
			||||||
                    if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
 | 
					                    if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendMessageAsync($"💢 Role **{role.Name}** is already in the list.").ConfigureAwait(false);
 | 
					                        msg = GetText("role_in_list", Format.Bold(role.Name));
 | 
				
			||||||
                        return;
 | 
					                        error = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        uow.SelfAssignedRoles.Add(new SelfAssignedRole {
 | 
					                        uow.SelfAssignedRoles.Add(new SelfAssignedRole
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
                            RoleId = role.Id,
 | 
					                            RoleId = role.Id,
 | 
				
			||||||
                            GuildId = role.Guild.Id
 | 
					                            GuildId = role.Guild.Id
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        await uow.CompleteAsync();
 | 
					                        await uow.CompleteAsync();
 | 
				
			||||||
                        msg = $"🆗 Role **{role.Name}** added to the list.";
 | 
					                        msg = GetText("role_added", Format.Bold(role.Name));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync(msg.ToString()).ConfigureAwait(false);
 | 
					                if (error)
 | 
				
			||||||
 | 
					                    await Context.Channel.SendErrorAsync(msg).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -70,8 +75,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					            [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
            public async Task Rsar([Remainder] IRole role)
 | 
					            public async Task Rsar([Remainder] IRole role)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                bool success;
 | 
					                bool success;
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -80,18 +83,16 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                if (!success)
 | 
					                if (!success)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("❎ That role is not self-assignable.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("self_assign_rem", Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task Lsar()
 | 
					            public async Task Lsar()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
 | 
					                var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
 | 
				
			||||||
                var removeMsg = new StringBuilder();
 | 
					                var removeMsg = new StringBuilder();
 | 
				
			||||||
                var msg = new StringBuilder();
 | 
					                var msg = new StringBuilder();
 | 
				
			||||||
@@ -116,11 +117,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    foreach (var role in toRemove)
 | 
					                    foreach (var role in toRemove)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        removeMsg.AppendLine($"`{role.RoleId} not found. Cleaned up.`");
 | 
					                        removeMsg.AppendLine(GetText("role_clean", role.RoleId));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await uow.CompleteAsync();
 | 
					                    await uow.CompleteAsync();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"ℹ️ There are `{roleCnt}` self assignable roles:", msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false);
 | 
					                await Context.Channel.SendConfirmAsync(GetText("self_assign_list", roleCnt), msg + "\n\n" + removeMsg).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -128,8 +129,6 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					            [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
            public async Task Tesar()
 | 
					            public async Task Tesar()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                bool areExclusive;
 | 
					                bool areExclusive;
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -138,15 +137,16 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
 | 
					                    areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
 | 
				
			||||||
                    await uow.CompleteAsync();
 | 
					                    await uow.CompleteAsync();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                string exl = areExclusive ? "**exclusive**." : "**not exclusive**.";
 | 
					                if(areExclusive)
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("ℹ️ Self assigned roles are now " + exl);
 | 
					                    await ReplyConfirmLocalized("self_assign_excl").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("self_assign_no_excl").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task Iam([Remainder] IRole role)
 | 
					            public async Task Iam([Remainder] IRole role)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
                var guildUser = (IGuildUser)Context.User;
 | 
					                var guildUser = (IGuildUser)Context.User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                GuildConfig conf;
 | 
					                GuildConfig conf;
 | 
				
			||||||
@@ -156,25 +156,24 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
					                    conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
 | 
				
			||||||
                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
					                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                SelfAssignedRole roleModel;
 | 
					                if (roles.FirstOrDefault(r=>r.RoleId == role.Id) == null)
 | 
				
			||||||
                if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("That role is not self-assignable.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (guildUser.RoleIds.Contains(role.Id))
 | 
					                if (guildUser.RoleIds.Contains(role.Id))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync($"You already have **{role.Name}** role.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_already", Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (conf.ExclusiveSelfAssignedRoles)
 | 
					                if (conf.ExclusiveSelfAssignedRoles)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var sameRoleId = guildUser.RoleIds.Where(r => roles.Select(sar => sar.RoleId).Contains(r)).FirstOrDefault();
 | 
					                    var sameRoleId = guildUser.RoleIds.FirstOrDefault(r => roles.Select(sar => sar.RoleId).Contains(r));
 | 
				
			||||||
                    var sameRole = Context.Guild.GetRole(sameRoleId);
 | 
					                    var sameRole = Context.Guild.GetRole(sameRoleId);
 | 
				
			||||||
                    if (sameRoleId != default(ulong))
 | 
					                    if (sameRoleId != default(ulong))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendErrorAsync($"You already have **{sameRole?.Name}** `exclusive self-assigned` role.").ConfigureAwait(false);
 | 
					                        await ReplyErrorLocalized("self_assign_already_excl", Format.Bold(sameRole?.Name)).ConfigureAwait(false);
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -184,11 +183,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false);
 | 
				
			||||||
                    Console.WriteLine(ex);
 | 
					                    Console.WriteLine(ex);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var msg = await Context.Channel.SendConfirmAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false);
 | 
					                var msg = await ReplyConfirmLocalized("self_assign_success",Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (conf.AutoDeleteSelfAssignedRoleMessages)
 | 
					                if (conf.AutoDeleteSelfAssignedRoleMessages)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -210,15 +209,14 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
 | 
					                    autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
 | 
				
			||||||
                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
					                    roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                SelfAssignedRole roleModel;
 | 
					                if (roles.FirstOrDefault(r => r.RoleId == role.Id) == null)
 | 
				
			||||||
                if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("💢 That role is not self-assignable.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (!guildUser.RoleIds.Contains(role.Id))
 | 
					                if (!guildUser.RoleIds.Contains(role.Id))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_not_have",Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
@@ -227,10 +225,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception)
 | 
					                catch (Exception)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't remove roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var msg = await Context.Channel.SendConfirmAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false);
 | 
					                var msg = await ReplyConfirmLocalized("self_assign_remove", Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (autoDeleteSelfAssignedRoleMessages)
 | 
					                if (autoDeleteSelfAssignedRoleMessages)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,19 +3,97 @@ using Discord.Commands;
 | 
				
			|||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Discord.WebSocket;
 | 
				
			||||||
 | 
					using NadekoBot.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Modules.Administration
 | 
					namespace NadekoBot.Modules.Administration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        class SelfCommands : ModuleBase
 | 
					        public class SelfCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            private static volatile bool _forwardDMs;
 | 
				
			||||||
 | 
					            private static volatile bool _forwardDMsToAllOwners;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private static readonly object _locker = new object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            static SelfCommands()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var config = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
 | 
					                    _forwardDMs = config.ForwardMessages;
 | 
				
			||||||
 | 
					                    _forwardDMsToAllOwners = config.ForwardToAllOwners;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            [OwnerOnly]
 | 
				
			||||||
 | 
					            public async Task ForwardMessages()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var config = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
 | 
					                    lock (_locker)
 | 
				
			||||||
 | 
					                        _forwardDMs = config.ForwardMessages = !config.ForwardMessages;
 | 
				
			||||||
 | 
					                    uow.Complete();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (_forwardDMs)
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					            [OwnerOnly]
 | 
				
			||||||
 | 
					            public async Task ForwardToAll()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var config = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
 | 
					                    lock (_locker)
 | 
				
			||||||
 | 
					                        _forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
 | 
				
			||||||
 | 
					                    uow.Complete();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (_forwardDMsToAllOwners)
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public static async Task HandleDmForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (_forwardDMs && ownerChannels.Any())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var title =
 | 
				
			||||||
 | 
					                        GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo,
 | 
				
			||||||
 | 
					                            typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})";
 | 
				
			||||||
 | 
					                    if (_forwardDMsToAllOwners)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
 | 
				
			||||||
 | 
					                            .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var firstOwnerChannel = ownerChannels.First();
 | 
				
			||||||
 | 
					                        if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
 | 
				
			||||||
 | 
					                            try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); }
 | 
				
			||||||
 | 
					                            catch
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                // ignored
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task ConnectShard(int shardid)
 | 
					            public async Task ConnectShard(int shardid)
 | 
				
			||||||
@@ -24,14 +102,14 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (shard == null)
 | 
					                if (shard == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("No shard by that id found.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("no_shard_id").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnecting.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("shard_reconnecting", Format.Bold("#" + shardid)).ConfigureAwait(false);
 | 
				
			||||||
                    await shard.ConnectAsync().ConfigureAwait(false);
 | 
					                    await shard.ConnectAsync().ConfigureAwait(false);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnected.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("shard_reconnected", Format.Bold("#" + shardid)).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -49,18 +127,18 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (server == null)
 | 
					                if (server == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("no_server").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (server.OwnerId != NadekoBot.Client.CurrentUser.Id)
 | 
					                if (server.OwnerId != NadekoBot.Client.CurrentUser.Id)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await server.LeaveAsync().ConfigureAwait(false);
 | 
					                    await server.LeaveAsync().ConfigureAwait(false);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await server.DeleteAsync().ConfigureAwait(false);
 | 
					                    await server.DeleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("Deleted server " + server.Name).ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("deleted_server",Format.Bold(server.Name)).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,7 +147,14 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task Die()
 | 
					            public async Task Die()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try { await Context.Channel.SendConfirmAsync("ℹ️ **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await ReplyConfirmLocalized("shutting_down").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                await Task.Delay(2000).ConfigureAwait(false);
 | 
					                await Task.Delay(2000).ConfigureAwait(false);
 | 
				
			||||||
                Environment.Exit(0);
 | 
					                Environment.Exit(0);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -83,7 +168,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
 | 
					                await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"Bot name changed to **{newName}**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -92,7 +177,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
 | 
					                await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"Bot status changed to **{status}**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -114,7 +199,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🆒 **New avatar set.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("set_avatar").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -123,7 +208,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false);
 | 
					                await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("set_game").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -134,7 +219,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false);
 | 
					                await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("ℹ️ **New stream set.**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("set_stream").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -148,7 +233,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                if (ids.Length != 2)
 | 
					                if (ids.Length != 2)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                var sid = ulong.Parse(ids[0]);
 | 
					                var sid = ulong.Parse(ids[0]);
 | 
				
			||||||
                var server = NadekoBot.Client.GetGuilds().Where(s => s.Id == sid).FirstOrDefault();
 | 
					                var server = NadekoBot.Client.GetGuilds().FirstOrDefault(s => s.Id == sid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (server == null)
 | 
					                if (server == null)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
@@ -156,7 +241,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                if (ids[1].ToUpperInvariant().StartsWith("C:"))
 | 
					                if (ids[1].ToUpperInvariant().StartsWith("C:"))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var cid = ulong.Parse(ids[1].Substring(2));
 | 
					                    var cid = ulong.Parse(ids[1].Substring(2));
 | 
				
			||||||
                    var ch = server.TextChannels.Where(c => c.Id == cid).FirstOrDefault();
 | 
					                    var ch = server.TextChannels.FirstOrDefault(c => c.Id == cid);
 | 
				
			||||||
                    if (ch == null)
 | 
					                    if (ch == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
@@ -166,7 +251,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                else if (ids[1].ToUpperInvariant().StartsWith("U:"))
 | 
					                else if (ids[1].ToUpperInvariant().StartsWith("U:"))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var uid = ulong.Parse(ids[1].Substring(2));
 | 
					                    var uid = ulong.Parse(ids[1].Substring(2));
 | 
				
			||||||
                    var user = server.Users.Where(u => u.Id == uid).FirstOrDefault();
 | 
					                    var user = server.Users.FirstOrDefault(u => u.Id == uid);
 | 
				
			||||||
                    if (user == null)
 | 
					                    if (user == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
@@ -175,8 +260,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("invalid_format").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                await ReplyConfirmLocalized("message_sent").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -186,10 +273,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
 | 
					                var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
 | 
				
			||||||
                if (channels == null)
 | 
					                if (channels == null)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))
 | 
					                await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message)))
 | 
				
			||||||
                        .ConfigureAwait(false);
 | 
					                        .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("message_sent").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -197,7 +284,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            public async Task ReloadImages()
 | 
					            public async Task ReloadImages()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var time = await NadekoBot.Images.Reload().ConfigureAwait(false);
 | 
					                var time = await NadekoBot.Images.Reload().ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"Images loaded after {time.TotalSeconds:F3}s!").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
 | 
					            private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,9 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.Commands;
 | 
					using Discord.Commands;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.DataStructures;
 | 
					using NadekoBot.DataStructures;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using NadekoBot.Services.Database;
 | 
					 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
@@ -18,10 +16,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class ServerGreetCommands : ModuleBase
 | 
					        public class ServerGreetCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //make this to a field in the guildconfig table
 | 
					            //make this to a field in the guildconfig table
 | 
				
			||||||
            class GreetSettings
 | 
					            private class GreetSettings
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                public int AutoDeleteGreetMessagesTimer { get; set; }
 | 
					                public int AutoDeleteGreetMessagesTimer { get; set; }
 | 
				
			||||||
                public int AutoDeleteByeMessagesTimer { get; set; }
 | 
					                public int AutoDeleteByeMessagesTimer { get; set; }
 | 
				
			||||||
@@ -53,9 +51,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					            private new static Logger _log { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache { get; } = new ConcurrentDictionary<ulong, GreetSettings>();
 | 
					            private static ConcurrentDictionary<ulong, GreetSettings> guildConfigsCache { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static ServerGreetCommands()
 | 
					            static ServerGreetCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -63,13 +61,13 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                NadekoBot.Client.UserLeft += UserLeft;
 | 
					                NadekoBot.Client.UserLeft += UserLeft;
 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					                _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, (g) => GreetSettings.Create(g)));
 | 
					                guildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static GreetSettings GetOrAddSettingsForGuild(ulong guildId)
 | 
					            private static GreetSettings GetOrAddSettingsForGuild(ulong guildId)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                GreetSettings settings;
 | 
					                GreetSettings settings;
 | 
				
			||||||
                GuildConfigsCache.TryGetValue(guildId, out settings);
 | 
					                guildConfigsCache.TryGetValue(guildId, out settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (settings != null)
 | 
					                if (settings != null)
 | 
				
			||||||
                    return settings;
 | 
					                    return settings;
 | 
				
			||||||
@@ -80,7 +78,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    settings = GreetSettings.Create(gc);
 | 
					                    settings = GreetSettings.Create(gc);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                GuildConfigsCache.TryAdd(guildId, settings);
 | 
					                guildConfigsCache.TryAdd(guildId, settings);
 | 
				
			||||||
                return settings;
 | 
					                return settings;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -129,7 +127,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            catch (Exception ex) { _log.Warn(ex); }
 | 
					                            catch (Exception ex) { _log.Warn(ex); }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch { }
 | 
					                    catch
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // ignored
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
                return Task.CompletedTask;
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -212,7 +213,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch { }
 | 
					                    catch
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // ignored
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
                return Task.CompletedTask;
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -222,16 +226,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [RequireUserPermission(GuildPermission.ManageGuild)]
 | 
					            [RequireUserPermission(GuildPermission.ManageGuild)]
 | 
				
			||||||
            public async Task GreetDel(int timer = 30)
 | 
					            public async Task GreetDel(int timer = 30)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
                if (timer < 0 || timer > 600)
 | 
					                if (timer < 0 || timer > 600)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
 | 
					                await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (timer > 0)
 | 
					                if (timer > 0)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task SetGreetDel(ulong id, int timer)
 | 
					            private static async Task SetGreetDel(ulong id, int timer)
 | 
				
			||||||
@@ -245,7 +248,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    conf.AutoDeleteGreetMessagesTimer = timer;
 | 
					                    conf.AutoDeleteGreetMessagesTimer = timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -259,9 +262,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
 | 
					                var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (enabled)
 | 
					                if (enabled)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greet_on").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Greeting messages **disabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greet_off").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
 | 
					            private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
 | 
				
			||||||
@@ -274,7 +277,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    conf.GreetMessageChannelId = channelId;
 | 
					                    conf.GreetMessageChannelId = channelId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -293,15 +296,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText;
 | 
					                        channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("Current greet message: ", channelGreetMessageText?.SanitizeMentions());
 | 
					                    await ReplyConfirmLocalized("greetmsg_cur", channelGreetMessageText?.SanitizeMentions()).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text);
 | 
					                var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🆗 New greet message **set**.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false);
 | 
				
			||||||
                if (!sendGreetEnabled)
 | 
					                if (!sendGreetEnabled)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Enable greet messsages by typing `.greet`").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greetmsg_enable", $"`{Prefix}greet`").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static bool SetGreetMessage(ulong guildId, ref string message)
 | 
					            public static bool SetGreetMessage(ulong guildId, ref string message)
 | 
				
			||||||
@@ -319,7 +322,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    greetMsgEnabled = conf.SendChannelGreetMessage;
 | 
					                    greetMsgEnabled = conf.SendChannelGreetMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    uow.Complete();
 | 
					                    uow.Complete();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -334,9 +337,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
 | 
					                var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (enabled)
 | 
					                if (enabled)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Greet announcements **disabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greetdm_off").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
 | 
					            private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
 | 
				
			||||||
@@ -348,7 +351,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
 | 
					                    enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -367,15 +370,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        config = uow.GuildConfigs.For(Context.Guild.Id);
 | 
					                        config = uow.GuildConfigs.For(Context.Guild.Id);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`");
 | 
					                    await ReplyConfirmLocalized("greetdmmsg_cur", config.DmGreetMessageText?.SanitizeMentions()).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text);
 | 
					                var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🆗 New DM greet message **set**.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false);
 | 
				
			||||||
                if (!sendGreetEnabled)
 | 
					                if (!sendGreetEnabled)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"ℹ️ Enable DM greet messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}greetdm`").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("greetdmmsg_enable", $"`{Prefix}greetdm`").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static bool SetGreetDmMessage(ulong guildId, ref string message)
 | 
					            public static bool SetGreetDmMessage(ulong guildId, ref string message)
 | 
				
			||||||
@@ -393,7 +396,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    greetMsgEnabled = conf.SendDmGreetMessage;
 | 
					                    greetMsgEnabled = conf.SendDmGreetMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    uow.Complete();
 | 
					                    uow.Complete();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -408,9 +411,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
 | 
					                var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (enabled)
 | 
					                if (enabled)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("bye_on").ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Bye announcements **disabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("bye_off").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
 | 
					            private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
 | 
				
			||||||
@@ -423,7 +426,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    conf.ByeMessageChannelId = channelId;
 | 
					                    conf.ByeMessageChannelId = channelId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync();
 | 
					                    await uow.CompleteAsync();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -442,15 +445,15 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText;
 | 
					                        byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Current **bye** message: `" + byeMessageText?.SanitizeMentions() + "`");
 | 
					                    await ReplyConfirmLocalized("byemsg_cur", byeMessageText?.SanitizeMentions()).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text);
 | 
					                var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🆗 New bye message **set**.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false);
 | 
				
			||||||
                if (!sendByeEnabled)
 | 
					                if (!sendByeEnabled)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"ℹ️ Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("byemsg_enable", $"`{Prefix}bye`").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static bool SetByeMessage(ulong guildId, ref string message)
 | 
					            public static bool SetByeMessage(ulong guildId, ref string message)
 | 
				
			||||||
@@ -468,7 +471,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    byeMsgEnabled = conf.SendChannelByeMessage;
 | 
					                    byeMsgEnabled = conf.SendChannelByeMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    uow.Complete();
 | 
					                    uow.Complete();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -483,9 +486,9 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
 | 
					                await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (timer > 0)
 | 
					                if (timer > 0)
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false);
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("ℹ️ Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("byedel_off").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task SetByeDel(ulong guildId, int timer)
 | 
					            private static async Task SetByeDel(ulong guildId, int timer)
 | 
				
			||||||
@@ -499,7 +502,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    conf.AutoDeleteByeMessagesTimer = timer;
 | 
					                    conf.AutoDeleteByeMessagesTimer = timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = GreetSettings.Create(conf);
 | 
					                    var toAdd = GreetSettings.Create(conf);
 | 
				
			||||||
                    GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
					                    guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,21 +19,20 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
    public partial class Administration
 | 
					    public partial class Administration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class VoicePlusTextCommands : ModuleBase
 | 
					        public class VoicePlusTextCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
 | 
					            private new static readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
 | 
					            private static readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, SemaphoreSlim> guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
 | 
					            private static readonly ConcurrentHashSet<ulong> _voicePlusTextCache;
 | 
				
			||||||
 | 
					            private static readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
 | 
				
			||||||
            static VoicePlusTextCommands()
 | 
					            static VoicePlusTextCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var _log = LogManager.GetCurrentClassLogger();
 | 
					                _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
                var sw = Stopwatch.StartNew();
 | 
					                var sw = Stopwatch.StartNew();
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					
 | 
				
			||||||
                {
 | 
					                _voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
 | 
				
			||||||
                    voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
 | 
					                NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                sw.Stop();
 | 
					                sw.Stop();
 | 
				
			||||||
@@ -53,7 +52,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                if (before.VoiceChannel == after.VoiceChannel)
 | 
					                if (before.VoiceChannel == after.VoiceChannel)
 | 
				
			||||||
                    return Task.CompletedTask;
 | 
					                    return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!voicePlusTextCache.Contains(guild.Id))
 | 
					                if (!_voicePlusTextCache.Contains(guild.Id))
 | 
				
			||||||
                    return Task.CompletedTask;
 | 
					                    return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var _ = Task.Run(async () =>
 | 
					                var _ = Task.Run(async () =>
 | 
				
			||||||
@@ -66,20 +65,25 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            try
 | 
					                            try
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                await guild.Owner.SendErrorAsync(
 | 
					                                await guild.Owner.SendErrorAsync(
 | 
				
			||||||
                                    "⚠️ I don't have **manage server** and/or **manage channels** permission," +
 | 
					                                    GetTextStatic("vt_exit",
 | 
				
			||||||
                                    $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
 | 
					                                        NadekoBot.Localization.GetCultureInfo(guild),
 | 
				
			||||||
 | 
					                                        typeof(Administration).Name.ToLowerInvariant(),
 | 
				
			||||||
 | 
					                                        Format.Bold(guild.Name))).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            catch
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                // ignored
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            catch { }
 | 
					 | 
				
			||||||
                            using (var uow = DbHandler.UnitOfWork())
 | 
					                            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
 | 
					                                uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
 | 
				
			||||||
                                voicePlusTextCache.TryRemove(guild.Id);
 | 
					                                _voicePlusTextCache.TryRemove(guild.Id);
 | 
				
			||||||
                                await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                                await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            return;
 | 
					                            return;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        var semaphore = guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1));
 | 
					                        var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        try
 | 
					                        try
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -106,18 +110,16 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
 | 
					                            if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                var roleName = GetRoleName(afterVch);
 | 
					                                var roleName = GetRoleName(afterVch);
 | 
				
			||||||
                                IRole roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName);
 | 
					                                var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ??
 | 
				
			||||||
                                if (roleToAdd == null)
 | 
					                                                  (IRole) await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
 | 
				
			||||||
                                    roleToAdd = await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                ITextChannel textChannel = guild.TextChannels
 | 
					                                ITextChannel textChannel = guild.TextChannels
 | 
				
			||||||
                                                            .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
 | 
					                                                            .FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant());
 | 
				
			||||||
                                                            .FirstOrDefault();
 | 
					 | 
				
			||||||
                                if (textChannel == null)
 | 
					                                if (textChannel == null)
 | 
				
			||||||
                                {
 | 
					                                {
 | 
				
			||||||
                                    var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
 | 
					                                    var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch { }
 | 
					                                    try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/}
 | 
				
			||||||
                                    await Task.Delay(50).ConfigureAwait(false);
 | 
					                                    await Task.Delay(50).ConfigureAwait(false);
 | 
				
			||||||
                                    await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions(
 | 
					                                    await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions(
 | 
				
			||||||
                                        readMessages: PermValue.Allow,
 | 
					                                        readMessages: PermValue.Allow,
 | 
				
			||||||
@@ -148,7 +150,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static string GetChannelName(string voiceName) =>
 | 
					            private static string GetChannelName(string voiceName) =>
 | 
				
			||||||
                channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
 | 
					                _channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static string GetRoleName(IVoiceChannel ch) =>
 | 
					            private static string GetRoleName(IVoiceChannel ch) =>
 | 
				
			||||||
                "nvoice-" + ch.Id;
 | 
					                "nvoice-" + ch.Id;
 | 
				
			||||||
@@ -164,7 +166,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
 | 
					                var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
 | 
				
			||||||
                if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels)
 | 
					                if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`");
 | 
					                    await ReplyErrorLocalized("vt_no_perms").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -172,10 +174,12 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendErrorAsync("⚠️ You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. " +
 | 
					                        await ReplyErrorLocalized("vt_no_admin").ConfigureAwait(false);
 | 
				
			||||||
                      "`This may cause some issues, and you will have to clean up text channels yourself afterwards.`");
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // ignored
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch { }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -188,7 +192,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    if (!isEnabled)
 | 
					                    if (!isEnabled)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        voicePlusTextCache.TryRemove(guild.Id);
 | 
					                        _voicePlusTextCache.TryRemove(guild.Id);
 | 
				
			||||||
                        foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice")))
 | 
					                        foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice")))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
					                            try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
				
			||||||
@@ -200,11 +204,11 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                            try { await role.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
					                            try { await role.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
				
			||||||
                            await Task.Delay(500).ConfigureAwait(false);
 | 
					                            await Task.Delay(500).ConfigureAwait(false);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        await Context.Channel.SendConfirmAsync("ℹ️ Successfuly **removed** voice + text feature.").ConfigureAwait(false);
 | 
					                        await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false);
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    voicePlusTextCache.Add(guild.Id);
 | 
					                    _voicePlusTextCache.Add(guild.Id);
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false);
 | 
					                    await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
@@ -224,7 +228,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
 | 
					                var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
 | 
				
			||||||
                if (!botUser.GuildPermissions.Administrator)
 | 
					                if (!botUser.GuildPermissions.Administrator)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("I need **Administrator permission** to do that.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("need_admin").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -251,7 +255,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await Task.Delay(500).ConfigureAwait(false);
 | 
					                    await Task.Delay(500).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("cleaned_up").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,18 +17,14 @@ using NLog;
 | 
				
			|||||||
namespace NadekoBot.Modules.ClashOfClans
 | 
					namespace NadekoBot.Modules.ClashOfClans
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("ClashOfClans", ",")]
 | 
					    [NadekoModule("ClashOfClans", ",")]
 | 
				
			||||||
    public class ClashOfClans : DiscordModule
 | 
					    public class ClashOfClans : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
 | 
					        public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static Timer checkWarTimer { get; }
 | 
					        private static Timer checkWarTimer { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static new readonly Logger _log;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        static ClashOfClans()
 | 
					        static ClashOfClans()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					 | 
				
			||||||
            var sw = Stopwatch.StartNew();
 | 
					 | 
				
			||||||
            using (var uow = DbHandler.UnitOfWork())
 | 
					            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
 | 
					                ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
 | 
				
			||||||
@@ -73,7 +69,11 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        SaveWar(war);
 | 
					                        SaveWar(war);
 | 
				
			||||||
                        await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
 | 
					                        await war.Channel.SendErrorAsync(GetTextStatic("claim_expired", 
 | 
				
			||||||
 | 
					                                    NadekoBot.Localization.GetCultureInfo(war.Channel.GuildId), 
 | 
				
			||||||
 | 
					                                    typeof(ClashOfClans).Name.ToLowerInvariant(), 
 | 
				
			||||||
 | 
					                                    Format.Bold(Bases[i].CallUser), 
 | 
				
			||||||
 | 
					                                    war.ShortPrint()));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch { }
 | 
					                    catch { }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -92,7 +92,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (size < 10 || size > 50 || size % 5 != 0)
 | 
					            if (size < 10 || size > 50 || size % 5 != 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("invalid_size").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            List<ClashWar> wars;
 | 
					            List<ClashWar> wars;
 | 
				
			||||||
@@ -107,7 +107,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
 | 
					            var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            wars.Add(cw);
 | 
					            wars.Add(cw);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
 | 
					            await ReplyErrorLocalized("war_created", cw.ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -120,18 +120,18 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var warsInfo = GetWarInfo(Context.Guild, num);
 | 
					            var warsInfo = GetWarInfo(Context.Guild, num);
 | 
				
			||||||
            if (warsInfo == null)
 | 
					            if (warsInfo == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var war = warsInfo.Item1[warsInfo.Item2];
 | 
					            var war = warsInfo.Item1[warsInfo.Item2];
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                war.Start();
 | 
					                war.Start();
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("war_started", war.ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch
 | 
					            catch
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_already_started", war.ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            SaveWar(war);
 | 
					            SaveWar(war);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -149,22 +149,20 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                ClashWars.TryGetValue(Context.Guild.Id, out wars);
 | 
					                ClashWars.TryGetValue(Context.Guild.Id, out wars);
 | 
				
			||||||
                if (wars == null || wars.Count == 0)
 | 
					                if (wars == null || wars.Count == 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var sb = new StringBuilder();
 | 
					                var sb = new StringBuilder();
 | 
				
			||||||
                sb.AppendLine("🔰 **LIST OF ACTIVE WARS**");
 | 
					 | 
				
			||||||
                sb.AppendLine("**-------------------------**");
 | 
					                sb.AppendLine("**-------------------------**");
 | 
				
			||||||
                for (var i = 0; i < wars.Count; i++)
 | 
					                for (var i = 0; i < wars.Count; i++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    sb.AppendLine($"**#{i + 1}.**  `Enemy:` **{wars[i].EnemyClan}**");
 | 
					                    sb.AppendLine($"**#{i + 1}.**  `{GetText("enemy")}:` **{wars[i].EnemyClan}**");
 | 
				
			||||||
                    sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**");
 | 
					                    sb.AppendLine($"\t\t`{GetText("size")}:` **{wars[i].Size} v {wars[i].Size}**");
 | 
				
			||||||
                    sb.AppendLine("**-------------------------**");
 | 
					                    sb.AppendLine("**-------------------------**");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false);
 | 
					                await Context.Channel.SendConfirmAsync(GetText("list_active_wars"), sb.ToString()).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var num = 0;
 | 
					            var num = 0;
 | 
				
			||||||
            int.TryParse(number, out num);
 | 
					            int.TryParse(number, out num);
 | 
				
			||||||
@@ -172,10 +170,11 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var warsInfo = GetWarInfo(Context.Guild, num);
 | 
					            var warsInfo = GetWarInfo(Context.Guild, num);
 | 
				
			||||||
            if (warsInfo == null)
 | 
					            if (warsInfo == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false);
 | 
					            var war = warsInfo.Item1[warsInfo.Item2];
 | 
				
			||||||
 | 
					            await Context.Channel.SendConfirmAsync(war.Localize("info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), war.ToPrettyString()).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -185,7 +184,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var warsInfo = GetWarInfo(Context.Guild, number);
 | 
					            var warsInfo = GetWarInfo(Context.Guild, number);
 | 
				
			||||||
            if (warsInfo == null || warsInfo.Item1.Count == 0)
 | 
					            if (warsInfo == null || warsInfo.Item1.Count == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var usr =
 | 
					            var usr =
 | 
				
			||||||
@@ -197,11 +196,11 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                var war = warsInfo.Item1[warsInfo.Item2];
 | 
					                var war = warsInfo.Item1[warsInfo.Item2];
 | 
				
			||||||
                war.Call(usr, baseNumber - 1);
 | 
					                war.Call(usr, baseNumber - 1);
 | 
				
			||||||
                SaveWar(war);
 | 
					                SaveWar(war);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false);
 | 
					                await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, war.ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
 | 
					                await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -233,15 +232,14 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var warsInfo = GetWarInfo(Context.Guild, number);
 | 
					            var warsInfo = GetWarInfo(Context.Guild, number);
 | 
				
			||||||
            if (warsInfo == null)
 | 
					            if (warsInfo == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var war = warsInfo.Item1[warsInfo.Item2];
 | 
					            var war = warsInfo.Item1[warsInfo.Item2];
 | 
				
			||||||
            war.End();
 | 
					            war.End();
 | 
				
			||||||
            SaveWar(war);
 | 
					            SaveWar(war);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var size = warsInfo.Item1[warsInfo.Item2].Size;
 | 
					 | 
				
			||||||
            warsInfo.Item1.RemoveAt(warsInfo.Item2);
 | 
					            warsInfo.Item1.RemoveAt(warsInfo.Item2);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -252,7 +250,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var warsInfo = GetWarInfo(Context.Guild, number);
 | 
					            var warsInfo = GetWarInfo(Context.Guild, number);
 | 
				
			||||||
            if (warsInfo == null || warsInfo.Item1.Count == 0)
 | 
					            if (warsInfo == null || warsInfo.Item1.Count == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var usr =
 | 
					            var usr =
 | 
				
			||||||
@@ -264,11 +262,11 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                var war = warsInfo.Item1[warsInfo.Item2];
 | 
					                var war = warsInfo.Item1[warsInfo.Item2];
 | 
				
			||||||
                var baseNumber = war.Uncall(usr);
 | 
					                var baseNumber = war.Uncall(usr);
 | 
				
			||||||
                SaveWar(war);
 | 
					                SaveWar(war);
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, war.ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
 | 
					                await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -277,7 +275,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            var warInfo = GetWarInfo(Context.Guild, number);
 | 
					            var warInfo = GetWarInfo(Context.Guild, number);
 | 
				
			||||||
            if (warInfo == null || warInfo.Item1.Count == 0)
 | 
					            if (warInfo == null || warInfo.Item1.Count == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var war = warInfo.Item1[warInfo.Item2];
 | 
					            var war = warInfo.Item1[warInfo.Item2];
 | 
				
			||||||
@@ -292,7 +290,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    war.FinishClaim(baseNumber, stars);
 | 
					                    war.FinishClaim(baseNumber, stars);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"❗🔰{Context.User.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("base_destroyed", baseNumber +1, war.ShortPrint()).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,13 +27,13 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
        public static void Call(this ClashWar cw, string u, int baseNumber)
 | 
					        public static void Call(this ClashWar cw, string u, int baseNumber)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
 | 
					            if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
 | 
				
			||||||
                throw new ArgumentException("Invalid base number");
 | 
					                throw new ArgumentException(cw.Localize("invalid_base_number"));
 | 
				
			||||||
            if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
 | 
					            if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
 | 
				
			||||||
                throw new ArgumentException("That base is already destroyed.");
 | 
					                throw new ArgumentException(cw.Localize("base_already_claimed"));
 | 
				
			||||||
            for (var i = 0; i < cw.Bases.Count; i++)
 | 
					            for (var i = 0; i < cw.Bases.Count; i++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
 | 
					                if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
 | 
				
			||||||
                    throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one.");
 | 
					                    throw new ArgumentException(cw.Localize("claimed_other", u, i + 1));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var cc = cw.Bases[baseNumber];
 | 
					            var cc = cw.Bases[baseNumber];
 | 
				
			||||||
@@ -45,7 +45,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
        public static void Start(this ClashWar cw)
 | 
					        public static void Start(this ClashWar cw)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (cw.WarState == StateOfWar.Started)
 | 
					            if (cw.WarState == StateOfWar.Started)
 | 
				
			||||||
                throw new InvalidOperationException("War already started");
 | 
					                throw new InvalidOperationException("war_already_started");
 | 
				
			||||||
            //if (Started)
 | 
					            //if (Started)
 | 
				
			||||||
            //    throw new InvalidOperationException();
 | 
					            //    throw new InvalidOperationException();
 | 
				
			||||||
            //Started = true;
 | 
					            //Started = true;
 | 
				
			||||||
@@ -66,7 +66,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                cw.Bases[i].CallUser = null;
 | 
					                cw.Bases[i].CallUser = null;
 | 
				
			||||||
                return i;
 | 
					                return i;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            throw new InvalidOperationException("You are not participating in that war.");
 | 
					            throw new InvalidOperationException(cw.Localize("not_partic"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static string ShortPrint(this ClashWar cw) =>
 | 
					        public static string ShortPrint(this ClashWar cw) =>
 | 
				
			||||||
@@ -76,7 +76,6 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var sb = new StringBuilder();
 | 
					            var sb = new StringBuilder();
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            sb.AppendLine($"🔰**WAR AGAINST `{cw.EnemyClan}` ({cw.Size} v {cw.Size}) INFO:**");
 | 
					 | 
				
			||||||
            if (cw.WarState == StateOfWar.Created)
 | 
					            if (cw.WarState == StateOfWar.Created)
 | 
				
			||||||
                sb.AppendLine("`not started`");
 | 
					                sb.AppendLine("`not started`");
 | 
				
			||||||
            var twoHours = new TimeSpan(2, 0, 0);
 | 
					            var twoHours = new TimeSpan(2, 0, 0);
 | 
				
			||||||
@@ -84,7 +83,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (cw.Bases[i].CallUser == null)
 | 
					                if (cw.Bases[i].CallUser == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    sb.AppendLine($"`{i + 1}.` ❌*unclaimed*");
 | 
					                    sb.AppendLine($"`{i + 1}.` ❌*{cw.Localize("not_claimed")}*");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -120,7 +119,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
                cw.Bases[i].Stars = stars;
 | 
					                cw.Bases[i].Stars = stars;
 | 
				
			||||||
                return i;
 | 
					                return i;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");
 | 
					            throw new InvalidOperationException(cw.Localize("not_partic_or_destroyed", user));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static void FinishClaim(this ClashWar cw, int index, int stars = 3)
 | 
					        public static void FinishClaim(this ClashWar cw, int index, int stars = 3)
 | 
				
			||||||
@@ -128,10 +127,22 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
				
			|||||||
            if (index < 0 || index > cw.Bases.Count)
 | 
					            if (index < 0 || index > cw.Bases.Count)
 | 
				
			||||||
                throw new ArgumentOutOfRangeException(nameof(index));
 | 
					                throw new ArgumentOutOfRangeException(nameof(index));
 | 
				
			||||||
            var toFinish = cw.Bases[index];
 | 
					            var toFinish = cw.Bases[index];
 | 
				
			||||||
            if (toFinish.BaseDestroyed != false) throw new InvalidOperationException("That base is already destroyed.");
 | 
					            if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(cw.Localize("base_already_destroyed"));
 | 
				
			||||||
            if (toFinish.CallUser == null) throw new InvalidOperationException("That base is unclaimed.");
 | 
					            if (toFinish.CallUser == null) throw new InvalidOperationException(cw.Localize("base_already_unclaimed"));
 | 
				
			||||||
            toFinish.BaseDestroyed = true;
 | 
					            toFinish.BaseDestroyed = true;
 | 
				
			||||||
            toFinish.Stars = stars;
 | 
					            toFinish.Stars = stars;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static string Localize(this ClashWar cw, string key)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return NadekoModule.GetTextStatic(key,
 | 
				
			||||||
 | 
					                NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId),
 | 
				
			||||||
 | 
					                typeof(ClashOfClans).Name.ToLowerInvariant());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static string Localize(this ClashWar cw, string key, params object[] replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return string.Format(cw.Localize(key), replacements);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -17,7 +17,7 @@ using NadekoBot.DataStructures;
 | 
				
			|||||||
namespace NadekoBot.Modules.CustomReactions
 | 
					namespace NadekoBot.Modules.CustomReactions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("CustomReactions", ".")]
 | 
					    [NadekoModule("CustomReactions", ".")]
 | 
				
			||||||
    public class CustomReactions : DiscordModule
 | 
					    public class CustomReactions : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static CustomReaction[] _globalReactions = new CustomReaction[] { };
 | 
					        private static CustomReaction[] _globalReactions = new CustomReaction[] { };
 | 
				
			||||||
        public static CustomReaction[] GlobalReactions => _globalReactions;
 | 
					        public static CustomReaction[] GlobalReactions => _globalReactions;
 | 
				
			||||||
@@ -139,7 +139,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
 | 
					            if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
 | 
					                await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -165,8 +165,8 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id,
 | 
					                GuildReactions.AddOrUpdate(Context.Guild.Id,
 | 
				
			||||||
                    Array.Empty<CustomReaction>(),
 | 
					                    new CustomReaction[] { cr },
 | 
				
			||||||
                    (k, old) =>
 | 
					                    (k, old) =>
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        Array.Resize(ref old, old.Length + 1);
 | 
					                        Array.Resize(ref old, old.Length + 1);
 | 
				
			||||||
@@ -176,10 +176,10 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
					            await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                .WithTitle("New Custom Reaction")
 | 
					                .WithTitle(GetText("new_cust_react"))
 | 
				
			||||||
                .WithDescription($"#{cr.Id}")
 | 
					                .WithDescription($"#{cr.Id}")
 | 
				
			||||||
                .AddField(efb => efb.WithName("Trigger").WithValue(key))
 | 
					                .AddField(efb => efb.WithName(GetText("trigger")).WithValue(key))
 | 
				
			||||||
                .AddField(efb => efb.WithName("Response").WithValue(message))
 | 
					                .AddField(efb => efb.WithName(GetText("response")).WithValue(message))
 | 
				
			||||||
                ).ConfigureAwait(false);
 | 
					                ).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -196,19 +196,20 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
                customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
 | 
					                customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (customReactions == null || !customReactions.Any())
 | 
					            if (customReactions == null || !customReactions.Any())
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var lastPage = customReactions.Length / 20;
 | 
					                await ReplyErrorLocalized("no_found").ConfigureAwait(false);
 | 
				
			||||||
                await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
 | 
					                return;
 | 
				
			||||||
                    new EmbedBuilder().WithOkColor()
 | 
					 | 
				
			||||||
                        .WithTitle("Custom reactions")
 | 
					 | 
				
			||||||
                        .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
 | 
					 | 
				
			||||||
                                                     .Skip((curPage - 1) * 20)
 | 
					 | 
				
			||||||
                                                     .Take(20)
 | 
					 | 
				
			||||||
                                                     .Select(cr => $"`#{cr.Id}`  `Trigger:` {cr.Trigger}"))), lastPage)
 | 
					 | 
				
			||||||
                                 .ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var lastPage = customReactions.Length / 20;
 | 
				
			||||||
 | 
					            await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
 | 
				
			||||||
 | 
					                new EmbedBuilder().WithOkColor()
 | 
				
			||||||
 | 
					                    .WithTitle(GetText("name"))
 | 
				
			||||||
 | 
					                    .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
 | 
				
			||||||
 | 
					                                                    .Skip((curPage - 1) * 20)
 | 
				
			||||||
 | 
					                                                    .Take(20)
 | 
				
			||||||
 | 
					                                                    .Select(cr => $"`#{cr.Id}`  `{GetText("trigger")}:` {cr.Trigger}"))), lastPage)
 | 
				
			||||||
 | 
					                                .ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public enum All
 | 
					        public enum All
 | 
				
			||||||
@@ -227,20 +228,22 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
                customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
 | 
					                customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (customReactions == null || !customReactions.Any())
 | 
					            if (customReactions == null || !customReactions.Any())
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
 | 
					                await ReplyErrorLocalized("no_found").ConfigureAwait(false);
 | 
				
			||||||
                                                          .OrderBy(cr => cr.Key)
 | 
					                return;
 | 
				
			||||||
                                                          .Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
 | 
					 | 
				
			||||||
                                                          .ToJson()
 | 
					 | 
				
			||||||
                                                          .ToStream()
 | 
					 | 
				
			||||||
                                                          .ConfigureAwait(false);
 | 
					 | 
				
			||||||
                if (Context.Guild == null) // its a private one, just send back
 | 
					 | 
				
			||||||
                    await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
 | 
				
			||||||
 | 
					                                                        .OrderBy(cr => cr.Key)
 | 
				
			||||||
 | 
					                                                        .Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
 | 
				
			||||||
 | 
					                                                        .ToJson()
 | 
				
			||||||
 | 
					                                                        .ToStream()
 | 
				
			||||||
 | 
					                                                        .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (Context.Guild == null) // its a private one, just send back
 | 
				
			||||||
 | 
					                await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -255,7 +258,9 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
                customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
 | 
					                customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (customReactions == null || !customReactions.Any())
 | 
					            if (customReactions == null || !customReactions.Any())
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyErrorLocalized("no_found").ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var ordered = customReactions
 | 
					                var ordered = customReactions
 | 
				
			||||||
@@ -266,7 +271,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
                var lastPage = ordered.Count / 20;
 | 
					                var lastPage = ordered.Count / 20;
 | 
				
			||||||
                await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) =>
 | 
					                await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) =>
 | 
				
			||||||
                    new EmbedBuilder().WithOkColor()
 | 
					                    new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                        .WithTitle($"Custom Reactions (grouped)")
 | 
					                        .WithTitle(GetText("name"))
 | 
				
			||||||
                        .WithDescription(string.Join("\r\n", ordered
 | 
					                        .WithDescription(string.Join("\r\n", ordered
 | 
				
			||||||
                                                         .Skip((curPage - 1) * 20)
 | 
					                                                         .Skip((curPage - 1) * 20)
 | 
				
			||||||
                                                         .Take(20)
 | 
					                                                         .Take(20)
 | 
				
			||||||
@@ -287,13 +292,16 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
            var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
 | 
					            var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (found == null)
 | 
					            if (found == null)
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyErrorLocalized("no_found_id").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
					                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                    .WithDescription($"#{id}")
 | 
					                    .WithDescription($"#{id}")
 | 
				
			||||||
                    .AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
 | 
					                    .AddField(efb => efb.WithName(GetText("trigger")).WithValue(found.Trigger))
 | 
				
			||||||
                    .AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
 | 
					                    .AddField(efb => efb.WithName(GetText("response")).WithValue(found.Response + "\n```css\n" + found.Response + "```"))
 | 
				
			||||||
                    ).ConfigureAwait(false);
 | 
					                    ).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -303,7 +311,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
 | 
					            if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
 | 
					                await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -313,32 +321,42 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                toDelete = uow.CustomReactions.Get(id);
 | 
					                toDelete = uow.CustomReactions.Get(id);
 | 
				
			||||||
                if (toDelete == null) //not found
 | 
					                if (toDelete == null) //not found
 | 
				
			||||||
                    return;
 | 
					                    success = false;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
                if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    uow.CustomReactions.Remove(toDelete);
 | 
					                    if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
 | 
				
			||||||
                    //todo i can dramatically improve performance of this, if Ids are ordered.
 | 
					 | 
				
			||||||
                    _globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
 | 
					 | 
				
			||||||
                    success = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    uow.CustomReactions.Remove(toDelete);
 | 
					 | 
				
			||||||
                    GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
 | 
					 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
 | 
					                        uow.CustomReactions.Remove(toDelete);
 | 
				
			||||||
                    });
 | 
					                        //todo i can dramatically improve performance of this, if Ids are ordered.
 | 
				
			||||||
                    success = true;
 | 
					                        _globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
 | 
				
			||||||
 | 
					                        success = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        uow.CustomReactions.Remove(toDelete);
 | 
				
			||||||
 | 
					                        GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        success = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (success)
 | 
				
			||||||
 | 
					                        await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (success)
 | 
					 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (success)
 | 
					            if (success)
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false);
 | 
					            {
 | 
				
			||||||
 | 
					                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
 | 
					                    .WithTitle(GetText("deleted"))
 | 
				
			||||||
 | 
					                    .WithDescription("#" + toDelete.Id)
 | 
				
			||||||
 | 
					                    .AddField(efb => efb.WithName(GetText("trigger")).WithValue(toDelete.Trigger))
 | 
				
			||||||
 | 
					                    .AddField(efb => efb.WithName(GetText("response")).WithValue(toDelete.Response)));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await Context.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false);
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyErrorLocalized("no_found_id").ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -348,18 +366,18 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
            if (string.IsNullOrWhiteSpace(trigger))
 | 
					            if (string.IsNullOrWhiteSpace(trigger))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ClearStats();
 | 
					                ClearStats();
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                uint throwaway;
 | 
					                uint throwaway;
 | 
				
			||||||
                if (ReactionStats.TryRemove(trigger, out throwaway))
 | 
					                if (ReactionStats.TryRemove(trigger, out throwaway))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("stats_not_found").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -376,7 +394,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
            await Context.Channel.SendPaginatedConfirmAsync(page,
 | 
					            await Context.Channel.SendPaginatedConfirmAsync(page,
 | 
				
			||||||
                (curPage) => ordered.Skip((curPage - 1) * 9)
 | 
					                (curPage) => ordered.Skip((curPage - 1) * 9)
 | 
				
			||||||
                                    .Take(9)
 | 
					                                    .Take(9)
 | 
				
			||||||
                                    .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction Stats"),
 | 
					                                    .Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")),
 | 
				
			||||||
                                            (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage)
 | 
					                                            (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage)
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
using Discord.Commands;
 | 
					 | 
				
			||||||
using NLog;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace NadekoBot.Modules
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public abstract class DiscordModule : ModuleBase
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        protected Logger _log { get; }
 | 
					 | 
				
			||||||
        protected string _prefix { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public DiscordModule()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string prefix;
 | 
					 | 
				
			||||||
            if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix))
 | 
					 | 
				
			||||||
                _prefix = prefix;
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                _prefix = "?missing_prefix?";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    public partial class Gambling
 | 
					    public partial class Gambling
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class AnimalRacing : ModuleBase
 | 
					        public class AnimalRacing : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
 | 
					            public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task Race()
 | 
					            public async Task Race()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel);
 | 
					                var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (ar.Fail)
 | 
					                if (ar.Fail)
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false);
 | 
					                    await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false);
 | 
				
			||||||
@@ -59,13 +59,16 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                public List<Participant> participants = new List<Participant>();
 | 
					                public List<Participant> participants = new List<Participant>();
 | 
				
			||||||
                private ulong serverId;
 | 
					                private ulong serverId;
 | 
				
			||||||
                private int messagesSinceGameStarted = 0;
 | 
					                private int messagesSinceGameStarted = 0;
 | 
				
			||||||
 | 
					                private readonly string _prefix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                private Logger _log { get; }
 | 
					                private Logger _log { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                public ITextChannel raceChannel { get; set; }
 | 
					                public ITextChannel raceChannel { get; set; }
 | 
				
			||||||
                public bool Started { get; private set; } = false;
 | 
					                public bool Started { get; private set; } = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                public AnimalRace(ulong serverId, ITextChannel ch)
 | 
					                public AnimalRace(ulong serverId, ITextChannel ch, string prefix)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					                    this._prefix = prefix;
 | 
				
			||||||
                    this._log = LogManager.GetCurrentClassLogger();
 | 
					                    this._log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
                    this.serverId = serverId;
 | 
					                    this.serverId = serverId;
 | 
				
			||||||
                    this.raceChannel = ch;
 | 
					                    this.raceChannel = ch;
 | 
				
			||||||
@@ -75,10 +78,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    using (var uow = DbHandler.UnitOfWork())
 | 
					                    animals = new ConcurrentQueue<string>(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle());
 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        animals = new ConcurrentQueue<string>(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle());
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var cancelSource = new CancellationTokenSource();
 | 
					                    var cancelSource = new CancellationTokenSource();
 | 
				
			||||||
@@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                            try
 | 
					                            try
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.",
 | 
					                                await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.",
 | 
				
			||||||
                                    footer: $"Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race.");
 | 
					                                    footer: $"Type {_prefix}jr to join the race.");
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            catch (Exception ex)
 | 
					                            catch (Exception ex)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
@@ -280,9 +280,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                public override bool Equals(object obj)
 | 
					                public override bool Equals(object obj)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var p = obj as Participant;
 | 
					                    var p = obj as Participant;
 | 
				
			||||||
                    return p == null ?
 | 
					                    return p != null && p.User == User;
 | 
				
			||||||
                        false :
 | 
					 | 
				
			||||||
                        p.User == User;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                public override string ToString()
 | 
					                public override string ToString()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,9 @@ using NadekoBot.Extensions;
 | 
				
			|||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Services.Database;
 | 
					 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,7 +16,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    public partial class Gambling
 | 
					    public partial class Gambling
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class CurrencyEvents : ModuleBase
 | 
					        public class CurrencyEvents : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public enum CurrencyEvent
 | 
					            public enum CurrencyEvent
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -27,7 +24,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                SneakyGameStatus
 | 
					                SneakyGameStatus
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            //flower reaction event
 | 
					            //flower reaction event
 | 
				
			||||||
            public static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>();
 | 
					            private static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10)
 | 
					            private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10)
 | 
				
			||||||
@@ -36,32 +33,25 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                .Select(x => (char)x)
 | 
					                .Select(x => (char)x)
 | 
				
			||||||
                .ToArray();
 | 
					                .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static string _secretCode = String.Empty;
 | 
					            private static string _secretCode = string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task StartEvent(CurrencyEvent e, int arg = -1)
 | 
					            public async Task StartEvent(CurrencyEvent e, int arg = -1)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var channel = (ITextChannel)Context.Channel;
 | 
					                switch (e)
 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    switch (e)
 | 
					                    case CurrencyEvent.FlowerReaction:
 | 
				
			||||||
                    {
 | 
					                        await FlowerReactionEvent(Context).ConfigureAwait(false);
 | 
				
			||||||
                        case CurrencyEvent.FlowerReaction:
 | 
					                        break;
 | 
				
			||||||
                            await FlowerReactionEvent(Context).ConfigureAwait(false);
 | 
					                    case CurrencyEvent.SneakyGameStatus:
 | 
				
			||||||
                            break;
 | 
					                        await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
 | 
				
			||||||
                        case CurrencyEvent.SneakyGameStatus:
 | 
					                        break;
 | 
				
			||||||
                            await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                        default:
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static async Task SneakyGameStatusEvent(CommandContext Context, int? arg)
 | 
					            public async Task SneakyGameStatusEvent(CommandContext context, int? arg)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                int num;
 | 
					                int num;
 | 
				
			||||||
                if (arg == null || arg < 5)
 | 
					                if (arg == null || arg < 5)
 | 
				
			||||||
@@ -69,26 +59,27 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                else
 | 
					                else
 | 
				
			||||||
                    num = arg.Value;
 | 
					                    num = arg.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (_secretCode != String.Empty)
 | 
					                if (_secretCode != string.Empty)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                var rng = new NadekoRandom();
 | 
					                var rng = new NadekoRandom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (int i = 0; i < 5; i++)
 | 
					                for (var i = 0; i < 5; i++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)];
 | 
					                    _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)];
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                var game = NadekoBot.Client.Game?.Name;
 | 
					 | 
				
			||||||
                await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName)
 | 
					                await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName)
 | 
				
			||||||
                    .ConfigureAwait(false);
 | 
					                    .ConfigureAwait(false);
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync($"SneakyGameStatus event started",
 | 
					                    var title = GetText("sneakygamestatus_title");
 | 
				
			||||||
                        $"Users must type a secret code to get 100 currency.\n" +
 | 
					                    var desc = GetText("sneakygamestatus_desc", Format.Bold(100.ToString()) + CurrencySign, Format.Bold(num.ToString()));
 | 
				
			||||||
                        $"Lasts {num} seconds. Don't tell anyone. Shhh.")
 | 
					                    await context.Channel.SendConfirmAsync(title, desc).ConfigureAwait(false);
 | 
				
			||||||
                        .ConfigureAwait(false);
 | 
					                }
 | 
				
			||||||
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler;
 | 
					                NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler;
 | 
				
			||||||
@@ -97,9 +88,9 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                var cnt = _sneakyGameAwardedUsers.Count;
 | 
					                var cnt = _sneakyGameAwardedUsers.Count;
 | 
				
			||||||
                _sneakyGameAwardedUsers.Clear();
 | 
					                _sneakyGameAwardedUsers.Clear();
 | 
				
			||||||
                _secretCode = String.Empty;
 | 
					                _secretCode = string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await NadekoBot.Client.SetGameAsync($"SneakyGame event ended. {cnt} users received a reward.")
 | 
					                await NadekoBot.Client.SetGameAsync(GetText("sneakygamestatus_end", cnt))
 | 
				
			||||||
                    .ConfigureAwait(false);
 | 
					                    .ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -114,29 +105,41 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                            .ConfigureAwait(false);
 | 
					                            .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
 | 
					                        try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
 | 
				
			||||||
                        catch { }
 | 
					                        catch
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // ignored
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return Task.Delay(0);
 | 
					                return Task.Delay(0);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static Task FlowerReactionEvent(CommandContext Context) =>
 | 
					            public async Task FlowerReactionEvent(CommandContext context)
 | 
				
			||||||
                new FlowerReactionEvent().Start(Context);
 | 
					            {
 | 
				
			||||||
 | 
					                var title = GetText("flowerreaction_title");
 | 
				
			||||||
 | 
					                var desc = GetText("flowerreaction_desc", "🌸", Format.Bold(100.ToString()) + CurrencySign);
 | 
				
			||||||
 | 
					                var footer = GetText("flowerreaction_footer", 24);
 | 
				
			||||||
 | 
					                var msg = await context.Channel.SendConfirmAsync(title,
 | 
				
			||||||
 | 
					                        desc, footer: footer)
 | 
				
			||||||
 | 
					                    .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await new FlowerReactionEvent().Start(msg, context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public abstract class CurrencyEvent
 | 
					    public abstract class CurrencyEvent
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public abstract Task Start(CommandContext channel);
 | 
					        public abstract Task Start(IUserMessage msg, CommandContext channel);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class FlowerReactionEvent : CurrencyEvent
 | 
					    public class FlowerReactionEvent : CurrencyEvent
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
 | 
					        private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private IUserMessage msg { get; set; } = null;
 | 
					        private IUserMessage msg { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private CancellationTokenSource source { get; }
 | 
					        private CancellationTokenSource source { get; }
 | 
				
			||||||
        private CancellationToken cancelToken { get; }
 | 
					        private CancellationToken cancelToken { get; }
 | 
				
			||||||
@@ -163,19 +166,15 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            if (msg?.Id == id)
 | 
					            if (msg?.Id == id)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log.Warn("Stopping flower reaction event because message is deleted.");
 | 
					                _log.Warn("Stopping flower reaction event because message is deleted.");
 | 
				
			||||||
                Task.Run(() => End());
 | 
					                var __ = Task.Run(End);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public override async Task Start(CommandContext context)
 | 
					        public override async Task Start(IUserMessage umsg, CommandContext context)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            msg = await context.Channel.SendConfirmAsync("Flower reaction event started!",
 | 
					            msg = umsg;
 | 
				
			||||||
                    "Add 🌸 reaction to this message to get 100" + NadekoBot.BotConfig.CurrencySign,
 | 
					 | 
				
			||||||
                    footer: "This event is active for up to 24 hours.")
 | 
					 | 
				
			||||||
                                               .ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler;
 | 
					            NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); }
 | 
					            try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); }
 | 
				
			||||||
@@ -194,10 +193,14 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
 | 
					                    if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { }
 | 
					                        await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false)
 | 
				
			||||||
 | 
					                            .ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    public partial class Gambling
 | 
					    public partial class Gambling
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class DriceRollCommands : ModuleBase
 | 
					        public class DriceRollCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
 | 
					            private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
 | 
				
			||||||
            private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);
 | 
					            private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,6 @@ using ImageSharp;
 | 
				
			|||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using NadekoBot.Modules.Gambling.Models;
 | 
					using NadekoBot.Modules.Gambling.Models;
 | 
				
			||||||
using NLog;
 | 
					 | 
				
			||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
@@ -17,17 +15,17 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    public partial class Gambling
 | 
					    public partial class Gambling
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class DrawCommands : ModuleBase
 | 
					        public class DrawCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static readonly ConcurrentDictionary<IGuild, Cards> AllDecks = new ConcurrentDictionary<IGuild, Cards>();
 | 
					            private static readonly ConcurrentDictionary<IGuild, Cards> _allDecks = new ConcurrentDictionary<IGuild, Cards>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private const string cardsPath = "data/images/cards";
 | 
					            private const string _cardsPath = "data/images/cards";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task Draw(int num = 1)
 | 
					            public async Task Draw(int num = 1)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var cards = AllDecks.GetOrAdd(Context.Guild, (s) => new Cards());
 | 
					                var cards = _allDecks.GetOrAdd(Context.Guild, (s) => new Cards());
 | 
				
			||||||
                var images = new List<Image>();
 | 
					                var images = new List<Image>();
 | 
				
			||||||
                var cardObjects = new List<Cards.Card>();
 | 
					                var cardObjects = new List<Cards.Card>();
 | 
				
			||||||
                if (num > 5) num = 5;
 | 
					                if (num > 5) num = 5;
 | 
				
			||||||
@@ -35,12 +33,19 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (cards.CardPool.Count == 0 && i != 0)
 | 
					                    if (cards.CardPool.Count == 0 && i != 0)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        try { await Context.Channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch { }
 | 
					                        try
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await ReplyErrorLocalized("no_more_cards").ConfigureAwait(false);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        catch
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // ignored
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    var currentCard = cards.DrawACard();
 | 
					                    var currentCard = cards.DrawACard();
 | 
				
			||||||
                    cardObjects.Add(currentCard);
 | 
					                    cardObjects.Add(currentCard);
 | 
				
			||||||
                    using (var stream = File.OpenRead(Path.Combine(cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
 | 
					                    using (var stream = File.OpenRead(Path.Combine(_cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
 | 
				
			||||||
                        images.Add(new Image(stream));
 | 
					                        images.Add(new Image(stream));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                MemoryStream bitmapStream = new MemoryStream();
 | 
					                MemoryStream bitmapStream = new MemoryStream();
 | 
				
			||||||
@@ -59,7 +64,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                //var channel = (ITextChannel)Context.Channel;
 | 
					                //var channel = (ITextChannel)Context.Channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AllDecks.AddOrUpdate(Context.Guild,
 | 
					                _allDecks.AddOrUpdate(Context.Guild,
 | 
				
			||||||
                        (g) => new Cards(),
 | 
					                        (g) => new Cards(),
 | 
				
			||||||
                        (g, c) =>
 | 
					                        (g, c) =>
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -67,7 +72,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                            return c;
 | 
					                            return c;
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("deck_reshuffled").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,10 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.Commands;
 | 
					using Discord.Commands;
 | 
				
			||||||
using ImageSharp;
 | 
					 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Image = ImageSharp.Image;
 | 
					using Image = ImageSharp.Image;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,7 +13,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    public partial class Gambling
 | 
					    public partial class Gambling
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class FlipCoinCommands : ModuleBase
 | 
					        public class FlipCoinCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly IImagesService _images;
 | 
					            private readonly IImagesService _images;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,7 +22,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            public FlipCoinCommands()
 | 
					            public FlipCoinCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //todo DI in the future, can't atm
 | 
					                //todo DI in the future, can't atm
 | 
				
			||||||
                this._images = NadekoBot.Images;
 | 
					                _images = NadekoBot.Images;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -36,21 +34,21 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        using (var heads = _images.Heads.ToStream())
 | 
					                        using (var heads = _images.Heads.ToStream())
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            await Context.Channel.SendFileAsync(heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false);
 | 
					                            await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads"))) + ".").ConfigureAwait(false);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        using (var tails = _images.Tails.ToStream())
 | 
					                        using (var tails = _images.Tails.ToStream())
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            await Context.Channel.SendFileAsync(tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false);
 | 
					                            await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails"))) + ".").ConfigureAwait(false);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (count > 10 || count < 1)
 | 
					                if (count > 10 || count < 1)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("flip_invalid", 10).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var imgs = new Image[count];
 | 
					                var imgs = new Image[count];
 | 
				
			||||||
@@ -76,14 +74,13 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (amount < NadekoBot.BotConfig.MinimumBetAmount)
 | 
					                if (amount < NadekoBot.BotConfig.MinimumBetAmount)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync($"You can't bet less than {NadekoBot.BotConfig.MinimumBetAmount}{CurrencySign}.")
 | 
					                    await ReplyErrorLocalized("min_bet_limit", NadekoBot.BotConfig.MinimumBetAmount + CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                                 .ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var removed = await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false);
 | 
					                var removed = await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false);
 | 
				
			||||||
                if (!removed)
 | 
					                if (!removed)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                //heads = true
 | 
					                //heads = true
 | 
				
			||||||
@@ -91,7 +88,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                //todo this seems stinky, no time to look at it right now
 | 
					                //todo this seems stinky, no time to look at it right now
 | 
				
			||||||
                var isHeads = guessStr == "HEADS" || guessStr == "H";
 | 
					                var isHeads = guessStr == "HEADS" || guessStr == "H";
 | 
				
			||||||
                bool result = false;
 | 
					                var result = false;
 | 
				
			||||||
                IEnumerable<byte> imageToSend;
 | 
					                IEnumerable<byte> imageToSend;
 | 
				
			||||||
                if (rng.Next(0, 2) == 1)
 | 
					                if (rng.Next(0, 2) == 1)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -107,12 +104,12 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                if (isHeads == result)
 | 
					                if (isHeads == result)
 | 
				
			||||||
                { 
 | 
					                { 
 | 
				
			||||||
                    var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier);
 | 
					                    var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier);
 | 
				
			||||||
                    str = $"{Context.User.Mention}`You guessed it!` You won {toWin}{CurrencySign}";
 | 
					                    str = Context.User.Mention + " " + GetText("flip_guess", toWin + CurrencySign);
 | 
				
			||||||
                    await CurrencyHandler.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false);
 | 
					                    await CurrencyHandler.AddCurrencyAsync(Context.User, GetText("betflip_gamble"), toWin, false).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    str = $"{Context.User.Mention}`Better luck next time.`";
 | 
					                    str = Context.User.Mention + " " + GetText("better_luck");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                using (var toSend = imageToSend.ToStream())
 | 
					                using (var toSend = imageToSend.ToStream())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ using NadekoBot.Extensions;
 | 
				
			|||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
@@ -16,12 +15,12 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    public partial class Gambling
 | 
					    public partial class Gambling
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class Slots : ModuleBase
 | 
					        public class Slots : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static int totalBet = 0;
 | 
					            private static int _totalBet;
 | 
				
			||||||
            private static int totalPaidOut = 0;
 | 
					            private static int _totalPaidOut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const int alphaCutOut = byte.MaxValue / 3;
 | 
					            private const int _alphaCutOut = byte.MaxValue / 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //here is a payout chart
 | 
					            //here is a payout chart
 | 
				
			||||||
            //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
 | 
					            //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
 | 
				
			||||||
@@ -31,14 +30,14 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            public Slots()
 | 
					            public Slots()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                this._images = NadekoBot.Images;
 | 
					                _images = NadekoBot.Images;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public class SlotMachine
 | 
					            public class SlotMachine
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                public const int MaxValue = 5;
 | 
					                public const int MaxValue = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                static readonly List<Func<int[], int>> winningCombos = new List<Func<int[], int>>()
 | 
					                static readonly List<Func<int[], int>> _winningCombos = new List<Func<int[], int>>()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    //three flowers
 | 
					                    //three flowers
 | 
				
			||||||
                    (arr) => arr.All(a=>a==MaxValue) ? 30 : 0,
 | 
					                    (arr) => arr.All(a=>a==MaxValue) ? 30 : 0,
 | 
				
			||||||
@@ -53,14 +52,14 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                public static SlotResult Pull()
 | 
					                public static SlotResult Pull()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var numbers = new int[3];
 | 
					                    var numbers = new int[3];
 | 
				
			||||||
                    for (int i = 0; i < numbers.Length; i++)
 | 
					                    for (var i = 0; i < numbers.Length; i++)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        numbers[i] = new NadekoRandom().Next(0, MaxValue + 1);
 | 
					                        numbers[i] = new NadekoRandom().Next(0, MaxValue + 1);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    int multi = 0;
 | 
					                    var multi = 0;
 | 
				
			||||||
                    for (int i = 0; i < winningCombos.Count; i++)
 | 
					                    foreach (var t in _winningCombos)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        multi = winningCombos[i](numbers);
 | 
					                        multi = t(numbers);
 | 
				
			||||||
                        if (multi != 0)
 | 
					                        if (multi != 0)
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -74,8 +73,8 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                    public int Multiplier { get; }
 | 
					                    public int Multiplier { get; }
 | 
				
			||||||
                    public SlotResult(int[] nums, int multi)
 | 
					                    public SlotResult(int[] nums, int multi)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        this.Numbers = nums;
 | 
					                        Numbers = nums;
 | 
				
			||||||
                        this.Multiplier = multi;
 | 
					                        Multiplier = multi;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -85,8 +84,8 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            public async Task SlotStats()
 | 
					            public async Task SlotStats()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //i remembered to not be a moron
 | 
					                //i remembered to not be a moron
 | 
				
			||||||
                var paid = totalPaidOut;
 | 
					                var paid = _totalPaidOut;
 | 
				
			||||||
                var bet = totalBet;
 | 
					                var bet = _totalBet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (bet <= 0)
 | 
					                if (bet <= 0)
 | 
				
			||||||
                    bet = 1;
 | 
					                    bet = 1;
 | 
				
			||||||
@@ -130,33 +129,34 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                    footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%");
 | 
					                    footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static HashSet<ulong> runningUsers = new HashSet<ulong>();
 | 
					            private static readonly HashSet<ulong> _runningUsers = new HashSet<ulong>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            public async Task Slot(int amount = 0)
 | 
					            public async Task Slot(int amount = 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!runningUsers.Add(Context.User.Id))
 | 
					                if (!_runningUsers.Add(Context.User.Id))
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (amount < 1)
 | 
					                    if (amount < 1)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendErrorAsync($"You can't bet less than 1{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					                        await ReplyErrorLocalized("min_bet_limit", 1 + CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (amount > 999)
 | 
					                    if (amount > 999)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendErrorAsync($"You can't bet more than 999{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					                        GetText("slot_maxbet", 999 + CurrencySign);
 | 
				
			||||||
 | 
					                        await ReplyErrorLocalized("max_bet_limit", 999 + CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false))
 | 
					                    if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencySign}.").ConfigureAwait(false);
 | 
					                        await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Interlocked.Add(ref totalBet, amount);
 | 
					                    Interlocked.Add(ref _totalBet, amount);
 | 
				
			||||||
                    using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream())
 | 
					                    using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var bgImage = new ImageSharp.Image(bgFileStream);
 | 
					                        var bgImage = new ImageSharp.Image(bgFileStream);
 | 
				
			||||||
@@ -179,7 +179,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                                                var x = 95 + 142 * i + j;
 | 
					                                                var x = 95 + 142 * i + j;
 | 
				
			||||||
                                                int y = 330 + k;
 | 
					                                                int y = 330 + k;
 | 
				
			||||||
                                                var toSet = toAdd[j, k];
 | 
					                                                var toSet = toAdd[j, k];
 | 
				
			||||||
                                                if (toSet.A < alphaCutOut)
 | 
					                                                if (toSet.A < _alphaCutOut)
 | 
				
			||||||
                                                    continue;
 | 
					                                                    continue;
 | 
				
			||||||
                                                bgPixels[x, y] = toAdd[j, k];
 | 
					                                                bgPixels[x, y] = toAdd[j, k];
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
@@ -203,7 +203,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                                        {
 | 
					                                        {
 | 
				
			||||||
                                            for (int j = 0; j < pixels.Height; j++)
 | 
					                                            for (int j = 0; j < pixels.Height; j++)
 | 
				
			||||||
                                            {
 | 
					                                            {
 | 
				
			||||||
                                                if (pixels[i, j].A < alphaCutOut)
 | 
					                                                if (pixels[i, j].A < _alphaCutOut)
 | 
				
			||||||
                                                    continue;
 | 
					                                                    continue;
 | 
				
			||||||
                                                var x = 230 - n * 16 + i;
 | 
					                                                var x = 230 - n * 16 + i;
 | 
				
			||||||
                                                bgPixels[x, 462 + j] = pixels[i, j];
 | 
					                                                bgPixels[x, 462 + j] = pixels[i, j];
 | 
				
			||||||
@@ -228,7 +228,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                                        {
 | 
					                                        {
 | 
				
			||||||
                                            for (int j = 0; j < pixels.Height; j++)
 | 
					                                            for (int j = 0; j < pixels.Height; j++)
 | 
				
			||||||
                                            {
 | 
					                                            {
 | 
				
			||||||
                                                if (pixels[i, j].A < alphaCutOut)
 | 
					                                                if (pixels[i, j].A < _alphaCutOut)
 | 
				
			||||||
                                                    continue;
 | 
					                                                    continue;
 | 
				
			||||||
                                                var x = 395 - n * 16 + i;
 | 
					                                                var x = 395 - n * 16 + i;
 | 
				
			||||||
                                                bgPixels[x, 462 + j] = pixels[i, j];
 | 
					                                                bgPixels[x, 462 + j] = pixels[i, j];
 | 
				
			||||||
@@ -240,30 +240,30 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                            } while ((printAmount /= 10) != 0);
 | 
					                            } while ((printAmount /= 10) != 0);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        var msg = "Better luck next time ^_^";
 | 
					                        var msg = GetText("better_luck");
 | 
				
			||||||
                        if (result.Multiplier != 0)
 | 
					                        if (result.Multiplier != 0)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false);
 | 
					                            await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false);
 | 
				
			||||||
                            Interlocked.Add(ref totalPaidOut, amount * result.Multiplier);
 | 
					                            Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier);
 | 
				
			||||||
                            if (result.Multiplier == 1)
 | 
					                            if (result.Multiplier == 1)
 | 
				
			||||||
                                msg = $"A single {NadekoBot.BotConfig.CurrencySign}, x1 - Try again!";
 | 
					                                msg = GetText("slot_single", CurrencySign, 1);
 | 
				
			||||||
                            else if (result.Multiplier == 4)
 | 
					                            else if (result.Multiplier == 4)
 | 
				
			||||||
                                msg = $"Good job! Two {NadekoBot.BotConfig.CurrencySign} - bet x4";
 | 
					                                msg = GetText("slot_two", CurrencySign, 4);
 | 
				
			||||||
                            else if (result.Multiplier == 10)
 | 
					                            else if (result.Multiplier == 10)
 | 
				
			||||||
                                msg = "Wow! Lucky! Three of a kind! x10";
 | 
					                                msg = GetText("slot_three", 10);
 | 
				
			||||||
                            else if (result.Multiplier == 30)
 | 
					                            else if (result.Multiplier == 30)
 | 
				
			||||||
                                msg = "WOAAHHHHHH!!! Congratulations!!! x30";
 | 
					                                msg = GetText("slot_jackpot", 30);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`Bet:`{amount} `Won:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					                        await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`{GetText("slot_bet")}:`{amount} `{GetText("slot_won")}:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                finally
 | 
					                finally
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var t = Task.Run(async () =>
 | 
					                    var _ = Task.Run(async () =>
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Task.Delay(2000);
 | 
					                        await Task.Delay(2000);
 | 
				
			||||||
                        runningUsers.Remove(Context.User.Id);
 | 
					                        _runningUsers.Remove(Context.User.Id);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class WaifuClaimCommands : ModuleBase
 | 
					        public class WaifuClaimCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
 | 
					            private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
 | 
					            private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
 | 
				
			||||||
@@ -197,8 +197,6 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task Divorce([Remainder]IUser target)
 | 
					            public async Task Divorce([Remainder]IUser target)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (target.Id == Context.User.Id)
 | 
					                if (target.Id == Context.User.Id)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -423,7 +421,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                var embed = new EmbedBuilder()
 | 
					                var embed = new EmbedBuilder()
 | 
				
			||||||
                    .WithOkColor()
 | 
					                    .WithOkColor()
 | 
				
			||||||
                    .WithTitle("Waifu " + w.Waifu.ToString() + " - \"the " + claimInfo.Title + "\"")
 | 
					                    .WithTitle("Waifu " + w.Waifu + " - \"the " + claimInfo.Title + "\"")
 | 
				
			||||||
                    .AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
using Discord;
 | 
					using System;
 | 
				
			||||||
 | 
					using Discord;
 | 
				
			||||||
using Discord.Commands;
 | 
					using Discord.Commands;
 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					using NadekoBot.Attributes;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
@@ -12,7 +12,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
namespace NadekoBot.Modules.Gambling
 | 
					namespace NadekoBot.Modules.Gambling
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Gambling", "$")]
 | 
					    [NadekoModule("Gambling", "$")]
 | 
				
			||||||
    public partial class Gambling : DiscordModule
 | 
					    public partial class Gambling : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static string CurrencyName { get; set; }
 | 
					        public static string CurrencyName { get; set; }
 | 
				
			||||||
        public static string CurrencyPluralName { get; set; }
 | 
					        public static string CurrencyPluralName { get; set; }
 | 
				
			||||||
@@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
 | 
					            var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
 | 
				
			||||||
            var membersArray = members as IUser[] ?? members.ToArray();
 | 
					            var membersArray = members as IUser[] ?? members.ToArray();
 | 
				
			||||||
            var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
 | 
					            var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
 | 
					            await Context.Channel.SendConfirmAsync("🎟 "+ GetText("raffled_user"), $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -50,15 +50,14 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
        public async Task Cash([Remainder] IUser user = null)
 | 
					        public async Task Cash([Remainder] IUser user = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            user = user ?? Context.User;
 | 
					            user = user ?? Context.User;
 | 
				
			||||||
 | 
					            await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [Priority(1)]
 | 
					        [Priority(1)]
 | 
				
			||||||
        public async Task Cash(ulong userId)
 | 
					        public async Task Cash(ulong userId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -70,11 +69,12 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false);
 | 
					            var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false);
 | 
				
			||||||
            if (!success)
 | 
					            if (!success)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
 | 
					            await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"{Context.User.Mention} gifted {amount}{CurrencySign} to {Format.Bold(receiver.ToString())}!").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString()))
 | 
				
			||||||
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -93,8 +93,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
 | 
					            await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount}{CurrencySign} to <@{usrId}>!").ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -103,7 +102,6 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
        [Priority(0)]
 | 
					        [Priority(0)]
 | 
				
			||||||
        public async Task Award(int amount, [Remainder] IRole role)
 | 
					        public async Task Award(int amount, [Remainder] IRole role)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
            var users = (await Context.Guild.GetUsersAsync())
 | 
					            var users = (await Context.Guild.GetUsersAsync())
 | 
				
			||||||
                               .Where(u => u.GetRoles().Contains(role))
 | 
					                               .Where(u => u.GetRoles().Contains(role))
 | 
				
			||||||
                               .ToList();
 | 
					                               .ToList();
 | 
				
			||||||
@@ -112,9 +110,10 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                                                      amount)))
 | 
					                                                      amount)))
 | 
				
			||||||
                         .ConfigureAwait(false);
 | 
					                         .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"Awarded `{amount}` {CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.")
 | 
					            await ReplyConfirmLocalized("mass_award", 
 | 
				
			||||||
                         .ConfigureAwait(false);
 | 
					                amount + CurrencySign, 
 | 
				
			||||||
 | 
					                Format.Bold(users.Count.ToString()), 
 | 
				
			||||||
 | 
					                Format.Bold(role.Name)).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -126,9 +125,9 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false))
 | 
					            if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false))
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user}!").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user} because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -140,9 +139,9 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false))
 | 
					            if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false))
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //[NadekoCommand, Usage, Description, Aliases]
 | 
					        //[NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -206,49 +205,48 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            if (amount < 1)
 | 
					            if (amount < 1)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            long userFlowers;
 | 
					            if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false))
 | 
				
			||||||
            using (var uow = DbHandler.UnitOfWork())
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                userFlowers = uow.Currency.GetOrCreate(Context.User.Id).Amount;
 | 
					                await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (userFlowers < amount)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}. You only have {userFlowers}{CurrencySign}.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false);
 | 
					            var rnd = new NadekoRandom().Next(0, 101);
 | 
				
			||||||
 | 
					            var str = Context.User.Mention + Format.Code(GetText("roll", rnd));
 | 
				
			||||||
            var rng = new NadekoRandom().Next(0, 101);
 | 
					            if (rnd < 67)
 | 
				
			||||||
            var str = $"{Context.User.Mention} `You rolled {rng}.` ";
 | 
					 | 
				
			||||||
            if (rng < 67)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                str += "Better luck next time.";
 | 
					                str += GetText("better_luck");
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (rng < 91)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll67Multiplier}{CurrencySign} for rolling above 66";
 | 
					 | 
				
			||||||
                await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (rng < 100)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll91Multiplier}{CurrencySign} for rolling above 90.";
 | 
					 | 
				
			||||||
                await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                str += $"👑 Congratulations! You won {amount * NadekoBot.BotConfig.Betroll100Multiplier}{CurrencySign} for rolling **100**. 👑";
 | 
					                if (rnd < 91)
 | 
				
			||||||
                await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false);
 | 
					                {
 | 
				
			||||||
 | 
					                    str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll67Multiplier) + CurrencySign, 66);
 | 
				
			||||||
 | 
					                    await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
 | 
				
			||||||
 | 
					                        (int) (amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (rnd < 100)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll91Multiplier) + CurrencySign, 90);
 | 
				
			||||||
 | 
					                    await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
 | 
				
			||||||
 | 
					                        (int) (amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll100Multiplier) + CurrencySign, 100) + " 👑";
 | 
				
			||||||
 | 
					                    await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
 | 
				
			||||||
 | 
					                        (int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Console.WriteLine("started sending");
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false);
 | 
					            await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            Console.WriteLine("done sending");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        public async Task Leaderboard()
 | 
					        public async Task Leaderboard()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var richest = new List<Currency>();
 | 
					            List<Currency> richest;
 | 
				
			||||||
            using (var uow = DbHandler.UnitOfWork())
 | 
					            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                richest = uow.Currency.GetTopRichest(9).ToList();
 | 
					                richest = uow.Currency.GetTopRichest(9).ToList();
 | 
				
			||||||
@@ -256,22 +254,22 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            if (!richest.Any())
 | 
					            if (!richest.Any())
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
            var embed = new EmbedBuilder()
 | 
					            var embed = new EmbedBuilder()
 | 
				
			||||||
                .WithOkColor()
 | 
					                .WithOkColor()
 | 
				
			||||||
                .WithTitle(NadekoBot.BotConfig.CurrencySign + " Leaderboard");
 | 
					                .WithTitle(NadekoBot.BotConfig.CurrencySign + " " + GetText("leaderboard"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (var i = 0; i < richest.Count; i++)
 | 
					            for (var i = 0; i < richest.Count; i++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var x = richest[i];
 | 
					                var x = richest[i];
 | 
				
			||||||
                var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false);
 | 
					                var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false);
 | 
				
			||||||
                var usrStr = "";
 | 
					                var usrStr = usr == null 
 | 
				
			||||||
                if (usr == null)
 | 
					                    ? x.UserId.ToString() 
 | 
				
			||||||
                    usrStr = x.UserId.ToString();
 | 
					                    : usr.Username?.TrimTo(20, true);
 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    usrStr = usr.Username?.TrimTo(20, true);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                embed.AddField(efb => efb.WithName("#" + (i + 1) + " " + usrStr).WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign).WithIsInline(true));
 | 
					                var j = i;
 | 
				
			||||||
 | 
					                embed.AddField(efb => efb.WithName("#" + (j + 1) + " " + usrStr)
 | 
				
			||||||
 | 
					                                         .WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign)
 | 
				
			||||||
 | 
					                                         .WithIsInline(true));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					            await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,12 +21,6 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            private static Logger _log { get; }
 | 
					            private static Logger _log { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            class CleverAnswer
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                public string Status { get; set; }
 | 
					 | 
				
			||||||
                public string Response { get; set; }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>();
 | 
					            public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            static CleverBotCommands()
 | 
					            static CleverBotCommands()
 | 
				
			||||||
@@ -34,14 +28,12 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					                _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
                var sw = Stopwatch.StartNew();
 | 
					                var sw = Stopwatch.StartNew();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                
 | 
				
			||||||
                {
 | 
					                var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
 | 
				
			||||||
                    var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
 | 
					                CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
 | 
				
			||||||
                    CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
 | 
					                    NadekoBot.AllGuildConfigs
 | 
				
			||||||
                        NadekoBot.AllGuildConfigs
 | 
					                        .Where(gc => gc.CleverbotEnabled)
 | 
				
			||||||
                            .Where(gc => gc.CleverbotEnabled)
 | 
					                        .ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
 | 
				
			||||||
                            .ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                sw.Stop();
 | 
					                sw.Stop();
 | 
				
			||||||
                _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
 | 
					                _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,8 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
            static HangmanCommands()
 | 
					            static HangmanCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					                _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
                typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys);
 | 
					                typesStr =
 | 
				
			||||||
 | 
					                    string.Format("`List of \"{0}hangman\" term types:`\n", NadekoBot.ModulePrefixes[typeof(Games).Name]) + String.Join(", ", HangmanTermPool.data.Keys);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        /// https://discord.gg/0TYNJfCU4De7YIk8
 | 
					        /// https://discord.gg/0TYNJfCU4De7YIk8
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class PlantPickCommands : ModuleBase
 | 
					        public class PlantPickCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
 | 
					            private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
 | 
				
			||||||
            //channelid/message
 | 
					            //channelid/message
 | 
				
			||||||
@@ -37,13 +37,8 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
            //channelId/last generation
 | 
					            //channelId/last generation
 | 
				
			||||||
            private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
 | 
					            private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static ConcurrentHashSet<ulong> usersRecentlyPicked { get; } = new ConcurrentHashSet<ulong>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            private static Logger _log { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            static PlantPickCommands()
 | 
					            static PlantPickCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if !GLOBAL_NADEKO
 | 
					#if !GLOBAL_NADEKO
 | 
				
			||||||
                NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
 | 
					                NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
 | 
				
			||||||
@@ -103,7 +98,8 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                                    var sent = await channel.SendFileAsync(
 | 
					                                    var sent = await channel.SendFileAsync(
 | 
				
			||||||
                                        fileStream,
 | 
					                                        fileStream,
 | 
				
			||||||
                                        file.Key,
 | 
					                                        file.Key,
 | 
				
			||||||
                                        $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
 | 
					                                            string.Format("❗ {0} Pick it up by typing `{1}pick`", firstPart,
 | 
				
			||||||
 | 
					                                                NadekoBot.ModulePrefixes[typeof(Games).Name]))
 | 
				
			||||||
                                            .ConfigureAwait(false);
 | 
					                                            .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    msgs[0] = sent;
 | 
					                                    msgs[0] = sent;
 | 
				
			||||||
@@ -115,7 +111,7 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch (Exception ex)
 | 
					                    catch (Exception ex)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        _log.Warn(ex);
 | 
					                        LogManager.GetCurrentClassLogger().Warn(ex);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
                return Task.CompletedTask;
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
@@ -129,32 +125,18 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
 | 
					                if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
#if GLOBAL_NADEKO
 | 
					
 | 
				
			||||||
                if (!usersRecentlyPicked.Add(Context.User.Id))
 | 
					                List<IUserMessage> msgs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
				
			||||||
 | 
					                if (!plantedFlowers.TryRemove(channel.Id, out msgs))
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    List<IUserMessage> msgs;
 | 
					                await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
					                await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
 | 
				
			||||||
                    if (!plantedFlowers.TryRemove(channel.Id, out msgs))
 | 
					                var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false);
 | 
				
			||||||
                        return;
 | 
					                msg.DeleteAfter(10);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    msg.DeleteAfter(10);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                finally
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
#if GLOBAL_NADEKO
 | 
					 | 
				
			||||||
                    await Task.Delay(60000);
 | 
					 | 
				
			||||||
                    usersRecentlyPicked.TryRemove(Context.User.Id);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -174,7 +156,7 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                var imgData = GetRandomCurrencyImage();
 | 
					                var imgData = GetRandomCurrencyImage();
 | 
				
			||||||
                var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]);
 | 
					                var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick";
 | 
					                var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {Prefix}pick";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                IUserMessage msg;
 | 
					                IUserMessage msg;
 | 
				
			||||||
                using (var toSend = imgData.Value.ToStream())
 | 
					                using (var toSend = imgData.Value.ToStream())
 | 
				
			||||||
@@ -236,17 +218,6 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                return images[rng.Next(0, images.Length)];
 | 
					                return images[rng.Next(0, images.Length)];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            int GetRandomNumber()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                using (var rg = RandomNumberGenerator.Create())
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    byte[] rno = new byte[4];
 | 
					 | 
				
			||||||
                    rg.GetBytes(rno);
 | 
					 | 
				
			||||||
                    int randomvalue = BitConverter.ToInt32(rno, 0);
 | 
					 | 
				
			||||||
                    return randomvalue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -307,7 +307,7 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                                var del2 = previousMessage?.DeleteAsync();
 | 
					                                var del2 = previousMessage?.DeleteAsync();
 | 
				
			||||||
                                try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
 | 
					                                try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
 | 
				
			||||||
                                try { await del1; } catch { }
 | 
					                                try { await del1; } catch { }
 | 
				
			||||||
                                try { await del2; } catch { }
 | 
					                                try { if (del2 != null) await del2; } catch { }
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            curUserIndex ^= 1;
 | 
					                            curUserIndex ^= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ using NadekoBot.Extensions;
 | 
				
			|||||||
namespace NadekoBot.Modules.Games
 | 
					namespace NadekoBot.Modules.Games
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Games", ">")]
 | 
					    [NadekoModule("Games", ">")]
 | 
				
			||||||
    public partial class Games : DiscordModule
 | 
					    public partial class Games : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray();
 | 
					        private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(question))
 | 
					            if (string.IsNullOrWhiteSpace(question))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
                var rng = new NadekoRandom();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
					            await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
				
			||||||
                               .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
 | 
					                               .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,19 +13,26 @@ using System.Collections.Generic;
 | 
				
			|||||||
namespace NadekoBot.Modules.Help
 | 
					namespace NadekoBot.Modules.Help
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Help", "-")]
 | 
					    [NadekoModule("Help", "-")]
 | 
				
			||||||
    public partial class Help : DiscordModule
 | 
					    public class Help : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static string helpString { get; } = NadekoBot.BotConfig.HelpString;
 | 
					        private static string helpString { get; } = NadekoBot.BotConfig.HelpString;
 | 
				
			||||||
        public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]);
 | 
					        public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString;
 | 
					        public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public const string PatreonUrl = "https://patreon.com/nadekobot";
 | 
				
			||||||
 | 
					        public const string PaypalUrl = "https://paypal.me/Kwoth";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        public async Task Modules()
 | 
					        public async Task Modules()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var embed = new EmbedBuilder().WithOkColor()
 | 
				
			||||||
            var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" ℹ️ Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`"))
 | 
					                .WithFooter(efb => efb.WithText("ℹ️" + GetText("modules_footer", Prefix)))
 | 
				
			||||||
                .WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule()).Select(m => m.Key.Name).OrderBy(s => s)));
 | 
					                .WithTitle(GetText("list_of_modules"))
 | 
				
			||||||
 | 
					                .WithDescription(string.Join("\n",
 | 
				
			||||||
 | 
					                                     NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule())
 | 
				
			||||||
 | 
					                                         .Select(m => "• " + m.Key.Name)
 | 
				
			||||||
 | 
					                                         .OrderBy(s => s)));
 | 
				
			||||||
            await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					            await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,18 +52,13 @@ namespace NadekoBot.Modules.Help
 | 
				
			|||||||
            var cmdsArray = cmds as CommandInfo[] ?? cmds.ToArray();
 | 
					            var cmdsArray = cmds as CommandInfo[] ?? cmds.ToArray();
 | 
				
			||||||
            if (!cmdsArray.Any())
 | 
					            if (!cmdsArray.Any())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await channel.SendErrorAsync("That module does not exist.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("module_not_found").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (module != "customreactions" && module != "conversations")
 | 
					
 | 
				
			||||||
            {
 | 
					            await channel.SendTableAsync($"📃 **{GetText("list_of_commands")}**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
 | 
				
			||||||
                await channel.SendTableAsync("📃 **List Of Commands:**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
 | 
					
 | 
				
			||||||
            }
 | 
					            await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false);
 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Aliases.First()}")));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            await channel.SendConfirmAsync($"ℹ️ **Type** `\"{NadekoBot.ModulePrefixes[typeof(Help).Name]}h CommandName\"` **to see the help for that specified command.** ***e.g.*** `-h >8ball`").ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -75,32 +77,33 @@ namespace NadekoBot.Modules.Help
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (com == null)
 | 
					            if (com == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await channel.SendErrorAsync("I can't find that command. Please check the **command** and **command prefix** before trying again.");
 | 
					                await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var str = $"**`{com.Aliases.First()}`**";
 | 
					            var str = string.Format("**`{0}`**", com.Aliases.First());
 | 
				
			||||||
            var alias = com.Aliases.Skip(1).FirstOrDefault();
 | 
					            var alias = com.Aliases.Skip(1).FirstOrDefault();
 | 
				
			||||||
            if (alias != null)
 | 
					            if (alias != null)
 | 
				
			||||||
                str += $" **/ `{alias}`**";
 | 
					                str += string.Format(" **/ `{0}`**", alias);
 | 
				
			||||||
                var embed = new EmbedBuilder()
 | 
					            var embed = new EmbedBuilder()
 | 
				
			||||||
                .AddField(fb => fb.WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Aliases.First())} { GetCommandRequirements(com)}").WithIsInline(true))
 | 
					                .AddField(fb => fb.WithName(str).WithValue($"{string.Format(com.Summary, com.Module.Aliases.First())} {GetCommandRequirements(com)}").WithIsInline(true))
 | 
				
			||||||
                .AddField(fb => fb.WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Aliases.First())}").WithIsInline(false))
 | 
					                .AddField(fb => fb.WithName(GetText("usage")).WithValue(string.Format(com.Remarks, com.Module.Aliases.First())).WithIsInline(false))
 | 
				
			||||||
                .WithColor(NadekoBot.OkColor);
 | 
					                .WithColor(NadekoBot.OkColor);
 | 
				
			||||||
            await channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					            await channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string GetCommandRequirements(CommandInfo cmd) => 
 | 
					        private string GetCommandRequirements(CommandInfo cmd) => 
 | 
				
			||||||
            String.Join(" ", cmd.Preconditions
 | 
					            string.Join(" ", cmd.Preconditions
 | 
				
			||||||
                  .Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
 | 
					                  .Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
 | 
				
			||||||
                  .Select(ca =>
 | 
					                  .Select(ca =>
 | 
				
			||||||
                  {
 | 
					                  {
 | 
				
			||||||
                      if (ca is OwnerOnlyAttribute)
 | 
					                      if (ca is OwnerOnlyAttribute)
 | 
				
			||||||
                          return "**Bot Owner only.**";
 | 
					                          return Format.Bold(GetText("bot_owner_only"));
 | 
				
			||||||
                      var cau = (RequireUserPermissionAttribute)ca;
 | 
					                      var cau = (RequireUserPermissionAttribute)ca;
 | 
				
			||||||
                      if (cau.GuildPermission != null)
 | 
					                      if (cau.GuildPermission != null)
 | 
				
			||||||
                          return $"**Requires {cau.GuildPermission} server permission.**".Replace("Guild", "Server");
 | 
					                          return Format.Bold(GetText("server_permission", cau.GuildPermission))
 | 
				
			||||||
                      else
 | 
					                                       .Replace("Guild", "Server");
 | 
				
			||||||
                          return $"**Requires {cau.ChannelPermission} channel permission.**".Replace("Guild", "Server");
 | 
					                      return Format.Bold(GetText("channel_permission", cau.ChannelPermission))
 | 
				
			||||||
 | 
					                                       .Replace("Guild", "Server");
 | 
				
			||||||
                  }));
 | 
					                  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -109,14 +112,14 @@ namespace NadekoBot.Modules.Help
 | 
				
			|||||||
        public async Task Hgit()
 | 
					        public async Task Hgit()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var helpstr = new StringBuilder();
 | 
					            var helpstr = new StringBuilder();
 | 
				
			||||||
            helpstr.AppendLine("You can support the project on patreon: <https://patreon.com/nadekobot> or paypal: <https://www.paypal.me/Kwoth>\n");
 | 
					            helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n");
 | 
				
			||||||
            helpstr.AppendLine("##Table Of Contents");
 | 
					            helpstr.AppendLine("##"+ GetText("table_of_contents"));
 | 
				
			||||||
            helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
 | 
					            helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
 | 
				
			||||||
                .Select(m => m.GetTopLevelModule().Name)
 | 
					                .Select(m => m.GetTopLevelModule().Name)
 | 
				
			||||||
                .Distinct()
 | 
					                .Distinct()
 | 
				
			||||||
                .OrderBy(m => m)
 | 
					                .OrderBy(m => m)
 | 
				
			||||||
                .Prepend("Help")
 | 
					                .Prepend("Help")
 | 
				
			||||||
                .Select(m => $"- [{m}](#{m.ToLowerInvariant()})")));
 | 
					                .Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant()))));
 | 
				
			||||||
            helpstr.AppendLine();
 | 
					            helpstr.AppendLine();
 | 
				
			||||||
            string lastModule = null;
 | 
					            string lastModule = null;
 | 
				
			||||||
            foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
 | 
					            foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
 | 
				
			||||||
@@ -127,44 +130,35 @@ namespace NadekoBot.Modules.Help
 | 
				
			|||||||
                    if (lastModule != null)
 | 
					                    if (lastModule != null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        helpstr.AppendLine();
 | 
					                        helpstr.AppendLine();
 | 
				
			||||||
                        helpstr.AppendLine("###### [Back to TOC](#table-of-contents)");
 | 
					                        helpstr.AppendLine($"###### [{GetText("back_to_toc")}](#{GetText("table_of_contents").ToLowerInvariant().Replace(' ', '-')})");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    helpstr.AppendLine();
 | 
					                    helpstr.AppendLine();
 | 
				
			||||||
                    helpstr.AppendLine("### " + module.Name + "  ");
 | 
					                    helpstr.AppendLine("### " + module.Name + "  ");
 | 
				
			||||||
                    helpstr.AppendLine("Command and aliases | Description | Usage");
 | 
					                    helpstr.AppendLine($"{GetText("cmd_and_alias")} | {GetText("desc")} | {GetText("usage")}");
 | 
				
			||||||
                    helpstr.AppendLine("----------------|--------------|-------");
 | 
					                    helpstr.AppendLine("----------------|--------------|-------");
 | 
				
			||||||
                    lastModule = module.Name;
 | 
					                    lastModule = module.Name;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} | {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.GetPrefix())}");
 | 
					                helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
 | 
				
			||||||
 | 
					                                   $" {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} |" +
 | 
				
			||||||
 | 
					                                   $" {string.Format(com.Remarks, com.Module.GetPrefix())}");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName");
 | 
					            helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName");
 | 
				
			||||||
            File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
 | 
					            File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync("Commandlist Regenerated").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
        public async Task Guide()
 | 
					        public async Task Guide()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var channel = (ITextChannel)Context.Channel;
 | 
					            await ConfirmLocalized("guide", 
 | 
				
			||||||
 | 
					                "http://nadekobot.readthedocs.io/en/latest/Commands%20List/",
 | 
				
			||||||
            await channel.SendConfirmAsync(
 | 
					                "http://nadekobot.readthedocs.io/en/latest/").ConfigureAwait(false);
 | 
				
			||||||
@"**LIST OF COMMANDS**: <http://nadekobot.readthedocs.io/en/latest/Commands%20List/>
 | 
					 | 
				
			||||||
**Hosting Guides and docs can be found here**: <http://nadekobot.readthedocs.io/en/latest/>").ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
        public async Task Donate()
 | 
					        public async Task Donate()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var channel = (ITextChannel)Context.Channel;
 | 
					            await ReplyConfirmLocalized("donate", PatreonUrl, PaypalUrl).ConfigureAwait(false);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            await channel.SendConfirmAsync(
 | 
					 | 
				
			||||||
$@"You can support the NadekoBot project on patreon. <https://patreon.com/nadekobot> or
 | 
					 | 
				
			||||||
Paypal <https://paypal.me/Kwoth>
 | 
					 | 
				
			||||||
Don't forget to leave your discord name or id in the message.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Thank you** ♥️").ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Music
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Music", "!!")]
 | 
					    [NadekoModule("Music", "!!")]
 | 
				
			||||||
    [DontAutoLoad]
 | 
					    [DontAutoLoad]
 | 
				
			||||||
    public partial class Music : DiscordModule
 | 
					    public partial class Music : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
 | 
					        public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -415,27 +415,26 @@ namespace NadekoBot.Modules.Music
 | 
				
			|||||||
            var arg = directory;
 | 
					            var arg = directory;
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(arg))
 | 
					            if (string.IsNullOrWhiteSpace(arg))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            try
 | 
					            var dir = new DirectoryInfo(arg);
 | 
				
			||||||
 | 
					            var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
 | 
				
			||||||
 | 
					                                .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
 | 
				
			||||||
 | 
					            var gusr = (IGuildUser)Context.User;
 | 
				
			||||||
 | 
					            foreach (var file in fileEnum)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var dir = new DirectoryInfo(arg);
 | 
					                try
 | 
				
			||||||
                var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
 | 
					 | 
				
			||||||
                                    .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
 | 
					 | 
				
			||||||
                var gusr = (IGuildUser)Context.User;
 | 
					 | 
				
			||||||
                foreach (var file in fileEnum)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    await QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
 | 
				
			||||||
                    {
 | 
					                }
 | 
				
			||||||
                        await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
 | 
					                catch (PlaylistFullException)
 | 
				
			||||||
                    }
 | 
					                {
 | 
				
			||||||
                    catch (PlaylistFullException)
 | 
					                    break;
 | 
				
			||||||
                    {
 | 
					                }
 | 
				
			||||||
                        break;
 | 
					                catch
 | 
				
			||||||
                    }
 | 
					                {
 | 
				
			||||||
                    catch { }
 | 
					                    // ignored
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch { }
 | 
					            await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,11 +17,11 @@ using System.Collections.Concurrent;
 | 
				
			|||||||
namespace NadekoBot.Modules.NSFW
 | 
					namespace NadekoBot.Modules.NSFW
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("NSFW", "~")]
 | 
					    [NadekoModule("NSFW", "~")]
 | 
				
			||||||
    public class NSFW : DiscordModule
 | 
					    public class NSFW : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static ConcurrentDictionary<ulong, Timer> AutoHentaiTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
 | 
					        private static readonly ConcurrentDictionary<ulong, Timer> AutoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
 | 
				
			||||||
        private static ConcurrentHashSet<ulong> _hentaiBombBlacklist { get; } = new ConcurrentHashSet<ulong>();
 | 
					        private static readonly ConcurrentHashSet<ulong> HentaiBombBlacklist = new ConcurrentHashSet<ulong>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
 | 
					        private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -30,7 +30,7 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
            tag = "rating%3Aexplicit+" + tag;
 | 
					            tag = "rating%3Aexplicit+" + tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var rng = new NadekoRandom();
 | 
					            var rng = new NadekoRandom();
 | 
				
			||||||
            Task<string> provider = Task.FromResult("");
 | 
					            var provider = Task.FromResult("");
 | 
				
			||||||
            switch (rng.Next(0, 4))
 | 
					            switch (rng.Next(0, 4))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case 0:
 | 
					                case 0:
 | 
				
			||||||
@@ -45,20 +45,18 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
                case 3:
 | 
					                case 3:
 | 
				
			||||||
                    provider = GetYandereImageLink(tag);
 | 
					                    provider = GetYandereImageLink(tag);
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                default:
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var link = await provider.ConfigureAwait(false);
 | 
					            var link = await provider.ConfigureAwait(false);
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(link))
 | 
					            if (string.IsNullOrWhiteSpace(link))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!noError)
 | 
					                if (!noError)
 | 
				
			||||||
                    await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("not_found").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
					            await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                .WithImageUrl(link)
 | 
					                .WithImageUrl(link)
 | 
				
			||||||
                .WithDescription("Tag: " + tag))
 | 
					                .WithDescription($"{GetText("tag")}: " + tag))
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,11 +72,10 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (interval == 0)
 | 
					            if (interval == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t))
 | 
					                if (!AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return;
 | 
				
			||||||
                {
 | 
					
 | 
				
			||||||
                    t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
 | 
					                t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
 | 
				
			||||||
                    await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,7 +93,10 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
                    else
 | 
					                    else
 | 
				
			||||||
                        await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false);
 | 
					                        await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }, null, interval * 1000, interval * 1000);
 | 
					            }, null, interval * 1000, interval * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
 | 
					            AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
 | 
				
			||||||
@@ -105,15 +105,16 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
                return t;
 | 
					                return t;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}")
 | 
					            await ReplyConfirmLocalized("autohentai_started", 
 | 
				
			||||||
                                 .ConfigureAwait(false);
 | 
					                interval, 
 | 
				
			||||||
 | 
					                string.Join(", ", tagsArr)).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        public async Task HentaiBomb([Remainder] string tag = null)
 | 
					        public async Task HentaiBomb([Remainder] string tag = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!_hentaiBombBlacklist.Add(Context.User.Id))
 | 
					            if (!HentaiBombBlacklist.Add(Context.User.Id))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -125,19 +126,19 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
                                               GetKonachanImageLink(tag),
 | 
					                                               GetKonachanImageLink(tag),
 | 
				
			||||||
                                               GetYandereImageLink(tag)).ConfigureAwait(false);
 | 
					                                               GetYandereImageLink(tag)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var linksEnum = links?.Where(l => l != null);
 | 
					                var linksEnum = links?.Where(l => l != null).ToArray();
 | 
				
			||||||
                if (links == null || !linksEnum.Any())
 | 
					                if (links == null || !linksEnum.Any())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("not_found").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false);
 | 
					                await Context.Channel.SendMessageAsync(string.Join("\n\n", linksEnum)).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            finally
 | 
					            finally
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Task.Delay(5000).ConfigureAwait(false);
 | 
					                await Task.Delay(5000).ConfigureAwait(false);
 | 
				
			||||||
                _hentaiBombBlacklist.TryRemove(Context.User.Id);
 | 
					                HentaiBombBlacklist.TryRemove(Context.User.Id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -157,7 +158,7 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
            var url = await GetE621ImageLink(tag).ConfigureAwait(false);
 | 
					            var url = await GetE621ImageLink(tag).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (url == null)
 | 
					            if (url == null)
 | 
				
			||||||
                await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.");
 | 
					                await ReplyErrorLocalized("not_found").ConfigureAwait(false);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
					                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                    .WithDescription(Context.User.Mention + " " + tag)
 | 
					                    .WithDescription(Context.User.Mention + " " + tag)
 | 
				
			||||||
@@ -178,7 +179,7 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
            var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
 | 
					            var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (url == null)
 | 
					            if (url == null)
 | 
				
			||||||
                await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("not_found").ConfigureAwait(false);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
					                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                    .WithDescription(Context.User.Mention + " " + tag)
 | 
					                    .WithDescription(Context.User.Mention + " " + tag)
 | 
				
			||||||
@@ -227,9 +228,9 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
                JToken obj;
 | 
					                JToken obj;
 | 
				
			||||||
                using (var http = new HttpClient())
 | 
					                using (var http = new HttpClient())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10330) }").ConfigureAwait(false))[0];
 | 
					                    obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{new NadekoRandom().Next(0, 10330)}").ConfigureAwait(false))[0];
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
 | 
					                await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{obj["preview"]}").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -245,9 +246,9 @@ namespace NadekoBot.Modules.NSFW
 | 
				
			|||||||
                JToken obj;
 | 
					                JToken obj;
 | 
				
			||||||
                using (var http = new HttpClient())
 | 
					                using (var http = new HttpClient())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4335) }").ConfigureAwait(false))[0];
 | 
					                    obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{new NadekoRandom().Next(0, 4335)}").ConfigureAwait(false))[0];
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
 | 
					                await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{obj["preview"]}").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										129
									
								
								src/NadekoBot/Modules/NadekoModule.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/NadekoBot/Modules/NadekoModule.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					using Discord;
 | 
				
			||||||
 | 
					using Discord.Commands;
 | 
				
			||||||
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using NLog;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Modules
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public abstract class NadekoModule : ModuleBase
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        protected readonly Logger _log;
 | 
				
			||||||
 | 
					        protected CultureInfo _cultureInfo;
 | 
				
			||||||
 | 
					        public readonly string Prefix;
 | 
				
			||||||
 | 
					        public readonly string ModuleTypeName;
 | 
				
			||||||
 | 
					        public readonly string LowerModuleTypeName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected NadekoModule(bool isTopLevelModule = true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            //if it's top level module
 | 
				
			||||||
 | 
					            ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name;
 | 
				
			||||||
 | 
					            LowerModuleTypeName = ModuleTypeName.ToLowerInvariant();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out Prefix))
 | 
				
			||||||
 | 
					                Prefix = "?err?";
 | 
				
			||||||
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override void BeforeExecute()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _cultureInfo = NadekoBot.Localization.GetCultureInfo(Context.Guild?.Id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _log.Warn("Culture info is {0}", _cultureInfo);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //public Task<IUserMessage> ReplyConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null)
 | 
				
			||||||
 | 
					        //{
 | 
				
			||||||
 | 
					        //    var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo);
 | 
				
			||||||
 | 
					        //    var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
 | 
				
			||||||
 | 
					        //    return Context.Channel.SendConfirmAsync(title, text, url, footer);
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //public Task<IUserMessage> ReplyConfirmLocalized(string textKey)
 | 
				
			||||||
 | 
					        //{
 | 
				
			||||||
 | 
					        //    var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
 | 
				
			||||||
 | 
					        //    return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + textKey);
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //public Task<IUserMessage> ReplyErrorLocalized(string titleKey, string textKey, string url = null, string footer = null)
 | 
				
			||||||
 | 
					        //{
 | 
				
			||||||
 | 
					        //    var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo);
 | 
				
			||||||
 | 
					        //    var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
 | 
				
			||||||
 | 
					        //    return Context.Channel.SendErrorAsync(title, text, url, footer);
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Used as failsafe in case response key doesn't exist in the selected or default language.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        private static readonly CultureInfo _usCultureInfo = new CultureInfo("en-US");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, cultureInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(text))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS.");
 | 
				
			||||||
 | 
					                text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!";
 | 
				
			||||||
 | 
					                if (string.IsNullOrWhiteSpace(text))
 | 
				
			||||||
 | 
					                    return "I cant tell if you command is executed, because there was an error printing out the response. Key '" +
 | 
				
			||||||
 | 
					                        lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this.";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return text;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName,
 | 
				
			||||||
 | 
					            params object[] replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return string.Format(GetTextStatic(key, cultureInfo, lowerModuleTypeName), replacements);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (FormatException)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return "I cant tell if you command is executed, because there was an error printing out the response. Key '" +
 | 
				
			||||||
 | 
					                       lowerModuleTypeName + "_" + key + "' " + "is not properly formatted. Please report this.";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected string GetText(string key) =>
 | 
				
			||||||
 | 
					            GetTextStatic(key, _cultureInfo, LowerModuleTypeName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected string GetText(string key, params object[] replacements) =>
 | 
				
			||||||
 | 
					            GetTextStatic(key, _cultureInfo, LowerModuleTypeName, replacements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Task<IUserMessage> ErrorLocalized(string textKey, params object[] replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var text = GetText(textKey, replacements);
 | 
				
			||||||
 | 
					            return Context.Channel.SendErrorAsync(text);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Task<IUserMessage> ReplyErrorLocalized(string textKey, params object[] replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var text = GetText(textKey, replacements);
 | 
				
			||||||
 | 
					            return Context.Channel.SendErrorAsync(Context.User.Mention + " " + text);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Task<IUserMessage> ConfirmLocalized(string textKey, params object[] replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var text = GetText(textKey, replacements);
 | 
				
			||||||
 | 
					            return Context.Channel.SendConfirmAsync(text);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Task<IUserMessage> ReplyConfirmLocalized(string textKey, params object[] replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var text = GetText(textKey, replacements);
 | 
				
			||||||
 | 
					            return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public abstract class NadekoSubmodule : NadekoModule
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        protected NadekoSubmodule() : base(false)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/NadekoBot/Modules/NadekoModuleExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Modules/NadekoModuleExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					using Discord;
 | 
				
			||||||
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Modules
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class NadekoModuleExtensions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -101,23 +101,23 @@ namespace NadekoBot.Modules.Permissions
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return true;
 | 
					                        return true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    activeCdsForGuild.Add(new ActiveCooldown()
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        activeCdsForGuild.Add(new ActiveCooldown()
 | 
					                        UserId = user.Id,
 | 
				
			||||||
 | 
					                        Command = cmd.Aliases.First().ToLowerInvariant(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    var _ = Task.Run(async () =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        try
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            UserId = user.Id,
 | 
					                            await Task.Delay(cdRule.Seconds * 1000);
 | 
				
			||||||
                            Command = cmd.Aliases.First().ToLowerInvariant(),
 | 
					                            activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
 | 
				
			||||||
                        });
 | 
					                        }
 | 
				
			||||||
                        var t = Task.Run(async () =>
 | 
					                        catch
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            try
 | 
					                            // ignored
 | 
				
			||||||
                            {
 | 
					                        }
 | 
				
			||||||
                                await Task.Delay(cdRule.Seconds * 1000);
 | 
					                    });
 | 
				
			||||||
                                activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            catch { }
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,23 +42,19 @@ namespace NadekoBot.Modules.Permissions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            static FilterCommands()
 | 
					            static FilterCommands()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                using (var uow = DbHandler.UnitOfWork())
 | 
					                var guildConfigs = NadekoBot.AllGuildConfigs;
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var guildConfigs = NadekoBot.AllGuildConfigs;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
 | 
					                InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
 | 
				
			||||||
                    InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
 | 
					                InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
 | 
					                var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
 | 
					                ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
 | 
					                var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
 | 
				
			||||||
                    WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
 | 
					                WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
 | 
					                WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
 | 
				
			||||||
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ using NLog;
 | 
				
			|||||||
namespace NadekoBot.Modules.Permissions
 | 
					namespace NadekoBot.Modules.Permissions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Permissions", ";")]
 | 
					    [NadekoModule("Permissions", ";")]
 | 
				
			||||||
    public partial class Permissions : DiscordModule
 | 
					    public partial class Permissions : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public class PermissionCache
 | 
					        public class PermissionCache
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,21 +16,21 @@ using System.Collections.Concurrent;
 | 
				
			|||||||
namespace NadekoBot.Modules.Pokemon
 | 
					namespace NadekoBot.Modules.Pokemon
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Pokemon", ">")]
 | 
					    [NadekoModule("Pokemon", ">")]
 | 
				
			||||||
    public partial class Pokemon : DiscordModule
 | 
					    public class Pokemon : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static List<PokemonType> PokemonTypes = new List<PokemonType>();
 | 
					        private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>();
 | 
				
			||||||
        private static ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>();
 | 
					        private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public const string PokemonTypesFile = "data/pokemon_types.json";
 | 
					        public const string PokemonTypesFile = "data/pokemon_types.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static new Logger _log { get; }
 | 
					        private new static Logger _log { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        static Pokemon()
 | 
					        static Pokemon()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            if (File.Exists(PokemonTypesFile))
 | 
					            if (File.Exists(PokemonTypesFile))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
 | 
					                _pokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -42,21 +42,18 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
        private int GetDamage(PokemonType usertype, PokemonType targetType)
 | 
					        private int GetDamage(PokemonType usertype, PokemonType targetType)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var rng = new Random();
 | 
					            var rng = new Random();
 | 
				
			||||||
            int damage = rng.Next(40, 60);
 | 
					            var damage = rng.Next(40, 60);
 | 
				
			||||||
            foreach (PokemonMultiplier Multiplier in usertype.Multipliers)
 | 
					            foreach (var multiplierObj in usertype.Multipliers)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (Multiplier.Type == targetType.Name)
 | 
					                if (multiplierObj.Type != targetType.Name) continue;
 | 
				
			||||||
                {
 | 
					                damage = (int)(damage * multiplierObj.Multiplication);
 | 
				
			||||||
                    var multiplier = Multiplier.Multiplication;
 | 
					 | 
				
			||||||
                    damage = (int)(damage * multiplier);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return damage;
 | 
					            return damage;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private PokemonType GetPokeType(ulong id)
 | 
					        private static PokemonType GetPokeType(ulong id)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Dictionary<ulong, string> setTypes;
 | 
					            Dictionary<ulong, string> setTypes;
 | 
				
			||||||
@@ -69,20 +66,18 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                return StringToPokemonType(setTypes[id]);
 | 
					                return StringToPokemonType(setTypes[id]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            int count = PokemonTypes.Count;
 | 
					            var count = _pokemonTypes.Count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            int remainder = Math.Abs((int)(id % (ulong)count));
 | 
					            var remainder = Math.Abs((int)(id % (ulong)count));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return PokemonTypes[remainder];
 | 
					            return _pokemonTypes[remainder];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        private static PokemonType StringToPokemonType(string v)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private PokemonType StringToPokemonType(string v)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var str = v?.ToUpperInvariant();
 | 
					            var str = v?.ToUpperInvariant();
 | 
				
			||||||
            var list = PokemonTypes;
 | 
					            var list = _pokemonTypes;
 | 
				
			||||||
            foreach (PokemonType p in list)
 | 
					            foreach (var p in list)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (str == p.Name)
 | 
					                if (str == p.Name)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -92,7 +87,6 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        public async Task Attack(string move, IGuildUser targetUser = null)
 | 
					        public async Task Attack(string move, IGuildUser targetUser = null)
 | 
				
			||||||
@@ -105,46 +99,44 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (targetUser == null)
 | 
					            if (targetUser == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("user_not_found").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (targetUser == user)
 | 
					            if (targetUser == user)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync("You can't attack yourself.").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("cant_attack_yourself").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                   
 | 
					                   
 | 
				
			||||||
            // Checking stats first, then move
 | 
					            // Checking stats first, then move
 | 
				
			||||||
            //Set up the userstats
 | 
					            //Set up the userstats
 | 
				
			||||||
            PokeStats userStats;
 | 
					            var userStats = _stats.GetOrAdd(user.Id, new PokeStats());
 | 
				
			||||||
            userStats = Stats.GetOrAdd(user.Id, new PokeStats());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //Check if able to move
 | 
					            //Check if able to move
 | 
				
			||||||
            //User not able if HP < 0, has made more than 4 attacks
 | 
					            //User not able if HP < 0, has made more than 4 attacks
 | 
				
			||||||
            if (userStats.Hp < 0)
 | 
					            if (userStats.Hp < 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{user.Mention} has fainted and was not able to move!").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("you_fainted").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (userStats.MovesMade >= 5)
 | 
					            if (userStats.MovesMade >= 5)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{user.Mention} has used too many moves in a row and was not able to move!").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("too_many_moves").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (userStats.LastAttacked.Contains(targetUser.Id))
 | 
					            if (userStats.LastAttacked.Contains(targetUser.Id))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{user.Mention} can't attack again without retaliation!").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("cant_attack_again").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            //get target stats
 | 
					            //get target stats
 | 
				
			||||||
            PokeStats targetStats;
 | 
					            var targetStats = _stats.GetOrAdd(targetUser.Id, new PokeStats());
 | 
				
			||||||
            targetStats = Stats.GetOrAdd(targetUser.Id, new PokeStats());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //If target's HP is below 0, no use attacking
 | 
					            //If target's HP is below 0, no use attacking
 | 
				
			||||||
            if (targetStats.Hp <= 0)
 | 
					            if (targetStats.Hp <= 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{targetUser.Mention} has already fainted!").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("too_many_moves", targetUser).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -154,7 +146,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            var enabledMoves = userType.Moves;
 | 
					            var enabledMoves = userType.Moves;
 | 
				
			||||||
            if (!enabledMoves.Contains(move.ToLowerInvariant()))
 | 
					            if (!enabledMoves.Contains(move.ToLowerInvariant()))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{user.Mention} is not able to use **{move}**. Type {NadekoBot.ModulePrefixes[typeof(Pokemon).Name]}ml to see moves").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("invalid_move", Format.Bold(move), Prefix).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -165,31 +157,31 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            //apply damage to target
 | 
					            //apply damage to target
 | 
				
			||||||
            targetStats.Hp -= damage;
 | 
					            targetStats.Hp -= damage;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            var response = $"{user.Mention} used **{move}**{userType.Icon} on {targetUser.Mention}{targetType.Icon} for **{damage}** damage";
 | 
					            var response = GetText("attack", Format.Bold(move), userType.Icon, Format.Bold(targetUser.ToString()), targetType.Icon, Format.Bold(damage.ToString()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //Damage type
 | 
					            //Damage type
 | 
				
			||||||
            if (damage < 40)
 | 
					            if (damage < 40)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                response += "\nIt's not effective..";
 | 
					                response += "\n" + GetText("not_effective");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (damage > 60)
 | 
					            else if (damage > 60)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                response += "\nIt's super effective!";
 | 
					                response += "\n" + GetText("super_effective");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                response += "\nIt's somewhat effective";
 | 
					                response += "\n" + GetText("somewhat_effective");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //check fainted
 | 
					            //check fainted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (targetStats.Hp <= 0)
 | 
					            if (targetStats.Hp <= 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                response += $"\n**{targetUser.Mention}** has fainted!";
 | 
					                response += "\n" + GetText("fainted", Format.Bold(targetUser.ToString()));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                response += $"\n**{targetUser.Mention}** has {targetStats.Hp} HP remaining";
 | 
					                response += "\n" + GetText("hp_remaining", Format.Bold(targetUser.ToString()), targetStats.Hp);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //update other stats
 | 
					            //update other stats
 | 
				
			||||||
@@ -203,10 +195,10 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            //update dictionary
 | 
					            //update dictionary
 | 
				
			||||||
            //This can stay the same right?
 | 
					            //This can stay the same right?
 | 
				
			||||||
            Stats[user.Id] = userStats;
 | 
					            _stats[user.Id] = userStats;
 | 
				
			||||||
            Stats[targetUser.Id] = targetStats;
 | 
					            _stats[targetUser.Id] = targetStats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
 | 
					            await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -218,12 +210,10 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var userType = GetPokeType(user.Id);
 | 
					            var userType = GetPokeType(user.Id);
 | 
				
			||||||
            var movesList = userType.Moves;
 | 
					            var movesList = userType.Moves;
 | 
				
			||||||
            var str = $"**Moves for `{userType.Name}` type.**";
 | 
					            var embed = new EmbedBuilder().WithOkColor()
 | 
				
			||||||
            foreach (string m in movesList)
 | 
					                .WithTitle(GetText("moves", userType))
 | 
				
			||||||
            {
 | 
					                .WithDescription(string.Join("\n", movesList.Select(m => userType.Icon + " " + m)));
 | 
				
			||||||
                str += $"\n{userType.Icon}{m}";
 | 
					            await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            await Context.Channel.SendMessageAsync(str).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
@@ -232,17 +222,18 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            IGuildUser user = (IGuildUser)Context.User;
 | 
					            IGuildUser user = (IGuildUser)Context.User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (targetUser == null) {
 | 
					            if (targetUser == null)
 | 
				
			||||||
                await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false);
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyErrorLocalized("user_not_found").ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (Stats.ContainsKey(targetUser.Id))
 | 
					            if (_stats.ContainsKey(targetUser.Id))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var targetStats = Stats[targetUser.Id];
 | 
					                var targetStats = _stats[targetUser.Id];
 | 
				
			||||||
                if (targetStats.Hp == targetStats.MaxHp)
 | 
					                if (targetStats.Hp == targetStats.MaxHp)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                //Payment~
 | 
					                //Payment~
 | 
				
			||||||
@@ -251,11 +242,11 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
                var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
 | 
					                var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
 | 
				
			||||||
                if (amount > 0)
 | 
					                if (amount > 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                        if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
 | 
					                    if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
 | 
				
			||||||
                        {
 | 
					                    {
 | 
				
			||||||
                            try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { }
 | 
					                        await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                            return;
 | 
					                        return;
 | 
				
			||||||
                        }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                //healing
 | 
					                //healing
 | 
				
			||||||
@@ -263,23 +254,20 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
                if (targetStats.Hp < 0)
 | 
					                if (targetStats.Hp < 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    //Could heal only for half HP?
 | 
					                    //Could heal only for half HP?
 | 
				
			||||||
                    Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
 | 
					                    _stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
 | 
				
			||||||
                    if (target == "yourself")
 | 
					                    if (target == "yourself")
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        await Context.Channel.SendMessageAsync($"You revived yourself with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					                        await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					
 | 
				
			||||||
                    {
 | 
					                    await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                        await Context.Channel.SendMessageAsync($"{user.Mention} revived {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                   return;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{user.Mention} healed {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
 | 
					                await ErrorLocalized("already_full", Format.Bold(targetUser.ToString()));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -288,15 +276,9 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        public async Task Type(IGuildUser targetUser = null)
 | 
					        public async Task Type(IGuildUser targetUser = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            IGuildUser user = (IGuildUser)Context.User;
 | 
					            targetUser = targetUser ?? (IGuildUser)Context.User;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (targetUser == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var pType = GetPokeType(targetUser.Id);
 | 
					            var pType = GetPokeType(targetUser.Id);
 | 
				
			||||||
            await Context.Channel.SendMessageAsync($"Type of {targetUser.Mention} is **{pType.Name.ToLowerInvariant()}**{pType.Icon}").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("type_of_user", Format.Bold(targetUser.ToString()), pType).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -309,7 +291,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            var targetType = StringToPokemonType(typeTargeted);
 | 
					            var targetType = StringToPokemonType(typeTargeted);
 | 
				
			||||||
            if (targetType == null)
 | 
					            if (targetType == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), 
 | 
					                await Context.Channel.EmbedAsync(_pokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), 
 | 
				
			||||||
                        (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
 | 
					                        (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
 | 
				
			||||||
                                                          .WithValue(pt.Icon)
 | 
					                                                          .WithValue(pt.Icon)
 | 
				
			||||||
                                                          .WithIsInline(true)))
 | 
					                                                          .WithIsInline(true)))
 | 
				
			||||||
@@ -318,7 +300,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            if (targetType == GetPokeType(user.Id))
 | 
					            if (targetType == GetPokeType(user.Id))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Context.Channel.SendMessageAsync($"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Icon}").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("already_that_type", targetType).ConfigureAwait(false);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -326,20 +308,19 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            var amount = 1;
 | 
					            var amount = 1;
 | 
				
			||||||
            if (amount > 0)
 | 
					            if (amount > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user.Mention} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
 | 
					                if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { }
 | 
					                    await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //Actually changing the type here
 | 
					            //Actually changing the type here
 | 
				
			||||||
            Dictionary<ulong, string> setTypes;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var uow = DbHandler.UnitOfWork())
 | 
					            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var pokeUsers = uow.PokeGame.GetAll();
 | 
					                var pokeUsers = uow.PokeGame.GetAll().ToArray();
 | 
				
			||||||
                setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
 | 
					                var setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
 | 
				
			||||||
                var pt = new UserPokeTypes
 | 
					                var pt = new UserPokeTypes
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    UserId = user.Id,
 | 
					                    UserId = user.Id,
 | 
				
			||||||
@@ -353,7 +334,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    //update user in db
 | 
					                    //update user in db
 | 
				
			||||||
                    var pokeUserCmd = pokeUsers.Where(p => p.UserId == user.Id).FirstOrDefault();
 | 
					                    var pokeUserCmd = pokeUsers.FirstOrDefault(p => p.UserId == user.Id);
 | 
				
			||||||
                    pokeUserCmd.type = targetType.Name;
 | 
					                    pokeUserCmd.type = targetType.Name;
 | 
				
			||||||
                    uow.PokeGame.Update(pokeUserCmd);
 | 
					                    uow.PokeGame.Update(pokeUserCmd);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -361,9 +342,10 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //Now for the response
 | 
					            //Now for the response
 | 
				
			||||||
            await Context.Channel.SendMessageAsync($"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
 | 
					            await ReplyConfirmLocalized("settype_success", 
 | 
				
			||||||
 | 
					                targetType, 
 | 
				
			||||||
 | 
					                NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,9 @@ namespace NadekoBot.Modules.Pokemon
 | 
				
			|||||||
        public List<PokemonMultiplier> Multipliers { get; set; }
 | 
					        public List<PokemonMultiplier> Multipliers { get; set; }
 | 
				
			||||||
        public string Icon { get; set; }
 | 
					        public string Icon { get; set; }
 | 
				
			||||||
        public string[] Moves { get; set; }
 | 
					        public string[] Moves { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override string ToString() => 
 | 
				
			||||||
 | 
					            Icon + "**" + Name.ToLowerInvariant() + "**" + Icon;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public class PokemonMultiplier
 | 
					    public class PokemonMultiplier
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
                    var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
 | 
					                    var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
 | 
				
			||||||
                    using (var http = new HttpClient())
 | 
					                    using (var http = new HttpClient())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var res = await http.GetStringAsync("http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
					                        var res = await http.GetStringAsync(link + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
				
			||||||
                        var smallObj = JArray.Parse(res)[0];
 | 
					                        var smallObj = JArray.Parse(res)[0];
 | 
				
			||||||
                        var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
					                        var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    var rankimg = $"{model.Competitive.rank_img}";
 | 
					                    var rankimg = $"{model.Competitive.rank_img}";
 | 
				
			||||||
                    var rank = $"{model.Competitive.rank}";
 | 
					                    var rank = $"{model.Competitive.rank}";
 | 
				
			||||||
                    var competitiveplay = $"{model.Games.Competitive.played}";
 | 
					                    //var competitiveplay = $"{model.Games.Competitive.played}";
 | 
				
			||||||
                    if (string.IsNullOrWhiteSpace(rank))
 | 
					                    if (string.IsNullOrWhiteSpace(rank))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var embed = new EmbedBuilder()
 | 
					                        var embed = new EmbedBuilder()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,8 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class PlaceCommands : ModuleBase
 | 
					        public class PlaceCommands : ModuleBase
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private static string typesStr { get; } = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Searches).Name]}place\" tags:`\n" + String.Join(", ", Enum.GetNames(typeof(PlaceType)));
 | 
					            private static string typesStr { get; } =
 | 
				
			||||||
 | 
					                string.Format("`List of \"{0}place\" tags:`\n", NadekoBot.ModulePrefixes[typeof(Searches).Name]) + String.Join(", ", Enum.GetNames(typeof(PlaceType)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public enum PlaceType
 | 
					            public enum PlaceType
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,19 +103,23 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
                                oldStatus.IsLive != newStatus.IsLive)
 | 
					                                oldStatus.IsLive != newStatus.IsLive)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                var server = NadekoBot.Client.GetGuild(fs.GuildId);
 | 
					                                var server = NadekoBot.Client.GetGuild(fs.GuildId);
 | 
				
			||||||
                                if (server == null)
 | 
					                                var channel = server?.GetTextChannel(fs.ChannelId);
 | 
				
			||||||
                                    return;
 | 
					 | 
				
			||||||
                                var channel = server.GetTextChannel(fs.ChannelId);
 | 
					 | 
				
			||||||
                                if (channel == null)
 | 
					                                if (channel == null)
 | 
				
			||||||
                                    return;
 | 
					                                    return;
 | 
				
			||||||
                                try
 | 
					                                try
 | 
				
			||||||
                                {
 | 
					                                {
 | 
				
			||||||
                                    var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false);
 | 
					                                    var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false);
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                catch { }
 | 
					                                catch
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    // ignored
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        catch { }
 | 
					                        catch
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // ignored
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }));
 | 
					                    }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    FirstPass = false;
 | 
					                    FirstPass = false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ using System.Xml.Linq;
 | 
				
			|||||||
namespace NadekoBot.Modules.Searches
 | 
					namespace NadekoBot.Modules.Searches
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Searches", "~")]
 | 
					    [NadekoModule("Searches", "~")]
 | 
				
			||||||
    public partial class Searches : DiscordModule
 | 
					    public partial class Searches : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        public async Task Weather([Remainder] string query)
 | 
					        public async Task Weather([Remainder] string query)
 | 
				
			||||||
@@ -499,12 +499,12 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                var data = JsonConvert.DeserializeObject<DefineModel>(res);
 | 
					                var data = JsonConvert.DeserializeObject<DefineModel>(res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0];
 | 
					                var sense = data.Results.FirstOrDefault(x => x.Senses?[0].Definition != null)?.Senses[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (sense?.Definition == null)
 | 
					                if (sense?.Definition == null)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                string definition = sense.Definition.ToString();
 | 
					                var definition = sense.Definition.ToString();
 | 
				
			||||||
                if (!(sense.Definition is string))
 | 
					                if (!(sense.Definition is string))
 | 
				
			||||||
                    definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
 | 
					                    definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -536,7 +536,7 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
 | 
					            await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
 | 
				
			||||||
            string res = "";
 | 
					            var res = "";
 | 
				
			||||||
            using (var http = new HttpClient())
 | 
					            using (var http = new HttpClient())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                http.DefaultRequestHeaders.Clear();
 | 
					                http.DefaultRequestHeaders.Clear();
 | 
				
			||||||
@@ -548,7 +548,7 @@ namespace NadekoBot.Modules.Searches
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                var items = JObject.Parse(res);
 | 
					                var items = JObject.Parse(res);
 | 
				
			||||||
                var item = items["defs"]["def"];
 | 
					                var item = items["defs"]["def"];
 | 
				
			||||||
                var hashtag = item["hashtag"].ToString();
 | 
					                //var hashtag = item["hashtag"].ToString();
 | 
				
			||||||
                var link = item["uri"].ToString();
 | 
					                var link = item["uri"].ToString();
 | 
				
			||||||
                var desc = item["text"].ToString();
 | 
					                var desc = item["text"].ToString();
 | 
				
			||||||
                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
					                await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                var voicechn = (await guild.GetVoiceChannelsAsync()).Count();
 | 
					                var voicechn = (await guild.GetVoiceChannelsAsync()).Count();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
 | 
					                var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
 | 
				
			||||||
                var sb = new StringBuilder();
 | 
					 | 
				
			||||||
                var users = await guild.GetUsersAsync().ConfigureAwait(false);
 | 
					                var users = await guild.GetUsersAsync().ConfigureAwait(false);
 | 
				
			||||||
                var features = string.Join("\n", guild.Features);
 | 
					                var features = string.Join("\n", guild.Features);
 | 
				
			||||||
                if (string.IsNullOrWhiteSpace(features))
 | 
					                if (string.IsNullOrWhiteSpace(features))
 | 
				
			||||||
@@ -45,13 +44,13 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                    .AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true))
 | 
				
			||||||
                    .WithImageUrl(guild.IconUrl)
 | 
					                    .WithImageUrl(guild.IconUrl)
 | 
				
			||||||
                    .WithColor(NadekoBot.OkColor);
 | 
					                    .WithColor(NadekoBot.OkColor);
 | 
				
			||||||
                if (guild.Emojis.Count() > 0)
 | 
					                if (guild.Emojis.Any())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>"))));
 | 
					                    embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>"))));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -71,7 +70,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                    .WithTitle(ch.Name)
 | 
					                    .WithTitle(ch.Name)
 | 
				
			||||||
                    .WithDescription(ch.Topic?.SanitizeMentions())
 | 
					                    .WithDescription(ch.Topic?.SanitizeMentions())
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .WithColor(NadekoBot.OkColor);
 | 
					                    .WithColor(NadekoBot.OkColor);
 | 
				
			||||||
                await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
					                await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
 | 
				
			||||||
@@ -81,7 +80,6 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public async Task UserInfo(IGuildUser usr = null)
 | 
					            public async Task UserInfo(IGuildUser usr = null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
                var user = usr ?? Context.User as IGuildUser;
 | 
					                var user = usr ?? Context.User as IGuildUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (user == null)
 | 
					                if (user == null)
 | 
				
			||||||
@@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
 | 
					                embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
 | 
				
			||||||
                    .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
 | 
					                    .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
 | 
				
			||||||
                    .WithColor(NadekoBot.OkColor);
 | 
					                    .WithColor(NadekoBot.OkColor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                public void Reset()
 | 
					                public void Reset()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    source.Cancel();
 | 
					                    source.Cancel();
 | 
				
			||||||
                    var t = Task.Run(Run);
 | 
					                    var _ = Task.Run(Run);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                public void Stop()
 | 
					                public void Stop()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,15 +45,15 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                foreach (var r in reminders)
 | 
					                foreach (var r in reminders)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try { var t = StartReminder(r); } catch (Exception ex) { _log.Warn(ex); }
 | 
					                    Task.Run(() => StartReminder(r));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static async Task StartReminder(Reminder r)
 | 
					            private static async Task StartReminder(Reminder r)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var now = DateTime.Now;
 | 
					                var now = DateTime.Now;
 | 
				
			||||||
                var twoMins = new TimeSpan(0, 2, 0);
 | 
					
 | 
				
			||||||
                TimeSpan time = r.When - now;
 | 
					                var time = r.When - now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (time.TotalMilliseconds > int.MaxValue)
 | 
					                if (time.TotalMilliseconds > int.MaxValue)
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ using NadekoBot.Services;
 | 
				
			|||||||
namespace NadekoBot.Modules.Utility
 | 
					namespace NadekoBot.Modules.Utility
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [NadekoModule("Utility", ".")]
 | 
					    [NadekoModule("Utility", ".")]
 | 
				
			||||||
    public partial class Utility : DiscordModule
 | 
					    public partial class Utility : NadekoModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
 | 
					        private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -468,7 +468,6 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
        [OwnerOnly]
 | 
					        [OwnerOnly]
 | 
				
			||||||
        public async Task SaveChat(int cnt)
 | 
					        public async Task SaveChat(int cnt)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var sb = new StringBuilder();
 | 
					 | 
				
			||||||
            var msgs = new List<IMessage>(cnt);
 | 
					            var msgs = new List<IMessage>(cnt);
 | 
				
			||||||
            await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false);
 | 
					            await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,12 @@ using System.Collections.Generic;
 | 
				
			|||||||
using NadekoBot.Modules.Permissions;
 | 
					using NadekoBot.Modules.Permissions;
 | 
				
			||||||
using NadekoBot.TypeReaders;
 | 
					using NadekoBot.TypeReaders;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					using System.Collections.Immutable;
 | 
				
			||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
using NadekoBot.Modules.Music;
 | 
					using NadekoBot.Modules.Music;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
 | 
					using System.Resources;
 | 
				
			||||||
 | 
					using NadekoBot.Resources;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot
 | 
					namespace NadekoBot
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -29,7 +33,10 @@ namespace NadekoBot
 | 
				
			|||||||
        public static CommandService CommandService { get; private set; }
 | 
					        public static CommandService CommandService { get; private set; }
 | 
				
			||||||
        public static CommandHandler CommandHandler { get; private set; }
 | 
					        public static CommandHandler CommandHandler { get; private set; }
 | 
				
			||||||
        public static DiscordShardedClient Client { get; private set; }
 | 
					        public static DiscordShardedClient Client { get; private set; }
 | 
				
			||||||
        public static BotCredentials Credentials { get; private set; }
 | 
					        public static BotCredentials Credentials { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static Localization Localization { get; private set; }
 | 
				
			||||||
 | 
					        public static ResourceManager ResponsesResourceManager { get; } = new ResourceManager(typeof(ResponseStrings));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static GoogleApiService Google { get; private set; }
 | 
					        public static GoogleApiService Google { get; private set; }
 | 
				
			||||||
        public static StatsService Stats { get; private set; }
 | 
					        public static StatsService Stats { get; private set; }
 | 
				
			||||||
@@ -38,7 +45,7 @@ namespace NadekoBot
 | 
				
			|||||||
        public static ConcurrentDictionary<string, string> ModulePrefixes { get; private set; }
 | 
					        public static ConcurrentDictionary<string, string> ModulePrefixes { get; private set; }
 | 
				
			||||||
        public static bool Ready { get; private set; }
 | 
					        public static bool Ready { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static IEnumerable<GuildConfig> AllGuildConfigs { get; }
 | 
					        public static ImmutableArray<GuildConfig> AllGuildConfigs { get; }
 | 
				
			||||||
        public static BotConfig BotConfig { get; }
 | 
					        public static BotConfig BotConfig { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        static NadekoBot()
 | 
					        static NadekoBot()
 | 
				
			||||||
@@ -48,7 +55,7 @@ namespace NadekoBot
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            using (var uow = DbHandler.UnitOfWork())
 | 
					            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs();
 | 
					                AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs().ToImmutableArray();
 | 
				
			||||||
                BotConfig = uow.BotConfig.GetOrCreate();
 | 
					                BotConfig = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
                OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16));
 | 
					                OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16));
 | 
				
			||||||
                ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16));
 | 
					                ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16));
 | 
				
			||||||
@@ -79,6 +86,7 @@ namespace NadekoBot
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //initialize Services
 | 
					            //initialize Services
 | 
				
			||||||
 | 
					            Localization = new Localization(NadekoBot.BotConfig.Locale, NadekoBot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale));
 | 
				
			||||||
            CommandService = new CommandService(new CommandServiceConfig() {
 | 
					            CommandService = new CommandService(new CommandServiceConfig() {
 | 
				
			||||||
                CaseSensitiveCommands = false,
 | 
					                CaseSensitiveCommands = false,
 | 
				
			||||||
                DefaultRunMode = RunMode.Sync
 | 
					                DefaultRunMode = RunMode.Sync
 | 
				
			||||||
@@ -102,13 +110,16 @@ namespace NadekoBot
 | 
				
			|||||||
            CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader());
 | 
					            CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader());
 | 
				
			||||||
            CommandService.AddTypeReader<IGuild>(new GuildTypeReader());
 | 
					            CommandService.AddTypeReader<IGuild>(new GuildTypeReader());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var sw = Stopwatch.StartNew();
 | 
				
			||||||
            //connect
 | 
					            //connect
 | 
				
			||||||
            await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
 | 
					            await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
 | 
				
			||||||
            await Client.ConnectAsync().ConfigureAwait(false);
 | 
					            await Client.ConnectAsync().ConfigureAwait(false);
 | 
				
			||||||
            //await Client.DownloadAllUsersAsync().ConfigureAwait(false);
 | 
					            //await Client.DownloadAllUsersAsync().ConfigureAwait(false);
 | 
				
			||||||
            Stats.Initialize();
 | 
					            Stats.Initialize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _log.Info("Connected");
 | 
					            sw.Stop();
 | 
				
			||||||
 | 
					            _log.Info("Connected in " + sw.Elapsed.TotalSeconds.ToString("F2"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //load commands and prefixes
 | 
					            //load commands and prefixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								src/NadekoBot/NadekoBot.xproj.DotSettings
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/NadekoBot/NadekoBot.xproj.DotSettings
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
 | 
				
			||||||
 | 
						<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cadministration_005Ccommands/@EntryIndexedValue">True</s:Boolean>
 | 
				
			||||||
 | 
						<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgambling_005Ccommands/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
 | 
				
			||||||
							
								
								
									
										83
									
								
								src/NadekoBot/Resources/CommandStrings.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										83
									
								
								src/NadekoBot/Resources/CommandStrings.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -3704,6 +3704,87 @@ namespace NadekoBot.Resources {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to languageset langset.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languageset_cmd {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languageset_cmd", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languageset_desc {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languageset_desc", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to `{0}langset de-DE ` or `{0}langset default`.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languageset_usage {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languageset_usage", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to langsetdefault langsetd.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languagesetdefault_cmd {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languagesetdefault_cmd", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languagesetdefault_desc {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languagesetdefault_desc", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to `{0}langsetd en-US` or `{0}langsetd default`.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languagesetdefault_usage {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languagesetdefault_usage", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to languageslist langli.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languageslist_cmd {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languageslist_cmd", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to List of languages for which translation (or part of it) exist atm..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languageslist_desc {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languageslist_desc", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to `{0}langli`.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string languageslist_usage {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("languageslist_usage", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        ///    Looks up a localized string similar to lcsc.
 | 
					        ///    Looks up a localized string similar to lcsc.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
@@ -8988,7 +9069,7 @@ namespace NadekoBot.Resources {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        ///    Looks up a localized string similar to {0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`.
 | 
					        ///    Looks up a localized string similar to `{0}yoda my feelings hurt`.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public static string yodify_usage {
 | 
					        public static string yodify_usage {
 | 
				
			||||||
            get {
 | 
					            get {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2698,7 +2698,7 @@
 | 
				
			|||||||
    <value>Translates your normal sentences into Yoda styled sentences!</value>
 | 
					    <value>Translates your normal sentences into Yoda styled sentences!</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
  <data name="yodify_usage" xml:space="preserve">
 | 
					  <data name="yodify_usage" xml:space="preserve">
 | 
				
			||||||
    <value>{0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`</value>
 | 
					    <value>`{0}yoda my feelings hurt`</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
  <data name="attack_cmd" xml:space="preserve">
 | 
					  <data name="attack_cmd" xml:space="preserve">
 | 
				
			||||||
    <value>attack</value>
 | 
					    <value>attack</value>
 | 
				
			||||||
@@ -3114,4 +3114,35 @@
 | 
				
			|||||||
  <data name="timezone_usage" xml:space="preserve">
 | 
					  <data name="timezone_usage" xml:space="preserve">
 | 
				
			||||||
    <value>`{0}timezone`</value>
 | 
					    <value>`{0}timezone`</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
</root>
 | 
					</root>
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					  <data name="languagesetdefault_cmd" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>langsetdefault langsetd</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languagesetdefault_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languagesetdefault_usage" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>`{0}langsetd en-US` or `{0}langsetd default`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languageset_cmd" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>languageset langset</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languageset_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languageset_usage" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>`{0}langset de-DE ` or `{0}langset default`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languageslist_cmd" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>languageslist langli</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languageslist_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>List of languages for which translation (or part of it) exist atm.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="languageslist_usage" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>`{0}langli`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					</root>
 | 
				
			||||||
 | 
					>>>>>>> Kwoth/dev
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2723
									
								
								src/NadekoBot/Resources/ResponseStrings.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2723
									
								
								src/NadekoBot/Resources/ResponseStrings.Designer.cs
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -117,4 +117,930 @@
 | 
				
			|||||||
  <resheader name="writer">
 | 
					  <resheader name="writer">
 | 
				
			||||||
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
					    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
				
			||||||
  </resheader>
 | 
					  </resheader>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_already_claimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That base is already claimed or destroyed.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_already_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That base is already destroyed.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_already_unclaimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That base is not claimed.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>**DESTROYED** base #{0} in a war against {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_unclaimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has **UNCLAIMED** base #{1} in a war against {2}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_claimed_base" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} claimed a base #{1} in a war against {2}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_claimed_other" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>@{0} You already claimed base #{1}. You can't claim a new one.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_claim_expired" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Claim from @{0} for a war against {1} has expired.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_enemy" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Enemy</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_info_about_war" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Info about war against {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_invalid_base_number" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Invalid base number.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_invalid_size" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Not a Valid war size.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_list_active_wars" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>List Of Active Wars</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_not_claimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>not claimed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_not_partic" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You are not participating in that war.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_not_partic_or_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>@{0} You are either not participating in that war, or that base is already destroyed.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_no_active_wars" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No active wars.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_size" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Size</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_already_started" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>War against {0} has already started.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_created" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>War against {0} created.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_ended" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>War against {0} ended.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_not_exist" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That war does not exist.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_started" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>War against {0} started!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_all_stats_cleared" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>All custom reaction stats cleared.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_deleted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Custom Reaction deleted</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_insuff_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for server custom reactions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_list_all" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>List of all custom reactions</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_name" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Custom Reactions</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_new_cust_react" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New Custom Reaction</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_no_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No custom reactions found.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_no_found_id" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No custom reaction found with that id.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_response" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Response</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_stats" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Custom Reaction Stats</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_stats_cleared" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Stats cleared for {0} custom reaction.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_stats_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No stats for that trigger found, no action taken.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_trigger" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Trigger</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_autohentai_stopped" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Autohentai stopped.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No results found.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_already_fainted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has already fainted.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_already_full" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} already has full HP.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_already_that_type" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Your type is already {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_attack" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>used {0}{1} on {2}{3} for {4} damage.</value>
 | 
				
			||||||
 | 
					    <comment>Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage.</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_cant_attack_again" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't attack again without retaliation!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_cant_attack_yourself" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't attack yourself.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_fainted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has fainted!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_healed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>healed {0} with one {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_hp_remaining" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has {1} HP remaining.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_invalid_move" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't use {0}. Type `{1}ml` to see a list of moves you can use.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_moves" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Movelist for {0} type</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_not_effective" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>It's not effective.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_no_currency" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You don't have enough {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_revive_other" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>revived {0} with one {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_revive_yourself" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You revived yourself with one {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_settype_success" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Your type has been changed to {0} for a {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_somewhat_effective" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>It's somewhat effective.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_super_effective" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>It's super effective!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_too_many_moves" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You used too many moves in a row, so you can't move!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_type_of_user" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Type of {0} is {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_user_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User not found.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_you_fainted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You fainted, so you are not able to move!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_aar_disabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>**Auto assign role** on user join is now **disabled**.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_aar_enabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>**Auto assign role** on user join is now **enabled**.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_attachments" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Attachments</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_avatar_changed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Avatar Changed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_bandm" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You have been banned from {0} server.
 | 
				
			||||||
 | 
					Reason: {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_banned_pl" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>banned</value>
 | 
				
			||||||
 | 
					    <comment>PLURAL</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_banned_user" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Banned</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_bot_name" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bot name changed to {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_bot_status" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bot status changed to {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_byedel_off" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Automatic deletion of bye messages has been disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_byedel_on" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bye messages will be deleted after {0} seconds.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_byemsg_cur" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Current bye message: {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_byemsg_enable" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Enable bye messages by typing {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_byemsg_new" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New bye message set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_bye_off" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bye announcements disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_bye_on" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bye announcements enabled on this channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ch_name_change" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Channel Name Changed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ch_old_name" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Old Name</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ch_topic_change" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Channel Topic Changed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_cleaned_up" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Cleaned up.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_content" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Content</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_cr" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Sucessfully created role {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_createtextchan" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Text channel {0} created.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_createvoich" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Voice channel {0} created.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_deafen" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Deafen successful.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_deleted_server" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Deleted server {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_delmsg_off" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Stopped automatic deletion of successful command invokations.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_delmsg_on" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Now automatically deleting sucessful command invokations.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_deltextchan" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Text channel {0} deleted.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_delvoich" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Voice channel {0} deleted.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_dm_from" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>DM from</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_donadd" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Sucessfully added a new donator.Total donated amount from this user: {0} 👑</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_donators" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Thanks to the people listed below for making this project hjappen!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_fwall_start" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I will forward DMs to all owners.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_fwall_stop" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I will forward DMs only to the first owner.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_fwdm_start" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I will forward DMs from now on.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_fwdm_stop" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I will stop forwarding DMs from now on.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdel_off" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Automatic deletion of greet messages has been disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdel_on" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Greet messages will be deleted after {0} seconds.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdmmsg_cur" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Current DM greet message: {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdmmsg_enable" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Enable DM greet messages by typing {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdmmsg_new" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New DM greet message set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdm_off" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>DM greet announcements disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetdm_on" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>DM greet announcements enabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetmsg_cur" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Current greet message: {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetmsg_enable" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Enable greet messages by typing {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greetmsg_new" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New greet message set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greet_off" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Greet announcements disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_greet_on" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Greet announcements enabled on this channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_hierarchy" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't use this command on users with a role higher or equal to yours in the role hierarchy.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_images_loaded" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Images loaded after {0} seconds!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_invalid_format" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Invalid input format.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_invalid_params" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Invalid parameters.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_joined" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has joined {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_kickdm" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You have been kicked from {0} server.
 | 
				
			||||||
 | 
					Reason: {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_kicked_user" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Kicked</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_lang_list" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>List Of Languages
 | 
				
			||||||
 | 
					{0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_lang_set" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Your server's locale is now {0} - {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_lang_set_bot" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bot's default locale is now {0} - {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_lang_set_bot_show" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bot's language is set to {0} - {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_lang_set_fail" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Failed setting locale. Revisit this command's help.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_lang_set_show" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>This server's language is set to {0} - {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_left" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has left {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_left_server" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Left server {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Logging {0} event in this channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log_all" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Logging all events in this channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log_disabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Logging disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log_events" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Log events you can subscribe to:</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log_ignore" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Logging will ignore {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log_not_ignore" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Logging will not ignore {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_log_stop" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Stopped logging {0} event.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_menrole" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has invoked a mention on the following roles</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_message_from_bo" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Message from {0} `[Bot Owner]`:</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_message_sent" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Message sent.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_moved" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} moved from {1} to {2}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_msg_del" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Message Deleted in #{0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_msg_update" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Message Updated in #{0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_muted_pl" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Muted</value>
 | 
				
			||||||
 | 
					    <comment>PLURAL (users have been muted)</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_muted_sn" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Muted</value>
 | 
				
			||||||
 | 
					    <comment>singular "User muted."</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_mute_error" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I don't have the permission necessary for that most likely.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_mute_role_set" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New mute role set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_need_admin" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I need **Administration** permission to do that.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_new_msg" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New Message</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_new_nick" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New Nickname</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_new_topic" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New Topic</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_nick_change" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Nickname Changed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_no_server" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Can't find that server</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_no_shard_id" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No shard with that ID found.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_old_msg" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Old Message</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_old_nick" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Old Nickname</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_old_topic" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Old Topic</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Error. Most likely I don't have sufficient permissions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_perms_reset" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Permissions for this server are reset.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_prot_active" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Active Protections</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_prot_disable" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **disabled** on this server.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_prot_enable" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} Enabled</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_prot_error" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Error. I need ManageRoles permission</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_prot_none" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No protections enabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_raid_cnt" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User threshold must be between {0} and {1}.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_raid_stats" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>If {0} or more users join within {1} seconds, I will {2} them.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_raid_time" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Time must be between {0} and {1} seconds.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_rar" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Successfully removed all roles from user {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_rar_err" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Failed to remove roles. I have insufficient permissions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_rc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Color of {0} role has been changed.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_rc_not_exist" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That role does not exist.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_rc_params" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>The parameters specified are invalid.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_rc_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Error occured due to invalid color or insufficient permissions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_remrole" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Successfully removed role {0} from user {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_remrole_err" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Failed to remove role. I have insufficient permissions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_renrole" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Role renamed.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_renrole_err" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Failed to rename role. I have insufficient permissions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_renrole_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't edit roles higher than your highest role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_reprm" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Removed the playing message: {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_role_added" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Role {0} as been added to the list.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_role_clean" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} not found.Cleaned up.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_role_in_list" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Role {0} is already in the list.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ropl_added" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Added.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ropl_disabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Rotating playing status disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ropl_enabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Rotating playing status enabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ropl_list" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Here is a list of rotating statuses:
 | 
				
			||||||
 | 
					{0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_ropl_not_set" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No rotating playing statuses set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_already" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You already have {0} role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_already_excl" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You already have {0} exclusive self-assigned role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_excl" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Self assigned roles are now exclusive!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_list" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>There are {0} self assignable roles</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_not" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That role is not self-assignable.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_not_have" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You don't have {0} role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_no_excl" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Self assigned roles are now not exclusive!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_rem" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been removed from the list of self-assignable roles.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_remove" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You no longer have {0} role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_self_assign_sucess" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You now have {0} role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_setrole" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Sucessfully added role {0} to user {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_setrole_err" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Failed to add role. I have insufficient permissions.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_set_avatar" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New avatar set!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_set_channel_name" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New channel name set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_set_game" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New game set!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_set_stream" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New stream set!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_set_topic" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>New channel topic set.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_shard_reconnected" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Shard {0} reconnected.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_shard_reconnecting" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Shard {0} reconnecting.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_shutting_down" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Shutting down</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_slowmode_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Users can't send more than {0} messages every {1} seconds.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_slowmode_disabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Slow mode disabled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_slowmode_init" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Slow mode initiated</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_soft_banned_pl" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>soft-banned (kicked)</value>
 | 
				
			||||||
 | 
					    <comment>PLURAL</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_spam_ignore" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} will ignore this channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_spam_not_ignore" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} will no longer ignore this channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_spam_stats" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>If a user posts {0} same messages in a row, I will {1} them.
 | 
				
			||||||
 | 
					    __IgnoredChannels__: {2}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_text_chan_created" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Text Channel Destroyed </value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_text_chan_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Text Channel Destroyed </value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_undeafen" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Undeafen successful.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_unmuted_sn" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Unmuted</value>
 | 
				
			||||||
 | 
					    <comment>singular</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_username" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Username</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_username_changed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Username Changed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_users" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Users</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_banned" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Banned</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_chat_mute" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **muted** from chatting.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_chat_unmute" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **unmuted** from chatting.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_joined" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Joined</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_left" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Left</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_muted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **muted** from text and voice chat.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_role_add" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User's Role Added</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_role_rem" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User's  Role Removed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_status_change" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} is now {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_unmuted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **unmuted** from text and voice chat.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_vjoined" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has joined {1} voice channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_vleft" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has left {1} voice channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_vmoved" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} moved from {1} to {2} voice channel.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_voice_mute" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **voice muted**.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_user_voice_unmute" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has been **voice unmuted**.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_voice_chan_created" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Voice Channel Destroyed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_voice_chan_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Voice Channel Destroyed</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_vt_disabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Disabled voice + text feature.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_vt_enabled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Enabled voice + text feature.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_vt_exit" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_vt_no_admin" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. This may cause some issues, and you will have to clean up text channels yourself afterwards.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_vt_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I require atleast **manage roles** and **manage channels** permissions to enable this feature. (preffered Administration permission)</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_xmuted_text" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User {0} from text chat</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_xmuted_text_and_voice" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User {0} from text and voice chat</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administration_xmuted_voice" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User {0} from voice chat</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administraton_sbdm" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You have been soft-banned from {0} server.
 | 
				
			||||||
 | 
					Reason: {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="administraton_user_unbanned" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Unbanned</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="adminsitration_migration_done" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Migration done!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="adminsitration_migration_error" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Error while migrating, check bot's console for more information.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="adminsitration_presence_updates" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Presence Updates</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="adminsitration_sb_user" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>User Soft-Banned</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_awarded" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>has awarded {0} to {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_betflip_gamble" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Betflip Gamble</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_better_luck" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Better luck next time ^_^</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_br_win" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Congratulations! You won {0} for rolling above {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_deck_reshuffled" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Deck reshuffled.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_flipped" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>flipped {0}.</value>
 | 
				
			||||||
 | 
					    <comment>User flipped tails.</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_flip_guess" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You guessed it! You won {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_flip_invalid" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Invalid number specified. You can flip 1 to {0} coins.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_flowerreaction_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Add {0} reaction to this message to get {1} </value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_flowerreaction_footer" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>This event is active for up to {0} hours.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_flowerreaction_title" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Flower reaction event started!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_gifted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>has gifted {0} to {1}</value>
 | 
				
			||||||
 | 
					    <comment>X has gifted 15 flowers to Y</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_has" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} has {1}</value>
 | 
				
			||||||
 | 
					    <comment>X has Y flowers</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_heads" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Heads</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_leaderboard" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Leaderboard</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_mass_award" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Awarded {0} to {1} users from {2} role.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_max_bet_limit" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't bet more than {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_min_bet_limit" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can't bet less than {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_not_enough" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You don't have enough {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_no_more_cards" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>No more cards in the deck.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_raffled_user" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Raffled User</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_roll" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You rolled {0}.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_slot_bet" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bet</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_slot_jackpot" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>WOAAHHHHHH!!! Congratulations!!! x{0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_slot_single" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>A single {0}, x{1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_slot_three" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Wow! Lucky! Three of a kind! x{0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_slot_two" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Good job! Two {0} - bet x{1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_slot_won" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Won</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_sneakygamestatus_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Users must type a secret code to get {0}.
 | 
				
			||||||
 | 
					Lasts {1} seconds. Don't tell anyone. Shhh.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_sneakygamestatus_end" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>SneakyGame event ended. {0} users received the reward.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_sneakygamestatus_title" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>SneakyGameStatus event started</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_tails" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Tails</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_take" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>successfully took {0} from {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="gambling_take_fail" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>was unable to take {0} from{1} because the user doesn't have that much {2}!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_back_to_toc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Back to ToC</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_bot_owner_only" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Bot Owner Only</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_channel_permission" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Requires {0} channel permission.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_cmdlist_donate" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can support the project on patreon: <{0}> or paypal: <{1}></value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_cmd_and_alias" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Command and aliases</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_commandlist_regen" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Commandlist Regenerated.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_commands_instr" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Type `{0}h CommandName` to see the help for that specified command. e.g. `{0}h >8ball`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_command_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>I can't find that command. Please verify that the command exists before trying again.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Description</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_donate" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>You can support the NadekoBot project on 
 | 
				
			||||||
 | 
					Patreon <{0}> or
 | 
				
			||||||
 | 
					Paypal <{1}>
 | 
				
			||||||
 | 
					Don't forget to leave your discord name or id in the message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Thank you** ♥️</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_guide" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>**List of Commands**: <{0}>
 | 
				
			||||||
 | 
					**Hosting Guides and docs can be found here**: <{1}></value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_list_of_commands" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>List Of Commands</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_list_of_modules" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>List Of Modules</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_modules_footer" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Type `{0}cmds ModuleName` to get a list of commands in that module. eg `{0}cmds games`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_module_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>That module does not exist.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_server_permission" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Requires {0} server permission.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_table_of_contents" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Table Of Contents</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_usage" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Usage</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_autohentai_started" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Autohentai started. Reposting every {0}s with one of the following tags:
 | 
				
			||||||
 | 
					{1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_tag" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Tag</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
</root>
 | 
					</root>
 | 
				
			||||||
							
								
								
									
										260
									
								
								src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,260 @@
 | 
				
			|||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// <auto-generated>
 | 
				
			||||||
 | 
					//     This code was generated by a tool.
 | 
				
			||||||
 | 
					//     Runtime Version:4.0.30319.42000
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     Changes to this file may cause incorrect behavior and will be lost if
 | 
				
			||||||
 | 
					//     the code is regenerated.
 | 
				
			||||||
 | 
					// </auto-generated>
 | 
				
			||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Resources {
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    using System.Reflection;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    ///    A strongly-typed resource class, for looking up localized strings, etc.
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    // This class was auto-generated by the StronglyTypedResourceBuilder
 | 
				
			||||||
 | 
					    // class via a tool like ResGen or Visual Studio.
 | 
				
			||||||
 | 
					    // To add or remove a member, edit your .ResX file then rerun ResGen
 | 
				
			||||||
 | 
					    // with the /str option, or rebuild your VS project.
 | 
				
			||||||
 | 
					    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
 | 
				
			||||||
 | 
					    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
 | 
				
			||||||
 | 
					    public class ResponseStrings_sr_SP {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private static global::System.Resources.ResourceManager resourceMan;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private static global::System.Globalization.CultureInfo resourceCulture;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        internal ResponseStrings_sr_SP() {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Returns the cached ResourceManager instance used by this class.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
 | 
				
			||||||
 | 
					        public static global::System.Resources.ResourceManager ResourceManager {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                if (object.ReferenceEquals(resourceMan, null)) {
 | 
				
			||||||
 | 
					                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NadekoBot.Resources.ResponseStrings-sr-SP", typeof(ResponseStrings_sr_SP).GetTypeInfo().Assembly);
 | 
				
			||||||
 | 
					                    resourceMan = temp;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return resourceMan;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Overrides the current thread's CurrentUICulture property for all
 | 
				
			||||||
 | 
					        ///    resource lookups using this strongly typed resource class.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
 | 
				
			||||||
 | 
					        public static global::System.Globalization.CultureInfo Culture {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return resourceCulture;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            set {
 | 
				
			||||||
 | 
					                resourceCulture = value;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to {0} has already fainted..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_already_fainted {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_already_fainted", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to {0} already has full HP..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_already_full {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_already_full", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to Your type is already {0}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_already_that_type {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_already_that_type", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to used {0}{1} on {2}{3} for {4} damage..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_attack {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_attack", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You can't attack again without retaliation!.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_cant_attack_again {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_cant_attack_again", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You can't attack yourself..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_cant_attack_yourself {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_cant_attack_yourself", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to {0} has fainted!.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_fainted {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_fainted", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to healed {0} with one {1}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_healed {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_healed", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to {0} has {1} HP remaining..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_hp_remaining {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_hp_remaining", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You can't use {0}. Type `{1}ml` to see a list of moves you can use..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_invalid_move {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_invalid_move", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to Movelist for {0} type.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_moves {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_moves", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You don't have enough {0}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_no_currency {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_no_currency", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to It's not effective..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_not_effective {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_not_effective", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to revived {0} with one {1}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_revive_other {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_revive_other", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You revived yourself with one {0}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_revive_yourself {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_revive_yourself", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to Your type has been changed to {0} for a {1}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_settype_success {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_settype_success", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to It's somewhat effective..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_somewhat_effective {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_somewhat_effective", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to It's super effective!.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_super_effective {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_super_effective", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You used too many moves in a row, so you can't move!.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_too_many_moves {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_too_many_moves", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to Type of {0} is {1}.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_type_of_user {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_type_of_user", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to User not found..
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_user_not_found {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_user_not_found", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        ///    Looks up a localized string similar to You fainted, so you are not able to move!.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static string pokemon_you_fainted {
 | 
				
			||||||
 | 
					            get {
 | 
				
			||||||
 | 
					                return ResourceManager.GetString("pokemon_you_fainted", resourceCulture);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										368
									
								
								src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,368 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<root>
 | 
				
			||||||
 | 
					  <!-- 
 | 
				
			||||||
 | 
					    Microsoft ResX Schema 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Version 2.0
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    The primary goals of this format is to allow a simple XML format 
 | 
				
			||||||
 | 
					    that is mostly human readable. The generation and parsing of the 
 | 
				
			||||||
 | 
					    various data types are done through the TypeConverter classes 
 | 
				
			||||||
 | 
					    associated with the data types.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Example:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    ... ado.net/XML headers & schema ...
 | 
				
			||||||
 | 
					    <resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
				
			||||||
 | 
					    <resheader name="version">2.0</resheader>
 | 
				
			||||||
 | 
					    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
				
			||||||
 | 
					    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
				
			||||||
 | 
					    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
 | 
				
			||||||
 | 
					    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
				
			||||||
 | 
					    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
				
			||||||
 | 
					        <value>[base64 mime encoded serialized .NET Framework object]</value>
 | 
				
			||||||
 | 
					    </data>
 | 
				
			||||||
 | 
					    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
				
			||||||
 | 
					        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
 | 
				
			||||||
 | 
					        <comment>This is a comment</comment>
 | 
				
			||||||
 | 
					    </data>
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					    There are any number of "resheader" rows that contain simple 
 | 
				
			||||||
 | 
					    name/value pairs.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Each data row contains a name, and value. The row also contains a 
 | 
				
			||||||
 | 
					    type or mimetype. Type corresponds to a .NET class that support 
 | 
				
			||||||
 | 
					    text/value conversion through the TypeConverter architecture. 
 | 
				
			||||||
 | 
					    Classes that don't support this are serialized and stored with the 
 | 
				
			||||||
 | 
					    mimetype set.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    The mimetype is used for serialized objects, and tells the 
 | 
				
			||||||
 | 
					    ResXResourceReader how to depersist the object. This is currently not 
 | 
				
			||||||
 | 
					    extensible. For a given mimetype the value must be set accordingly:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
				
			||||||
 | 
					    that the ResXResourceWriter will generate, however the reader can 
 | 
				
			||||||
 | 
					    read any of the formats listed below.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    mimetype: application/x-microsoft.net.object.binary.base64
 | 
				
			||||||
 | 
					    value   : The object must be serialized with 
 | 
				
			||||||
 | 
					            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
 | 
				
			||||||
 | 
					            : and then encoded with base64 encoding.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    mimetype: application/x-microsoft.net.object.soap.base64
 | 
				
			||||||
 | 
					    value   : The object must be serialized with 
 | 
				
			||||||
 | 
					            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
				
			||||||
 | 
					            : and then encoded with base64 encoding.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
				
			||||||
 | 
					    value   : The object must be serialized into a byte array 
 | 
				
			||||||
 | 
					            : using a System.ComponentModel.TypeConverter
 | 
				
			||||||
 | 
					            : and then encoded with base64 encoding.
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
				
			||||||
 | 
					    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
 | 
				
			||||||
 | 
					    <xsd:element name="root" msdata:IsDataSet="true">
 | 
				
			||||||
 | 
					      <xsd:complexType>
 | 
				
			||||||
 | 
					        <xsd:choice maxOccurs="unbounded">
 | 
				
			||||||
 | 
					          <xsd:element name="metadata">
 | 
				
			||||||
 | 
					            <xsd:complexType>
 | 
				
			||||||
 | 
					              <xsd:sequence>
 | 
				
			||||||
 | 
					                <xsd:element name="value" type="xsd:string" minOccurs="0" />
 | 
				
			||||||
 | 
					              </xsd:sequence>
 | 
				
			||||||
 | 
					              <xsd:attribute name="name" use="required" type="xsd:string" />
 | 
				
			||||||
 | 
					              <xsd:attribute name="type" type="xsd:string" />
 | 
				
			||||||
 | 
					              <xsd:attribute name="mimetype" type="xsd:string" />
 | 
				
			||||||
 | 
					              <xsd:attribute ref="xml:space" />
 | 
				
			||||||
 | 
					            </xsd:complexType>
 | 
				
			||||||
 | 
					          </xsd:element>
 | 
				
			||||||
 | 
					          <xsd:element name="assembly">
 | 
				
			||||||
 | 
					            <xsd:complexType>
 | 
				
			||||||
 | 
					              <xsd:attribute name="alias" type="xsd:string" />
 | 
				
			||||||
 | 
					              <xsd:attribute name="name" type="xsd:string" />
 | 
				
			||||||
 | 
					            </xsd:complexType>
 | 
				
			||||||
 | 
					          </xsd:element>
 | 
				
			||||||
 | 
					          <xsd:element name="data">
 | 
				
			||||||
 | 
					            <xsd:complexType>
 | 
				
			||||||
 | 
					              <xsd:sequence>
 | 
				
			||||||
 | 
					                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
				
			||||||
 | 
					                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
				
			||||||
 | 
					              </xsd:sequence>
 | 
				
			||||||
 | 
					              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
 | 
				
			||||||
 | 
					              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
				
			||||||
 | 
					              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
				
			||||||
 | 
					              <xsd:attribute ref="xml:space" />
 | 
				
			||||||
 | 
					            </xsd:complexType>
 | 
				
			||||||
 | 
					          </xsd:element>
 | 
				
			||||||
 | 
					          <xsd:element name="resheader">
 | 
				
			||||||
 | 
					            <xsd:complexType>
 | 
				
			||||||
 | 
					              <xsd:sequence>
 | 
				
			||||||
 | 
					                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
				
			||||||
 | 
					              </xsd:sequence>
 | 
				
			||||||
 | 
					              <xsd:attribute name="name" type="xsd:string" use="required" />
 | 
				
			||||||
 | 
					            </xsd:complexType>
 | 
				
			||||||
 | 
					          </xsd:element>
 | 
				
			||||||
 | 
					        </xsd:choice>
 | 
				
			||||||
 | 
					      </xsd:complexType>
 | 
				
			||||||
 | 
					    </xsd:element>
 | 
				
			||||||
 | 
					  </xsd:schema>
 | 
				
			||||||
 | 
					  <resheader name="resmimetype">
 | 
				
			||||||
 | 
					    <value>text/microsoft-resx</value>
 | 
				
			||||||
 | 
					  </resheader>
 | 
				
			||||||
 | 
					  <resheader name="version">
 | 
				
			||||||
 | 
					    <value>2.0</value>
 | 
				
			||||||
 | 
					  </resheader>
 | 
				
			||||||
 | 
					  <resheader name="reader">
 | 
				
			||||||
 | 
					    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
				
			||||||
 | 
					  </resheader>
 | 
				
			||||||
 | 
					  <resheader name="writer">
 | 
				
			||||||
 | 
					    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
				
			||||||
 | 
					  </resheader>
 | 
				
			||||||
 | 
					  <data name="pokemon_already_fainted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} је већ онесвешћен.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_already_full" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} је већ потпуно здрав.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_already_that_type" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Твој тип већ јесте {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_attack" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>је искористио {0}{1} на {2}{3} и нанео {4} штете.</value>
 | 
				
			||||||
 | 
					    <comment>Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage.</comment>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_cant_attack_again" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Не можеш напасти пре узвратног ударца.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_cant_attack_yourself" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Не можеш напасти себе.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_fainted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} се онесвестио.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_healed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>је излечио {0} са једним {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_hp_remaining" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} има {1} преосталог здравља.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_invalid_move" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Не можеш искористити {0}. Укуцај `{1}ml` да би видео листу удараца које можеш користити.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_moves" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Листа покрета за {0} тип</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_not_effective" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Није ефективно.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_no_currency" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Немаш довољно {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_revive_other" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>је оживео {0} са једним {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_revive_yourself" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Оживео си себе са једним {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_settype_success" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Твој тип је промењен на {0} за један {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_somewhat_effective" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Има неког ефекта!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_super_effective" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Супер ефективно!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_too_many_moves" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Користио си превише напада узастопно, не можеш да се крећеш!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_type_of_user" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0}-ов тип је {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_user_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Корисник није нађен.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="pokemon_you_fainted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Онесвешћен си, не можеш да се крећеш.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_already_claimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Та база је већ под захетвом или је уништена.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_already_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Та база је већ под захтевом.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_already_unclaimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Та база није под захтевом.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_base_unclaimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} је **ПОНИШТИО ЗАХТЕВ** за базу #{1} у рату против {2}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_claimed_base" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>{0} захтева базу #{1} у рату против {2}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_claimed_other" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>@{0} Ти већ захтеваш базу #{1}. Не можеш захтевати још једну.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_claim_expired" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Захтев од @{0} за рат против {1} је истекао.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_enemy" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Противник</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_info_about_war" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Подаци о рату против {0}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_invalid_base_number" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Број базе није валидан.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_invalid_size" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Величина рата није валидна.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_list_active_wars" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Листа Ратова У Току</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_not_claimed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>нема захтева</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_not_partic" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Ти не учествујеш у том рату.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_not_partic_or_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>@{0} Ти или не учествујеш у рату или је та база већ уништена.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_no_active_wars" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Нема ратова у току.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_size" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Величина</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_already_started" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Рат против {0} је већ почео.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_created" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Рат против {0} је направљен.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_ended" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Рат против {0} је завршен.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_not_exist" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Тај рат не постоји.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clashofclans_war_started" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Рат против {0} је започет!</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="clasofclans_base_destroyed" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>је **УНИШТИО** базу #{0} у рату против {1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_all_stats_cleared" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Сва статистика Реакција по  Избору је обрисана.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_deleted" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Реакција по Избору обрисана</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_insuff_perms" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Немате дозволу. Потребно је поседовати бота за глобалне Реакције по Избору, а Администратор за серверске Реакције по Избору.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_list_all" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Листа свих реакција по избору</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_name" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Реакције по Избору</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_new_cust_react" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Нова Реакција по Избору</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_no_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Реакција по избору није нађена.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_no_found_id" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Није нађена реакција са тим идентификатором.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_response" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Одговор</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_stats" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Статистика Реакција по Избору</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_stats_cleared" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Статистика је обрисана за  {0} реакцију по избору.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_stats_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Није нађена статистика за тај окидач. Нема промене.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="customreactions_trigger" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Окидач</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_back_to_toc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Назад на Садржај</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_bot_owner_only" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Само Власник Бота</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_channel_permission" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Захтева {0} дозволу на каналу.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_cmdlist_donate" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Можете подржати пројекат на Пејтриону: <{0}> или Пејпалу: <{1}></value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_cmd_and_alias" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Команде и псеудоними</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_commandlist_regen" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Листа команди је обновљена.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_commands_instr" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Укуцај `{0}h ИмеКоманде` да видиш помоћ за ту команду. нпр. `{0}h >8ball`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_command_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Не могу да нађем ту команду. Проверите да ли команда постоји, па покушајте поново.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_desc" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Опис</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_donate" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Можете подржати НадекоБот пројекат на 
 | 
				
			||||||
 | 
					Пејтриону <{0}> или
 | 
				
			||||||
 | 
					Пејпалу <{1}>
 | 
				
			||||||
 | 
					Не заборавите да оставите своје корисничко име или ИД.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Хвала** ♥️</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_guide" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>**List of Commands**: <{0}>
 | 
				
			||||||
 | 
					**Hosting Guides and docs can be found here**: <{1}></value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_list_of_commands" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Листа Команди</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_list_of_modules" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Листа Модула</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_modules_footer" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Укуцај `{0}cmds ИмеМодула` да видиш листу свих команди у том модулу. нпр `{0}cmds games`</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_module_not_found" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Тај модул не постоји.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_server_permission" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Захтева {0} дозволу на серверу.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_table_of_contents" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Садржај</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="help_usage" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Упутство</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_autohentai_started" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Аутохентаи започет. Постоваћу сваких {0} сек. користећи један од следећих тагова:
 | 
				
			||||||
 | 
					{1}</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_autohentai_stopped" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>АутоХентаи заустављен.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_no_results" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Нема резултата.</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					  <data name="nsfw_tag" xml:space="preserve">
 | 
				
			||||||
 | 
					    <value>Таг</value>
 | 
				
			||||||
 | 
					  </data>
 | 
				
			||||||
 | 
					</root>
 | 
				
			||||||
@@ -30,9 +30,9 @@ namespace Services.CleverBotApi
 | 
				
			|||||||
        public static ChatterBot Create(ChatterBotType type, object arg)
 | 
					        public static ChatterBot Create(ChatterBotType type, object arg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
#if GLOBAL_NADEKO
 | 
					#if GLOBAL_NADEKO
 | 
				
			||||||
            var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=nadekobot";
 | 
					            var url = "http://www.cleverbot.com/webservicemin?uc=777&botapi=nadekobot";
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
            var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=chatterbotapi";
 | 
					            var url = "http://www.cleverbot.com/webservicemin?uc=777&botapi=chatterbotapi";
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch (type)
 | 
					            switch (type)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,7 +73,7 @@ namespace NadekoBot.Services
 | 
				
			|||||||
            if (!ownerChannels.Any())
 | 
					            if (!ownerChannels.Any())
 | 
				
			||||||
                _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
 | 
					                _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Length} owner message channels.");
 | 
					                _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _client.MessageReceived += MessageReceivedHandler;
 | 
					            _client.MessageReceived += MessageReceivedHandler;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -273,7 +273,7 @@ namespace NadekoBot.Services
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                            await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
 | 
					                            await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false);
 | 
					                            await SelfCommands.HandleDmForwarding(msg, ownerChannels).ConfigureAwait(false);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,6 +60,7 @@ Nadeko Support Server: https://discord.gg/nadekobot";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string OkColor { get; set; } = "71cd40";
 | 
					        public string OkColor { get; set; } = "71cd40";
 | 
				
			||||||
        public string ErrorColor { get; set; } = "ee281f";
 | 
					        public string ErrorColor { get; set; } = "ee281f";
 | 
				
			||||||
 | 
					        public string Locale { get; set; } = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class PlayingStatus :DbEntity
 | 
					    public class PlayingStatus :DbEntity
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ namespace NadekoBot.Services.Database.Models
 | 
				
			|||||||
        public string Trigger { get; set; }
 | 
					        public string Trigger { get; set; }
 | 
				
			||||||
        public bool IsRegex { get; set; }
 | 
					        public bool IsRegex { get; set; }
 | 
				
			||||||
        public bool OwnerOnly { get; set; }
 | 
					        public bool OwnerOnly { get; set; }
 | 
				
			||||||
        public override string ToString() => $"`#{Id}`  `Trigger:` {Trigger}\n `Response:` {Response}";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class ReactionResponse : DbEntity
 | 
					    public class ReactionResponse : DbEntity
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,6 +64,9 @@ namespace NadekoBot.Services.Database.Models
 | 
				
			|||||||
        public AntiRaidSetting AntiRaidSetting { get; set; }
 | 
					        public AntiRaidSetting AntiRaidSetting { get; set; }
 | 
				
			||||||
        public AntiSpamSetting AntiSpamSetting { get; set; }
 | 
					        public AntiSpamSetting AntiSpamSetting { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Locale { get; set; } = null;
 | 
				
			||||||
 | 
					        public string TimeZoneId { get; set; } = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
 | 
					        //public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,7 +132,7 @@ namespace NadekoBot.Services.Database
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            #region QUOTES
 | 
					            #region QUOTES
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            var quoteEntity = modelBuilder.Entity<Quote>();
 | 
					            //var quoteEntity = modelBuilder.Entity<Quote>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #endregion
 | 
					            #endregion
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
@@ -166,7 +166,7 @@ namespace NadekoBot.Services.Database
 | 
				
			|||||||
            #endregion
 | 
					            #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region BotConfig
 | 
					            #region BotConfig
 | 
				
			||||||
            var botConfigEntity = modelBuilder.Entity<BotConfig>();
 | 
					            //var botConfigEntity = modelBuilder.Entity<BotConfig>();
 | 
				
			||||||
            //botConfigEntity
 | 
					            //botConfigEntity
 | 
				
			||||||
            //    .HasMany(c => c.ModulePrefixes)
 | 
					            //    .HasMany(c => c.ModulePrefixes)
 | 
				
			||||||
            //    .WithOne(mp => mp.BotConfig)
 | 
					            //    .WithOne(mp => mp.BotConfig)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.Immutable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services
 | 
					namespace NadekoBot.Services
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -9,7 +11,7 @@ namespace NadekoBot.Services
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        string Token { get; }
 | 
					        string Token { get; }
 | 
				
			||||||
        string GoogleApiKey { get; }
 | 
					        string GoogleApiKey { get; }
 | 
				
			||||||
        ulong[] OwnerIds { get; }
 | 
					        ImmutableHashSet<ulong> OwnerIds { get; }
 | 
				
			||||||
        string MashapeKey { get; }
 | 
					        string MashapeKey { get; }
 | 
				
			||||||
        string LoLApiKey { get; }
 | 
					        string LoLApiKey { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,8 @@ using Discord;
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.Immutable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.Impl
 | 
					namespace NadekoBot.Services.Impl
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -21,7 +23,7 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string Token { get; }
 | 
					        public string Token { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ulong[] OwnerIds { get; }
 | 
					        public ImmutableHashSet<ulong> OwnerIds { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string LoLApiKey { get; }
 | 
					        public string LoLApiKey { get; }
 | 
				
			||||||
        public string OsuApiKey { get; }
 | 
					        public string OsuApiKey { get; }
 | 
				
			||||||
@@ -61,7 +63,7 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
                Token = data[nameof(Token)];
 | 
					                Token = data[nameof(Token)];
 | 
				
			||||||
                if (string.IsNullOrWhiteSpace(Token))
 | 
					                if (string.IsNullOrWhiteSpace(Token))
 | 
				
			||||||
                    throw new ArgumentNullException(nameof(Token), "Token is missing from credentials.json or Environment varibles.");
 | 
					                    throw new ArgumentNullException(nameof(Token), "Token is missing from credentials.json or Environment varibles.");
 | 
				
			||||||
                OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToArray();
 | 
					                OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToImmutableHashSet();
 | 
				
			||||||
                LoLApiKey = data[nameof(LoLApiKey)];
 | 
					                LoLApiKey = data[nameof(LoLApiKey)];
 | 
				
			||||||
                GoogleApiKey = data[nameof(GoogleApiKey)];
 | 
					                GoogleApiKey = data[nameof(GoogleApiKey)];
 | 
				
			||||||
                MashapeKey = data[nameof(MashapeKey)];
 | 
					                MashapeKey = data[nameof(MashapeKey)];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,45 +1,122 @@
 | 
				
			|||||||
namespace NadekoBot.Services
 | 
					using Discord;
 | 
				
			||||||
 | 
					using Discord.Commands;
 | 
				
			||||||
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					using NLog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Services
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class Localization
 | 
					    public class Localization
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public string this[string key] => LoadCommandString(key);
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
 | 
				
			||||||
 | 
					        public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Localization() { }
 | 
				
			||||||
 | 
					        public Localization(string defaultCulture, IDictionary<ulong, string> cultureInfoNames)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					            if (string.IsNullOrWhiteSpace(defaultCulture))
 | 
				
			||||||
 | 
					                DefaultCultureInfo = new CultureInfo("en-US");
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    DefaultCultureInfo = new CultureInfo(defaultCulture);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _log.Warn("Unable to load default bot's locale/language. Using en-US.");
 | 
				
			||||||
 | 
					                    DefaultCultureInfo = new CultureInfo("en-US");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            GuildCultureInfos = new ConcurrentDictionary<ulong, CultureInfo>(cultureInfoNames.ToDictionary(x => x.Key, x =>
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                  CultureInfo cultureInfo = null;
 | 
				
			||||||
 | 
					                  try
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                      cultureInfo = new CultureInfo(x.Value);
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  catch { }
 | 
				
			||||||
 | 
					                  return cultureInfo;
 | 
				
			||||||
 | 
					              }).Where(x => x.Value != null));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SetGuildCulture(IGuild guild, CultureInfo ci) =>
 | 
				
			||||||
 | 
					            SetGuildCulture(guild.Id, ci);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SetGuildCulture(ulong guildId, CultureInfo ci)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (ci == DefaultCultureInfo)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RemoveGuildCulture(guildId);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var gc = uow.GuildConfigs.For(guildId, set => set);
 | 
				
			||||||
 | 
					                gc.Locale = ci.Name;
 | 
				
			||||||
 | 
					                uow.Complete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            GuildCultureInfos.AddOrUpdate(guildId, ci, (id, old) => ci);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void RemoveGuildCulture(IGuild guild) => 
 | 
				
			||||||
 | 
					            RemoveGuildCulture(guild.Id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void RemoveGuildCulture(ulong guildId) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CultureInfo throwaway;
 | 
				
			||||||
 | 
					            if (GuildCultureInfos.TryRemove(guildId, out throwaway))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var gc = uow.GuildConfigs.For(guildId, set => set);
 | 
				
			||||||
 | 
					                    gc.Locale = null;
 | 
				
			||||||
 | 
					                    uow.Complete();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SetDefaultCulture(CultureInfo ci)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var uow = DbHandler.UnitOfWork())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var bc = uow.BotConfig.GetOrCreate();
 | 
				
			||||||
 | 
					                bc.Locale = ci.Name;
 | 
				
			||||||
 | 
					                uow.Complete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            DefaultCultureInfo = ci;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void ResetDefaultCulture() =>
 | 
				
			||||||
 | 
					            SetDefaultCulture(CultureInfo.CurrentCulture);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public CultureInfo GetCultureInfo(IGuild guild) =>
 | 
				
			||||||
 | 
					            GetCultureInfo(guild?.Id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public CultureInfo GetCultureInfo(ulong? guildId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (guildId == null)
 | 
				
			||||||
 | 
					                return DefaultCultureInfo;
 | 
				
			||||||
 | 
					            CultureInfo info = null;
 | 
				
			||||||
 | 
					            GuildCultureInfos.TryGetValue(guildId.Value, out info);
 | 
				
			||||||
 | 
					            return info ?? DefaultCultureInfo;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static string LoadCommandString(string key)
 | 
					        public static string LoadCommandString(string key)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string toReturn = Resources.CommandStrings.ResourceManager.GetString(key);
 | 
					            string toReturn = Resources.CommandStrings.ResourceManager.GetString(key);
 | 
				
			||||||
            return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn;
 | 
					            return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        //private static string GetCommandString(string key)
 | 
					 | 
				
			||||||
        //{
 | 
					 | 
				
			||||||
        //    return key;
 | 
					 | 
				
			||||||
            //var resx = new List<DictionaryEntry>();
 | 
					 | 
				
			||||||
            //var fs = new StreamReader(File.OpenRead("./Strings.resx"));
 | 
					 | 
				
			||||||
            //Console.WriteLine(fs.ReadToEnd());
 | 
					 | 
				
			||||||
            //using (var reader = new ResourceReader(fs.BaseStream))
 | 
					 | 
				
			||||||
            //{
 | 
					 | 
				
			||||||
            //    List<DictionaryEntry> existing = new List<DictionaryEntry>();
 | 
					 | 
				
			||||||
            //    foreach (DictionaryEntry item in reader)
 | 
					 | 
				
			||||||
            //    {
 | 
					 | 
				
			||||||
            //        existing.Add(item);
 | 
					 | 
				
			||||||
            //    }
 | 
					 | 
				
			||||||
            //    var existingResource = resx.Where(r => r.Key.ToString() == key).FirstOrDefault();
 | 
					 | 
				
			||||||
            //    if (existingResource.Key == null)
 | 
					 | 
				
			||||||
            //    {
 | 
					 | 
				
			||||||
            //        resx.Add(new DictionaryEntry() { Key = key, Value = key });
 | 
					 | 
				
			||||||
            //    }
 | 
					 | 
				
			||||||
            //    else
 | 
					 | 
				
			||||||
            //        return existingResource.Value.ToString();
 | 
					 | 
				
			||||||
            //}
 | 
					 | 
				
			||||||
            //using (var writer = new ResourceWriter(new FileStream("./Strings.resx", FileMode.OpenOrCreate)))
 | 
					 | 
				
			||||||
            //{
 | 
					 | 
				
			||||||
            //    resx.ForEach(r =>
 | 
					 | 
				
			||||||
            //    {
 | 
					 | 
				
			||||||
            //        writer.AddResource(r.Key.ToString(), r.Value.ToString());
 | 
					 | 
				
			||||||
            //    });
 | 
					 | 
				
			||||||
            //    writer.Generate();
 | 
					 | 
				
			||||||
            //}
 | 
					 | 
				
			||||||
            //return key;
 | 
					 | 
				
			||||||
        //}
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
        private DiscordShardedClient client;
 | 
					        private DiscordShardedClient client;
 | 
				
			||||||
        private DateTime started;
 | 
					        private DateTime started;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public const string BotVersion = "1.1.6";
 | 
					        public const string BotVersion = "1.1.8-alpha";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Author => "Kwoth#2560";
 | 
					        public string Author => "Kwoth#2560";
 | 
				
			||||||
        public string Library => "Discord.Net";
 | 
					        public string Library => "Discord.Net";
 | 
				
			||||||
@@ -96,18 +96,21 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
                            content.Headers.Clear();
 | 
					                            content.Headers.Clear();
 | 
				
			||||||
                            content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
 | 
					                            content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            var res = await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false);
 | 
					                            await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch { }
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // ignored
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
 | 
					            }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Initialize()
 | 
					        public void Initialize()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var guilds = this.client.GetGuilds();
 | 
					            var guilds = this.client.GetGuilds().ToArray();
 | 
				
			||||||
            _textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count());
 | 
					            _textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel));
 | 
				
			||||||
            _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels;
 | 
					            _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,6 @@ namespace NadekoBot.Services
 | 
				
			|||||||
                return minValue;
 | 
					                return minValue;
 | 
				
			||||||
            var bytes = new byte[sizeof(int)];
 | 
					            var bytes = new byte[sizeof(int)];
 | 
				
			||||||
            rng.GetBytes(bytes);
 | 
					            rng.GetBytes(bytes);
 | 
				
			||||||
            var num = BitConverter.ToInt32(bytes, 0);
 | 
					 | 
				
			||||||
            var sign = Math.Sign(BitConverter.ToInt32(bytes, 0));
 | 
					            var sign = Math.Sign(BitConverter.ToInt32(bytes, 0));
 | 
				
			||||||
            return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue;
 | 
					            return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "AngleSharp": "0.9.9",
 | 
					    "AngleSharp": "0.9.9",
 | 
				
			||||||
    "libvideo": "1.0.0",
 | 
					    "libvideo": "1.0.1",
 | 
				
			||||||
    "CoreCLR-NCalc": "2.1.2",
 | 
					    "CoreCLR-NCalc": "2.1.2",
 | 
				
			||||||
    "Google.Apis.Urlshortener.v1": "1.19.0.138",
 | 
					    "Google.Apis.Urlshortener.v1": "1.19.0.138",
 | 
				
			||||||
    "Google.Apis.YouTube.v3": "1.20.0.701",
 | 
					    "Google.Apis.YouTube.v3": "1.20.0.701",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user