Merge branch 'shard-process' into 1.4
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,6 @@
 | 
				
			|||||||
#Manually added files
 | 
					#Manually added files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					patreon_rewards.json
 | 
				
			||||||
command_errors*.txt
 | 
					command_errors*.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
src/NadekoBot/Command Errors*.txt
 | 
					src/NadekoBot/Command Errors*.txt
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
					Microsoft Visual Studio Solution File, Format Version 12.00
 | 
				
			||||||
# Visual Studio 15
 | 
					# Visual Studio 15
 | 
				
			||||||
VisualStudioVersion = 15.0.26430.6
 | 
					VisualStudioVersion = 15.0.26430.12
 | 
				
			||||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
					MinimumVisualStudioVersion = 10.0.40219.1
 | 
				
			||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}"
 | 
					Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,8 @@ If you do not see `credentials.json` you will need to rename `credentials_exampl
 | 
				
			|||||||
  "OsuApiKey": "4c8c8fdff8e1234581725db27fd140a7d93320d6",
 | 
					  "OsuApiKey": "4c8c8fdff8e1234581725db27fd140a7d93320d6",
 | 
				
			||||||
  "PatreonAccessToken": "",
 | 
					  "PatreonAccessToken": "",
 | 
				
			||||||
  "Db": null,
 | 
					  "Db": null,
 | 
				
			||||||
  "TotalShards": 1
 | 
					  "TotalShards": 1,
 | 
				
			||||||
 | 
					  "ShardRunCommand": ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
@@ -155,7 +156,20 @@ It should look like:
 | 
				
			|||||||
- **TotalShards** 
 | 
					- **TotalShards** 
 | 
				
			||||||
	- Required if the bot will be connected to more than 1500 servers. 
 | 
						- Required if the bot will be connected to more than 1500 servers. 
 | 
				
			||||||
	- Most likely unnecessary to change until your bot is added to more than 1500 servers.  
 | 
						- Most likely unnecessary to change until your bot is added to more than 1500 servers.  
 | 
				
			||||||
 | 
					- **ShardRunCommand**
 | 
				
			||||||
 | 
						- Command with which to run shards 1+
 | 
				
			||||||
 | 
						- Required if you're sharding your bot on windows using .exe, or in a custom way.
 | 
				
			||||||
 | 
						- This internally defaults to `dotnet`
 | 
				
			||||||
 | 
						- For example, if you want to shard your NadekoBot which you installed using windows installer, you would want to set it to something like this: `C:\Program Files\NadekoBot\system\NadekoBot.exe`
 | 
				
			||||||
 | 
					- **ShardRunArguments**
 | 
				
			||||||
 | 
						- Arguments to the shard run command
 | 
				
			||||||
 | 
						- Required if you're sharding your bot on windows using .exe, or in a custom way. 
 | 
				
			||||||
 | 
						- This internally defaults to `run -c Release -- {0} {1} {2}` which will be enough to run linux and other 'from source' setups
 | 
				
			||||||
 | 
						- {0} will be replaced by the `shard ID` of the shard being ran, {1} by the shard 0's process id, and {2} by the port shard communication is happening on
 | 
				
			||||||
 | 
						- If shard0 (main window) is closed, all other shards will close too
 | 
				
			||||||
 | 
						- For example, if you want to shard your NadekoBot which you installed using windows installer, you would want to set it to `{0} {1} {2}`
 | 
				
			||||||
 | 
					- **ShardRunPort**
 | 
				
			||||||
 | 
						- Bot uses a random UDP port in [5000, 6000) range for communication between shards
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## DB files
 | 
					## DB files
 | 
				
			||||||
@@ -163,6 +177,7 @@ It should look like:
 | 
				
			|||||||
Nadeko saves all the settings and infomations in `NadekoBot.db` file here:		
 | 
					Nadeko saves all the settings and infomations in `NadekoBot.db` file here:		
 | 
				
			||||||
`NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data/NadekoBot.db` (macOS and Linux) 		
 | 
					`NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data/NadekoBot.db` (macOS and Linux) 		
 | 
				
			||||||
`NadekoBot\system\data` (Windows)  
 | 
					`NadekoBot\system\data` (Windows)  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
in order to open the database file you will need [DB Browser for SQLite](http://sqlitebrowser.org/).
 | 
					in order to open the database file you will need [DB Browser for SQLite](http://sqlitebrowser.org/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*NOTE: You don't have to worry if you don't have `NadekoBot.db` file, it gets auto created once you run the bot successfully.*		
 | 
					*NOTE: You don't have to worry if you don't have `NadekoBot.db` file, it gets auto created once you run the bot successfully.*		
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,6 @@ namespace NadekoBot.DataStructures.ModuleBehaviors
 | 
				
			|||||||
        /// Try to execute some logic within some module's service.
 | 
					        /// Try to execute some logic within some module's service.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <returns>Whether it should block other command executions after it.</returns>
 | 
					        /// <returns>Whether it should block other command executions after it.</returns>
 | 
				
			||||||
        Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg);
 | 
					        Task<bool> TryExecuteEarly(DiscordSocketClient client, IGuild guild, IUserMessage msg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ namespace NadekoBot.DataStructures.ModuleBehaviors
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public interface ILateBlocker
 | 
					    public interface ILateBlocker
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild, 
 | 
					        Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild, 
 | 
				
			||||||
            IMessageChannel channel, IUser user, string moduleName, string commandName);
 | 
					            IMessageChannel channel, IUser user, string moduleName, string commandName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,6 @@ namespace NadekoBot.DataStructures.ModuleBehaviors
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public interface ILateExecutor
 | 
					    public interface ILateExecutor
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg);
 | 
					        Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								src/NadekoBot/DataStructures/Shard0Precondition.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/NadekoBot/DataStructures/Shard0Precondition.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					using Discord.Commands;
 | 
				
			||||||
 | 
					using Discord.WebSocket;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.DataStructures
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class Shard0Precondition : PreconditionAttribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var c = (DiscordSocketClient)context.Client;
 | 
				
			||||||
 | 
					            if (c.ShardId == 0)
 | 
				
			||||||
 | 
					                return Task.FromResult(PreconditionResult.FromSuccess());
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                return Task.FromResult(PreconditionResult.FromError("Must be ran from shard #0"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/NadekoBot/DataStructures/ShardCom/IShardComMessage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/NadekoBot/DataStructures/ShardCom/IShardComMessage.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					using Discord;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.DataStructures.ShardCom
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ShardComMessage
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public int ShardId { get; set; }
 | 
				
			||||||
 | 
					        public ConnectionState ConnectionState { get; set; }
 | 
				
			||||||
 | 
					        public int Guilds { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								src/NadekoBot/DataStructures/ShardCom/ShardComClient.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/NadekoBot/DataStructures/ShardCom/ShardComClient.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Net;
 | 
				
			||||||
 | 
					using System.Net.Sockets;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.DataStructures.ShardCom
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ShardComClient
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private int port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ShardComClient(int port)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            this.port = port;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task Send(ShardComMessage data)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var msg = JsonConvert.SerializeObject(data);
 | 
				
			||||||
 | 
					            using (var client = new UdpClient())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var bytes = Encoding.UTF8.GetBytes(msg);
 | 
				
			||||||
 | 
					                await client.SendAsync(bytes, bytes.Length, IPAddress.Loopback.ToString(), port).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/NadekoBot/DataStructures/ShardCom/ShardComServer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/NadekoBot/DataStructures/ShardCom/ShardComServer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Net;
 | 
				
			||||||
 | 
					using System.Net.Sockets;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.DataStructures.ShardCom
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ShardComServer : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly UdpClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ShardComServer(int port)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _client = new UdpClient(port);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Start()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var ip = new IPEndPoint(IPAddress.Any, 0);
 | 
				
			||||||
 | 
					                while (true)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var recv = await _client.ReceiveAsync();
 | 
				
			||||||
 | 
					                    var data = Encoding.UTF8.GetString(recv.Buffer);
 | 
				
			||||||
 | 
					                    var _ = OnDataReceived(JsonConvert.DeserializeObject<ShardComMessage>(data));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _client.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public event Func<ShardComMessage, Task> OnDataReceived = delegate { return Task.CompletedTask; };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,9 +7,9 @@ namespace NadekoBot.TypeReaders
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class GuildTypeReader : TypeReader
 | 
					    public class GuildTypeReader : TypeReader
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public GuildTypeReader(DiscordShardedClient client)
 | 
					        public GuildTypeReader(DiscordSocketClient client)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,17 +126,17 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var guser = (IGuildUser)Context.User;
 | 
					            var guser = (IGuildUser)Context.User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var userRoles = user.GetRoles();
 | 
					            var userRoles = user.GetRoles().Except(new[] { guser.Guild.EveryoneRole });
 | 
				
			||||||
            if (guser.Id != Context.Guild.OwnerId && 
 | 
					            if (user.Id == Context.Guild.OwnerId || (Context.User.Id != Context.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= userRoles.Max(x => x.Position)))
 | 
				
			||||||
                (user.Id == Context.Guild.OwnerId || guser.GetRoles().Max(x => x.Position) <= userRoles.Max(x => x.Position)))
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await user.RemoveRolesAsync(userRoles).ConfigureAwait(false);
 | 
					                await user.RemoveRolesAsync(userRoles).ConfigureAwait(false);
 | 
				
			||||||
                await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
					                await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                Console.WriteLine(ex);
 | 
				
			||||||
                await ReplyErrorLocalized("rar_err").ConfigureAwait(false);
 | 
					                await ReplyErrorLocalized("rar_err").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -140,7 +140,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    await uow.CompleteAsync();
 | 
					                    await uow.CompleteAsync();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendPaginatedConfirmAsync((DiscordShardedClient)Context.Client, page, (curPage) =>
 | 
					                await Context.Channel.SendPaginatedConfirmAsync((DiscordSocketClient)Context.Client, page, (curPage) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return new EmbedBuilder()
 | 
					                    return new EmbedBuilder()
 | 
				
			||||||
                        .WithTitle(GetText("self_assign_list", roleCnt))
 | 
					                        .WithTitle(GetText("self_assign_list", roleCnt))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,8 @@ using NadekoBot.Services;
 | 
				
			|||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using NadekoBot.Services.Administration;
 | 
					using NadekoBot.Services.Administration;
 | 
				
			||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
 | 
					using NadekoBot.DataStructures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Modules.Administration
 | 
					namespace NadekoBot.Modules.Administration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -25,10 +27,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            private static readonly object _locker = new object();
 | 
					            private static readonly object _locker = new object();
 | 
				
			||||||
            private readonly SelfService _service;
 | 
					            private readonly SelfService _service;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly IImagesService _images;
 | 
					            private readonly IImagesService _images;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public SelfCommands(DbService db, SelfService service, DiscordShardedClient client,
 | 
					            public SelfCommands(DbService db, SelfService service, DiscordSocketClient client,
 | 
				
			||||||
                IImagesService images)
 | 
					                IImagesService images)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _db = db;
 | 
					                _db = db;
 | 
				
			||||||
@@ -204,28 +206,28 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            //todo 2 shard commands
 | 
				
			||||||
            [OwnerOnly]
 | 
					            //[NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            public async Task ConnectShard(int shardid)
 | 
					            //[Shard0Precondition]
 | 
				
			||||||
            {
 | 
					            //[OwnerOnly]
 | 
				
			||||||
                var shard = _client.GetShard(shardid);
 | 
					            //public async Task RestartShard(int shardid)
 | 
				
			||||||
 | 
					            //{
 | 
				
			||||||
                if (shard == null)
 | 
					            //    if (shardid == 0 || shardid > b)
 | 
				
			||||||
                {
 | 
					            //    {
 | 
				
			||||||
                    await ReplyErrorLocalized("no_shard_id").ConfigureAwait(false);
 | 
					            //        await ReplyErrorLocalized("no_shard_id").ConfigureAwait(false);
 | 
				
			||||||
                    return;
 | 
					            //        return;
 | 
				
			||||||
                }
 | 
					            //    }
 | 
				
			||||||
                try
 | 
					            //    try
 | 
				
			||||||
                {
 | 
					            //    {
 | 
				
			||||||
                    await ReplyConfirmLocalized("shard_reconnecting", Format.Bold("#" + shardid)).ConfigureAwait(false);
 | 
					            //        await ReplyConfirmLocalized("shard_reconnecting", Format.Bold("#" + shardid)).ConfigureAwait(false);
 | 
				
			||||||
                    await shard.StartAsync().ConfigureAwait(false);
 | 
					            //        await shard.StartAsync().ConfigureAwait(false);
 | 
				
			||||||
                    await ReplyConfirmLocalized("shard_reconnected", Format.Bold("#" + shardid)).ConfigureAwait(false);
 | 
					            //        await ReplyConfirmLocalized("shard_reconnected", Format.Bold("#" + shardid)).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					            //    }
 | 
				
			||||||
                catch (Exception ex)
 | 
					            //    catch (Exception ex)
 | 
				
			||||||
                {
 | 
					            //    {
 | 
				
			||||||
                    _log.Warn(ex);
 | 
					            //        _log.Warn(ex);
 | 
				
			||||||
                }
 | 
					            //    }
 | 
				
			||||||
            }
 | 
					            //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
@@ -417,8 +419,10 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task ReloadImages()
 | 
					            public async Task ReloadImages()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var time = _images.Reload();
 | 
					                var sw = Stopwatch.StartNew();
 | 
				
			||||||
                await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false);
 | 
					                _images.Reload();
 | 
				
			||||||
 | 
					                sw.Stop();
 | 
				
			||||||
 | 
					                await ReplyConfirmLocalized("images_loaded", sw.Elapsed.TotalSeconds.ToString("F3")).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
 | 
					            private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Administration
 | 
				
			|||||||
                    .ToArray();
 | 
					                    .ToArray();
 | 
				
			||||||
                var timezonesPerPage = 20;
 | 
					                var timezonesPerPage = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendPaginatedConfirmAsync((DiscordShardedClient)Context.Client, page, 
 | 
					                await Context.Channel.SendPaginatedConfirmAsync((DiscordSocketClient)Context.Client, page, 
 | 
				
			||||||
                    (curPage) => new EmbedBuilder()
 | 
					                    (curPage) => new EmbedBuilder()
 | 
				
			||||||
                        .WithOkColor()
 | 
					                        .WithOkColor()
 | 
				
			||||||
                        .WithTitle(GetText("timezones_available"))
 | 
					                        .WithTitle(GetText("timezones_available"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,10 +17,10 @@ namespace NadekoBot.Modules.CustomReactions
 | 
				
			|||||||
        private readonly IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly CustomReactionsService _crs;
 | 
					        private readonly CustomReactionsService _crs;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public CustomReactions(IBotCredentials creds, DbService db, CustomReactionsService crs,
 | 
					        public CustomReactions(IBotCredentials creds, DbService db, CustomReactionsService crs,
 | 
				
			||||||
            DiscordShardedClient client)
 | 
					            DiscordSocketClient client)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _creds = creds;
 | 
					            _creds = creds;
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,12 +22,12 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly BotConfig _bc;
 | 
					            private readonly BotConfig _bc;
 | 
				
			||||||
            private readonly CurrencyService _cs;
 | 
					            private readonly CurrencyService _cs;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
 | 
					            public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public AnimalRacing(BotConfig bc, CurrencyService cs, DiscordShardedClient client)
 | 
					            public AnimalRacing(BotConfig bc, CurrencyService cs, DiscordSocketClient client)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _bc = bc;
 | 
					                _bc = bc;
 | 
				
			||||||
                _cs = cs;
 | 
					                _cs = cs;
 | 
				
			||||||
@@ -82,14 +82,14 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                private readonly ITextChannel _raceChannel;
 | 
					                private readonly ITextChannel _raceChannel;
 | 
				
			||||||
                private readonly BotConfig _bc;
 | 
					                private readonly BotConfig _bc;
 | 
				
			||||||
                private readonly CurrencyService _cs;
 | 
					                private readonly CurrencyService _cs;
 | 
				
			||||||
                private readonly DiscordShardedClient _client;
 | 
					                private readonly DiscordSocketClient _client;
 | 
				
			||||||
                private readonly ILocalization _localization;
 | 
					                private readonly ILocalization _localization;
 | 
				
			||||||
                private readonly NadekoStrings _strings;
 | 
					                private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                public bool Started { get; private set; }
 | 
					                public bool Started { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                public AnimalRace(ulong serverId, ITextChannel channel, string prefix, BotConfig bc,
 | 
					                public AnimalRace(ulong serverId, ITextChannel channel, string prefix, BotConfig bc,
 | 
				
			||||||
                    CurrencyService cs, DiscordShardedClient client, ILocalization localization,
 | 
					                    CurrencyService cs, DiscordSocketClient client, ILocalization localization,
 | 
				
			||||||
                    NadekoStrings strings)
 | 
					                    NadekoStrings strings)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _prefix = prefix;
 | 
					                    _prefix = prefix;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,11 +34,11 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                .ToArray();
 | 
					                .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private string _secretCode = string.Empty;
 | 
					            private string _secretCode = string.Empty;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly BotConfig _bc;
 | 
					            private readonly BotConfig _bc;
 | 
				
			||||||
            private readonly CurrencyService _cs;
 | 
					            private readonly CurrencyService _cs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public CurrencyEvents(DiscordShardedClient client, BotConfig bc, CurrencyService cs)
 | 
					            public CurrencyEvents(DiscordSocketClient client, BotConfig bc, CurrencyService cs)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
                _bc = bc;
 | 
					                _bc = bc;
 | 
				
			||||||
@@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
 | 
					        private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly CurrencyService _cs;
 | 
					        private readonly CurrencyService _cs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private IUserMessage StartingMessage { get; set; }
 | 
					        private IUserMessage StartingMessage { get; set; }
 | 
				
			||||||
@@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
        private CancellationTokenSource Source { get; }
 | 
					        private CancellationTokenSource Source { get; }
 | 
				
			||||||
        private CancellationToken CancelToken { get; }
 | 
					        private CancellationToken CancelToken { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public FlowerReactionEvent(DiscordShardedClient client, CurrencyService cs)
 | 
					        public FlowerReactionEvent(DiscordSocketClient client, CurrencyService cs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
            private readonly BotConfig _bc;
 | 
					            private readonly BotConfig _bc;
 | 
				
			||||||
            private readonly DbService _db;
 | 
					            private readonly DbService _db;
 | 
				
			||||||
            private readonly CurrencyService _cs;
 | 
					            private readonly CurrencyService _cs;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public enum Role
 | 
					            public enum Role
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Gambling
 | 
				
			|||||||
                List
 | 
					                List
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public FlowerShop(BotConfig bc, DbService db, CurrencyService cs, DiscordShardedClient client)
 | 
					            public FlowerShop(BotConfig bc, DbService db, CurrencyService cs, DiscordSocketClient client)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _db = db;
 | 
					                _db = db;
 | 
				
			||||||
                _bc = bc;
 | 
					                _bc = bc;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,12 +20,12 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class Acropobia : NadekoSubmodule
 | 
					        public class Acropobia : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //channelId, game
 | 
					            //channelId, game
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
 | 
					            public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public Acropobia(DiscordShardedClient client)
 | 
					            public Acropobia(DiscordSocketClient client)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -86,10 +86,10 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
            //text, votes
 | 
					            //text, votes
 | 
				
			||||||
            private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>();
 | 
					            private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>();
 | 
				
			||||||
            private readonly Logger _log;
 | 
					            private readonly Logger _log;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly NadekoStrings _strings;
 | 
					            private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public AcrophobiaGame(DiscordShardedClient client, NadekoStrings strings, ITextChannel channel, int time)
 | 
					            public AcrophobiaGame(DiscordSocketClient client, NadekoStrings strings, ITextChannel channel, int time)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log = LogManager.GetCurrentClassLogger();
 | 
					                _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Games.Hangman
 | 
				
			|||||||
    public class HangmanGame: IDisposable
 | 
					    public class HangmanGame: IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IMessageChannel GameChannel { get; }
 | 
					        public IMessageChannel GameChannel { get; }
 | 
				
			||||||
        public HashSet<char> Guesses { get; } = new HashSet<char>();
 | 
					        public HashSet<char> Guesses { get; } = new HashSet<char>();
 | 
				
			||||||
@@ -82,7 +82,7 @@ namespace NadekoBot.Modules.Games.Hangman
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public event Action<HangmanGame> OnEnded;
 | 
					        public event Action<HangmanGame> OnEnded;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public HangmanGame(DiscordShardedClient client, IMessageChannel channel, string type)
 | 
					        public HangmanGame(DiscordSocketClient client, IMessageChannel channel, string type)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,9 +15,9 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class HangmanCommands : NadekoSubmodule
 | 
					        public class HangmanCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public HangmanCommands(DiscordShardedClient client)
 | 
					            public HangmanCommands(DiscordSocketClient client)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,13 +20,13 @@ namespace NadekoBot.Modules.Games.Models
 | 
				
			|||||||
        public bool IsActive { get; private set; }
 | 
					        public bool IsActive { get; private set; }
 | 
				
			||||||
        private readonly Stopwatch sw;
 | 
					        private readonly Stopwatch sw;
 | 
				
			||||||
        private readonly List<ulong> finishedUserIds;
 | 
					        private readonly List<ulong> finishedUserIds;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly GamesService _games;
 | 
					        private readonly GamesService _games;
 | 
				
			||||||
        private readonly string _prefix;
 | 
					        private readonly string _prefix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Logger _log { get; }
 | 
					        private Logger _log { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TypingGame(GamesService games, DiscordShardedClient client, ITextChannel channel, string prefix) //kek@prefix
 | 
					        public TypingGame(GamesService games, DiscordSocketClient client, ITextChannel channel, string prefix) //kek@prefix
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _games = games;
 | 
					            _games = games;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                bool enabled;
 | 
					                bool enabled;
 | 
				
			||||||
                using (var uow = _db.UnitOfWork)
 | 
					                using (var uow = _db.UnitOfWork)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var guildConfig = uow.GuildConfigs.For(channel.Id, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
 | 
					                    var guildConfig = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var toAdd = new GCChannelId() { ChannelId = channel.Id };
 | 
					                    var toAdd = new GCChannelId() { ChannelId = channel.Id };
 | 
				
			||||||
                    if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
 | 
					                    if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,10 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class PollCommands : NadekoSubmodule
 | 
					        public class PollCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly PollService _polls;
 | 
					            private readonly PollService _polls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public PollCommands(DiscordShardedClient client, PollService polls)
 | 
					            public PollCommands(DiscordSocketClient client, PollService polls)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
                _polls = polls;
 | 
					                _polls = polls;
 | 
				
			||||||
@@ -26,13 +26,7 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
					            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            public Task Poll([Remainder] string arg = null)
 | 
					            public Task Poll([Remainder] string arg = null)
 | 
				
			||||||
                => InternalStartPoll(arg, false);
 | 
					                => InternalStartPoll(arg);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
					 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
            public Task PublicPoll([Remainder] string arg = null)
 | 
					 | 
				
			||||||
                => InternalStartPoll(arg, true);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
					            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
@@ -45,9 +39,9 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
                await Context.Channel.EmbedAsync(poll.GetStats(GetText("current_poll_results")));
 | 
					                await Context.Channel.EmbedAsync(poll.GetStats(GetText("current_poll_results")));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private async Task InternalStartPoll(string arg, bool isPublic = false)
 | 
					            private async Task InternalStartPoll(string arg)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if(await _polls.StartPoll((ITextChannel)Context.Channel, Context.Message, arg, isPublic) == false)
 | 
					                if(await _polls.StartPoll((ITextChannel)Context.Channel, Context.Message, arg) == false)
 | 
				
			||||||
                    await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,9 +20,9 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
 | 
					            public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
 | 
				
			||||||
            private readonly GamesService _games;
 | 
					            private readonly GamesService _games;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public SpeedTypingCommands(DiscordShardedClient client, GamesService games)
 | 
					            public SpeedTypingCommands(DiscordSocketClient client, GamesService games)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _games = games;
 | 
					                _games = games;
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
            private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>();
 | 
					            private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
 | 
					            private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public TicTacToeCommands(DiscordShardedClient client)
 | 
					            public TicTacToeCommands(DiscordSocketClient client)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -87,9 +87,9 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
            private IUserMessage _previousMessage;
 | 
					            private IUserMessage _previousMessage;
 | 
				
			||||||
            private Timer _timeoutTimer;
 | 
					            private Timer _timeoutTimer;
 | 
				
			||||||
            private readonly NadekoStrings _strings;
 | 
					            private readonly NadekoStrings _strings;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public TicTacToe(NadekoStrings strings, DiscordShardedClient client, ITextChannel channel, IGuildUser firstUser)
 | 
					            public TicTacToe(NadekoStrings strings, DiscordSocketClient client, ITextChannel channel, IGuildUser firstUser)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _channel = channel;
 | 
					                _channel = channel;
 | 
				
			||||||
                _strings = strings;
 | 
					                _strings = strings;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
				
			|||||||
        private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
 | 
					        private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly BotConfig _bc;
 | 
					        private readonly BotConfig _bc;
 | 
				
			||||||
        private readonly CurrencyService _cs;
 | 
					        private readonly CurrencyService _cs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public int WinRequirement { get; }
 | 
					        public int WinRequirement { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TriviaGame(NadekoStrings strings, DiscordShardedClient client, BotConfig bc,
 | 
					        public TriviaGame(NadekoStrings strings, DiscordSocketClient client, BotConfig bc,
 | 
				
			||||||
            CurrencyService cs, IGuild guild, ITextChannel channel,
 | 
					            CurrencyService cs, IGuild guild, ITextChannel channel,
 | 
				
			||||||
            bool showHints, int winReq, bool isPokemon)
 | 
					            bool showHints, int winReq, bool isPokemon)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,12 +18,12 @@ namespace NadekoBot.Modules.Games
 | 
				
			|||||||
        public class TriviaCommands : NadekoSubmodule
 | 
					        public class TriviaCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly CurrencyService _cs;
 | 
					            private readonly CurrencyService _cs;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly BotConfig _bc;
 | 
					            private readonly BotConfig _bc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
 | 
					            public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public TriviaCommands(DiscordShardedClient client, BotConfig bc, CurrencyService cs)
 | 
					            public TriviaCommands(DiscordSocketClient client, BotConfig bc, CurrencyService cs)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _cs = cs;
 | 
					                _cs = cs;
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,8 +155,8 @@ namespace NadekoBot.Modules.Help
 | 
				
			|||||||
        public async Task Guide()
 | 
					        public async Task Guide()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await ConfirmLocalized("guide", 
 | 
					            await ConfirmLocalized("guide", 
 | 
				
			||||||
                "http://nadekobot.readthedocs.io/en/latest/Commands%20List/",
 | 
					                "http://nadekobot.readthedocs.io/en/1.3x/Commands%20List/",
 | 
				
			||||||
                "http://nadekobot.readthedocs.io/en/latest/").ConfigureAwait(false);
 | 
					                "http://nadekobot.readthedocs.io/en/1.3x/").ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,12 +22,12 @@ namespace NadekoBot.Modules.Music
 | 
				
			|||||||
    public class Music : NadekoTopLevelModule 
 | 
					    public class Music : NadekoTopLevelModule 
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static MusicService _music;
 | 
					        private static MusicService _music;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
        private readonly IGoogleApiService _google;
 | 
					        private readonly IGoogleApiService _google;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Music(DiscordShardedClient client, IBotCredentials creds, IGoogleApiService google,
 | 
					        public Music(DiscordSocketClient client, IBotCredentials creds, IGoogleApiService google,
 | 
				
			||||||
            DbService db, MusicService music)
 | 
					            DbService db, MusicService music)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,12 +87,11 @@ namespace NadekoBot.Modules
 | 
				
			|||||||
            return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text);
 | 
					            return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // todo maybe make this generic and use
 | 
					        // TypeConverter typeConverter = TypeDescriptor.GetConverter(propType); ?
 | 
				
			||||||
        // TypeConverter typeConverter = TypeDescriptor.GetConverter(propType);
 | 
					 | 
				
			||||||
        public async Task<string> GetUserInputAsync(ulong userId, ulong channelId)
 | 
					        public async Task<string> GetUserInputAsync(ulong userId, ulong channelId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var userInputTask = new TaskCompletionSource<string>();
 | 
					            var userInputTask = new TaskCompletionSource<string>();
 | 
				
			||||||
            var dsc = (DiscordShardedClient)Context.Client;
 | 
					            var dsc = (DiscordSocketClient)Context.Client;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                dsc.MessageReceived += MessageReceived;
 | 
					                dsc.MessageReceived += MessageReceived;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,7 +195,7 @@ namespace NadekoBot.Modules.Permissions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                var fws = fwHash.ToArray();
 | 
					                var fws = fwHash.ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await channel.SendPaginatedConfirmAsync((DiscordShardedClient)Context.Client,
 | 
					                await channel.SendPaginatedConfirmAsync((DiscordSocketClient)Context.Client,
 | 
				
			||||||
                    page,
 | 
					                    page,
 | 
				
			||||||
                    (curPage) =>
 | 
					                    (curPage) =>
 | 
				
			||||||
                        new EmbedBuilder()
 | 
					                        new EmbedBuilder()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly CommandMapService _service;
 | 
					            private readonly CommandMapService _service;
 | 
				
			||||||
            private readonly DbService _db;
 | 
					            private readonly DbService _db;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public CommandMapCommands(CommandMapService service, DbService db, DiscordShardedClient client)
 | 
					            public CommandMapCommands(CommandMapService service, DbService db, DiscordSocketClient client)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _service = service;
 | 
					                _service = service;
 | 
				
			||||||
                _db = db;
 | 
					                _db = db;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,64 +0,0 @@
 | 
				
			|||||||
using Discord;
 | 
					 | 
				
			||||||
using Discord.Commands;
 | 
					 | 
				
			||||||
using NadekoBot.Attributes;
 | 
					 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					 | 
				
			||||||
using NadekoBot.Services;
 | 
					 | 
				
			||||||
using NadekoBot.Services.Utility;
 | 
					 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace NadekoBot.Modules.Utility
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public partial class Utility
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        [Group]
 | 
					 | 
				
			||||||
        public class CrossServerTextChannel : NadekoSubmodule
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            private readonly CrossServerTextService _service;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public CrossServerTextChannel(CrossServerTextService service)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _service = service;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
            [OwnerOnly]
 | 
					 | 
				
			||||||
            public async Task Scsc()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var token = new NadekoRandom().Next();
 | 
					 | 
				
			||||||
                var set = new ConcurrentHashSet<ITextChannel>();
 | 
					 | 
				
			||||||
                if (_service.Subscribers.TryAdd(token, set))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    set.Add((ITextChannel) Context.Channel);
 | 
					 | 
				
			||||||
                    await ((IGuildUser) Context.User).SendConfirmAsync(GetText("csc_token"), token.ToString())
 | 
					 | 
				
			||||||
                        .ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
            [RequireUserPermission(GuildPermission.ManageGuild)]
 | 
					 | 
				
			||||||
            public async Task Jcsc(int token)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ConcurrentHashSet<ITextChannel> set;
 | 
					 | 
				
			||||||
                if (!_service.Subscribers.TryGetValue(token, out set))
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                set.Add((ITextChannel) Context.Channel);
 | 
					 | 
				
			||||||
                await ReplyConfirmLocalized("csc_join").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
            [RequireUserPermission(GuildPermission.ManageGuild)]
 | 
					 | 
				
			||||||
            public async Task Lcsc()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                foreach (var subscriber in _service.Subscribers)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    subscriber.Value.TryRemove((ITextChannel) Context.Channel);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                await ReplyConfirmLocalized("csc_leave").ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -16,10 +16,10 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
        [Group]
 | 
					        [Group]
 | 
				
			||||||
        public class InfoCommands : NadekoSubmodule
 | 
					        public class InfoCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly IStatsService _stats;
 | 
					            private readonly IStatsService _stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public InfoCommands(DiscordShardedClient client, IStatsService stats, CommandHandler ch)
 | 
					            public InfoCommands(DiscordSocketClient client, IStatsService stats, CommandHandler ch)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
                _stats = stats;
 | 
					                _stats = stats;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,9 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
            [OwnerOnly]
 | 
					            [OwnerOnly]
 | 
				
			||||||
            public async Task PatreonRewardsReload()
 | 
					            public async Task PatreonRewardsReload()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await _patreon.LoadPledges().ConfigureAwait(false);
 | 
					                if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                await _patreon.RefreshPledges(true).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false);
 | 
					                await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -44,6 +46,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
 | 
					                if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (DateTime.UtcNow.Day < 5)
 | 
					                if (DateTime.UtcNow.Day < 5)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await ReplyErrorLocalized("clpa_too_early").ConfigureAwait(false);
 | 
					                    await ReplyErrorLocalized("clpa_too_early").ConfigureAwait(false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            [Priority(1)]
 | 
					            [Priority(0)]
 | 
				
			||||||
            public async Task Remind(MeOrHere meorhere, string timeStr, [Remainder] string message)
 | 
					            public async Task Remind(MeOrHere meorhere, string timeStr, [Remainder] string message)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ulong target;
 | 
					                ulong target;
 | 
				
			||||||
@@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
            [NadekoCommand, Usage, Description, Aliases]
 | 
					            [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
            [RequireContext(ContextType.Guild)]
 | 
					            [RequireContext(ContextType.Guild)]
 | 
				
			||||||
            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
					            [RequireUserPermission(GuildPermission.ManageMessages)]
 | 
				
			||||||
            [Priority(0)]
 | 
					            [Priority(1)]
 | 
				
			||||||
            public async Task Remind(ITextChannel channel, string timeStr, [Remainder] string message)
 | 
					            public async Task Remind(ITextChannel channel, string timeStr, [Remainder] string message)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var perms = ((IGuildUser)Context.User).GetPermissions((ITextChannel)channel);
 | 
					                var perms = ((IGuildUser)Context.User).GetPermissions((ITextChannel)channel);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,10 +22,10 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
        public class RepeatCommands : NadekoSubmodule
 | 
					        public class RepeatCommands : NadekoSubmodule
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private readonly MessageRepeaterService _service;
 | 
					            private readonly MessageRepeaterService _service;
 | 
				
			||||||
            private readonly DiscordShardedClient _client;
 | 
					            private readonly DiscordSocketClient _client;
 | 
				
			||||||
            private readonly DbService _db;
 | 
					            private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public RepeatCommands(MessageRepeaterService service, DiscordShardedClient client, DbService db)
 | 
					            public RepeatCommands(MessageRepeaterService service, DiscordSocketClient client, DbService db)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _service = service;
 | 
					                _service = service;
 | 
				
			||||||
                _client = client;
 | 
					                _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ using NadekoBot.Services.Utility;
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace NadekoBot.Modules.Utility
 | 
					namespace NadekoBot.Modules.Utility
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public partial class Utility
 | 
					    public partial class Utility
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,100 +18,26 @@ using Discord.WebSocket;
 | 
				
			|||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
using Color = Discord.Color;
 | 
					using Color = Discord.Color;
 | 
				
			||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
 | 
					using NadekoBot.DataStructures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Modules.Utility
 | 
					namespace NadekoBot.Modules.Utility
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public partial class Utility : NadekoTopLevelModule
 | 
					    public partial class Utility : NadekoTopLevelModule
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private static ConcurrentDictionary<ulong, Timer> _rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
 | 
					        private static ConcurrentDictionary<ulong, Timer> _rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly IStatsService _stats;
 | 
					        private readonly IStatsService _stats;
 | 
				
			||||||
        private readonly IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
 | 
					        private readonly NadekoBot _bot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Utility(DiscordShardedClient client, IStatsService stats, IBotCredentials creds)
 | 
					        public Utility(NadekoBot bot, DiscordSocketClient client, IStatsService stats, IBotCredentials creds)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _stats = stats;
 | 
					            _stats = stats;
 | 
				
			||||||
            _creds = creds;
 | 
					            _creds = creds;
 | 
				
			||||||
 | 
					            _bot = bot;
 | 
				
			||||||
        }        
 | 
					        }        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //[NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
        //[RequireContext(ContextType.Guild)]
 | 
					 | 
				
			||||||
        //public async Task Midorina([Remainder] string arg)
 | 
					 | 
				
			||||||
        //{
 | 
					 | 
				
			||||||
        //    var channel = (ITextChannel)Context.Channel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    var roleNames = arg?.Split(';');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    if (roleNames == null || roleNames.Length == 0)
 | 
					 | 
				
			||||||
        //        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    var j = 0;
 | 
					 | 
				
			||||||
        //    var roles = roleNames.Select(x => Context.Guild.Roles.FirstOrDefault(r => String.Compare(r.Name, x, StringComparison.OrdinalIgnoreCase) == 0))
 | 
					 | 
				
			||||||
        //            .Where(x => x != null)
 | 
					 | 
				
			||||||
        //            .Take(10)
 | 
					 | 
				
			||||||
        //            .ToArray();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    var rnd = new NadekoRandom();
 | 
					 | 
				
			||||||
        //    var reactions = new[] { "🎬", "🐧", "🌍", "🌺", "🚀", "☀", "🌲", "🍒", "🐾", "🏀" }
 | 
					 | 
				
			||||||
        //        .OrderBy(x => rnd.Next())
 | 
					 | 
				
			||||||
        //        .ToArray();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    var roleStrings = roles
 | 
					 | 
				
			||||||
        //            .Select(x => $"{reactions[j++]} -> {x.Name}");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    var msg = await Context.Channel.SendConfirmAsync("Pick a Role",
 | 
					 | 
				
			||||||
        //        string.Join("\n", roleStrings)).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    for (int i = 0; i < roles.Length; i++)
 | 
					 | 
				
			||||||
        //    {
 | 
					 | 
				
			||||||
        //        try { await msg.AddReactionAsync(reactions[i]).ConfigureAwait(false); }
 | 
					 | 
				
			||||||
        //        catch (Exception ex) { _log.Warn(ex); }
 | 
					 | 
				
			||||||
        //        await Task.Delay(1000).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        //    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //    msg.OnReaction((r) => Task.Run(async () =>
 | 
					 | 
				
			||||||
        //    {
 | 
					 | 
				
			||||||
        //        try
 | 
					 | 
				
			||||||
        //        {
 | 
					 | 
				
			||||||
        //            var usr = r.User.GetValueOrDefault() as IGuildUser;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //            if (usr == null)
 | 
					 | 
				
			||||||
        //                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //            var index = Array.IndexOf<string>(reactions, r.Emoji.Name);
 | 
					 | 
				
			||||||
        //            if (index == -1)
 | 
					 | 
				
			||||||
        //                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //            await usr.RemoveRolesAsync(roles[index]);
 | 
					 | 
				
			||||||
        //        }
 | 
					 | 
				
			||||||
        //        catch (Exception ex)
 | 
					 | 
				
			||||||
        //        {
 | 
					 | 
				
			||||||
        //            _log.Warn(ex);
 | 
					 | 
				
			||||||
        //        }
 | 
					 | 
				
			||||||
        //    }), (r) => Task.Run(async () =>
 | 
					 | 
				
			||||||
        //    {
 | 
					 | 
				
			||||||
        //        try
 | 
					 | 
				
			||||||
        //        {
 | 
					 | 
				
			||||||
        //            var usr = r.User.GetValueOrDefault() as IGuildUser;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //            if (usr == null)
 | 
					 | 
				
			||||||
        //                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //            var index = Array.IndexOf<string>(reactions, r.Emoji.Name);
 | 
					 | 
				
			||||||
        //            if (index == -1)
 | 
					 | 
				
			||||||
        //                return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //            await usr.RemoveRolesAsync(roles[index]);
 | 
					 | 
				
			||||||
        //        }
 | 
					 | 
				
			||||||
        //        catch (Exception ex)
 | 
					 | 
				
			||||||
        //        {
 | 
					 | 
				
			||||||
        //            _log.Warn(ex);
 | 
					 | 
				
			||||||
        //        }
 | 
					 | 
				
			||||||
        //    }));
 | 
					 | 
				
			||||||
        //}
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        [RequireContext(ContextType.Guild)]
 | 
					        [RequireContext(ContextType.Guild)]
 | 
				
			||||||
        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
					        [RequireUserPermission(GuildPermission.ManageRoles)]
 | 
				
			||||||
@@ -354,23 +280,25 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 | 
					        [Shard0Precondition]
 | 
				
			||||||
        public async Task ShardStats(int page = 1)
 | 
					        public async Task ShardStats(int page = 1)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (--page < 0)
 | 
					            if (--page < 0)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					            var statuses = _bot.ShardCoord.Statuses.ToArray()
 | 
				
			||||||
 | 
					                .Where(x => x != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var status = string.Join(", ", _client.Shards.GroupBy(x => x.ConnectionState)
 | 
					            var status = string.Join(", ", statuses
 | 
				
			||||||
 | 
					                .GroupBy(x => x.ConnectionState)
 | 
				
			||||||
                .Select(x => $"{x.Count()} {x.Key}")
 | 
					                .Select(x => $"{x.Count()} {x.Key}")
 | 
				
			||||||
                .ToArray());
 | 
					                .ToArray());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var allShardStrings = _client.Shards
 | 
					            var allShardStrings = statuses
 | 
				
			||||||
                .Select(x =>
 | 
					                .Select(x =>
 | 
				
			||||||
                    GetText("shard_stats_txt", x.ShardId.ToString(),
 | 
					                    GetText("shard_stats_txt", x.ShardId.ToString(),
 | 
				
			||||||
                        Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.Count.ToString())))
 | 
					                        Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.ToString())))
 | 
				
			||||||
                .ToArray();
 | 
					                .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) =>
 | 
					            await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -387,21 +315,9 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
            }, allShardStrings.Length / 25);
 | 
					            }, allShardStrings.Length / 25);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					 | 
				
			||||||
        public async Task ShardId(IGuild guild)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var shardId = _client.GetShardIdFor(guild);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await Context.Channel.SendConfirmAsync(shardId.ToString()).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
        public async Task Stats()
 | 
					        public async Task Stats()
 | 
				
			||||||
        {            
 | 
					        {            
 | 
				
			||||||
            var shardId = Context.Guild != null
 | 
					 | 
				
			||||||
                ? _client.GetShardIdFor(Context.Guild)
 | 
					 | 
				
			||||||
                : 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await Context.Channel.EmbedAsync(
 | 
					            await Context.Channel.EmbedAsync(
 | 
				
			||||||
                new EmbedBuilder().WithOkColor()
 | 
					                new EmbedBuilder().WithOkColor()
 | 
				
			||||||
                    .WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
 | 
					                    .WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
 | 
				
			||||||
@@ -409,7 +325,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                                          .WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
 | 
					                                          .WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("author")).WithValue(_stats.Author).WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("author")).WithValue(_stats.Author).WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("botid")).WithValue(_client.CurrentUser.Id.ToString()).WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("botid")).WithValue(_client.CurrentUser.Id.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("shard")).WithValue($"#{shardId} / {_client.Shards.Count}").WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("shard")).WithValue($"#{_bot.ShardId} / {_creds.TotalShards}").WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("commands_ran")).WithValue(_stats.CommandsRan.ToString()).WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("commands_ran")).WithValue(_stats.CommandsRan.ToString()).WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("messages")).WithValue($"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("messages")).WithValue($"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("memory")).WithValue($"{_stats.Heap} MB").WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("memory")).WithValue($"{_stats.Heap} MB").WithIsInline(true))
 | 
				
			||||||
@@ -417,13 +333,7 @@ namespace NadekoBot.Modules.Utility
 | 
				
			|||||||
                    .AddField(efb => efb.WithName(GetText("uptime")).WithValue(_stats.GetUptimeString("\n")).WithIsInline(true))
 | 
					                    .AddField(efb => efb.WithName(GetText("uptime")).WithValue(_stats.GetUptimeString("\n")).WithIsInline(true))
 | 
				
			||||||
                    .AddField(efb => efb.WithName(GetText("presence")).WithValue(
 | 
					                    .AddField(efb => efb.WithName(GetText("presence")).WithValue(
 | 
				
			||||||
                        GetText("presence_txt",
 | 
					                        GetText("presence_txt",
 | 
				
			||||||
                            _client.Guilds.Count, _stats.TextChannels, _stats.VoiceChannels)).WithIsInline(true))
 | 
					                            _stats.GuildCount, _stats.TextChannels, _stats.VoiceChannels)).WithIsInline(true)));
 | 
				
			||||||
#if !GLOBAL_NADEKO
 | 
					 | 
				
			||||||
                    //.WithFooter(efb => efb.WithText(GetText("stats_songs",
 | 
					 | 
				
			||||||
                    //    _music.MusicPlayers.Count(mp => mp.Value.CurrentSong != null),
 | 
					 | 
				
			||||||
                    //    _music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count))))
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
					        [NadekoCommand, Usage, Description, Aliases]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,6 @@ using Discord.WebSocket;
 | 
				
			|||||||
using NadekoBot.Services;
 | 
					using NadekoBot.Services;
 | 
				
			||||||
using NadekoBot.Services.Impl;
 | 
					using NadekoBot.Services.Impl;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using NLog.Config;
 | 
					 | 
				
			||||||
using NLog.Targets;
 | 
					 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
@@ -27,8 +25,7 @@ using NadekoBot.Services.Utility;
 | 
				
			|||||||
using NadekoBot.Services.Help;
 | 
					using NadekoBot.Services.Help;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using NadekoBot.Services.Pokemon;
 | 
					using NadekoBot.Services.Pokemon;
 | 
				
			||||||
using NadekoBot.DataStructures;
 | 
					using NadekoBot.DataStructures.ShardCom;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot
 | 
					namespace NadekoBot
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -45,47 +42,63 @@ namespace NadekoBot
 | 
				
			|||||||
        public static Color OkColor { get; private set; }
 | 
					        public static Color OkColor { get; private set; }
 | 
				
			||||||
        public static Color ErrorColor { get; private set; }
 | 
					        public static Color ErrorColor { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ImmutableArray<GuildConfig> AllGuildConfigs { get; }
 | 
					        public ImmutableArray<GuildConfig> AllGuildConfigs { get; private set; }
 | 
				
			||||||
        public BotConfig BotConfig { get; }
 | 
					        public BotConfig BotConfig { get; }
 | 
				
			||||||
        public DbService Db { get; }
 | 
					        public DbService Db { get; }
 | 
				
			||||||
        public CommandService CommandService { get; }
 | 
					        public CommandService CommandService { get; }
 | 
				
			||||||
        public CommandHandler CommandHandler { get; private set; }
 | 
					        public CommandHandler CommandHandler { get; private set; }
 | 
				
			||||||
        public Localization Localization { get; }
 | 
					        public Localization Localization { get; private set; }
 | 
				
			||||||
        public NadekoStrings Strings { get; }
 | 
					        public NadekoStrings Strings { get; private set; }
 | 
				
			||||||
        public StatsService Stats { get; }
 | 
					        public StatsService Stats { get; private set; }
 | 
				
			||||||
        public ImagesService Images { get; }
 | 
					        public ImagesService Images { get; }
 | 
				
			||||||
        public CurrencyService Currency { get; }
 | 
					        public CurrencyService Currency { get; }
 | 
				
			||||||
        public GoogleApiService GoogleApi { get; }
 | 
					        public GoogleApiService GoogleApi { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DiscordShardedClient Client { get; }
 | 
					        public DiscordSocketClient Client { get; }
 | 
				
			||||||
        public bool Ready { get; private set; }
 | 
					        public bool Ready { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public INServiceProvider Services { get; private set; }
 | 
					        public INServiceProvider Services { get; private set; }
 | 
				
			||||||
        public BotCredentials Credentials { get; }
 | 
					        public BotCredentials Credentials { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public NadekoBot()
 | 
					        private const string _mutexName = @"Global\nadeko_shards_lock";
 | 
				
			||||||
 | 
					        private readonly Semaphore sem = new Semaphore(1, 1, _mutexName);
 | 
				
			||||||
 | 
					        public int ShardId { get; }
 | 
				
			||||||
 | 
					        public ShardsCoordinator ShardCoord { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly ShardComClient _comClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public NadekoBot(int shardId, int parentProcessId, int? port = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            SetupLogger();
 | 
					            if (shardId < 0)
 | 
				
			||||||
 | 
					                throw new ArgumentOutOfRangeException(nameof(shardId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ShardId = shardId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LogSetup.SetupLogger();
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            TerribleElevatedPermissionCheck();
 | 
					            TerribleElevatedPermissionCheck();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Credentials = new BotCredentials();
 | 
					            Credentials = new BotCredentials();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            port = port ?? Credentials.ShardRunPort;
 | 
				
			||||||
 | 
					            _comClient = new ShardComClient(port.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Db = new DbService(Credentials);
 | 
					            Db = new DbService(Credentials);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var uow = Db.UnitOfWork)
 | 
					            using (var uow = Db.UnitOfWork)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                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));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Client = new DiscordShardedClient(new DiscordSocketConfig
 | 
					            Client = new DiscordSocketClient(new DiscordSocketConfig
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                MessageCacheSize = 10,
 | 
					                MessageCacheSize = 10,
 | 
				
			||||||
                LogLevel = LogSeverity.Warning,
 | 
					                LogLevel = LogSeverity.Warning,
 | 
				
			||||||
                TotalShards = Credentials.TotalShards,
 | 
					 | 
				
			||||||
                ConnectionTimeout = int.MaxValue,
 | 
					                ConnectionTimeout = int.MaxValue,
 | 
				
			||||||
 | 
					                TotalShards = Credentials.TotalShards,
 | 
				
			||||||
 | 
					                ShardId = shardId,
 | 
				
			||||||
                AlwaysDownloadUsers = false,
 | 
					                AlwaysDownloadUsers = false,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,181 +108,244 @@ namespace NadekoBot
 | 
				
			|||||||
                DefaultRunMode = RunMode.Sync,
 | 
					                DefaultRunMode = RunMode.Sync,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            //foundation services
 | 
					 | 
				
			||||||
            Localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db);
 | 
					 | 
				
			||||||
            Strings = new NadekoStrings(Localization);
 | 
					 | 
				
			||||||
            CommandHandler = new CommandHandler(Client, Db, BotConfig, AllGuildConfigs, CommandService, Credentials, this);
 | 
					 | 
				
			||||||
            Stats = new StatsService(Client, CommandHandler, Credentials);
 | 
					 | 
				
			||||||
            Images = new ImagesService();
 | 
					            Images = new ImagesService();
 | 
				
			||||||
            Currency = new CurrencyService(BotConfig, Db);
 | 
					            Currency = new CurrencyService(BotConfig, Db);
 | 
				
			||||||
            GoogleApi = new GoogleApiService(Credentials);
 | 
					            GoogleApi = new GoogleApiService(Credentials);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SetupShard(shardId, parentProcessId, port.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if GLOBAL_NADEKO
 | 
					#if GLOBAL_NADEKO
 | 
				
			||||||
            Client.Log += Client_Log;
 | 
					            Client.Log += Client_Log;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void StartSendingData()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                while (true)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await _comClient.Send(new ShardComMessage()
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ConnectionState = Client.ConnectionState,
 | 
				
			||||||
 | 
					                        Guilds = Client.ConnectionState == ConnectionState.Connected ? Client.Guilds.Count : 0,
 | 
				
			||||||
 | 
					                        ShardId = Client.ShardId,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    await Task.Delay(1000);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddServices()
 | 
					        private void AddServices()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var soundcloudApiService = new SoundCloudApiService(Credentials);
 | 
					            var startingGuildIdList = Client.Guilds.Select(x => (long)x.Id).ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region help
 | 
					            //this unit of work will be used for initialization of all modules too, to prevent multiple queries from running
 | 
				
			||||||
            var helpService = new HelpService(BotConfig, CommandHandler, Strings);
 | 
					            using (var uow = Db.UnitOfWork)
 | 
				
			||||||
            #endregion
 | 
					            {
 | 
				
			||||||
 | 
					                AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
            //module services
 | 
					                Localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db);
 | 
				
			||||||
            //todo 90 - autodiscover, DI, and add instead of manual like this
 | 
					                Strings = new NadekoStrings(Localization);
 | 
				
			||||||
            #region utility
 | 
					                CommandHandler = new CommandHandler(Client, Db, BotConfig, AllGuildConfigs, CommandService, Credentials, this);
 | 
				
			||||||
            var crossServerTextService = new CrossServerTextService(AllGuildConfigs, Client);
 | 
					                Stats = new StatsService(Client, CommandHandler, Credentials, ShardCoord);
 | 
				
			||||||
            var remindService = new RemindService(Client, BotConfig, Db);
 | 
					 | 
				
			||||||
            var repeaterService = new MessageRepeaterService(this, Client, AllGuildConfigs);
 | 
					 | 
				
			||||||
            var converterService = new ConverterService(Db);
 | 
					 | 
				
			||||||
            var commandMapService = new CommandMapService(AllGuildConfigs);
 | 
					 | 
				
			||||||
            var patreonRewardsService = new PatreonRewardsService(Credentials, Db, Currency);
 | 
					 | 
				
			||||||
            var verboseErrorsService = new VerboseErrorsService(AllGuildConfigs, Db, CommandHandler, helpService);
 | 
					 | 
				
			||||||
            var pruneService = new PruneService();
 | 
					 | 
				
			||||||
            #endregion
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region permissions
 | 
					                var soundcloudApiService = new SoundCloudApiService(Credentials);
 | 
				
			||||||
            var permissionsService = new PermissionService(Db, BotConfig, CommandHandler);
 | 
					 | 
				
			||||||
            var blacklistService = new BlacklistService(BotConfig);
 | 
					 | 
				
			||||||
            var cmdcdsService = new CmdCdService(AllGuildConfigs);
 | 
					 | 
				
			||||||
            var filterService = new FilterService(Client, AllGuildConfigs);
 | 
					 | 
				
			||||||
            var globalPermsService = new GlobalPermissionService(BotConfig);
 | 
					 | 
				
			||||||
            #endregion
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region Searches
 | 
					                #region help
 | 
				
			||||||
            var searchesService = new SearchesService(Client, GoogleApi, Db);
 | 
					                var helpService = new HelpService(BotConfig, CommandHandler, Strings);
 | 
				
			||||||
            var streamNotificationService = new StreamNotificationService(Db, Client, Strings);
 | 
					                #endregion
 | 
				
			||||||
            var animeSearchService = new AnimeSearchService();
 | 
					 | 
				
			||||||
            #endregion
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var clashService = new ClashOfClansService(Client, Db, Localization, Strings);
 | 
					                //module services
 | 
				
			||||||
            var musicService = new MusicService(GoogleApi, Strings, Localization, Db, soundcloudApiService, Credentials, AllGuildConfigs);
 | 
					                //todo 90 - autodiscover, DI, and add instead of manual like this
 | 
				
			||||||
            var crService = new CustomReactionsService(permissionsService, Db, Client, CommandHandler, BotConfig);
 | 
					                #region utility
 | 
				
			||||||
 | 
					                var remindService = new RemindService(Client, BotConfig, Db, startingGuildIdList, uow);
 | 
				
			||||||
 | 
					                var repeaterService = new MessageRepeaterService(this, Client, AllGuildConfigs);
 | 
				
			||||||
 | 
					                //var converterService = new ConverterService(Db);
 | 
				
			||||||
 | 
					                var commandMapService = new CommandMapService(AllGuildConfigs);
 | 
				
			||||||
 | 
					                var patreonRewardsService = new PatreonRewardsService(Credentials, Db, Currency, Client);
 | 
				
			||||||
 | 
					                var verboseErrorsService = new VerboseErrorsService(AllGuildConfigs, Db, CommandHandler, helpService);
 | 
				
			||||||
 | 
					                var pruneService = new PruneService();
 | 
				
			||||||
 | 
					                #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region Games
 | 
					                #region permissions
 | 
				
			||||||
            var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, Strings, Images, CommandHandler);
 | 
					                var permissionsService = new PermissionService(Client, Db, BotConfig, CommandHandler, Strings);
 | 
				
			||||||
            var chatterBotService = new ChatterBotService(Client, permissionsService, AllGuildConfigs, CommandHandler);
 | 
					                var blacklistService = new BlacklistService(BotConfig);
 | 
				
			||||||
            var pollService = new PollService(Client, Strings);
 | 
					                var cmdcdsService = new CmdCdService(AllGuildConfigs);
 | 
				
			||||||
            #endregion
 | 
					                var filterService = new FilterService(Client, AllGuildConfigs);
 | 
				
			||||||
 | 
					                var globalPermsService = new GlobalPermissionService(BotConfig);
 | 
				
			||||||
 | 
					                #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region administration
 | 
					                #region Searches
 | 
				
			||||||
            var administrationService = new AdministrationService(AllGuildConfigs, CommandHandler);
 | 
					                var searchesService = new SearchesService(Client, GoogleApi, Db);
 | 
				
			||||||
            var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, Db);
 | 
					                var streamNotificationService = new StreamNotificationService(Db, Client, Strings);
 | 
				
			||||||
            var selfService = new SelfService(Client, this, CommandHandler, Db, BotConfig, Localization, Strings, Credentials);
 | 
					                var animeSearchService = new AnimeSearchService();
 | 
				
			||||||
            var vcRoleService = new VcRoleService(Client, AllGuildConfigs, Db);
 | 
					                #endregion
 | 
				
			||||||
            var vPlusTService = new VplusTService(Client, AllGuildConfigs, Strings, Db);
 | 
					 | 
				
			||||||
            var muteService = new MuteService(Client, AllGuildConfigs, Db);
 | 
					 | 
				
			||||||
            var ratelimitService = new SlowmodeService(Client, AllGuildConfigs);
 | 
					 | 
				
			||||||
            var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService);
 | 
					 | 
				
			||||||
            var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService);
 | 
					 | 
				
			||||||
            var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs);
 | 
					 | 
				
			||||||
            var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs);
 | 
					 | 
				
			||||||
            var logCommandService = new LogCommandService(Client, Strings, AllGuildConfigs, Db, muteService, protectionService);
 | 
					 | 
				
			||||||
            var guildTimezoneService = new GuildTimezoneService(AllGuildConfigs, Db);
 | 
					 | 
				
			||||||
            #endregion
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #region pokemon 
 | 
					                var clashService = new ClashOfClansService(Client, Db, Localization, Strings, uow, startingGuildIdList);
 | 
				
			||||||
            var pokemonService = new PokemonService();
 | 
					                var musicService = new MusicService(GoogleApi, Strings, Localization, Db, soundcloudApiService, Credentials, AllGuildConfigs);
 | 
				
			||||||
            #endregion
 | 
					                var crService = new CustomReactionsService(permissionsService, Db, Strings, Client, CommandHandler, BotConfig, uow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                #region Games
 | 
				
			||||||
 | 
					                var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, Strings, Images, CommandHandler);
 | 
				
			||||||
 | 
					                var chatterBotService = new ChatterBotService(Client, permissionsService, AllGuildConfigs, CommandHandler, Strings);
 | 
				
			||||||
 | 
					                var pollService = new PollService(Client, Strings);
 | 
				
			||||||
 | 
					                #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                #region administration
 | 
				
			||||||
 | 
					                var administrationService = new AdministrationService(AllGuildConfigs, CommandHandler);
 | 
				
			||||||
 | 
					                var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, Db);
 | 
				
			||||||
 | 
					                var selfService = new SelfService(Client, this, CommandHandler, Db, BotConfig, Localization, Strings, Credentials);
 | 
				
			||||||
 | 
					                var vcRoleService = new VcRoleService(Client, AllGuildConfigs, Db);
 | 
				
			||||||
 | 
					                var vPlusTService = new VplusTService(Client, AllGuildConfigs, Strings, Db);
 | 
				
			||||||
 | 
					                var muteService = new MuteService(Client, AllGuildConfigs, Db);
 | 
				
			||||||
 | 
					                var ratelimitService = new SlowmodeService(Client, AllGuildConfigs);
 | 
				
			||||||
 | 
					                var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService);
 | 
				
			||||||
 | 
					                var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService);
 | 
				
			||||||
 | 
					                var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs);
 | 
				
			||||||
 | 
					                var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs);
 | 
				
			||||||
 | 
					                var logCommandService = new LogCommandService(Client, Strings, AllGuildConfigs, Db, muteService, protectionService);
 | 
				
			||||||
 | 
					                var guildTimezoneService = new GuildTimezoneService(AllGuildConfigs, Db);
 | 
				
			||||||
 | 
					                #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                #region pokemon 
 | 
				
			||||||
 | 
					                var pokemonService = new PokemonService();
 | 
				
			||||||
 | 
					                #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //initialize Services
 | 
					 | 
				
			||||||
            Services = new NServiceProvider.ServiceProviderBuilder()
 | 
					 | 
				
			||||||
                .Add<ILocalization>(Localization)
 | 
					 | 
				
			||||||
                .Add<IStatsService>(Stats)
 | 
					 | 
				
			||||||
                .Add<IImagesService>(Images)
 | 
					 | 
				
			||||||
                .Add<IGoogleApiService>(GoogleApi)
 | 
					 | 
				
			||||||
                .Add<IStatsService>(Stats)
 | 
					 | 
				
			||||||
                .Add<IBotCredentials>(Credentials)
 | 
					 | 
				
			||||||
                .Add<CommandService>(CommandService)
 | 
					 | 
				
			||||||
                .Add<NadekoStrings>(Strings)
 | 
					 | 
				
			||||||
                .Add<DiscordShardedClient>(Client)
 | 
					 | 
				
			||||||
                .Add<BotConfig>(BotConfig)
 | 
					 | 
				
			||||||
                .Add<CurrencyService>(Currency)
 | 
					 | 
				
			||||||
                .Add<CommandHandler>(CommandHandler)
 | 
					 | 
				
			||||||
                .Add<DbService>(Db)
 | 
					 | 
				
			||||||
                //modules
 | 
					 | 
				
			||||||
                    .Add(crossServerTextService)
 | 
					 | 
				
			||||||
                    .Add(commandMapService)
 | 
					 | 
				
			||||||
                    .Add(remindService)
 | 
					 | 
				
			||||||
                    .Add(repeaterService)
 | 
					 | 
				
			||||||
                    .Add(converterService)
 | 
					 | 
				
			||||||
                    .Add(verboseErrorsService)
 | 
					 | 
				
			||||||
                    .Add(patreonRewardsService)
 | 
					 | 
				
			||||||
                    .Add(pruneService)
 | 
					 | 
				
			||||||
                .Add<SearchesService>(searchesService)
 | 
					 | 
				
			||||||
                    .Add(streamNotificationService)
 | 
					 | 
				
			||||||
                    .Add(animeSearchService)
 | 
					 | 
				
			||||||
                .Add<ClashOfClansService>(clashService)
 | 
					 | 
				
			||||||
                .Add<MusicService>(musicService)
 | 
					 | 
				
			||||||
                .Add<GreetSettingsService>(greetSettingsService)
 | 
					 | 
				
			||||||
                .Add<CustomReactionsService>(crService)
 | 
					 | 
				
			||||||
                .Add<HelpService>(helpService)
 | 
					 | 
				
			||||||
                .Add<GamesService>(gamesService)
 | 
					 | 
				
			||||||
                    .Add(chatterBotService)
 | 
					 | 
				
			||||||
                    .Add(pollService)
 | 
					 | 
				
			||||||
                .Add<AdministrationService>(administrationService)
 | 
					 | 
				
			||||||
                    .Add(selfService)
 | 
					 | 
				
			||||||
                    .Add(vcRoleService)
 | 
					 | 
				
			||||||
                    .Add(vPlusTService)
 | 
					 | 
				
			||||||
                    .Add(muteService)
 | 
					 | 
				
			||||||
                    .Add(ratelimitService)
 | 
					 | 
				
			||||||
                    .Add(playingRotateService)
 | 
					 | 
				
			||||||
                    .Add(gameVcService)
 | 
					 | 
				
			||||||
                    .Add(autoAssignRoleService)
 | 
					 | 
				
			||||||
                    .Add(protectionService)
 | 
					 | 
				
			||||||
                    .Add(logCommandService)
 | 
					 | 
				
			||||||
                    .Add(guildTimezoneService)
 | 
					 | 
				
			||||||
                .Add<PermissionService>(permissionsService)
 | 
					 | 
				
			||||||
                    .Add(blacklistService)
 | 
					 | 
				
			||||||
                    .Add(cmdcdsService)
 | 
					 | 
				
			||||||
                    .Add(filterService)
 | 
					 | 
				
			||||||
                    .Add(globalPermsService)
 | 
					 | 
				
			||||||
                .Add<PokemonService>(pokemonService)
 | 
					 | 
				
			||||||
                .Build();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            CommandHandler.AddServices(Services);
 | 
					                //initialize Services
 | 
				
			||||||
 | 
					                Services = new NServiceProvider.ServiceProviderBuilder()
 | 
				
			||||||
 | 
					                    .Add<ILocalization>(Localization)
 | 
				
			||||||
 | 
					                    .Add<IStatsService>(Stats)
 | 
				
			||||||
 | 
					                    .Add<IImagesService>(Images)
 | 
				
			||||||
 | 
					                    .Add<IGoogleApiService>(GoogleApi)
 | 
				
			||||||
 | 
					                    .Add<IStatsService>(Stats)
 | 
				
			||||||
 | 
					                    .Add<IBotCredentials>(Credentials)
 | 
				
			||||||
 | 
					                    .Add<CommandService>(CommandService)
 | 
				
			||||||
 | 
					                    .Add<NadekoStrings>(Strings)
 | 
				
			||||||
 | 
					                    .Add<DiscordSocketClient>(Client)
 | 
				
			||||||
 | 
					                    .Add<BotConfig>(BotConfig)
 | 
				
			||||||
 | 
					                    .Add<CurrencyService>(Currency)
 | 
				
			||||||
 | 
					                    .Add<CommandHandler>(CommandHandler)
 | 
				
			||||||
 | 
					                    .Add<DbService>(Db)
 | 
				
			||||||
 | 
					                        //modules
 | 
				
			||||||
 | 
					                        .Add(commandMapService)
 | 
				
			||||||
 | 
					                        .Add(remindService)
 | 
				
			||||||
 | 
					                        .Add(repeaterService)
 | 
				
			||||||
 | 
					                        //.Add(converterService)
 | 
				
			||||||
 | 
					                        .Add(verboseErrorsService)
 | 
				
			||||||
 | 
					                        .Add(patreonRewardsService)
 | 
				
			||||||
 | 
					                        .Add(pruneService)
 | 
				
			||||||
 | 
					                    .Add<SearchesService>(searchesService)
 | 
				
			||||||
 | 
					                        .Add(streamNotificationService)
 | 
				
			||||||
 | 
					                        .Add(animeSearchService)
 | 
				
			||||||
 | 
					                    .Add<ClashOfClansService>(clashService)
 | 
				
			||||||
 | 
					                    .Add<MusicService>(musicService)
 | 
				
			||||||
 | 
					                    .Add<GreetSettingsService>(greetSettingsService)
 | 
				
			||||||
 | 
					                    .Add<CustomReactionsService>(crService)
 | 
				
			||||||
 | 
					                    .Add<HelpService>(helpService)
 | 
				
			||||||
 | 
					                    .Add<GamesService>(gamesService)
 | 
				
			||||||
 | 
					                        .Add(chatterBotService)
 | 
				
			||||||
 | 
					                        .Add(pollService)
 | 
				
			||||||
 | 
					                    .Add<AdministrationService>(administrationService)
 | 
				
			||||||
 | 
					                        .Add(selfService)
 | 
				
			||||||
 | 
					                        .Add(vcRoleService)
 | 
				
			||||||
 | 
					                        .Add(vPlusTService)
 | 
				
			||||||
 | 
					                        .Add(muteService)
 | 
				
			||||||
 | 
					                        .Add(ratelimitService)
 | 
				
			||||||
 | 
					                        .Add(playingRotateService)
 | 
				
			||||||
 | 
					                        .Add(gameVcService)
 | 
				
			||||||
 | 
					                        .Add(autoAssignRoleService)
 | 
				
			||||||
 | 
					                        .Add(protectionService)
 | 
				
			||||||
 | 
					                        .Add(logCommandService)
 | 
				
			||||||
 | 
					                        .Add(guildTimezoneService)
 | 
				
			||||||
 | 
					                    .Add<PermissionService>(permissionsService)
 | 
				
			||||||
 | 
					                        .Add(blacklistService)
 | 
				
			||||||
 | 
					                        .Add(cmdcdsService)
 | 
				
			||||||
 | 
					                        .Add(filterService)
 | 
				
			||||||
 | 
					                        .Add(globalPermsService)
 | 
				
			||||||
 | 
					                    .Add<PokemonService>(pokemonService)
 | 
				
			||||||
 | 
					                    .Add<NadekoBot>(this)
 | 
				
			||||||
 | 
					                    .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //setup typereaders
 | 
					
 | 
				
			||||||
            CommandService.AddTypeReader<PermissionAction>(new PermissionActionTypeReader());
 | 
					                CommandHandler.AddServices(Services);
 | 
				
			||||||
            CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader(CommandService, CommandHandler));
 | 
					
 | 
				
			||||||
            CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader(crService, CommandService, CommandHandler));
 | 
					                //setup typereaders
 | 
				
			||||||
            CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader(CommandService));
 | 
					                CommandService.AddTypeReader<PermissionAction>(new PermissionActionTypeReader());
 | 
				
			||||||
            CommandService.AddTypeReader<ModuleOrCrInfo>(new ModuleOrCrTypeReader(CommandService));
 | 
					                CommandService.AddTypeReader<CommandInfo>(new CommandTypeReader(CommandService, CommandHandler));
 | 
				
			||||||
            CommandService.AddTypeReader<IGuild>(new GuildTypeReader(Client));
 | 
					                CommandService.AddTypeReader<CommandOrCrInfo>(new CommandOrCrTypeReader(crService, CommandService, CommandHandler));
 | 
				
			||||||
            CommandService.AddTypeReader<GuildDateTime>(new GuildDateTimeTypeReader(guildTimezoneService));
 | 
					                CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader(CommandService));
 | 
				
			||||||
 | 
					                CommandService.AddTypeReader<ModuleOrCrInfo>(new ModuleOrCrTypeReader(CommandService));
 | 
				
			||||||
 | 
					                CommandService.AddTypeReader<IGuild>(new GuildTypeReader(Client));
 | 
				
			||||||
 | 
					                CommandService.AddTypeReader<GuildDateTime>(new GuildDateTimeTypeReader(guildTimezoneService));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task LoginAsync(string token)
 | 
					        private async Task LoginAsync(string token)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log.Info("Logging in...");
 | 
					            var clientReady = new TaskCompletionSource<bool>();
 | 
				
			||||||
            //connect
 | 
					 | 
				
			||||||
            await Client.LoginAsync(TokenType.Bot, token).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            await Client.StartAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _log.Info("Waiting for all shards to connect...");
 | 
					            Task SetClientReady()
 | 
				
			||||||
            while (!Client.Shards.All(x => x.ConnectionState == ConnectionState.Connected))
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log.Info("Connecting... {0}/{1}", Client.Shards.Count(x => x.ConnectionState == ConnectionState.Connected), Client.Shards.Count);
 | 
					                var _ = Task.Run(async () =>
 | 
				
			||||||
                await Task.Delay(1000).ConfigureAwait(false);
 | 
					                {
 | 
				
			||||||
 | 
					                    clientReady.TrySetResult(true);
 | 
				
			||||||
 | 
					                    try
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        foreach (var chan in (await Client.GetDMChannelsAsync()))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await chan.CloseAsync().ConfigureAwait(false);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // ignored
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    finally
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //connect
 | 
				
			||||||
 | 
					            try { sem.WaitOne(); } catch (AbandonedMutexException) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _log.Info("Shard {0} logging in ...", ShardId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await Client.LoginAsync(TokenType.Bot, token).ConfigureAwait(false);
 | 
				
			||||||
 | 
					                await Client.StartAsync().ConfigureAwait(false);
 | 
				
			||||||
 | 
					                Client.Ready += SetClientReady;
 | 
				
			||||||
 | 
					                await clientReady.Task.ConfigureAwait(false);
 | 
				
			||||||
 | 
					                Client.Ready -= SetClientReady;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            finally
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _log.Info("Shard {0} logged in.", ShardId);
 | 
				
			||||||
 | 
					                sem.Release();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task RunAsync(params string[] args)
 | 
					        public async Task RunAsync(params string[] args)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if(ShardId == 0)
 | 
				
			||||||
            _log.Info("Starting NadekoBot v" + StatsService.BotVersion);
 | 
					            _log.Info("Starting NadekoBot v" + StatsService.BotVersion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var sw = Stopwatch.StartNew();
 | 
					            var sw = Stopwatch.StartNew();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await LoginAsync(Credentials.Token).ConfigureAwait(false);
 | 
					            await LoginAsync(Credentials.Token).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _log.Info("Loading services...");
 | 
					            _log.Info($"Shard {ShardId} loading services...");
 | 
				
			||||||
            AddServices();
 | 
					            AddServices();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            sw.Stop();
 | 
					            sw.Stop();
 | 
				
			||||||
            _log.Info($"Connected in {sw.Elapsed.TotalSeconds:F2} s");
 | 
					            _log.Info($"Shard {ShardId} connected in {sw.Elapsed.TotalSeconds:F2}s");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var stats = Services.GetService<IStatsService>();
 | 
					            var stats = Services.GetService<IStatsService>();
 | 
				
			||||||
            stats.Initialize();
 | 
					            stats.Initialize();
 | 
				
			||||||
@@ -297,8 +373,10 @@ namespace NadekoBot
 | 
				
			|||||||
                .Where(x => x.Preconditions.Any(y => y.GetType() == typeof(NoPublicBot)))
 | 
					                .Where(x => x.Preconditions.Any(y => y.GetType() == typeof(NoPublicBot)))
 | 
				
			||||||
                .ForEach(x => CommandService.RemoveModuleAsync(x));
 | 
					                .ForEach(x => CommandService.RemoveModuleAsync(x));
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ready = true;
 | 
					            Ready = true;
 | 
				
			||||||
            _log.Info(await stats.Print().ConfigureAwait(false));
 | 
					            _log.Info($"Shard {ShardId} ready.");
 | 
				
			||||||
 | 
					            //_log.Info(await stats.Print().ConfigureAwait(false));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Task Client_Log(LogMessage arg)
 | 
					        private Task Client_Log(LogMessage arg)
 | 
				
			||||||
@@ -313,7 +391,13 @@ namespace NadekoBot
 | 
				
			|||||||
        public async Task RunAndBlockAsync(params string[] args)
 | 
					        public async Task RunAndBlockAsync(params string[] args)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await RunAsync(args).ConfigureAwait(false);
 | 
					            await RunAsync(args).ConfigureAwait(false);
 | 
				
			||||||
            await Task.Delay(-1).ConfigureAwait(false);
 | 
					            StartSendingData();
 | 
				
			||||||
 | 
					            if (ShardCoord != null)
 | 
				
			||||||
 | 
					                await ShardCoord.RunAndBlockAsync();
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await Task.Delay(-1).ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void TerribleElevatedPermissionCheck()
 | 
					        private void TerribleElevatedPermissionCheck()
 | 
				
			||||||
@@ -331,18 +415,29 @@ namespace NadekoBot
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static void SetupLogger()
 | 
					        private void SetupShard(int shardId, int parentProcessId, int port)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var logConfig = new LoggingConfiguration();
 | 
					            if (shardId != 0)
 | 
				
			||||||
            var consoleTarget = new ColoredConsoleTarget()
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Layout = @"${date:format=HH\:mm\:ss} ${logger} | ${message}"
 | 
					                new Thread(new ThreadStart(() =>
 | 
				
			||||||
            };
 | 
					                {
 | 
				
			||||||
            logConfig.AddTarget("Console", consoleTarget);
 | 
					                    try
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
            logConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, consoleTarget));
 | 
					                        var p = Process.GetProcessById(parentProcessId);
 | 
				
			||||||
 | 
					                        if (p == null)
 | 
				
			||||||
            LogManager.Configuration = logConfig;
 | 
					                            return;
 | 
				
			||||||
 | 
					                        p.WaitForExit();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    finally
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Environment.Exit(10);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                })).Start();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ShardCoord = new ShardsCoordinator(port);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,5 +90,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <Folder Include="Modules\Music\Classes\" />
 | 
					    <Folder Include="Modules\Music\Classes\" />
 | 
				
			||||||
 | 
					    <Folder Include="Utility\Services\" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,17 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class Program
 | 
					    public class Program
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static void Main(string[] args) => 
 | 
					        public static void Main(string[] args)
 | 
				
			||||||
            new NadekoBot().RunAndBlockAsync(args).GetAwaiter().GetResult();
 | 
					        {
 | 
				
			||||||
 | 
					            if (args.Length == 3 && int.TryParse(args[0], out int shardId) && int.TryParse(args[1], out int parentProcessId))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                int? port = null;
 | 
				
			||||||
 | 
					                if (int.TryParse(args[2], out var outPort))
 | 
				
			||||||
 | 
					                    port = outPort;
 | 
				
			||||||
 | 
					                new NadekoBot(shardId, parentProcessId, outPort).RunAndBlockAsync(args).GetAwaiter().GetResult();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                new NadekoBot(0, 0).RunAndBlockAsync(args).GetAwaiter().GetResult();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								src/NadekoBot/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/NadekoBot/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "profiles": {
 | 
				
			||||||
 | 
					    "NadekoBot": {
 | 
				
			||||||
 | 
					      "commandName": "Project"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3096,14 +3096,14 @@
 | 
				
			|||||||
  <data name="shardstats_usage" xml:space="preserve">
 | 
					  <data name="shardstats_usage" xml:space="preserve">
 | 
				
			||||||
    <value>`{0}shardstats` or `{0}shardstats 2`</value>
 | 
					    <value>`{0}shardstats` or `{0}shardstats 2`</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
  <data name="connectshard_cmd" xml:space="preserve">
 | 
					  <data name="restartshard_cmd" xml:space="preserve">
 | 
				
			||||||
    <value>connectshard</value>
 | 
					    <value>restartshard</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
  <data name="connectshard_desc" xml:space="preserve">
 | 
					  <data name="restartshard_desc" xml:space="preserve">
 | 
				
			||||||
    <value>Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors.</value>
 | 
					    <value>Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors.</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
  <data name="connectshard_usage" xml:space="preserve">
 | 
					  <data name="restartshard_usage" xml:space="preserve">
 | 
				
			||||||
    <value>`{0}connectshard 2`</value>
 | 
					    <value>`{0}restartshard 2`</value>
 | 
				
			||||||
  </data>
 | 
					  </data>
 | 
				
			||||||
  <data name="shardid_cmd" xml:space="preserve">
 | 
					  <data name="shardid_cmd" xml:space="preserve">
 | 
				
			||||||
    <value>shardid</value>
 | 
					    <value>shardid</value>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,12 +12,12 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
    public class AutoAssignRoleService
 | 
					    public class AutoAssignRoleService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //guildid/roleid
 | 
					        //guildid/roleid
 | 
				
			||||||
        public ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
 | 
					        public ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public AutoAssignRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
 | 
					        public AutoAssignRoleService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,9 +16,9 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public GameVoiceChannelService(DiscordShardedClient client, DbService db, IEnumerable<GuildConfig> gcs)
 | 
					        public GameVoiceChannelService(DiscordSocketClient client, DbService db, IEnumerable<GuildConfig> gcs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
    public class LogCommandService
 | 
					    public class LogCommandService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string PrettyCurrentTime => $"【{DateTime.UtcNow:HH:mm:ss}】";
 | 
					        private string PrettyCurrentTime => $"【{DateTime.UtcNow:HH:mm:ss}】";
 | 
				
			||||||
@@ -31,7 +31,7 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        private readonly MuteService _mute;
 | 
					        private readonly MuteService _mute;
 | 
				
			||||||
        private readonly ProtectionService _prot;
 | 
					        private readonly ProtectionService _prot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public LogCommandService(DiscordShardedClient client, NadekoStrings strings,
 | 
					        public LogCommandService(DiscordSocketClient client, NadekoStrings strings,
 | 
				
			||||||
            IEnumerable<GuildConfig> gcs, DbService db, MuteService mute, ProtectionService prot)
 | 
					            IEnumerable<GuildConfig> gcs, DbService db, MuteService mute, ProtectionService prot)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
@@ -74,7 +74,7 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            _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;
 | 
				
			||||||
@@ -576,48 +576,48 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
 | 
					        //private Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
 | 
				
			||||||
        {
 | 
					        //{
 | 
				
			||||||
            var _ = Task.Run(async () =>
 | 
					        //    var _ = Task.Run(async () =>
 | 
				
			||||||
            {
 | 
					        //    {
 | 
				
			||||||
                try
 | 
					        //        try
 | 
				
			||||||
                {
 | 
					        //        {
 | 
				
			||||||
                    var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild;
 | 
					        //            var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (guild == null)
 | 
					        //            if (guild == null)
 | 
				
			||||||
                        return;
 | 
					        //                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting)
 | 
					        //            if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting)
 | 
				
			||||||
                        || (logSetting.LogUserPresenceId == null)
 | 
					        //                || (logSetting.LogUserPresenceId == null)
 | 
				
			||||||
                        || before.Status == after.Status)
 | 
					        //                || before.Status == after.Status)
 | 
				
			||||||
                        return;
 | 
					        //                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    ITextChannel logChannel;
 | 
					        //            ITextChannel logChannel;
 | 
				
			||||||
                    if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) == null)
 | 
					        //            if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) == null)
 | 
				
			||||||
                        return;
 | 
					        //                return;
 | 
				
			||||||
                    string str = "";
 | 
					        //            string str = "";
 | 
				
			||||||
                    if (before.Status != after.Status)
 | 
					        //            if (before.Status != after.Status)
 | 
				
			||||||
                        str = "🎭" + Format.Code(PrettyCurrentTime) +
 | 
					        //                str = "🎭" + Format.Code(PrettyCurrentTime) +
 | 
				
			||||||
                              GetText(logChannel.Guild, "user_status_change",
 | 
					        //                      GetText(logChannel.Guild, "user_status_change",
 | 
				
			||||||
                                    "👤" + Format.Bold(usr.Username),
 | 
					        //                            "👤" + Format.Bold(usr.Username),
 | 
				
			||||||
                                    Format.Bold(after.Status.ToString()));
 | 
					        //                            Format.Bold(after.Status.ToString()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    //if (before.Game?.Name != after.Game?.Name)
 | 
					        //            //if (before.Game?.Name != after.Game?.Name)
 | 
				
			||||||
                    //{
 | 
					        //            //{
 | 
				
			||||||
                    //    if (str != "")
 | 
					        //            //    if (str != "")
 | 
				
			||||||
                    //        str += "\n";
 | 
					        //            //        str += "\n";
 | 
				
			||||||
                    //    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
 | 
					        //        catch
 | 
				
			||||||
                {
 | 
					        //        {
 | 
				
			||||||
                    // ignored
 | 
					        //            // ignored
 | 
				
			||||||
                }
 | 
					        //        }
 | 
				
			||||||
            });
 | 
					        //    });
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					        //    return Task.CompletedTask;
 | 
				
			||||||
        }
 | 
					        //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Task _client_UserLeft(IGuildUser usr)
 | 
					        private Task _client_UserLeft(IGuildUser usr)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,10 +33,10 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        private static readonly OverwritePermissions denyOverwrite = new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny);
 | 
					        private static readonly OverwritePermissions denyOverwrite = new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Logger _log = LogManager.GetCurrentClassLogger();
 | 
					        private readonly Logger _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MuteService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbService db)
 | 
					        public MuteService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, DbService db)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        public List<PlayingStatus> RotatingStatusMessages { get; }
 | 
					        public List<PlayingStatus> RotatingStatusMessages { get; }
 | 
				
			||||||
        public volatile bool RotatingStatuses;
 | 
					        public volatile bool RotatingStatuses;
 | 
				
			||||||
        private readonly Timer _t;
 | 
					        private readonly Timer _t;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly BotConfig _bc;
 | 
					        private readonly BotConfig _bc;
 | 
				
			||||||
        private readonly MusicService _music;
 | 
					        private readonly MusicService _music;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
@@ -27,7 +27,7 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            public int Index { get; set; }
 | 
					            public int Index { get; set; }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public PlayingRotateService(DiscordShardedClient client, BotConfig bc, MusicService music)
 | 
					        public PlayingRotateService(DiscordSocketClient client, BotConfig bc, MusicService music)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _bc = bc;
 | 
					            _bc = bc;
 | 
				
			||||||
@@ -52,17 +52,12 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
                    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(_client,_music)));
 | 
					                    PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(_client, _music)));
 | 
				
			||||||
                    var shards = _client.Shards;
 | 
					                    ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(client)));
 | 
				
			||||||
                    for (int i = 0; i < shards.Count; i++)
 | 
					                    try { await client.SetGameAsync(status).ConfigureAwait(false); }
 | 
				
			||||||
 | 
					                    catch (Exception ex)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var curShard = shards.ElementAt(i);
 | 
					                        _log.Warn(ex);
 | 
				
			||||||
                        ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
 | 
					 | 
				
			||||||
                        try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
 | 
					 | 
				
			||||||
                        catch (Exception ex)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            _log.Warn(ex);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
@@ -72,8 +67,8 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            }, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
 | 
					            }, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Dictionary<string, Func<DiscordShardedClient, MusicService, string>> PlayingPlaceholders { get; } =
 | 
					        public Dictionary<string, Func<DiscordSocketClient, MusicService, string>> PlayingPlaceholders { get; } =
 | 
				
			||||||
            new Dictionary<string, Func<DiscordShardedClient, MusicService, string>> {
 | 
					            new Dictionary<string, Func<DiscordSocketClient, MusicService, string>> {
 | 
				
			||||||
                    { "%servers%", (c, ms) => c.Guilds.Count.ToString()},
 | 
					                    { "%servers%", (c, ms) => c.Guilds.Count.ToString()},
 | 
				
			||||||
                    { "%users%", (c, ms) => c.Guilds.Sum(s => s.Users.Count).ToString()},
 | 
					                    { "%users%", (c, ms) => c.Guilds.Sum(s => s.Users.Count).ToString()},
 | 
				
			||||||
                    { "%playing%", (c, ms) => {
 | 
					                    { "%playing%", (c, ms) => {
 | 
				
			||||||
@@ -90,7 +85,6 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    { "%queued%", (c, ms) => ms.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
 | 
					                    { "%queued%", (c, ms) => ms.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
 | 
				
			||||||
                    { "%time%", (c, ms) => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
 | 
					                    { "%time%", (c, ms) => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
 | 
				
			||||||
                    { "%shardcount%", (c, ms) => c.Shards.Count.ToString() },
 | 
					 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
 | 
					        public Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,10 +21,10 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate { return Task.CompletedTask; };
 | 
					        public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate { return Task.CompletedTask; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly MuteService _mute;
 | 
					        private readonly MuteService _mute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ProtectionService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, MuteService mute)
 | 
					        public ProtectionService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, MuteService mute)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,9 +19,9 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
 | 
					        public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public SlowmodeService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
 | 
					        public SlowmodeService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,11 +23,11 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly ILocalization _localization;
 | 
					        private readonly ILocalization _localization;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
        private ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels = new ImmutableArray<AsyncLazy<IDMChannel>>();
 | 
					        private ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels = new ImmutableArray<AsyncLazy<IDMChannel>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public SelfService(DiscordShardedClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
 | 
					        public SelfService(DiscordSocketClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
 | 
				
			||||||
            BotConfig bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds)
 | 
					            BotConfig bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _bot = bot;
 | 
					            _bot = bot;
 | 
				
			||||||
@@ -39,12 +39,8 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _creds = creds;
 | 
					            _creds = creds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            ForwardDMs = bc.ForwardMessages;
 | 
				
			||||||
            {
 | 
					            ForwardDMsToAllOwners = bc.ForwardToAllOwners;
 | 
				
			||||||
                var config = uow.BotConfig.GetOrCreate();
 | 
					 | 
				
			||||||
                ForwardDMs = config.ForwardMessages;
 | 
					 | 
				
			||||||
                ForwardDMsToAllOwners = config.ForwardToAllOwners;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var _ = Task.Run(async () =>
 | 
					            var _ = Task.Run(async () =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -67,12 +63,8 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                _client.Guilds.SelectMany(g => g.Users);
 | 
					                _client.Guilds.SelectMany(g => g.Users);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                LoadOwnerChannels();
 | 
					                if(client.ShardId == 0)
 | 
				
			||||||
 | 
					                    LoadOwnerChannels();                
 | 
				
			||||||
                if (!ownerChannels.Any())
 | 
					 | 
				
			||||||
                    _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    _log.Info($"Created {ownerChannels.Length} out of {_creds.OwnerIds.Length} owner message channels.");
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,11 +73,9 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            var hs = new HashSet<ulong>(_creds.OwnerIds);
 | 
					            var hs = new HashSet<ulong>(_creds.OwnerIds);
 | 
				
			||||||
            var channels = new Dictionary<ulong, AsyncLazy<IDMChannel>>();
 | 
					            var channels = new Dictionary<ulong, AsyncLazy<IDMChannel>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var s in _client.Shards)
 | 
					            if (hs.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (hs.Count == 0)
 | 
					                foreach (var g in _client.Guilds)
 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                foreach (var g in s.Guilds)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (hs.Count == 0)
 | 
					                    if (hs.Count == 0)
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
@@ -105,10 +95,15 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
            ownerChannels = channels.OrderBy(x => _creds.OwnerIds.IndexOf(x.Key))
 | 
					            ownerChannels = channels.OrderBy(x => _creds.OwnerIds.IndexOf(x.Key))
 | 
				
			||||||
                    .Select(x => x.Value)
 | 
					                    .Select(x => x.Value)
 | 
				
			||||||
                    .ToImmutableArray();
 | 
					                    .ToImmutableArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!ownerChannels.Any())
 | 
				
			||||||
 | 
					                _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                _log.Info($"Created {ownerChannels.Length} out of {_creds.OwnerIds.Length} owner message channels.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // forwards dms
 | 
					        // forwards dms
 | 
				
			||||||
        public async Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg)
 | 
					        public async Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (msg.Channel is IDMChannel && ForwardDMs && ownerChannels.Length > 0)
 | 
					            if (msg.Channel is IDMChannel && ForwardDMs && ownerChannels.Length > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,11 +14,11 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
 | 
					        public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public VcRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbService db)
 | 
					        public VcRoleService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, DbService db)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,12 +20,12 @@ namespace NadekoBot.Services.Administration
 | 
				
			|||||||
        public readonly ConcurrentHashSet<ulong> VoicePlusTextCache;
 | 
					        public readonly ConcurrentHashSet<ulong> VoicePlusTextCache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
 | 
					        private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public VplusTService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings,
 | 
					        public VplusTService(DiscordSocketClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings,
 | 
				
			||||||
            DbService db)
 | 
					            DbService db)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
@@ -17,7 +18,7 @@ namespace NadekoBot.Services.ClashOfClans
 | 
				
			|||||||
    // shouldn't be here
 | 
					    // shouldn't be here
 | 
				
			||||||
    public class ClashOfClansService
 | 
					    public class ClashOfClansService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly ILocalization _localization;
 | 
					        private readonly ILocalization _localization;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
@@ -25,28 +26,27 @@ namespace NadekoBot.Services.ClashOfClans
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
 | 
					        public ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ClashOfClansService(DiscordShardedClient client, DbService db, ILocalization localization, NadekoStrings strings)
 | 
					        public ClashOfClansService(DiscordSocketClient client, DbService db, 
 | 
				
			||||||
 | 
					            ILocalization localization, NadekoStrings strings, IUnitOfWork uow, 
 | 
				
			||||||
 | 
					            List<long> guilds)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
            _localization = localization;
 | 
					            _localization = localization;
 | 
				
			||||||
            _strings = strings;
 | 
					            _strings = strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
 | 
				
			||||||
            {
 | 
					                uow.ClashOfClans
 | 
				
			||||||
                ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
 | 
					                    .GetAllWars(guilds)
 | 
				
			||||||
                    uow.ClashOfClans
 | 
					                    .Select(cw =>
 | 
				
			||||||
                        .GetAllWars()
 | 
					                    {
 | 
				
			||||||
                        .Select(cw =>
 | 
					                        cw.Channel = _client.GetGuild(cw.GuildId)?
 | 
				
			||||||
                        {
 | 
					                                                        .GetTextChannel(cw.ChannelId);
 | 
				
			||||||
                            cw.Channel = _client.GetGuild(cw.GuildId)?
 | 
					                        return cw;
 | 
				
			||||||
                                                         .GetTextChannel(cw.ChannelId);
 | 
					                    })
 | 
				
			||||||
                            return cw;
 | 
					                    .Where(cw => cw.Channel != null)
 | 
				
			||||||
                        })
 | 
					                    .GroupBy(cw => cw.GuildId)
 | 
				
			||||||
                        .Where(cw => cw.Channel != null)
 | 
					                    .ToDictionary(g => g.Key, g => g.ToList()));
 | 
				
			||||||
                        .GroupBy(cw => cw.GuildId)
 | 
					 | 
				
			||||||
                        .ToDictionary(g => g.Key, g => g.ToList()));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            checkWarTimer = new Timer(async _ =>
 | 
					            checkWarTimer = new Timer(async _ =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ namespace NadekoBot.Services
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public const int GlobalCommandsCooldown = 750;
 | 
					        public const int GlobalCommandsCooldown = 750;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly CommandService _commandService;
 | 
					        private readonly CommandService _commandService;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
@@ -48,7 +48,7 @@ namespace NadekoBot.Services
 | 
				
			|||||||
        public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>();
 | 
					        public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>();
 | 
				
			||||||
        private readonly Timer _clearUsersOnShortCooldown;
 | 
					        private readonly Timer _clearUsersOnShortCooldown;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public CommandHandler(DiscordShardedClient client, DbService db, BotConfig bc, IEnumerable<GuildConfig> gcs, CommandService commandService, IBotCredentials credentials, NadekoBot bot)
 | 
					        public CommandHandler(DiscordSocketClient client, DbService db, BotConfig bc, IEnumerable<GuildConfig> gcs, CommandService commandService, IBotCredentials credentials, NadekoBot bot)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _commandService = commandService;
 | 
					            _commandService = commandService;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ using System;
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using NadekoBot.Services.Permissions;
 | 
					using NadekoBot.Services.Permissions;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.CustomReactions
 | 
					namespace NadekoBot.Services.CustomReactions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -22,13 +23,14 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly PermissionService _perms;
 | 
					        private readonly PermissionService _perms;
 | 
				
			||||||
        private readonly CommandHandler _cmd;
 | 
					        private readonly CommandHandler _cmd;
 | 
				
			||||||
        private readonly BotConfig _bc;
 | 
					        private readonly BotConfig _bc;
 | 
				
			||||||
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public CustomReactionsService(PermissionService perms, DbService db, 
 | 
					        public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings,
 | 
				
			||||||
            DiscordShardedClient client, CommandHandler cmd, BotConfig bc)
 | 
					            DiscordSocketClient client, CommandHandler cmd, BotConfig bc, IUnitOfWork uow)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
@@ -36,16 +38,11 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
            _perms = perms;
 | 
					            _perms = perms;
 | 
				
			||||||
            _cmd = cmd;
 | 
					            _cmd = cmd;
 | 
				
			||||||
            _bc = bc;
 | 
					            _bc = bc;
 | 
				
			||||||
 | 
					            _strings = strings;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            var sw = Stopwatch.StartNew();
 | 
					            var items = uow.CustomReactions.GetAll();
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
 | 
				
			||||||
            {
 | 
					            GlobalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
 | 
				
			||||||
                var items = uow.CustomReactions.GetAll();
 | 
					 | 
				
			||||||
                GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
 | 
					 | 
				
			||||||
                GlobalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            sw.Stop();
 | 
					 | 
				
			||||||
            _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void ClearStats() => ReactionStats.Clear();
 | 
					        public void ClearStats() => ReactionStats.Clear();
 | 
				
			||||||
@@ -98,7 +95,7 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
            return greaction;
 | 
					            return greaction;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg)
 | 
					        public async Task<bool> TryExecuteEarly(DiscordSocketClient client, IGuild guild, IUserMessage msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // maybe this message is a custom reaction
 | 
					            // maybe this message is a custom reaction
 | 
				
			||||||
            var cr = await Task.Run(() => TryGetCustomReaction(msg)).ConfigureAwait(false);
 | 
					            var cr = await Task.Run(() => TryGetCustomReaction(msg)).ConfigureAwait(false);
 | 
				
			||||||
@@ -114,7 +111,7 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (pc.Verbose)
 | 
					                            if (pc.Verbose)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), sg)}** is preventing this action.";
 | 
					                                var returnMsg = _strings.GetText("trigger", guild.Id, "Permissions".ToLowerInvariant(), index + 1, Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)));
 | 
				
			||||||
                                try { await msg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
					                                try { await msg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
				
			||||||
                                _log.Info(returnMsg);
 | 
					                                _log.Info(returnMsg);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
            } },
 | 
					            } },
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Dictionary<string, Func<IUserMessage, DiscordShardedClient, string>> placeholders = new Dictionary<string, Func<IUserMessage, DiscordShardedClient, string>>()
 | 
					        public static Dictionary<string, Func<IUserMessage, DiscordSocketClient, string>> placeholders = new Dictionary<string, Func<IUserMessage, DiscordSocketClient, string>>()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            {"%mention%", (ctx, client) => { return $"<@{client.CurrentUser.Id}>"; } },
 | 
					            {"%mention%", (ctx, client) => { return $"<@{client.CurrentUser.Id}>"; } },
 | 
				
			||||||
            {"%user%", (ctx, client) => { return ctx.Author.Mention; } },
 | 
					            {"%user%", (ctx, client) => { return ctx.Author.Mention; } },
 | 
				
			||||||
@@ -94,7 +94,7 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
            } }
 | 
					            } }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static string ResolveTriggerString(this string str, IUserMessage ctx, DiscordShardedClient client)
 | 
					        private static string ResolveTriggerString(this string str, IUserMessage ctx, DiscordSocketClient client)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var ph in placeholders)
 | 
					            foreach (var ph in placeholders)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -104,7 +104,7 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
            return str;
 | 
					            return str;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static async Task<string> ResolveResponseStringAsync(this string str, IUserMessage ctx, DiscordShardedClient client, string resolvedTrigger)
 | 
					        private static async Task<string> ResolveResponseStringAsync(this string str, IUserMessage ctx, DiscordSocketClient client, string resolvedTrigger)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var ph in placeholders)
 | 
					            foreach (var ph in placeholders)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -127,13 +127,13 @@ namespace NadekoBot.Services.CustomReactions
 | 
				
			|||||||
            return str;
 | 
					            return str;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static string TriggerWithContext(this CustomReaction cr, IUserMessage ctx, DiscordShardedClient client)
 | 
					        public static string TriggerWithContext(this CustomReaction cr, IUserMessage ctx, DiscordSocketClient client)
 | 
				
			||||||
            => cr.Trigger.ResolveTriggerString(ctx, client);
 | 
					            => cr.Trigger.ResolveTriggerString(ctx, client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Task<string > ResponseWithContextAsync(this CustomReaction cr, IUserMessage ctx, DiscordShardedClient client)
 | 
					        public static Task<string > ResponseWithContextAsync(this CustomReaction cr, IUserMessage ctx, DiscordSocketClient client)
 | 
				
			||||||
            => cr.Response.ResolveResponseStringAsync(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client));
 | 
					            => cr.Response.ResolveResponseStringAsync(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context, DiscordShardedClient client, CustomReactionsService crs)
 | 
					        public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context, DiscordSocketClient client, CustomReactionsService crs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel;
 | 
					            var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,9 @@ namespace NadekoBot.Services.Database
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var optionsBuilder = new DbContextOptionsBuilder();
 | 
					            var optionsBuilder = new DbContextOptionsBuilder();
 | 
				
			||||||
            optionsBuilder.UseSqlite("Filename=./data/NadekoBot.db");
 | 
					            optionsBuilder.UseSqlite("Filename=./data/NadekoBot.db");
 | 
				
			||||||
            return new NadekoContext(optionsBuilder.Options);
 | 
					            var ctx = new NadekoContext(optionsBuilder.Options);
 | 
				
			||||||
 | 
					            ctx.Database.SetCommandTimeout(60);
 | 
				
			||||||
 | 
					            return ctx;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,6 @@ namespace NadekoBot.Services.Database.Repositories
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public interface IClashOfClansRepository : IRepository<ClashWar>
 | 
					    public interface IClashOfClansRepository : IRepository<ClashWar>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        IEnumerable<ClashWar> GetAllWars();
 | 
					        IEnumerable<ClashWar> GetAllWars(List<long> guilds);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,10 +11,10 @@ namespace NadekoBot.Services.Database.Repositories
 | 
				
			|||||||
        GuildConfig For(ulong guildId, Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes = null);
 | 
					        GuildConfig For(ulong guildId, Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes = null);
 | 
				
			||||||
        GuildConfig LogSettingsFor(ulong guildId);
 | 
					        GuildConfig LogSettingsFor(ulong guildId);
 | 
				
			||||||
        IEnumerable<GuildConfig> OldPermissionsForAll();
 | 
					        IEnumerable<GuildConfig> OldPermissionsForAll();
 | 
				
			||||||
        IEnumerable<GuildConfig> GetAllGuildConfigs();
 | 
					        IEnumerable<GuildConfig> GetAllGuildConfigs(List<long> availableGuilds);
 | 
				
			||||||
        IEnumerable<FollowedStream> GetAllFollowedStreams();
 | 
					        IEnumerable<FollowedStream> GetAllFollowedStreams(List<long> included);
 | 
				
			||||||
        void SetCleverbotEnabled(ulong id, bool cleverbotEnabled);
 | 
					        void SetCleverbotEnabled(ulong id, bool cleverbotEnabled);
 | 
				
			||||||
        IEnumerable<GuildConfig> Permissionsv2ForAll();
 | 
					        IEnumerable<GuildConfig> Permissionsv2ForAll(List<long> include);
 | 
				
			||||||
        GuildConfig GcWithPermissionsv2For(ulong guildId);
 | 
					        GuildConfig GcWithPermissionsv2For(ulong guildId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.Database.Repositories
 | 
					namespace NadekoBot.Services.Database.Repositories
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IReminderRepository : IRepository<Reminder>
 | 
					    public interface IReminderRepository : IRepository<Reminder>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        
 | 
					        IEnumerable<Reminder> GetIncludedReminders(List<long> guildIds);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,9 +11,11 @@ namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<ClashWar> GetAllWars()
 | 
					        public IEnumerable<ClashWar> GetAllWars(List<long> guilds)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var toReturn =  _set.Include(cw => cw.Bases)
 | 
					            var toReturn =  _set
 | 
				
			||||||
 | 
					                .Where(cw => guilds.Contains((long)cw.GuildId))
 | 
				
			||||||
 | 
					                .Include(cw => cw.Bases)
 | 
				
			||||||
                        .ToList();
 | 
					                        .ToList();
 | 
				
			||||||
            toReturn.ForEach(cw => cw.Bases = cw.Bases.Where(w => w.SequenceNumber != null).OrderBy(w => w.SequenceNumber).ToList());
 | 
					            toReturn.ForEach(cw => cw.Bases = cw.Bases.Where(w => w.SequenceNumber != null).OrderBy(w => w.SequenceNumber).ToList());
 | 
				
			||||||
            return toReturn;
 | 
					            return toReturn;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,8 +24,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<GuildConfig> GetAllGuildConfigs() =>
 | 
					        public IEnumerable<GuildConfig> GetAllGuildConfigs(List<long> availableGuilds) =>
 | 
				
			||||||
            _set.Include(gc => gc.LogSetting)
 | 
					            _set
 | 
				
			||||||
 | 
					                .Where(gc => availableGuilds.Contains((long)gc.GuildId))
 | 
				
			||||||
 | 
					                .Include(gc => gc.LogSetting)
 | 
				
			||||||
                    .ThenInclude(ls => ls.IgnoredChannels)
 | 
					                    .ThenInclude(ls => ls.IgnoredChannels)
 | 
				
			||||||
                .Include(gc => gc.MutedUsers)
 | 
					                .Include(gc => gc.MutedUsers)
 | 
				
			||||||
                .Include(gc => gc.CommandAliases)
 | 
					                .Include(gc => gc.CommandAliases)
 | 
				
			||||||
@@ -42,6 +44,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			|||||||
                .Include(gc => gc.SlowmodeIgnoredUsers)
 | 
					                .Include(gc => gc.SlowmodeIgnoredUsers)
 | 
				
			||||||
                .Include(gc => gc.AntiSpamSetting)
 | 
					                .Include(gc => gc.AntiSpamSetting)
 | 
				
			||||||
                    .ThenInclude(x => x.IgnoredChannels)
 | 
					                    .ThenInclude(x => x.IgnoredChannels)
 | 
				
			||||||
 | 
					                .Include(gc => gc.FollowedStreams)
 | 
				
			||||||
                .ToList();
 | 
					                .ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@@ -134,9 +137,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			|||||||
            return query.ToList();
 | 
					            return query.ToList();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<GuildConfig> Permissionsv2ForAll()
 | 
					        public IEnumerable<GuildConfig> Permissionsv2ForAll(List<long> include)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var query = _set
 | 
					            var query = _set
 | 
				
			||||||
 | 
					                .Where(x => include.Contains((long)x.GuildId))
 | 
				
			||||||
                .Include(gc => gc.Permissions);
 | 
					                .Include(gc => gc.Permissions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return query.ToList();
 | 
					            return query.ToList();
 | 
				
			||||||
@@ -167,8 +171,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			|||||||
            return config;
 | 
					            return config;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<FollowedStream> GetAllFollowedStreams() =>
 | 
					        public IEnumerable<FollowedStream> GetAllFollowedStreams(List<long> included) =>
 | 
				
			||||||
            _set.Include(gc => gc.FollowedStreams)
 | 
					            _set
 | 
				
			||||||
 | 
					                .Where(gc => included.Contains((long)gc.GuildId))
 | 
				
			||||||
 | 
					                .Include(gc => gc.FollowedStreams)
 | 
				
			||||||
                .SelectMany(gc => gc.FollowedStreams)
 | 
					                .SelectMany(gc => gc.FollowedStreams)
 | 
				
			||||||
                .ToList();
 | 
					                .ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,8 @@
 | 
				
			|||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.Database.Repositories.Impl
 | 
					namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -8,5 +11,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl
 | 
				
			|||||||
        public ReminderRepository(DbContext context) : base(context)
 | 
					        public ReminderRepository(DbContext context) : base(context)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IEnumerable<Reminder> GetIncludedReminders(List<long> guildIds)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return _set.Where(x => guildIds.Contains((long)x.ServerId)).ToList();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,9 +32,21 @@ namespace NadekoBot.Services
 | 
				
			|||||||
        public NadekoContext GetDbContext()
 | 
					        public NadekoContext GetDbContext()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var context = new NadekoContext(options);
 | 
					            var context = new NadekoContext(options);
 | 
				
			||||||
 | 
					            context.Database.SetCommandTimeout(60);
 | 
				
			||||||
            context.Database.Migrate();
 | 
					            context.Database.Migrate();
 | 
				
			||||||
            context.EnsureSeedData();
 | 
					            context.EnsureSeedData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //set important sqlite stuffs
 | 
				
			||||||
 | 
					            var conn = context.Database.GetDbConnection();
 | 
				
			||||||
 | 
					            conn.Open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            context.Database.ExecuteSqlCommand("PRAGMA journal_mode=WAL");
 | 
				
			||||||
 | 
					            using (var com = conn.CreateCommand())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                com.CommandText = "PRAGMA journal_mode=WAL; PRAGMA synchronous=OFF";
 | 
				
			||||||
 | 
					                com.ExecuteNonQuery();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return context;
 | 
					            return context;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@ namespace NadekoBot.Services.Discord
 | 
				
			|||||||
        public event Action<SocketReaction> OnReactionRemoved = delegate { };
 | 
					        public event Action<SocketReaction> OnReactionRemoved = delegate { };
 | 
				
			||||||
        public event Action OnReactionsCleared = delegate { };
 | 
					        public event Action OnReactionsCleared = delegate { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReactionEventWrapper(DiscordShardedClient client, IUserMessage msg)
 | 
					        public ReactionEventWrapper(DiscordSocketClient client, IUserMessage msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Message = msg ?? throw new ArgumentNullException(nameof(msg));
 | 
					            Message = msg ?? throw new ArgumentNullException(nameof(msg));
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
@@ -69,7 +69,7 @@ namespace NadekoBot.Services.Discord
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private bool disposing = false;
 | 
					        private bool disposing = false;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,19 +15,22 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ChatterBotService : IEarlyBlockingExecutor
 | 
					    public class ChatterBotService : IEarlyBlockingExecutor
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly PermissionService _perms;
 | 
					        private readonly PermissionService _perms;
 | 
				
			||||||
        private readonly CommandHandler _cmd;
 | 
					        private readonly CommandHandler _cmd;
 | 
				
			||||||
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> ChatterBotGuilds { get; }
 | 
					        public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> ChatterBotGuilds { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ChatterBotService(DiscordShardedClient client, PermissionService perms, IEnumerable<GuildConfig> gcs, CommandHandler cmd)
 | 
					        public ChatterBotService(DiscordSocketClient client, PermissionService perms, IEnumerable<GuildConfig> gcs, 
 | 
				
			||||||
 | 
					            CommandHandler cmd, NadekoStrings strings)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _perms = perms;
 | 
					            _perms = perms;
 | 
				
			||||||
            _cmd = cmd;
 | 
					            _cmd = cmd;
 | 
				
			||||||
 | 
					            _strings = strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ChatterBotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
 | 
					            ChatterBotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
 | 
				
			||||||
                    gcs.Where(gc => gc.CleverbotEnabled)
 | 
					                    gcs.Where(gc => gc.CleverbotEnabled)
 | 
				
			||||||
@@ -83,7 +86,7 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage usrMsg)
 | 
					        public async Task<bool> TryExecuteEarly(DiscordSocketClient client, IGuild guild, IUserMessage usrMsg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!(guild is SocketGuild sg))
 | 
					            if (!(guild is SocketGuild sg))
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
@@ -102,7 +105,7 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
                    if (pc.Verbose)
 | 
					                    if (pc.Verbose)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        //todo move this to permissions
 | 
					                        //todo move this to permissions
 | 
				
			||||||
                        var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), sg)}** is preventing this action.";
 | 
					                        var returnMsg = _strings.GetText("trigger", guild.Id, "Permissions".ToLowerInvariant(), index + 1, Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)));
 | 
				
			||||||
                        try { await usrMsg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
					                        try { await usrMsg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
				
			||||||
                        _log.Info(returnMsg);
 | 
					                        _log.Info(returnMsg);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,7 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
        public readonly ImmutableArray<string> EightBallResponses;
 | 
					        public readonly ImmutableArray<string> EightBallResponses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Timer _t;
 | 
					        private readonly Timer _t;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
        private readonly IImagesService _images;
 | 
					        private readonly IImagesService _images;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
@@ -33,7 +33,7 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
 | 
					        public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public GamesService(DiscordShardedClient client, BotConfig bc, IEnumerable<GuildConfig> gcs, 
 | 
					        public GamesService(DiscordSocketClient client, BotConfig bc, IEnumerable<GuildConfig> gcs, 
 | 
				
			||||||
            NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
 | 
					            NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _bc = bc;
 | 
					            _bc = bc;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,16 +18,13 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
        private string[] answers { get; }
 | 
					        private string[] answers { get; }
 | 
				
			||||||
        private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
 | 
					        private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
 | 
				
			||||||
        private readonly string _question;
 | 
					        private readonly string _question;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
        private bool running = false;
 | 
					        private bool running = false;
 | 
				
			||||||
        private HashSet<ulong> _guildUsers;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public event Action<ulong> OnEnded = delegate { };
 | 
					        public event Action<ulong> OnEnded = delegate { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsPublic { get; }
 | 
					        public Poll(DiscordSocketClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> enumerable)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Poll(DiscordShardedClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _strings = strings;
 | 
					            _strings = strings;
 | 
				
			||||||
@@ -36,7 +33,6 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
            _guild = ((ITextChannel)umsg.Channel).Guild;
 | 
					            _guild = ((ITextChannel)umsg.Channel).Guild;
 | 
				
			||||||
            _question = question;
 | 
					            _question = question;
 | 
				
			||||||
            answers = enumerable as string[] ?? enumerable.ToArray();
 | 
					            answers = enumerable as string[] ?? enumerable.ToArray();
 | 
				
			||||||
            IsPublic = isPublic;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public EmbedBuilder GetStats(string title)
 | 
					        public EmbedBuilder GetStats(string title)
 | 
				
			||||||
@@ -82,13 +78,7 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
            var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n";
 | 
					            var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n";
 | 
				
			||||||
            var num = 1;
 | 
					            var num = 1;
 | 
				
			||||||
            msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
 | 
					            msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
 | 
				
			||||||
            if (!IsPublic)
 | 
					            msgToSend += "\n" + Format.Bold(GetText("poll_vote_public"));
 | 
				
			||||||
                msgToSend += "\n" + Format.Bold(GetText("poll_vote_private"));
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                msgToSend += "\n" + Format.Bold(GetText("poll_vote_public"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!IsPublic)
 | 
					 | 
				
			||||||
                _guildUsers = new HashSet<ulong>((await _guild.GetUsersAsync().ConfigureAwait(false)).Select(x => x.Id));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
 | 
					            await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
 | 
				
			||||||
            running = true;
 | 
					            running = true;
 | 
				
			||||||
@@ -114,36 +104,16 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            IMessageChannel ch;
 | 
					            IMessageChannel ch;
 | 
				
			||||||
            if (IsPublic)
 | 
					            //if public, channel must be the same the poll started in
 | 
				
			||||||
            {
 | 
					            if (_originalMessage.Channel.Id != msg.Channel.Id)
 | 
				
			||||||
                //if public, channel must be the same the poll started in
 | 
					                return false;
 | 
				
			||||||
                if (_originalMessage.Channel.Id != msg.Channel.Id)
 | 
					            ch = msg.Channel;
 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
                ch = msg.Channel;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                //if private, channel must be dm channel
 | 
					 | 
				
			||||||
                if ((ch = msg.Channel as IDMChannel) == null)
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // user must be a member of the guild this poll is in
 | 
					 | 
				
			||||||
                if (!_guildUsers.Contains(msg.Author.Id))
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //user can vote only once
 | 
					            //user can vote only once
 | 
				
			||||||
            if (_participants.TryAdd(msg.Author.Id, vote))
 | 
					            if (_participants.TryAdd(msg.Author.Id, vote))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!IsPublic)
 | 
					                var toDelete = await ch.SendConfirmAsync(GetText("poll_voted", Format.Bold(msg.Author.ToString()))).ConfigureAwait(false);
 | 
				
			||||||
                {
 | 
					                toDelete.DeleteAfter(5);
 | 
				
			||||||
                    await ch.SendConfirmAsync(GetText("thanks_for_voting", Format.Bold(msg.Author.Username))).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var toDelete = await ch.SendConfirmAsync(GetText("poll_voted", Format.Bold(msg.Author.ToString()))).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    toDelete.DeleteAfter(5);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,17 +13,17 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
 | 
					        public ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public PollService(DiscordShardedClient client, NadekoStrings strings)
 | 
					        public PollService(DiscordSocketClient client, NadekoStrings strings)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _strings = strings;
 | 
					            _strings = strings;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<bool?> StartPoll(ITextChannel channel, IUserMessage msg, string arg, bool isPublic = false)
 | 
					        public async Task<bool?> StartPoll(ITextChannel channel, IUserMessage msg, string arg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
 | 
					            if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
@@ -31,7 +31,7 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
            if (data.Length < 3)
 | 
					            if (data.Length < 3)
 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var poll = new Poll(_client, _strings, msg, data[0], data.Skip(1), isPublic: isPublic);
 | 
					            var poll = new Poll(_client, _strings, msg, data[0], data.Skip(1));
 | 
				
			||||||
            if (ActivePolls.TryAdd(channel.Guild.Id, poll))
 | 
					            if (ActivePolls.TryAdd(channel.Guild.Id, poll))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                poll.OnEnded += (gid) =>
 | 
					                poll.OnEnded += (gid) =>
 | 
				
			||||||
@@ -45,20 +45,10 @@ namespace NadekoBot.Services.Games
 | 
				
			|||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg)
 | 
					        public async Task<bool> TryExecuteEarly(DiscordSocketClient client, IGuild guild, IUserMessage msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (guild == null)
 | 
					            if (guild == null)
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                foreach (var kvp in ActivePolls)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (!kvp.Value.IsPublic)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (await kvp.Value.TryVote(msg).ConfigureAwait(false))
 | 
					 | 
				
			||||||
                            return true;                        
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!ActivePolls.TryGetValue(guild.Id, out var poll))
 | 
					            if (!ActivePolls.TryGetValue(guild.Id, out var poll))
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,10 +17,10 @@ namespace NadekoBot.Services
 | 
				
			|||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache;
 | 
					        public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbService db)
 | 
					        public GreetSettingsService(DiscordSocketClient client, IEnumerable<GuildConfig> guildConfigs, DbService db)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ namespace NadekoBot.Services.Help
 | 
				
			|||||||
            _strings = strings;
 | 
					            _strings = strings;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg)
 | 
					        public async Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,9 @@ namespace NadekoBot.Services
 | 
				
			|||||||
        string OsuApiKey { get; }
 | 
					        string OsuApiKey { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool IsOwner(IUser u);
 | 
					        bool IsOwner(IUser u);
 | 
				
			||||||
 | 
					        int TotalShards { get; }
 | 
				
			||||||
 | 
					        string ShardRunCommand { get; }
 | 
				
			||||||
 | 
					        string ShardRunArguments { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class DBConfig
 | 
					    public class DBConfig
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,6 @@ namespace NadekoBot.Services
 | 
				
			|||||||
        ImmutableArray<byte> WifeMatrix { get; }
 | 
					        ImmutableArray<byte> WifeMatrix { get; }
 | 
				
			||||||
        ImmutableArray<byte> RategirlDot { get; }
 | 
					        ImmutableArray<byte> RategirlDot { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TimeSpan Reload();
 | 
					        void Reload();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ namespace NadekoBot.Services
 | 
				
			|||||||
        double MessagesPerSecond { get; }
 | 
					        double MessagesPerSecond { get; }
 | 
				
			||||||
        long TextChannels { get; }
 | 
					        long TextChannels { get; }
 | 
				
			||||||
        long VoiceChannels { get; }
 | 
					        long VoiceChannels { get; }
 | 
				
			||||||
 | 
					        int GuildCount { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TimeSpan GetUptime();
 | 
					        TimeSpan GetUptime();
 | 
				
			||||||
        string GetUptimeString(string separator = ", ");
 | 
					        string GetUptimeString(string separator = ", ");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,20 +30,23 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
        public int TotalShards { get; }
 | 
					        public int TotalShards { get; }
 | 
				
			||||||
        public string CarbonKey { get; }
 | 
					        public string CarbonKey { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string credsFileName { get; } = Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
 | 
					        private readonly string _credsFileName = Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
 | 
				
			||||||
        public string PatreonAccessToken { get; }
 | 
					        public string PatreonAccessToken { get; }
 | 
				
			||||||
 | 
					        public string ShardRunCommand { get; }
 | 
				
			||||||
 | 
					        public string ShardRunArguments { get; }
 | 
				
			||||||
 | 
					        public int ShardRunPort { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public BotCredentials()
 | 
					        public BotCredentials()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try { File.WriteAllText("./credentials_example.json", JsonConvert.SerializeObject(new CredentialsModel(), Formatting.Indented)); } catch { }
 | 
					            try { File.WriteAllText("./credentials_example.json", JsonConvert.SerializeObject(new CredentialsModel(), Formatting.Indented)); } catch { }
 | 
				
			||||||
            if(!File.Exists(credsFileName))
 | 
					            if(!File.Exists(_credsFileName))
 | 
				
			||||||
                _log.Warn($"credentials.json is missing. Attempting to load creds from environment variables prefixed with 'NadekoBot_'. Example is in {Path.GetFullPath("./credentials_example.json")}");
 | 
					                _log.Warn($"credentials.json is missing. Attempting to load creds from environment variables prefixed with 'NadekoBot_'. Example is in {Path.GetFullPath("./credentials_example.json")}");
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var configBuilder = new ConfigurationBuilder();
 | 
					                var configBuilder = new ConfigurationBuilder();
 | 
				
			||||||
                configBuilder.AddJsonFile(credsFileName, true)
 | 
					                configBuilder.AddJsonFile(_credsFileName, true)
 | 
				
			||||||
                    .AddEnvironmentVariables("NadekoBot_");
 | 
					                    .AddEnvironmentVariables("NadekoBot_");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var data = configBuilder.Build();
 | 
					                var data = configBuilder.Build();
 | 
				
			||||||
@@ -61,13 +64,24 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
                MashapeKey = data[nameof(MashapeKey)];
 | 
					                MashapeKey = data[nameof(MashapeKey)];
 | 
				
			||||||
                OsuApiKey = data[nameof(OsuApiKey)];
 | 
					                OsuApiKey = data[nameof(OsuApiKey)];
 | 
				
			||||||
                PatreonAccessToken = data[nameof(PatreonAccessToken)];
 | 
					                PatreonAccessToken = data[nameof(PatreonAccessToken)];
 | 
				
			||||||
 | 
					                ShardRunCommand = data[nameof(ShardRunCommand)];
 | 
				
			||||||
 | 
					                ShardRunArguments = data[nameof(ShardRunArguments)];
 | 
				
			||||||
 | 
					                if (string.IsNullOrWhiteSpace(ShardRunCommand))
 | 
				
			||||||
 | 
					                    ShardRunCommand = "dotnet";
 | 
				
			||||||
 | 
					                if (string.IsNullOrWhiteSpace(ShardRunArguments))
 | 
				
			||||||
 | 
					                    ShardRunArguments = "run -c Release -- {0} {1} {2}";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                var portStr = data[nameof(ShardRunPort)];
 | 
				
			||||||
 | 
					                if (string.IsNullOrWhiteSpace(portStr))
 | 
				
			||||||
 | 
					                    ShardRunPort = new NadekoRandom().Next(5000, 6000);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    ShardRunPort = int.Parse(portStr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                int ts = 1;
 | 
					                int ts = 1;
 | 
				
			||||||
                int.TryParse(data[nameof(TotalShards)], out ts);
 | 
					                int.TryParse(data[nameof(TotalShards)], out ts);
 | 
				
			||||||
                TotalShards = ts < 1 ? 1 : ts;
 | 
					                TotalShards = ts < 1 ? 1 : ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ulong clId = 0;
 | 
					                ulong.TryParse(data[nameof(ClientId)], out ulong clId);
 | 
				
			||||||
                ulong.TryParse(data[nameof(ClientId)], out clId);
 | 
					 | 
				
			||||||
                ClientId = clId;
 | 
					                ClientId = clId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                //var scId = data[nameof(SoundCloudClientId)];
 | 
					                //var scId = data[nameof(SoundCloudClientId)];
 | 
				
			||||||
@@ -107,6 +121,10 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
            public DBConfig Db { get; set; } = new DBConfig("sqlite", "Filename=./data/NadekoBot.db");
 | 
					            public DBConfig Db { get; set; } = new DBConfig("sqlite", "Filename=./data/NadekoBot.db");
 | 
				
			||||||
            public int TotalShards { get; set; } = 1;
 | 
					            public int TotalShards { get; set; } = 1;
 | 
				
			||||||
            public string PatreonAccessToken { get; set; } = "";
 | 
					            public string PatreonAccessToken { get; set; } = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public string ShardRunCommand { get; set; } = "";
 | 
				
			||||||
 | 
					            public string ShardRunArguments { get; set; } = "";
 | 
				
			||||||
 | 
					            public int? ShardRunPort { get; set; } = null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class DbModel
 | 
					        private class DbModel
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,12 +47,10 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
            this.Reload();
 | 
					            this.Reload();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TimeSpan Reload()
 | 
					        public void Reload()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log.Info("Loading images...");
 | 
					 | 
				
			||||||
                var sw = Stopwatch.StartNew();
 | 
					 | 
				
			||||||
                Heads = File.ReadAllBytes(_headsPath).ToImmutableArray();
 | 
					                Heads = File.ReadAllBytes(_headsPath).ToImmutableArray();
 | 
				
			||||||
                Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray();
 | 
					                Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,10 +77,6 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
 | 
					                WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
 | 
				
			||||||
                RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
 | 
					                RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
 | 
				
			||||||
 | 
					 | 
				
			||||||
                sw.Stop();
 | 
					 | 
				
			||||||
                _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!");
 | 
					 | 
				
			||||||
                return sw.Elapsed;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,11 +13,11 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class StatsService : IStatsService
 | 
					    public class StatsService : IStatsService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
        private readonly DateTime _started;
 | 
					        private readonly DateTime _started;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public const string BotVersion = "1.43";
 | 
					        public const string BotVersion = "1.5";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Author => "Kwoth#2560";
 | 
					        public string Author => "Kwoth#2560";
 | 
				
			||||||
        public string Library => "Discord.Net";
 | 
					        public string Library => "Discord.Net";
 | 
				
			||||||
@@ -35,11 +35,16 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
        public long CommandsRan => Interlocked.Read(ref _commandsRan);
 | 
					        public long CommandsRan => Interlocked.Read(ref _commandsRan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Timer _carbonitexTimer;
 | 
					        private readonly Timer _carbonitexTimer;
 | 
				
			||||||
 | 
					        private readonly ShardsCoordinator _sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public StatsService(DiscordShardedClient client, CommandHandler cmdHandler, IBotCredentials creds)
 | 
					        public int GuildCount =>
 | 
				
			||||||
 | 
					            _sc?.GuildCount ?? _client.Guilds.Count();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public StatsService(DiscordSocketClient client, CommandHandler cmdHandler, IBotCredentials creds, ShardsCoordinator sc)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _creds = creds;
 | 
					            _creds = creds;
 | 
				
			||||||
 | 
					            _sc = sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _started = DateTime.UtcNow;
 | 
					            _started = DateTime.UtcNow;
 | 
				
			||||||
            _client.MessageReceived += _ => Task.FromResult(Interlocked.Increment(ref _messageCounter));
 | 
					            _client.MessageReceived += _ => Task.FromResult(Interlocked.Increment(ref _messageCounter));
 | 
				
			||||||
@@ -121,31 +126,34 @@ namespace NadekoBot.Services.Impl
 | 
				
			|||||||
                return Task.CompletedTask;
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _carbonitexTimer = new Timer(async (state) =>
 | 
					            if (sc != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (string.IsNullOrWhiteSpace(_creds.CarbonKey))
 | 
					                _carbonitexTimer = new Timer(async (state) =>
 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    using (var http = new HttpClient())
 | 
					                    if (string.IsNullOrWhiteSpace(_creds.CarbonKey))
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        using (var content = new FormUrlEncodedContent(
 | 
					                        using (var http = new HttpClient())
 | 
				
			||||||
                            new Dictionary<string, string> {
 | 
					 | 
				
			||||||
                                { "servercount", _client.Guilds.Count.ToString() },
 | 
					 | 
				
			||||||
                                { "key", _creds.CarbonKey }}))
 | 
					 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            content.Headers.Clear();
 | 
					                            using (var content = new FormUrlEncodedContent(
 | 
				
			||||||
                            content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
 | 
					                                new Dictionary<string, string> {
 | 
				
			||||||
 | 
					                                { "servercount", sc.GuildCount.ToString() },
 | 
				
			||||||
 | 
					                                { "key", _creds.CarbonKey }}))
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                content.Headers.Clear();
 | 
				
			||||||
 | 
					                                content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            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
 | 
				
			||||||
                    // ignored
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
 | 
				
			||||||
            }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Initialize()
 | 
					        public void Initialize()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/NadekoBot/Services/LogSetup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/NadekoBot/Services/LogSetup.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					using NLog;
 | 
				
			||||||
 | 
					using NLog.Config;
 | 
				
			||||||
 | 
					using NLog.Targets;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Services
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class LogSetup
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static void SetupLogger()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var logConfig = new LoggingConfiguration();
 | 
				
			||||||
 | 
					            var consoleTarget = new ColoredConsoleTarget()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Layout = @"${date:format=HH\:mm\:ss} ${logger} | ${message}"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            logConfig.AddTarget("Console", consoleTarget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            logConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, consoleTarget));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LogManager.Configuration = logConfig;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,7 +21,7 @@ namespace NadekoBot.Services.Permissions
 | 
				
			|||||||
                                 v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
 | 
					                                 v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild, 
 | 
					        public Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild, 
 | 
				
			||||||
            IMessageChannel channel, IUser user, string moduleName, string commandName)
 | 
					            IMessageChannel channel, IUser user, string moduleName, string commandName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (guild == null)
 | 
					            if (guild == null)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ namespace NadekoBot.Services.Permissions
 | 
				
			|||||||
            return words;
 | 
					            return words;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public FilterService(DiscordShardedClient _client, IEnumerable<GuildConfig> gcs)
 | 
					        public FilterService(DiscordSocketClient _client, IEnumerable<GuildConfig> gcs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ namespace NadekoBot.Services.Permissions
 | 
				
			|||||||
            BlockedCommands = new ConcurrentHashSet<string>(bc.BlockedCommands.Select(x => x.Name));
 | 
					            BlockedCommands = new ConcurrentHashSet<string>(bc.BlockedCommands.Select(x => x.Name));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
 | 
					        public async Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await Task.Yield();
 | 
					            await Task.Yield();
 | 
				
			||||||
            commandName = commandName.ToLowerInvariant();
 | 
					            commandName = commandName.ToLowerInvariant();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using Microsoft.EntityFrameworkCore;
 | 
					
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using NadekoBot.DataStructures.ModuleBehaviors;
 | 
					using NadekoBot.DataStructures.ModuleBehaviors;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
@@ -10,6 +11,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using NadekoBot.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.Permissions
 | 
					namespace NadekoBot.Services.Permissions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -18,22 +20,26 @@ namespace NadekoBot.Services.Permissions
 | 
				
			|||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private readonly CommandHandler _cmd;
 | 
					        private readonly CommandHandler _cmd;
 | 
				
			||||||
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //guildid, root permission
 | 
					        //guildid, root permission
 | 
				
			||||||
        public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
 | 
					        public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
 | 
				
			||||||
            new ConcurrentDictionary<ulong, PermissionCache>();
 | 
					            new ConcurrentDictionary<ulong, PermissionCache>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public PermissionService(DbService db, BotConfig bc, CommandHandler cmd)
 | 
					        public PermissionService(DiscordSocketClient client, DbService db, BotConfig bc, CommandHandler cmd, NadekoStrings strings)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
            _cmd = cmd;
 | 
					            _cmd = cmd;
 | 
				
			||||||
 | 
					            _strings = strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var sw = Stopwatch.StartNew();
 | 
					            var sw = Stopwatch.StartNew();
 | 
				
			||||||
            TryMigratePermissions(bc);
 | 
					            if (client.ShardId == 0)
 | 
				
			||||||
 | 
					                TryMigratePermissions(bc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            using (var uow = _db.UnitOfWork)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var x in uow.GuildConfigs.Permissionsv2ForAll())
 | 
					                foreach (var x in uow.GuildConfigs.Permissionsv2ForAll(client.Guilds.ToArray().Select(x => (long)x.Id).ToList()))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Cache.TryAdd(x.GuildId, new PermissionCache()
 | 
					                    Cache.TryAdd(x.GuildId, new PermissionCache()
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@@ -68,10 +74,9 @@ namespace NadekoBot.Services.Permissions
 | 
				
			|||||||
        private void TryMigratePermissions(BotConfig bc)
 | 
					        private void TryMigratePermissions(BotConfig bc)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var log = LogManager.GetCurrentClassLogger();
 | 
					            var log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            if (bc.PermissionVersion <= 1)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var _bc = uow.BotConfig.GetOrCreate();
 | 
					                using (var uow = _db.UnitOfWork)
 | 
				
			||||||
                if (_bc.PermissionVersion <= 1)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    log.Info("Permission version is 1, upgrading to 2.");
 | 
					                    log.Info("Permission version is 1, upgrading to 2.");
 | 
				
			||||||
                    var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
 | 
					                    var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
 | 
				
			||||||
@@ -126,9 +131,13 @@ namespace NadekoBot.Services.Permissions
 | 
				
			|||||||
                        log.Info("Permission migration to v2 is done.");
 | 
					                        log.Info("Permission migration to v2 is done.");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    _bc.PermissionVersion = 2;
 | 
					                    bc.PermissionVersion = 2;
 | 
				
			||||||
 | 
					                    uow.Complete();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (_bc.PermissionVersion <= 2)
 | 
					            }
 | 
				
			||||||
 | 
					            if (bc.PermissionVersion <= 2)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using (var uow = _db.UnitOfWork)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" };
 | 
					                    var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" };
 | 
				
			||||||
                    uow._context.Database.ExecuteSqlCommand(
 | 
					                    uow._context.Database.ExecuteSqlCommand(
 | 
				
			||||||
@@ -144,9 +153,9 @@ WHERE secondaryTargetName LIKE '.%' OR
 | 
				
			|||||||
    secondaryTargetName LIKE '>%' OR
 | 
					    secondaryTargetName LIKE '>%' OR
 | 
				
			||||||
    secondaryTargetName LIKE '-%' OR
 | 
					    secondaryTargetName LIKE '-%' OR
 | 
				
			||||||
    secondaryTargetName LIKE '!%';");
 | 
					    secondaryTargetName LIKE '!%';");
 | 
				
			||||||
                    _bc.PermissionVersion = 3;
 | 
					                    bc.PermissionVersion = 3;
 | 
				
			||||||
 | 
					                    uow.Complete();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                uow.Complete();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -183,7 +192,7 @@ WHERE secondaryTargetName LIKE '.%' OR
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
 | 
					        public async Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await Task.Yield();
 | 
					            await Task.Yield();
 | 
				
			||||||
            if (guild == null)
 | 
					            if (guild == null)
 | 
				
			||||||
@@ -198,11 +207,9 @@ WHERE secondaryTargetName LIKE '.%' OR
 | 
				
			|||||||
                PermissionCache pc = GetCache(guild.Id);
 | 
					                PermissionCache pc = GetCache(guild.Id);
 | 
				
			||||||
                if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out int index))
 | 
					                if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out int index))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)}** is preventing this action.";
 | 
					 | 
				
			||||||
                    if (pc.Verbose)
 | 
					                    if (pc.Verbose)
 | 
				
			||||||
                        try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
					                        try { await channel.SendErrorAsync(_strings.GetText("trigger", guild.Id, "Permissions".ToLowerInvariant(), index + 1, Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)))).ConfigureAwait(false); } catch { }
 | 
				
			||||||
                    return true;
 | 
					                    return true;
 | 
				
			||||||
                    //return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, returnMsg));
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -215,7 +222,6 @@ WHERE secondaryTargetName LIKE '.%' OR
 | 
				
			|||||||
                        if (pc.Verbose)
 | 
					                        if (pc.Verbose)
 | 
				
			||||||
                            try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
					                            try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
 | 
				
			||||||
                        return true;
 | 
					                        return true;
 | 
				
			||||||
                        //return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"You need the **{pc.PermRole}** role in order to use permission commands."));
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,52 +1,18 @@
 | 
				
			|||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
using Newtonsoft.Json.Linq;
 | 
					 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.Searches
 | 
					namespace NadekoBot.Services.Searches
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class AnimeSearchService
 | 
					    public class AnimeSearchService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly Timer _anilistTokenRefresher;
 | 
					 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static string anilistToken { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public AnimeSearchService()
 | 
					        public AnimeSearchService()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _anilistTokenRefresher = new Timer(async (state) =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var headers = new Dictionary<string, string>
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            {"grant_type", "client_credentials"},
 | 
					 | 
				
			||||||
                            {"client_id", "kwoth-w0ki9"},
 | 
					 | 
				
			||||||
                            {"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"},
 | 
					 | 
				
			||||||
                        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    using (var http = new HttpClient())
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        //http.AddFakeHeaders();
 | 
					 | 
				
			||||||
                        http.DefaultRequestHeaders.Clear();
 | 
					 | 
				
			||||||
                        var formContent = new FormUrlEncodedContent(headers);
 | 
					 | 
				
			||||||
                        var response = await http.PostAsync("https://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                        var stringContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
                        anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    // ignored
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<AnimeResult> GetAnimeData(string query)
 | 
					        public async Task<AnimeResult> GetAnimeData(string query)
 | 
				
			||||||
@@ -56,19 +22,15 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
 | 
					                var link = "https://aniapi.nadekobot.me/anime/" + Uri.EscapeDataString(query.Replace("/", " "));
 | 
				
			||||||
                using (var http = new HttpClient())
 | 
					                using (var http = new HttpClient())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var res = await http.GetStringAsync(link + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
					                    var res = await http.GetStringAsync(link).ConfigureAwait(false);
 | 
				
			||||||
                    var smallObj = JArray.Parse(res)[0];
 | 
					                    return JsonConvert.DeserializeObject<AnimeResult>(res);
 | 
				
			||||||
                    var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return await Task.Run(() => { try { return JsonConvert.DeserializeObject<AnimeResult>(aniData); } catch { return null; } }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log.Warn(ex, "Failed anime search for {0}", query);
 | 
					 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -79,18 +41,16 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
                throw new ArgumentNullException(nameof(query));
 | 
					                throw new ArgumentNullException(nameof(query));
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var link = "https://aniapi.nadekobot.me/manga/" + Uri.EscapeDataString(query.Replace("/", " "));
 | 
				
			||||||
                using (var http = new HttpClient())
 | 
					                using (var http = new HttpClient())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var res = await http.GetStringAsync("http://anilist.co/api/manga/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
					                    var res = await http.GetStringAsync(link).ConfigureAwait(false);
 | 
				
			||||||
                    var smallObj = JArray.Parse(res)[0];
 | 
					                    return JsonConvert.DeserializeObject<MangaResult>(res);
 | 
				
			||||||
                    var aniData = await http.GetStringAsync("http://anilist.co/api/manga/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return await Task.Run(() => { try { return JsonConvert.DeserializeObject<MangaResult>(aniData); } catch { return null; } }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _log.Warn(ex, "Failed anime search for {0}", query);
 | 
					 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class SearchesService
 | 
					    public class SearchesService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly IGoogleApiService _google;
 | 
					        private readonly IGoogleApiService _google;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
@@ -31,7 +31,7 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
        public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
 | 
					        public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
 | 
				
			||||||
        public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
 | 
					        public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbService db)
 | 
					        public SearchesService(DiscordSocketClient client, IGoogleApiService google, DbService db)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
            _google = google;
 | 
					            _google = google;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using NadekoBot.Services;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
@@ -20,10 +22,10 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
        private readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
 | 
					        private readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly NadekoStrings _strings;
 | 
					        private readonly NadekoStrings _strings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public StreamNotificationService(DbService db, DiscordShardedClient client, NadekoStrings strings)
 | 
					        public StreamNotificationService(DbService db, DiscordSocketClient client, NadekoStrings strings)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
@@ -35,7 +37,7 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
                IEnumerable<FollowedStream> streams;
 | 
					                IEnumerable<FollowedStream> streams;
 | 
				
			||||||
                using (var uow = _db.UnitOfWork)
 | 
					                using (var uow = _db.UnitOfWork)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    streams = uow.GuildConfigs.GetAllFollowedStreams();
 | 
					                    streams = uow.GuildConfigs.GetAllFollowedStreams(client.Guilds.Select(x => (long)x.Id).ToList());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await Task.WhenAll(streams.Select(async fs =>
 | 
					                await Task.WhenAll(streams.Select(async fs =>
 | 
				
			||||||
@@ -73,7 +75,7 @@ namespace NadekoBot.Services.Searches
 | 
				
			|||||||
                }));
 | 
					                }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                firstStreamNotifPass = false;
 | 
					                firstStreamNotifPass = false;
 | 
				
			||||||
            }, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
 | 
					            }, null, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true)
 | 
					        public async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
using NadekoBot.Services.Database.Models;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
 | 
					using NadekoBot.Services;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
@@ -13,24 +15,26 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ConverterService
 | 
					    public class ConverterService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>();
 | 
					        public List<ConvertUnit> Units { get; } = new List<ConvertUnit>();
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
        private Timer _timer;
 | 
					        private readonly Timer _currencyUpdater;
 | 
				
			||||||
        private readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0);
 | 
					        private readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0);
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ConverterService(DbService db)
 | 
					        public ConverterService(DiscordSocketClient client, DbService db)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var data = JsonConvert.DeserializeObject<List<MeasurementUnit>>(File.ReadAllText("data/units.json")).Select(u => new ConvertUnit()
 | 
					                var data = JsonConvert.DeserializeObject<List<MeasurementUnit>>(
 | 
				
			||||||
                {
 | 
					                    File.ReadAllText("data/units.json"))
 | 
				
			||||||
                    Modifier = u.Modifier,
 | 
					                        .Select(u => new ConvertUnit()
 | 
				
			||||||
                    UnitType = u.UnitType,
 | 
					                        {
 | 
				
			||||||
                    InternalTrigger = string.Join("|", u.Triggers)
 | 
					                            Modifier = u.Modifier,
 | 
				
			||||||
                }).ToArray();
 | 
					                            UnitType = u.UnitType,
 | 
				
			||||||
 | 
					                            InternalTrigger = string.Join("|", u.Triggers)
 | 
				
			||||||
 | 
					                        }).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                using (var uow = _db.UnitOfWork)
 | 
					                using (var uow = _db.UnitOfWork)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -47,10 +51,10 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
                _log.Warn("Could not load units: " + ex.Message);
 | 
					                _log.Warn("Could not load units: " + ex.Message);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _timer = new Timer(async (obj) => await UpdateCurrency(), null, _updateInterval, _updateInterval);
 | 
					            _currencyUpdater = new Timer(async (shouldLoad) => await UpdateCurrency((bool)shouldLoad), client.ShardId == 0, _updateInterval, _updateInterval);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task<Rates> UpdateCurrencyRates()
 | 
					        private async Task<Rates> GetCurrencyRates()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using (var http = new HttpClient())
 | 
					            using (var http = new HttpClient())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -59,38 +63,48 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task UpdateCurrency()
 | 
					        private async Task UpdateCurrency(bool shouldLoad)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var currencyRates = await UpdateCurrencyRates();
 | 
					 | 
				
			||||||
                var unitTypeString = "currency";
 | 
					                var unitTypeString = "currency";
 | 
				
			||||||
                var range = currencyRates.ConversionRates.Select(u => new ConvertUnit()
 | 
					                if (shouldLoad)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    InternalTrigger = u.Key,
 | 
					                    var currencyRates = await GetCurrencyRates();
 | 
				
			||||||
                    Modifier = u.Value,
 | 
					                    var baseType = new ConvertUnit()
 | 
				
			||||||
                    UnitType = unitTypeString
 | 
					                    {
 | 
				
			||||||
                }).ToArray();
 | 
					                        Triggers = new[] { currencyRates.Base },
 | 
				
			||||||
                var baseType = new ConvertUnit()
 | 
					                        Modifier = decimal.One,
 | 
				
			||||||
                {
 | 
					                        UnitType = unitTypeString
 | 
				
			||||||
                    Triggers = new[] { currencyRates.Base },
 | 
					                    };
 | 
				
			||||||
                    Modifier = decimal.One,
 | 
					                    var range = currencyRates.ConversionRates.Select(u => new ConvertUnit()
 | 
				
			||||||
                    UnitType = unitTypeString
 | 
					                    {
 | 
				
			||||||
                };
 | 
					                        InternalTrigger = u.Key,
 | 
				
			||||||
                var toRemove = Units.Where(u => u.UnitType == unitTypeString);
 | 
					                        Modifier = u.Value,
 | 
				
			||||||
 | 
					                        UnitType = unitTypeString
 | 
				
			||||||
 | 
					                    }).ToArray();
 | 
				
			||||||
 | 
					                    var toRemove = Units.Where(u => u.UnitType == unitTypeString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                using (var uow = _db.UnitOfWork)
 | 
					                    using (var uow = _db.UnitOfWork)
 | 
				
			||||||
                {
 | 
					                    {
 | 
				
			||||||
                    uow.ConverterUnits.RemoveRange(toRemove.ToArray());
 | 
					                        uow.ConverterUnits.RemoveRange(toRemove.ToArray());
 | 
				
			||||||
                    uow.ConverterUnits.Add(baseType);
 | 
					                        uow.ConverterUnits.Add(baseType);
 | 
				
			||||||
                    uow.ConverterUnits.AddRange(range);
 | 
					                        uow.ConverterUnits.AddRange(range);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    await uow.CompleteAsync().ConfigureAwait(false);
 | 
					                        await uow.CompleteAsync().ConfigureAwait(false);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Units.RemoveAll(u => u.UnitType == unitTypeString);
 | 
				
			||||||
 | 
					                    Units.Add(baseType);
 | 
				
			||||||
 | 
					                    Units.AddRange(range);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    using (var uow = _db.UnitOfWork)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Units.RemoveAll(u => u.UnitType == unitTypeString);
 | 
				
			||||||
 | 
					                        Units.AddRange(uow.ConverterUnits.GetAll().ToArray());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Units.RemoveAll(u => u.UnitType == unitTypeString);
 | 
					 | 
				
			||||||
                Units.Add(baseType);
 | 
					 | 
				
			||||||
                Units.AddRange(range);
 | 
					 | 
				
			||||||
                _log.Info("Updated Currency");
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch
 | 
					            catch
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
        public ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> Repeaters { get; set; }
 | 
					        public ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> Repeaters { get; set; }
 | 
				
			||||||
        public bool RepeaterReady { get; private set; }
 | 
					        public bool RepeaterReady { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MessageRepeaterService(NadekoBot bot, DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
 | 
					        public MessageRepeaterService(NadekoBot bot, DiscordSocketClient client, IEnumerable<GuildConfig> gcs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var _ = Task.Run(async () =>
 | 
					            var _ = Task.Run(async () =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,15 @@
 | 
				
			|||||||
using NadekoBot.Services.Database.Models;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using NadekoBot.Services.Utility.Patreon;
 | 
					using NadekoBot.Services.Utility.Patreon;
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Collections.Immutable;
 | 
					using System.Collections.Immutable;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,12 +26,15 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
        private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1);
 | 
					        private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1);
 | 
				
			||||||
        private readonly Logger _log;
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public readonly TimeSpan Interval = TimeSpan.FromMinutes(15);
 | 
					        public readonly TimeSpan Interval = TimeSpan.FromMinutes(3);
 | 
				
			||||||
        private IBotCredentials _creds;
 | 
					        private readonly IBotCredentials _creds;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
        private readonly CurrencyService _currency;
 | 
					        private readonly CurrencyService _currency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency)
 | 
					        private readonly string cacheFileName = "./patreon-rewards.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency,
 | 
				
			||||||
 | 
					            DiscordSocketClient client)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _creds = creds;
 | 
					            _creds = creds;
 | 
				
			||||||
            _db = db;
 | 
					            _db = db;
 | 
				
			||||||
@@ -36,58 +42,65 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
            if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken))
 | 
					            if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            Updater = new Timer(async (_) => await LoadPledges(), null, TimeSpan.Zero, Interval);
 | 
					            Updater = new Timer(async (load) => await RefreshPledges((bool)load),
 | 
				
			||||||
 | 
					                client.ShardId == 0, client.ShardId == 0 ? TimeSpan.Zero : TimeSpan.FromMinutes(2), Interval);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task LoadPledges()
 | 
					        public async Task RefreshPledges(bool shouldLoad)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            LastUpdate = DateTime.UtcNow;
 | 
					            if (shouldLoad)
 | 
				
			||||||
            await getPledgesLocker.WaitAsync(1000).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var rewards = new List<PatreonPledge>();
 | 
					                LastUpdate = DateTime.UtcNow;
 | 
				
			||||||
                var users = new List<PatreonUser>();
 | 
					                await getPledgesLocker.WaitAsync().ConfigureAwait(false);
 | 
				
			||||||
                using (var http = new HttpClient())
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    http.DefaultRequestHeaders.Clear();
 | 
					                    var rewards = new List<PatreonPledge>();
 | 
				
			||||||
                    http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
 | 
					                    var users = new List<PatreonUser>();
 | 
				
			||||||
                    var data = new PatreonData()
 | 
					                    using (var http = new HttpClient())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        Links = new PatreonDataLinks()
 | 
					                        http.DefaultRequestHeaders.Clear();
 | 
				
			||||||
 | 
					                        http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
 | 
				
			||||||
 | 
					                        var data = new PatreonData()
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            next = "https://api.patreon.com/oauth2/api/campaigns/334038/pledges"
 | 
					                            Links = new PatreonDataLinks()
 | 
				
			||||||
                        }
 | 
					                            {
 | 
				
			||||||
                    };
 | 
					                                next = "https://api.patreon.com/oauth2/api/campaigns/334038/pledges"
 | 
				
			||||||
                    do
 | 
					                            }
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                        do
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            var res = await http.GetStringAsync(data.Links.next)
 | 
				
			||||||
 | 
					                                .ConfigureAwait(false);
 | 
				
			||||||
 | 
					                            data = JsonConvert.DeserializeObject<PatreonData>(res);
 | 
				
			||||||
 | 
					                            var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
 | 
				
			||||||
 | 
					                            rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
 | 
				
			||||||
 | 
					                                .Where(x => x.attributes.declined_since == null));
 | 
				
			||||||
 | 
					                            users.AddRange(data.Included
 | 
				
			||||||
 | 
					                                .Where(x => x["type"].ToString() == "user")
 | 
				
			||||||
 | 
					                                .Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
 | 
				
			||||||
 | 
					                        } while (!string.IsNullOrWhiteSpace(data.Links.next));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var res = await http.GetStringAsync(data.Links.next)
 | 
					                        User = y,
 | 
				
			||||||
                            .ConfigureAwait(false);
 | 
					                        Reward = x,
 | 
				
			||||||
                        data = JsonConvert.DeserializeObject<PatreonData>(res);
 | 
					                    }).ToImmutableArray();
 | 
				
			||||||
                        var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
 | 
					                    File.WriteAllText("./patreon_rewards.json", JsonConvert.SerializeObject(Pledges));
 | 
				
			||||||
                        rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
 | 
					 | 
				
			||||||
                            .Where(x => x.attributes.declined_since == null));
 | 
					 | 
				
			||||||
                        users.AddRange(data.Included
 | 
					 | 
				
			||||||
                            .Where(x => x["type"].ToString() == "user")
 | 
					 | 
				
			||||||
                            .Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
 | 
					 | 
				
			||||||
                    } while (!string.IsNullOrWhiteSpace(data.Links.next));
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    User = y,
 | 
					                    _log.Warn(ex);
 | 
				
			||||||
                    Reward = x,
 | 
					                }
 | 
				
			||||||
                }).ToImmutableArray();
 | 
					                finally
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _log.Warn(ex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            finally
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var _ = Task.Run(async () =>
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                    getPledgesLocker.Release();
 | 
					                    getPledgesLocker.Release();
 | 
				
			||||||
                });
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if(File.Exists(cacheFileName))
 | 
				
			||||||
 | 
					                Pledges = JsonConvert.DeserializeObject<PatreonUserAndReward[]>(File.ReadAllText("./patreon_rewards.json"))
 | 
				
			||||||
 | 
					                    .ToImmutableArray();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
using Discord;
 | 
					using Discord;
 | 
				
			||||||
using Discord.WebSocket;
 | 
					using Discord.WebSocket;
 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					using NadekoBot.Extensions;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Database;
 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					using NadekoBot.Services.Database.Models;
 | 
				
			||||||
using NLog;
 | 
					using NLog;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
@@ -30,10 +31,11 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
        private readonly CancellationTokenSource cancelSource;
 | 
					        private readonly CancellationTokenSource cancelSource;
 | 
				
			||||||
        private readonly CancellationToken cancelAllToken;
 | 
					        private readonly CancellationToken cancelAllToken;
 | 
				
			||||||
        private readonly BotConfig _config;
 | 
					        private readonly BotConfig _config;
 | 
				
			||||||
        private readonly DiscordShardedClient _client;
 | 
					        private readonly DiscordSocketClient _client;
 | 
				
			||||||
        private readonly DbService _db;
 | 
					        private readonly DbService _db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public RemindService(DiscordShardedClient client, BotConfig config, DbService db)
 | 
					        public RemindService(DiscordSocketClient client, BotConfig config, DbService db, 
 | 
				
			||||||
 | 
					            List<long> guilds, IUnitOfWork uow)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _config = config;
 | 
					            _config = config;
 | 
				
			||||||
            _client = client;
 | 
					            _client = client;
 | 
				
			||||||
@@ -42,11 +44,8 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            cancelSource = new CancellationTokenSource();
 | 
					            cancelSource = new CancellationTokenSource();
 | 
				
			||||||
            cancelAllToken = cancelSource.Token;
 | 
					            cancelAllToken = cancelSource.Token;
 | 
				
			||||||
            List<Reminder> reminders;
 | 
					            
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            var reminders = uow.Reminders.GetIncludedReminders(guilds).ToList();
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                reminders = uow.Reminders.GetAll().ToList();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            RemindMessageFormat = _config.RemindMessageFormat;
 | 
					            RemindMessageFormat = _config.RemindMessageFormat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var r in reminders)
 | 
					            foreach (var r in reminders)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
        private IUserMessage oldMsg = null;
 | 
					        private IUserMessage oldMsg = null;
 | 
				
			||||||
        private Timer _t;
 | 
					        private Timer _t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public RepeatRunner(DiscordShardedClient client, Repeater repeater)
 | 
					        public RepeatRunner(DiscordSocketClient client, Repeater repeater)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _log = LogManager.GetCurrentClassLogger();
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
            Repeater = repeater;
 | 
					            Repeater = repeater;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,69 +0,0 @@
 | 
				
			|||||||
using Discord;
 | 
					 | 
				
			||||||
using Discord.WebSocket;
 | 
					 | 
				
			||||||
using NadekoBot.Extensions;
 | 
					 | 
				
			||||||
using NadekoBot.Services.Database.Models;
 | 
					 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace NadekoBot.Services.Utility
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class CrossServerTextService
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers =
 | 
					 | 
				
			||||||
            new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
 | 
					 | 
				
			||||||
        private DiscordShardedClient _client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public CrossServerTextService(IEnumerable<GuildConfig> guildConfigs, DiscordShardedClient client)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _client = client;
 | 
					 | 
				
			||||||
            _client.MessageReceived += Client_MessageReceived;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Task Client_MessageReceived(SocketMessage imsg)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var _ = Task.Run(async () => {
 | 
					 | 
				
			||||||
                try
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (imsg.Author.IsBot)
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
                    var msg = imsg as IUserMessage;
 | 
					 | 
				
			||||||
                    if (msg == null)
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
                    var channel = imsg.Channel as ITextChannel;
 | 
					 | 
				
			||||||
                    if (channel == null)
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
                    if (msg.Author.Id == _client.CurrentUser.Id) return;
 | 
					 | 
				
			||||||
                    foreach (var subscriber in Subscribers)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        var set = subscriber.Value;
 | 
					 | 
				
			||||||
                        if (!set.Contains(channel))
 | 
					 | 
				
			||||||
                            continue;
 | 
					 | 
				
			||||||
                        foreach (var chan in set.Except(new[] { channel }))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            try
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                await chan.SendMessageAsync(GetMessage(channel, (IGuildUser)msg.Author,
 | 
					 | 
				
			||||||
                                    msg)).ConfigureAwait(false);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            catch
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                // ignored
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                catch
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    // ignored
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string GetMessage(ITextChannel channel, IGuildUser user, IUserMessage message) =>
 | 
					 | 
				
			||||||
            $"**{channel.Guild.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -50,12 +50,12 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public bool ToggleVerboseErrors(ulong guildId)
 | 
					        public bool ToggleVerboseErrors(ulong guildId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            bool enabled;
 | 
				
			||||||
            using (var uow = _db.UnitOfWork)
 | 
					            using (var uow = _db.UnitOfWork)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var gc = uow.GuildConfigs.For(guildId, set => set);
 | 
					                var gc = uow.GuildConfigs.For(guildId, set => set);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                gc.VerboseErrors = !gc.VerboseErrors;
 | 
					                enabled = gc.VerboseErrors = !gc.VerboseErrors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                uow.Complete();
 | 
					                uow.Complete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,15 +65,12 @@ namespace NadekoBot.Services.Utility
 | 
				
			|||||||
                    guildsEnabled.TryRemove(guildId);
 | 
					                    guildsEnabled.TryRemove(guildId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (guildsEnabled.Add(guildId))
 | 
					            if (enabled)
 | 
				
			||||||
            {
 | 
					                guildsEnabled.Add(guildId);
 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                guildsEnabled.TryRemove(guildId);
 | 
					                guildsEnabled.TryRemove(guildId);
 | 
				
			||||||
                return false;
 | 
					
 | 
				
			||||||
            }
 | 
					            return enabled;            
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										109
									
								
								src/NadekoBot/ShardsCoordinator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/NadekoBot/ShardsCoordinator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					using NadekoBot.DataStructures.ShardCom;
 | 
				
			||||||
 | 
					using NadekoBot.Services;
 | 
				
			||||||
 | 
					using NadekoBot.Services.Impl;
 | 
				
			||||||
 | 
					using NLog;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ShardsCoordinator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly BotCredentials Credentials;
 | 
				
			||||||
 | 
					        private Process[] ShardProcesses;
 | 
				
			||||||
 | 
					        public ShardComMessage[] Statuses { get; }
 | 
				
			||||||
 | 
					        public int GuildCount => Statuses.ToArray()
 | 
				
			||||||
 | 
					            .Where(x => x != null)
 | 
				
			||||||
 | 
					            .Sum(x => x.Guilds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly Logger _log;
 | 
				
			||||||
 | 
					        private readonly ShardComServer _comServer;
 | 
				
			||||||
 | 
					        private readonly int _port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ShardsCoordinator(int port)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogSetup.SetupLogger();
 | 
				
			||||||
 | 
					            Credentials = new BotCredentials();
 | 
				
			||||||
 | 
					            ShardProcesses = new Process[Credentials.TotalShards];
 | 
				
			||||||
 | 
					            Statuses = new ShardComMessage[Credentials.TotalShards];
 | 
				
			||||||
 | 
					            _log = LogManager.GetCurrentClassLogger();
 | 
				
			||||||
 | 
					            _port = port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _comServer = new ShardComServer(port);
 | 
				
			||||||
 | 
					            _comServer.Start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _comServer.OnDataReceived += _comServer_OnDataReceived;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Task _comServer_OnDataReceived(ShardComMessage msg)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Statuses[msg.ShardId] = msg;
 | 
				
			||||||
 | 
					            if (msg.ConnectionState == Discord.ConnectionState.Disconnected || msg.ConnectionState == Discord.ConnectionState.Disconnecting)
 | 
				
			||||||
 | 
					                _log.Error("!!! SHARD {0} IS IN {1} STATE", msg.ShardId, msg.ConnectionState.ToString());
 | 
				
			||||||
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task RunAsync()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var curProcessId = Process.GetCurrentProcess().Id;
 | 
				
			||||||
 | 
					            for (int i = 1; i < Credentials.TotalShards; i++)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var p = Process.Start(new ProcessStartInfo()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    FileName = Credentials.ShardRunCommand,
 | 
				
			||||||
 | 
					                    Arguments = string.Format(Credentials.ShardRunArguments, i, curProcessId, _port)
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                await Task.Delay(5000);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task RunAndBlockAsync()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await RunAsync().ConfigureAwait(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _log.Error(ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            await Task.Run(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string input;
 | 
				
			||||||
 | 
					                while ((input = Console.ReadLine()?.ToLowerInvariant()) != "quit")
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    try
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        switch (input)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            case "ls":
 | 
				
			||||||
 | 
					                                var groupStr = string.Join(",", Statuses
 | 
				
			||||||
 | 
					                                    .ToArray()
 | 
				
			||||||
 | 
					                                    .Where(x => x != null)
 | 
				
			||||||
 | 
					                                    .GroupBy(x => x.ConnectionState)
 | 
				
			||||||
 | 
					                                    .Select(x => x.Count() + " " + x.Key));
 | 
				
			||||||
 | 
					                                _log.Info(string.Join("\n", Statuses
 | 
				
			||||||
 | 
					                                    .ToArray()
 | 
				
			||||||
 | 
					                                    .Where(x => x != null)
 | 
				
			||||||
 | 
					                                    .Select(x => $"Shard {x.ShardId} is in {x.ConnectionState.ToString()} state with {x.Guilds} servers")) + "\n" + groupStr);
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                            default:
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch (Exception ex)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _log.Warn(ex);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            foreach (var p in ShardProcesses)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try { p.Kill(); } catch { }
 | 
				
			||||||
 | 
					                try { p.Dispose(); } catch { }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -70,7 +70,7 @@ namespace NadekoBot.Extensions
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// danny kamisama
 | 
					        /// danny kamisama
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordShardedClient client, int currentPage, Func<int, EmbedBuilder> pageFunc, int? lastPage = null, bool addPaginatedFooter = true)
 | 
					        public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordSocketClient client, int currentPage, Func<int, EmbedBuilder> pageFunc, int? lastPage = null, bool addPaginatedFooter = true)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var embed = pageFunc(currentPage);
 | 
					            var embed = pageFunc(currentPage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -134,7 +134,7 @@ namespace NadekoBot.Extensions
 | 
				
			|||||||
                return embed.WithFooter(efb => efb.WithText(curPage.ToString()));
 | 
					                return embed.WithFooter(efb => efb.WithText(curPage.ToString()));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static ReactionEventWrapper OnReaction(this IUserMessage msg, DiscordShardedClient client, Action<SocketReaction> reactionAdded, Action<SocketReaction> reactionRemoved = null)
 | 
					        public static ReactionEventWrapper OnReaction(this IUserMessage msg, DiscordSocketClient client, Action<SocketReaction> reactionAdded, Action<SocketReaction> reactionRemoved = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (reactionRemoved == null)
 | 
					            if (reactionRemoved == null)
 | 
				
			||||||
                reactionRemoved = delegate { };
 | 
					                reactionRemoved = delegate { };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -154,6 +154,7 @@
 | 
				
			|||||||
  "administration_old_topic": "Old topic",
 | 
					  "administration_old_topic": "Old topic",
 | 
				
			||||||
  "administration_perms": "Error. Most likely I don't have sufficient permissions.",
 | 
					  "administration_perms": "Error. Most likely I don't have sufficient permissions.",
 | 
				
			||||||
  "permissions_perms_reset": "Permissions for this server are reset.",
 | 
					  "permissions_perms_reset": "Permissions for this server are reset.",
 | 
				
			||||||
 | 
					  "permissions_trigger": "Permission number #{0} {1} is preventing this action.",
 | 
				
			||||||
  "administration_prot_active": "Active protections",
 | 
					  "administration_prot_active": "Active protections",
 | 
				
			||||||
  "administration_prot_disable": "{0} has been **disabled** on this server.",
 | 
					  "administration_prot_disable": "{0} has been **disabled** on this server.",
 | 
				
			||||||
  "administration_prot_enable": "{0} Enabled",
 | 
					  "administration_prot_enable": "{0} Enabled",
 | 
				
			||||||
@@ -446,7 +447,7 @@
 | 
				
			|||||||
  "music_skipped_to": "Skipped to `{0}:{1}`",
 | 
					  "music_skipped_to": "Skipped to `{0}:{1}`",
 | 
				
			||||||
  "music_songs_shuffled": "Songs shuffled",
 | 
					  "music_songs_shuffled": "Songs shuffled",
 | 
				
			||||||
  "music_song_moved": "Song moved",
 | 
					  "music_song_moved": "Song moved",
 | 
				
			||||||
  "music_song_not_found":  "No song found.",
 | 
					  "music_song_not_found": "No song found.",
 | 
				
			||||||
  "music_time_format": "{0}h {1}m {2}s",
 | 
					  "music_time_format": "{0}h {1}m {2}s",
 | 
				
			||||||
  "music_to_position": "To position",
 | 
					  "music_to_position": "To position",
 | 
				
			||||||
  "music_unlimited": "unlimited",
 | 
					  "music_unlimited": "unlimited",
 | 
				
			||||||
@@ -523,7 +524,7 @@
 | 
				
			|||||||
  "searches_cost": "Cost",
 | 
					  "searches_cost": "Cost",
 | 
				
			||||||
  "searches_date": "Date",
 | 
					  "searches_date": "Date",
 | 
				
			||||||
  "searches_define": "Define:",
 | 
					  "searches_define": "Define:",
 | 
				
			||||||
  "searches_define_unknown":  "Can't find the definition for that term.",
 | 
					  "searches_define_unknown": "Can't find the definition for that term.",
 | 
				
			||||||
  "searches_dropped": "Dropped",
 | 
					  "searches_dropped": "Dropped",
 | 
				
			||||||
  "searches_episodes": "Episodes",
 | 
					  "searches_episodes": "Episodes",
 | 
				
			||||||
  "searches_error_occured": "Error occurred.",
 | 
					  "searches_error_occured": "Error occurred.",
 | 
				
			||||||
@@ -534,7 +535,7 @@
 | 
				
			|||||||
  "searches_hashtag_error": "Failed finding a definition for that tag.",
 | 
					  "searches_hashtag_error": "Failed finding a definition for that tag.",
 | 
				
			||||||
  "searches_height_weight": "Height/Weight",
 | 
					  "searches_height_weight": "Height/Weight",
 | 
				
			||||||
  "searches_height_weight_val": "{0}m/{1}kg",
 | 
					  "searches_height_weight_val": "{0}m/{1}kg",
 | 
				
			||||||
  "searches_hex_invalid":  "Invalid color specified.",
 | 
					  "searches_hex_invalid": "Invalid color specified.",
 | 
				
			||||||
  "searches_humidity": "Humidity",
 | 
					  "searches_humidity": "Humidity",
 | 
				
			||||||
  "searches_image_search_for": "Image search for:",
 | 
					  "searches_image_search_for": "Image search for:",
 | 
				
			||||||
  "searches_imdb_fail": "Failed to find that movie.",
 | 
					  "searches_imdb_fail": "Failed to find that movie.",
 | 
				
			||||||
@@ -613,9 +614,6 @@
 | 
				
			|||||||
  "utility_convert_not_found": "Cannot convert {0} to {1}: units not found",
 | 
					  "utility_convert_not_found": "Cannot convert {0} to {1}: units not found",
 | 
				
			||||||
  "utility_convert_type_error": "Cannot convert {0} to {1}: types of unit are not equal",
 | 
					  "utility_convert_type_error": "Cannot convert {0} to {1}: types of unit are not equal",
 | 
				
			||||||
  "utility_created_at": "Created at",
 | 
					  "utility_created_at": "Created at",
 | 
				
			||||||
  "utility_csc_join": "Joined cross server channel.",
 | 
					 | 
				
			||||||
  "utility_csc_leave": "Left cross server channel.",
 | 
					 | 
				
			||||||
  "utility_csc_token": "This is your CSC token",
 | 
					 | 
				
			||||||
  "utility_custom_emojis": "Custom emojis",
 | 
					  "utility_custom_emojis": "Custom emojis",
 | 
				
			||||||
  "utility_error": "Error",
 | 
					  "utility_error": "Error",
 | 
				
			||||||
  "utility_features": "Features",
 | 
					  "utility_features": "Features",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,5 +15,9 @@
 | 
				
			|||||||
    "Type": "sqlite",
 | 
					    "Type": "sqlite",
 | 
				
			||||||
    "ConnectionString": "Filename=./data/NadekoBot.db"
 | 
					    "ConnectionString": "Filename=./data/NadekoBot.db"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "TotalShards": 1
 | 
					  "TotalShards": 1,
 | 
				
			||||||
 | 
					  "PatreonAccessToken": "",
 | 
				
			||||||
 | 
					  "ShardRunCommand": "",
 | 
				
			||||||
 | 
					  "ShardRunArguments": "",
 | 
				
			||||||
 | 
					  "ShardRunPort": null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user