From 0d918d39c957616b145b27d73372f46d94741323 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 30 Sep 2016 05:58:44 +0200 Subject: [PATCH] ShardedDiscordClient Implemented. I'm not feeling well --- .gitignore | 3 +- .../Modules/Administration/Administration.cs | 4 +- .../Administration/Commands/LogCommand.cs | 4 +- .../Commands/RatelimitCommand.cs | 2 +- .../Administration/Commands/SelfCommands.cs | 4 +- .../Modules/ClashOfClans/ClashOfClans.cs | 2 +- src/NadekoBot/Modules/DiscordModule.cs | 4 +- src/NadekoBot/Modules/Gambling/Gambling.cs | 2 +- .../Games/Commands/Trivia/TriviaGame.cs | 2 +- src/NadekoBot/Modules/Games/Games.cs | 4 +- src/NadekoBot/Modules/Help/Help.cs | 7 +- src/NadekoBot/Modules/Music/Music.cs | 29 +++++-- src/NadekoBot/Modules/NSFW/NSFW.cs | 2 +- .../Modules/Permissions/Permissions.cs | 2 +- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 2 +- src/NadekoBot/Modules/Searches/Searches.cs | 2 +- .../Modules/Translator/Translator.cs | 2 +- src/NadekoBot/Modules/Utility/Utility.cs | 2 +- src/NadekoBot/NadekoBot.cs | 6 +- src/NadekoBot/Services/CommandHandler.cs | 4 +- src/NadekoBot/Services/Impl/StatsService.cs | 12 ++- src/NadekoBot/ShardedDiscordClient.cs | 83 +++++++++++++++++++ 22 files changed, 145 insertions(+), 39 deletions(-) create mode 100644 src/NadekoBot/ShardedDiscordClient.cs diff --git a/.gitignore b/.gitignore index df1024a7..a8e78718 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ src/NadekoBot/project.lock.json NadekoBot.sln.iml .idea/workspace.xml .idea/vcs.xml -.idea/modules.xml \ No newline at end of file +.idea/modules.xml +src/tests/bin \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 5eca1ac9..a3ba38df 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Administration [NadekoModule("Administration", ".")] public partial class Administration : DiscordModule { - public Administration(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Administration(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; } @@ -614,7 +614,7 @@ namespace NadekoBot.Modules.Administration { var channel = (ITextChannel)umsg.Channel; - foreach (var ch in (await _client.GetGuildsAsync().ConfigureAwait(false)).Select(async g => await g.GetDefaultChannelAsync().ConfigureAwait(false))) + foreach (var ch in _client.GetGuilds().Select(async g => await g.GetDefaultChannelAsync().ConfigureAwait(false))) { await channel.SendMessageAsync(message).ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index d985da63..b7be6999 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Administration [Group] public class LogCommands { - private DiscordSocketClient _client { get; } + private ShardedDiscordClient _client { get; } private Logger _log { get; } private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; @@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Administration private Timer t; private IGoogleApiService _google { get; } - public LogCommands(DiscordSocketClient client, IGoogleApiService google) + public LogCommands(ShardedDiscordClient client, IGoogleApiService google) { _client = client; _google = google; diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index fa606adb..ee612047 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Administration { public static ConcurrentDictionary RatelimitingChannels = new ConcurrentDictionary(); - private DiscordSocketClient _client { get; } + private ShardedDiscordClient _client { get; } public class Ratelimiter { diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index d37c2398..78444817 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -12,9 +12,9 @@ namespace NadekoBot.Modules.Administration [Group] class SelfCommands { - private DiscordSocketClient _client; + private ShardedDiscordClient _client; - public SelfCommands(DiscordSocketClient client) + public SelfCommands(ShardedDiscordClient client) { this._client = client; } diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 04ac67aa..c643dd46 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.ClashOfClans { public static ConcurrentDictionary> ClashWars { get; set; } = new ConcurrentDictionary>(); - public ClashOfClans(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public ClashOfClans(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { using (var uow = DbHandler.UnitOfWork()) { diff --git a/src/NadekoBot/Modules/DiscordModule.cs b/src/NadekoBot/Modules/DiscordModule.cs index e73d1789..5ad80226 100644 --- a/src/NadekoBot/Modules/DiscordModule.cs +++ b/src/NadekoBot/Modules/DiscordModule.cs @@ -10,11 +10,11 @@ namespace NadekoBot.Modules { protected ILocalization _l { get; } protected CommandService _commands { get; } - protected DiscordSocketClient _client { get; } + protected ShardedDiscordClient _client { get; } protected Logger _log { get; } private string _prefix { get; } - public DiscordModule(ILocalization loc, CommandService cmds, DiscordSocketClient client) + public DiscordModule(ILocalization loc, CommandService cmds, ShardedDiscordClient client) { string prefix; if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix)) diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 216b5df0..6969155f 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Gambling public static string CurrencyPluralName { get; set; } public static string CurrencySign { get; set; } - public Gambling(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Gambling(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { using (var uow = DbHandler.UnitOfWork()) { diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index c4e960a6..c4b27ff5 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Games.Trivia { if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return; if ((umsg.Channel as ITextChannel).Guild != guild) return; - if (umsg.Author.Id == (await NadekoBot.Client.GetCurrentUserAsync()).Id) return; + if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; var guildUser = umsg.Author as IGuildUser; diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index ea57db12..0dbb4d58 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Games } } } - public Games(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Games(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } @@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Games else if ((pick == 0 && nadekoPick == 1) || (pick == 1 && nadekoPick == 2) || (pick == 2 && nadekoPick == 0)) - msg = $"{(await NadekoBot.Client.GetCurrentUserAsync()).Mention} won! :{GetRPSPick(nadekoPick)}: beats :{GetRPSPick(pick)}:"; + msg = $"{NadekoBot.Client.GetCurrentUser().Mention} won! :{GetRPSPick(nadekoPick)}: beats :{GetRPSPick(pick)}:"; else msg = $"{umsg.Author.Mention} won! :{GetRPSPick(pick)}: beats :{GetRPSPick(nadekoPick)}:"; diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 4a36bd43..bb6fcd65 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Help return str + String.Format(str, NadekoBot.Credentials.ClientId); } } - public Help(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Help(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } @@ -99,7 +99,7 @@ namespace NadekoBot.Modules.Help [LocalizedCommand, LocalizedRemarks, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [OwnerOnly] - public async Task Hgit(IUserMessage umsg) + public Task Hgit(IUserMessage umsg) { var helpstr = new StringBuilder(); @@ -115,12 +115,13 @@ namespace NadekoBot.Modules.Help } helpstr.AppendLine($"`{com.Text}` {string.Join(" ", com.Aliases.Skip(1).Select(a=>"`"+a+"`"))} | {com.Remarks} | {com.Summary}"); } - helpstr = helpstr.Replace((await NadekoBot.Client.GetCurrentUserAsync()).Username , "@BotName"); + helpstr = helpstr.Replace(NadekoBot.Client.GetCurrentUser().Username , "@BotName"); #if DEBUG File.WriteAllText("../../../../../docs/Commands List.md", helpstr.ToString()); #else File.WriteAllText("commandlist.md", helpstr.ToString()); #endif + return Task.CompletedTask; } [LocalizedCommand, LocalizedRemarks, LocalizedSummary, LocalizedAlias] diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index b9ad5fd6..abc9702d 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Music public const string MusicDataPath = "data/musicdata"; private IGoogleApiService _google; - public Music(ILocalization loc, CommandService cmds, DiscordSocketClient client, IGoogleApiService google) : base(loc, cmds, client) + public Music(ILocalization loc, CommandService cmds, ShardedDiscordClient client, IGoogleApiService google) : base(loc, cmds, client) { //it can fail if its currenctly opened or doesn't exist. Either way i don't care try { Directory.Delete(MusicDataPath, true); } catch { } @@ -520,16 +520,33 @@ namespace NadekoBot.Modules.Music await channel.SendMessageAsync($"🎵🔁`Repeat playlist {(currentValue ? "enabled" : "disabled")}`").ConfigureAwait(false); } - /// - //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[LocalizedCommand, LocalizedRemarks, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Save(IUserMessage umsg, [Remainder] string name) //{ // var channel = (ITextChannel)umsg.Channel; + // MusicPlayer musicPlayer; + // if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + // return; + // var curSong = musicPlayer.CurrentSong; + // var items = musicPlayer.Playlist.Append(curSong); + + // MusicPlaylist playlist; + // using (var uow = DbHandler.UnitOfWork()) + // { + // playlist = new MusicPlaylist + // { + // Name = name, + // Songs = items.ToList() + // }; + // uow.MusicPlaylists.Add(playlist); + // } + + // await channel.SendMessageAsync($"Playlist saved as {name}, id: {playlist.Id}."); //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[LocalizedCommand, LocalizedRemarks, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Load(IUserMessage umsg, [Remainder] string name) //{ @@ -537,7 +554,7 @@ namespace NadekoBot.Modules.Music //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[LocalizedCommand, LocalizedRemarks, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Playlists(IUserMessage umsg, [Remainder] string num) //{ @@ -545,7 +562,7 @@ namespace NadekoBot.Modules.Music //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[LocalizedCommand, LocalizedRemarks, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task DeletePlaylist(IUserMessage umsg, [Remainder] string pl) //{ diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 3b7a6e62..f64686d9 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -18,7 +18,7 @@ namespace NadekoBot.Modules.NSFW [NadekoModule("NSFW", "~")] public class NSFW : DiscordModule { - public NSFW(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public NSFW(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index 17227d3a..087d3755 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions [NadekoModule("Permissions", ";")] public class Permissions : DiscordModule { - public Permissions(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Permissions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 870aa482..1633252e 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -10,7 +10,7 @@ namespace NadekoBot.Modules.Games [Module(">", AppendSpace = false)] public partial class Pokemon : DiscordModule { - public Pokemon(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Pokemon(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index cb886063..06ab3933 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches { private IGoogleApiService _google { get; } - public Searches(ILocalization loc, CommandService cmds, DiscordSocketClient client, IGoogleApiService youtube) : base(loc, cmds, client) + public Searches(ILocalization loc, CommandService cmds, ShardedDiscordClient client, IGoogleApiService youtube) : base(loc, cmds, client) { _google = youtube; } diff --git a/src/NadekoBot/Modules/Translator/Translator.cs b/src/NadekoBot/Modules/Translator/Translator.cs index 4d66a7a6..bc0d6c8d 100644 --- a/src/NadekoBot/Modules/Translator/Translator.cs +++ b/src/NadekoBot/Modules/Translator/Translator.cs @@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Translator [NadekoModule("Translator", "~")] public class Translator : DiscordModule { - public Translator(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Translator(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 2614faea..32f37096 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Utility [NadekoModule("Utility", ".")] public partial class Utility : DiscordModule { - public Utility(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) + public Utility(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 63413e9b..9d4b0985 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -26,7 +26,7 @@ namespace NadekoBot public static CommandService CommandService { get; private set; } public static CommandHandler CommandHandler { get; private set; } - public static DiscordSocketClient Client { get; private set; } + public static ShardedDiscordClient Client { get; private set; } public static Localization Localizer { get; private set; } public static BotCredentials Credentials { get; private set; } @@ -43,7 +43,7 @@ namespace NadekoBot _log.Info("Starting NadekoBot v" + typeof(NadekoBot).GetTypeInfo().Assembly.GetCustomAttribute().InformationalVersion); //create client - Client = new DiscordSocketClient(new DiscordSocketConfig + Client = new ShardedDiscordClient (new DiscordSocketConfig { AudioMode = Discord.Audio.AudioMode.Outgoing, MessageCacheSize = 10, @@ -61,7 +61,7 @@ namespace NadekoBot //setup DI var depMap = new DependencyMap(); depMap.Add(Localizer); - depMap.Add(Client); + depMap.Add(Client); depMap.Add(CommandService); depMap.Add(Google); diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 01c18b59..7a562ca7 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -18,13 +18,13 @@ namespace NadekoBot.Services { public class CommandHandler { - private DiscordSocketClient _client; + private ShardedDiscordClient _client; private CommandService _commandService; private Logger _log; public event EventHandler CommandExecuted = delegate { }; - public CommandHandler(DiscordSocketClient client, CommandService commandService) + public CommandHandler(ShardedDiscordClient client, CommandService commandService) { _client = client; _commandService = commandService; diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 3d33bb9f..a2ec5b65 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Services.Impl public class StatsService : IStatsService { private int messageCounter; - private DiscordSocketClient client; + private ShardedDiscordClient client; private DateTime started; private int commandsRan = 0; @@ -22,7 +22,7 @@ namespace NadekoBot.Services.Impl public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); - public StatsService(DiscordSocketClient client, CommandHandler cmdHandler) + public StatsService(ShardedDiscordClient client, CommandHandler cmdHandler) { this.client = client; @@ -33,14 +33,18 @@ namespace NadekoBot.Services.Impl this.client.Disconnected += _ => Reset(); } - public Task Print() => Task.FromResult($@"`Author: Kwoth` `Library: Discord.Net` + public Task Print() + { + var curUser = client.GetCurrentUser(); + return Task.FromResult($@"`Author: Kwoth` `Library: Discord.Net` `Bot Version: {BotVersion}` -`Bot id: {(client.GetCurrentUser()).Id}` +`Bot id: {curUser.Id}` `Owners' Ids: {string.Join(", ", NadekoBot.Credentials.OwnerIds)}` `Uptime: {GetUptimeString()}` `Servers: {client.GetGuilds().Count} | TextChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is ITextChannel)).Count()} | VoiceChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is IVoiceChannel)).Count()}` `Commands Ran this session: {commandsRan}` `Messages: {messageCounter} ({messageCounter / (double)GetUptime().TotalSeconds:F2}/sec)` `Heap: {Heap} MB`"); + } public Task Reset() { diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs new file mode 100644 index 00000000..6e454a4e --- /dev/null +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -0,0 +1,83 @@ +using Discord; +using Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.API; +using Discord.Logging; +using System.IO; + +namespace NadekoBot +{ + public class ShardedDiscordClient + { + private DiscordSocketConfig discordSocketConfig; + + public Func UserJoined { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func MessageReceived { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func UserLeft { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func UserUpdated { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func, IMessage, Task> MessageUpdated { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func, Task> MessageDeleted { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func UserBanned { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func UserUnbanned { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func UserPresenceUpdated { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func UserVoiceStateUpdated { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func ChannelCreated { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func ChannelDestroyed { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func ChannelUpdated { get; internal set; } = delegate { return Task.CompletedTask; }; + public Func Disconnected { get; internal set; } = delegate { return Task.CompletedTask; }; + + private IReadOnlyList Clients { get; } + + public ShardedDiscordClient (DiscordSocketConfig discordSocketConfig) + { + this.discordSocketConfig = discordSocketConfig; + + var clientList = new List(); + for (int i = 0; i < discordSocketConfig.TotalShards; i++) + { + discordSocketConfig.ShardId = i; + var client = new DiscordSocketClient(discordSocketConfig); + clientList.Add(client); + client.UserJoined += async arg1 => await UserJoined(arg1); + client.MessageReceived += async arg1 => await MessageReceived(arg1); + client.UserLeft += async arg1 => await UserLeft(arg1); + client.UserUpdated += async (arg1, gu2) => await UserUpdated(arg1, gu2); + client.MessageUpdated += async (arg1, m2) => await MessageUpdated(arg1, m2); + client.MessageDeleted += async (arg1, arg2) => await MessageDeleted(arg1, arg2); + client.UserBanned += async (arg1, arg2) => await UserBanned(arg1, arg2); + client.UserPresenceUpdated += async (arg1, arg2, arg3) => await UserPresenceUpdated(arg1, arg2, arg3); + client.UserVoiceStateUpdated += async (arg1, arg2, arg3) => await UserVoiceStateUpdated(arg1, arg2, arg3); + client.ChannelCreated += async arg => await ChannelCreated(arg); + client.ChannelDestroyed += async arg => await ChannelDestroyed(arg); + client.ChannelUpdated += async (arg1, arg2) => await ChannelUpdated(arg1, arg2); + } + + Clients = clientList.AsReadOnly(); + } + + public ISelfUser GetCurrentUser() => + Clients.Select(c => c.GetCurrentUser()).FirstOrDefault(u => u != null); + + public IReadOnlyCollection GetGuilds() => + Clients.SelectMany(c => c.GetGuilds()).ToArray(); + + public IGuild GetGuild(ulong id) => + Clients.Select(c => c.GetGuild(id)).FirstOrDefault(g => g != null); + + public Task GetDMChannelAsync(ulong channelId) => + Clients.Select(async c => await c.GetDMChannelAsync(channelId).ConfigureAwait(false)).FirstOrDefault(c => c != null); + + internal Task LoginAsync(TokenType tokenType, string token) => + Task.WhenAll(Clients.Select(c => c.LoginAsync(tokenType, token))); + + internal Task ConnectAsync() => + Task.WhenAll(Clients.Select(c => c.ConnectAsync())); + + internal Task DownloadAllUsersAsync() => + Task.WhenAll(Clients.Select(c => c.DownloadAllUsersAsync())); + } +}