From 173df19439a7bf9bf4e5f8d749f91b5abb9c4b07 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 14 Feb 2016 13:45:58 -0400 Subject: [PATCH 01/51] Fixed crash with short mention activation strings --- src/Discord.Net.Commands/CommandService.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 5969b9691..8e6f42c51 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -97,10 +97,15 @@ namespace Discord.Commands //Check for mention if (cmdMsg == null && Config.AllowMentionPrefix) { - if (msg.StartsWith(client.CurrentUser.Mention)) - cmdMsg = msg.Substring(client.CurrentUser.Mention.Length + 1); - else if (msg.StartsWith($"@{client.CurrentUser.Name}")) - cmdMsg = msg.Substring(client.CurrentUser.Name.Length + 1); + string mention = client.CurrentUser.Mention; + if (msg.StartsWith(mention) && msg.Length > mention.Length) + cmdMsg = msg.Substring(mention.Length + 1); + else + { + mention = $"@{client.CurrentUser.Name}"; + if (msg.StartsWith(mention) && msg.Length > mention.Length) + cmdMsg = msg.Substring(mention.Length + 1); + } } //Check using custom activator From 2d0ff089a9a63cd10ddf116d5f19ab3265dcc326 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 14 Feb 2016 13:48:58 -0400 Subject: [PATCH 02/51] 0.9.0-rc3-1 --- src/Discord.Net.Commands/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/project.json b/src/Discord.Net.Commands/project.json index f93bb3d77..c460619e4 100644 --- a/src/Discord.Net.Commands/project.json +++ b/src/Discord.Net.Commands/project.json @@ -1,5 +1,5 @@ { - "version": "0.9.0-rc3", + "version": "0.9.0-rc3-1", "description": "A Discord.Net extension adding basic command support.", "authors": [ "RogueException" ], "tags": [ "discord", "discordapp" ], From 702125fdc67796314b75fa0dfb5fc9472bdd5ba2 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 14 Feb 2016 21:58:55 -0400 Subject: [PATCH 03/51] Stop adding users on PRESENCE_UPDATE --- src/Discord.Net/DiscordClient.cs | 2 +- src/Discord.Net/Models/Server.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 1d3343f16..01098cab0 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -902,7 +902,7 @@ namespace Discord break; } else - user = server.AddUser(data.User.Id); + user = server.GetUser(data.User.Id); } if (user != null) diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 6fb42ed92..aaed6aac8 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -456,8 +456,8 @@ namespace Discord if (_users.TryGetOrAdd(id, member, out member)) { - foreach (var channel in AllChannels) - channel.AddUser(member.User); + foreach (var channel in _channels) + channel.Value.AddUser(member.User); } return member.User; } @@ -467,8 +467,8 @@ namespace Discord Member member; if (_users.TryRemove(id, out member)) { - foreach (var channel in AllChannels) - channel.RemoveUser(id); + foreach (var channel in _channels) + channel.Value.RemoveUser(id); return member.User; } return null; From 5c64cb68bf8a82936e192ecb0b3ba99cf123bd57 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 15:08:59 -0400 Subject: [PATCH 04/51] Cleaned up a bit of the modules/services add/get format --- src/Discord.Net.Audio/AudioClient.cs | 2 +- src/Discord.Net.Audio/AudioExtensions.cs | 12 +++---- src/Discord.Net.Commands/CommandExtensions.cs | 4 +-- .../Levels/PermissionLevelChecker.cs | 2 +- .../Levels/PermissionLevelExtensions.cs | 2 +- .../Permissions/Userlist/BlacklistChecker.cs | 2 +- .../Userlist/BlacklistExtensions.cs | 12 +++---- .../Permissions/Userlist/WhitelistChecker.cs | 2 +- .../Userlist/WhitelistExtensions.cs | 12 +++---- src/Discord.Net.Modules/ModuleExtensions.cs | 18 +++++------ src/Discord.Net.Modules/ModuleManager.cs | 13 +++++++- src/Discord.Net.Modules/ModuleService.cs | 31 ++++++++++--------- src/Discord.Net.Modules/project.json | 2 +- src/Discord.Net.Net45/Discord.Net.csproj | 10 +++--- src/Discord.Net/DiscordClient.cs | 19 ++++++++++-- .../{Extensions.cs => InternalExtensions.cs} | 16 ---------- ...ServiceManager.cs => ServiceCollection.cs} | 4 +-- 17 files changed, 85 insertions(+), 78 deletions(-) rename src/Discord.Net/{Extensions.cs => InternalExtensions.cs} (93%) rename src/Discord.Net/{ServiceManager.cs => ServiceCollection.cs} (91%) diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs index 46474f6a4..436c7e8c8 100644 --- a/src/Discord.Net.Audio/AudioClient.cs +++ b/src/Discord.Net.Audio/AudioClient.cs @@ -71,7 +71,7 @@ namespace Discord.Audio { Id = id; _config = client.Config; - Service = client.Services.Get(); + Service = client.GetService(); Config = Service.Config; Serializer = client.Serializer; _gatewayState = (int)ConnectionState.Disconnected; diff --git a/src/Discord.Net.Audio/AudioExtensions.cs b/src/Discord.Net.Audio/AudioExtensions.cs index 50f508ff5..198542d8f 100644 --- a/src/Discord.Net.Audio/AudioExtensions.cs +++ b/src/Discord.Net.Audio/AudioExtensions.cs @@ -7,20 +7,20 @@ namespace Discord.Audio { public static DiscordClient UsingAudio(this DiscordClient client, AudioServiceConfig config = null) { - client.Services.Add(new AudioService(config)); + client.AddService(new AudioService(config)); return client; } public static DiscordClient UsingAudio(this DiscordClient client, Action configFunc = null) { var builder = new AudioServiceConfigBuilder(); configFunc(builder); - client.Services.Add(new AudioService(builder)); + client.AddService(new AudioService(builder)); return client; } - public static Task JoinAudio(this Channel channel) => channel.Client.Services.Get().Join(channel); - public static Task LeaveAudio(this Channel channel) => channel.Client.Services.Get().Leave(channel); - public static Task LeaveAudio(this Server server) => server.Client.Services.Get().Leave(server); - public static IAudioClient GetAudioClient(Server server) => server.Client.Services.Get().GetClient(server); + public static Task JoinAudio(this Channel channel) => channel.Client.GetService().Join(channel); + public static Task LeaveAudio(this Channel channel) => channel.Client.GetService().Leave(channel); + public static Task LeaveAudio(this Server server) => server.Client.GetService().Leave(server); + public static IAudioClient GetAudioClient(Server server) => server.Client.GetService().GetClient(server); } } diff --git a/src/Discord.Net.Commands/CommandExtensions.cs b/src/Discord.Net.Commands/CommandExtensions.cs index 557f5ac5a..c57cf099f 100644 --- a/src/Discord.Net.Commands/CommandExtensions.cs +++ b/src/Discord.Net.Commands/CommandExtensions.cs @@ -6,14 +6,14 @@ namespace Discord.Commands { public static DiscordClient UsingCommands(this DiscordClient client, CommandServiceConfig config = null) { - client.Services.Add(new CommandService(config)); + client.AddService(new CommandService(config)); return client; } public static DiscordClient UsingCommands(this DiscordClient client, Action configFunc = null) { var builder = new CommandServiceConfigBuilder(); configFunc(builder); - client.Services.Add(new CommandService(builder)); + client.AddService(new CommandService(builder)); return client; } } diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs index 0ebe5c890..0092c4edf 100644 --- a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs +++ b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs @@ -10,7 +10,7 @@ internal PermissionLevelChecker(DiscordClient client, int minPermissions) { - _service = client.Services.Get(true); + _service = client.GetService(true); _minPermissions = minPermissions; } diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs index 79cae8857..10f153215 100644 --- a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs +++ b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs @@ -6,7 +6,7 @@ namespace Discord.Commands.Permissions.Levels { public static DiscordClient UsingPermissionLevels(this DiscordClient client, Func permissionResolver) { - client.Services.Add(new PermissionLevelService(permissionResolver)); + client.AddService(new PermissionLevelService(permissionResolver)); return client; } diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs index 0c0b5500b..23c48cba9 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs @@ -6,7 +6,7 @@ internal BlacklistChecker(DiscordClient client) { - _service = client.Services.Get(true); + _service = client.GetService(true); } public bool CanRun(Command command, User user, Channel channel, out string error) diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs index 21de4076d..1ff51ee58 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs @@ -6,7 +6,7 @@ namespace Discord.Commands.Permissions.Userlist { public static DiscordClient UsingGlobalBlacklist(this DiscordClient client, params ulong[] initialUserIds) { - client.Services.Add(new BlacklistService(initialUserIds)); + client.AddService(new BlacklistService(initialUserIds)); return client; } @@ -27,22 +27,22 @@ namespace Discord.Commands.Permissions.Userlist } public static IEnumerable GetBlacklistedUserIds(this DiscordClient client) - => client.Services.Get().UserIds; + => client.GetService().UserIds; public static void BlacklistUser(this DiscordClient client, User user) { - client.Services.Get().Add(user.Id); + client.GetService().Add(user.Id); } public static void BlacklistUser(this DiscordClient client, ulong userId) { - client.Services.Get().Add(userId); + client.GetService().Add(userId); } public static void UnBlacklistUser(this DiscordClient client, User user) { - client.Services.Get().Remove(user.Id); + client.GetService().Remove(user.Id); } public static void UnBlacklistUser(this DiscordClient client, ulong userId) { - client.Services.Get().Remove(userId); + client.GetService().Remove(userId); } } } diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs index 783455e3a..fa441644f 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs @@ -6,7 +6,7 @@ internal WhitelistChecker(DiscordClient client) { - _service = client.Services.Get(true); + _service = client.GetService(true); } public bool CanRun(Command command, User user, Channel channel, out string error) diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs index eaa136075..391668298 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs @@ -6,7 +6,7 @@ namespace Discord.Commands.Permissions.Userlist { public static DiscordClient UsingGlobalWhitelist(this DiscordClient client, params ulong[] initialUserIds) { - client.Services.Add(new WhitelistService(initialUserIds)); + client.AddService(new WhitelistService(initialUserIds)); return client; } @@ -27,22 +27,22 @@ namespace Discord.Commands.Permissions.Userlist } public static IEnumerable GetWhitelistedUserIds(this DiscordClient client) - => client.Services.Get().UserIds; + => client.GetService().UserIds; public static void WhitelistUser(this DiscordClient client, User user) { - client.Services.Get().Add(user.Id); + client.GetService().Add(user.Id); } public static void WhitelistUser(this DiscordClient client, ulong userId) { - client.Services.Get().Add(userId); + client.GetService().Add(userId); } public static void UnWhitelistUser(this DiscordClient client, User user) { - client.Services.Get().Remove(user.Id); + client.GetService().Remove(user.Id); } public static void RemoveFromWhitelist(this DiscordClient client, ulong userId) { - client.Services.Get().Remove(userId); + client.GetService().Remove(userId); } } } diff --git a/src/Discord.Net.Modules/ModuleExtensions.cs b/src/Discord.Net.Modules/ModuleExtensions.cs index 070ac9084..7d51ac547 100644 --- a/src/Discord.Net.Modules/ModuleExtensions.cs +++ b/src/Discord.Net.Modules/ModuleExtensions.cs @@ -4,24 +4,22 @@ { public static DiscordClient UsingModules(this DiscordClient client) { - client.Services.Add(new ModuleService()); + client.AddService(new ModuleService()); return client; } - public static DiscordClient AddModule(this DiscordClient client, T instance, string name = null, ModuleFilter filter = ModuleFilter.None) + public static void AddModule(this DiscordClient client, T instance, string name = null, ModuleFilter filter = ModuleFilter.None) where T : class, IModule { - client.Modules().Add(instance, name ?? nameof(T), filter); - return client; + client.GetService().Add(instance, name ?? nameof(T), filter); } - public static DiscordClient AddModule(this DiscordClient client, string name = null, ModuleFilter filter = ModuleFilter.None) + public static void AddModule(this DiscordClient client, string name = null, ModuleFilter filter = ModuleFilter.None) where T : class, IModule, new() { - client.Modules().Add(new T(), name ?? nameof(T), filter); - return client; + client.GetService().Add(new T(), name ?? nameof(T), filter); } - - public static ModuleService Modules(this DiscordClient client, bool required = true) - => client.Services.Get(required); + public static ModuleManager GetModule(this DiscordClient client) + where T : class, IModule + => client.GetService().Get(); } } diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs index c2a8d400f..b00dc244f 100644 --- a/src/Discord.Net.Modules/ModuleManager.cs +++ b/src/Discord.Net.Modules/ModuleManager.cs @@ -7,6 +7,17 @@ using System.Linq; namespace Discord.Modules { + public class ModuleManager : ModuleManager + where T : class, IModule + { + public new T Instance => base.Instance as T; + + internal ModuleManager(DiscordClient client, T instance, string name, ModuleFilter filterType) + : base(client, instance, name, filterType) + { + } + } + public class ModuleManager { public event EventHandler ServerEnabled = delegate { }; @@ -115,7 +126,7 @@ namespace Discord.Modules public void CreateCommands(string prefix, Action config) { - var commandService = Client.Services.Get(); + var commandService = Client.GetService(); commandService.CreateGroup(prefix, x => { x.Category(Name); diff --git a/src/Discord.Net.Modules/ModuleService.cs b/src/Discord.Net.Modules/ModuleService.cs index 29297f8d4..0eadd0694 100644 --- a/src/Discord.Net.Modules/ModuleService.cs +++ b/src/Discord.Net.Modules/ModuleService.cs @@ -8,11 +8,11 @@ namespace Discord.Modules public DiscordClient Client { get; private set; } public IEnumerable Modules => _modules.Values; - private readonly Dictionary _modules; + private readonly Dictionary _modules; public ModuleService() { - _modules = new Dictionary(); + _modules = new Dictionary(); } void IService.Install(DiscordClient client) @@ -20,29 +20,30 @@ namespace Discord.Modules Client = client; } - public T Add(T module, string name, ModuleFilter type) + public T Add(T module, string name, ModuleFilter filterType) where T : class, IModule { if (module == null) throw new ArgumentNullException(nameof(module)); if (name == null) throw new ArgumentNullException(nameof(name)); if (Client == null) throw new InvalidOperationException("Service needs to be added to a DiscordClient before modules can be installed."); - if (_modules.ContainsKey(module)) + + Type type = typeof(T); + if (_modules.ContainsKey(type)) throw new InvalidOperationException("This module has already been added."); - var manager = new ModuleManager(Client, module, name, type); - _modules.Add(module, manager); + var manager = new ModuleManager(Client, module, name, filterType); + _modules.Add(type, manager); module.Install(manager); return module; } - - public ModuleManager GetManager(IModule module) - { - if (module == null) throw new ArgumentNullException(nameof(module)); - - ModuleManager result = null; - _modules.TryGetValue(module, out result); - return result; - } + public ModuleManager Get() + where T : class, IModule + { + ModuleManager manager; + if (_modules.TryGetValue(typeof(T), out manager)) + return manager as ModuleManager; + return null; + } } } diff --git a/src/Discord.Net.Modules/project.json b/src/Discord.Net.Modules/project.json index f9834fcf1..884339080 100644 --- a/src/Discord.Net.Modules/project.json +++ b/src/Discord.Net.Modules/project.json @@ -17,7 +17,7 @@ "dependencies": { "Discord.Net": "0.9.0-rc3-3", - "Discord.Net.Commands": "0.9.0-rc3" + "Discord.Net.Commands": "0.9.0-rc3-1" }, "frameworks": { "net45": { }, diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index aec38d9d5..d8023d6bd 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -481,15 +481,15 @@ Events\UserUpdatedEventArgs.cs - - Extensions.cs - Format.cs IMentionable.cs + + InternalExtensions.cs + IService.cs @@ -592,8 +592,8 @@ Net\WebSockets\WS4NetEngine.cs - - ServiceManager.cs + + ServiceCollection.cs TaskManager.cs diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 01098cab0..871838b38 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -27,6 +27,7 @@ namespace Discord private readonly ManualResetEvent _disconnectedEvent; private readonly ManualResetEventSlim _connectedEvent; private readonly TaskManager _taskManager; + private readonly ServiceCollection _services; private readonly ConcurrentDictionary _servers; private readonly ConcurrentDictionary _channels; private readonly ConcurrentDictionary _privateChannels; //Key = RecipientId @@ -44,8 +45,6 @@ namespace Discord public RestClient StatusAPI { get; } /// Gets the internal WebSocket for the Gateway event stream. public GatewaySocket GatewaySocket { get; } - /// Gets the service manager used for adding extensions to this client. - public ServiceManager Services { get; } /// Gets the queue used for outgoing messages, if enabled. public MessageQueue MessageQueue { get; } /// Gets the JSON serializer used by this client. @@ -66,6 +65,8 @@ namespace Discord /// Gets the game the current user is displayed as playing. public string CurrentGame { get; private set; } + /// Gets a collection of all extensions added to this DiscordClient. + public IEnumerable Services => _services; /// Gets a collection of all servers this client is a member of. public IEnumerable Servers => _servers.Select(x => x.Value); /// Gets a collection of all private channels this client is a member of. @@ -152,7 +153,7 @@ namespace Discord MessageQueue = new MessageQueue(ClientAPI, Log.CreateLogger("MessageQueue")); //Extensibility - Services = new ServiceManager(this); + _services = new ServiceCollection(this); } /// Connects to the Discord server with the provided email and password. @@ -1010,6 +1011,18 @@ namespace Discord } #endregion + #region Services + public T AddService(T instance) + where T : class, IService + => _services.Add(instance); + public T AddService() + where T : class, IService, new() + => _services.Add(new T()); + public T GetService(bool isRequired = true) + where T : class, IService + => _services.Get(isRequired); + #endregion + #region Async Wrapper /// Blocking call that will execute the provided async method and wait until the client has been manually stopped. This is mainly intended for use in console applications. public void ExecuteAndWait(Func asyncAction) diff --git a/src/Discord.Net/Extensions.cs b/src/Discord.Net/InternalExtensions.cs similarity index 93% rename from src/Discord.Net/Extensions.cs rename to src/Discord.Net/InternalExtensions.cs index ee4d26e9a..be9d90b3f 100644 --- a/src/Discord.Net/Extensions.cs +++ b/src/Discord.Net/InternalExtensions.cs @@ -8,22 +8,6 @@ using System.Runtime.CompilerServices; namespace Discord { - public static class DiscordClientExtensions - { - public static DiscordClient AddService(this DiscordClient client, T instance) - where T : class, IService - { - client.Services.Add(instance); - return client; - } - public static DiscordClient AddService(this DiscordClient client) - where T : class, IService, new() - { - client.Services.Add(new T()); - return client; - } - } - internal static class InternalExtensions { internal static readonly IFormatProvider _format = CultureInfo.InvariantCulture; diff --git a/src/Discord.Net/ServiceManager.cs b/src/Discord.Net/ServiceCollection.cs similarity index 91% rename from src/Discord.Net/ServiceManager.cs rename to src/Discord.Net/ServiceCollection.cs index 8bb7a678a..104f91dd4 100644 --- a/src/Discord.Net/ServiceManager.cs +++ b/src/Discord.Net/ServiceCollection.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; namespace Discord { - public class ServiceManager : IEnumerable + internal class ServiceCollection : IEnumerable { private readonly Dictionary _services; internal DiscordClient Client { get; } - internal ServiceManager(DiscordClient client) + internal ServiceCollection(DiscordClient client) { Client = client; _services = new Dictionary(); From d48d658e3a79b9ea0b16dc709584e187de17b8aa Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 16:09:24 -0400 Subject: [PATCH 05/51] Minor cleanup --- src/Discord.Net/Models/Channel.cs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index a6ff1162b..29e2f1a51 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -328,27 +328,17 @@ namespace Discord public IEnumerable FindUsers(string name, bool exactMatch = false) { if (name == null) throw new ArgumentNullException(nameof(name)); - return _users.Select(x => x.Value.User).Find(name, exactMatch: exactMatch); } - public Task SendMessage(string text) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (text == "") throw new ArgumentException("Value cannot be blank", nameof(text)); - return SendMessageInternal(text, false); - } - public Task SendTTSMessage(string text) + public Task SendMessage(string text) => SendMessageInternal(text, false); + public Task SendTTSMessage(string text) => SendMessageInternal(text, true); + private Task SendMessageInternal(string text, bool isTTS) { if (text == null) throw new ArgumentNullException(nameof(text)); if (text == "") throw new ArgumentException("Value cannot be blank", nameof(text)); - return SendMessageInternal(text, true); - } - private Task SendMessageInternal(string text, bool isTTS) - { if (text.Length > DiscordConfig.MaxMessageSize) - throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less."); - + throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less."); return Task.FromResult(Client.MessageQueue.QueueSend(this, text, isTTS)); } @@ -374,8 +364,7 @@ namespace Discord return msg; } - public Task SendIsTyping() - => Client.ClientAPI.Send(new SendIsTypingRequest(Id)); + public Task SendIsTyping() => Client.ClientAPI.Send(new SendIsTypingRequest(Id)); #endregion #region Permissions From bc7edbf3c7ef9f6cbc54fd46af7b8b09480e8b40 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 19:14:18 -0400 Subject: [PATCH 06/51] Added path property to all models for logging --- src/Discord.Net/Models/Channel.cs | 8 +++++--- src/Discord.Net/Models/Invite.cs | 6 ++++-- src/Discord.Net/Models/Message.cs | 2 ++ src/Discord.Net/Models/Profile.cs | 2 +- src/Discord.Net/Models/Role.cs | 2 ++ src/Discord.Net/Models/Server.cs | 2 ++ src/Discord.Net/Models/User.cs | 2 ++ 7 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 29e2f1a51..438646bdf 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -41,11 +41,11 @@ namespace Discord Permissions = new ChannelPermissionOverrides(allow, deny); } } - + private readonly ConcurrentDictionary _users; private readonly ConcurrentDictionary _messages; private Dictionary _permissionOverwrites; - + public DiscordClient Client { get; } /// Gets the unique identifier for this channel. @@ -64,6 +64,8 @@ namespace Discord /// Gets the type of this channel). public ChannelType Type { get; private set; } + /// Gets the path to this object. + internal string Path => $"{Server?.Name ?? "[Private]"}/{Name}"; /// Gets true if this is a private chat with another user. public bool IsPrivate => Recipient != null; /// Gets the string used to mention this channel. @@ -345,7 +347,7 @@ namespace Discord public async Task SendFile(string filePath) { using (var stream = File.OpenRead(filePath)) - return await SendFile(Path.GetFileName(filePath), stream).ConfigureAwait(false); + return await SendFile(System.IO.Path.GetFileName(filePath), stream).ConfigureAwait(false); } public async Task SendFile(string filename, Stream stream) { diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs index 56df207d9..a6a0407b1 100644 --- a/src/Discord.Net/Models/Invite.cs +++ b/src/Discord.Net/Models/Invite.cs @@ -85,8 +85,10 @@ namespace Discord /// Gets when this invite was created. public DateTime CreatedAt { get; private set; } - /// Returns a URL for this invite using XkcdCode if available or Id if not. - public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; + /// Gets the path to this object. + internal string Path => $"{Server?.Name ?? "[Private]"}/{Code}"; + /// Returns a URL for this invite using XkcdCode if available or Id if not. + public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; internal Invite(DiscordClient client, string code, string xkcdPass) { diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index c589df91e..324acd3ae 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -201,6 +201,8 @@ namespace Discord internal int Nonce { get; set; } + /// Gets the path to this object. + internal string Path => $"{Server?.Name ?? "[Private]"}/{Id}"; /// Returns the server containing the channel this message was sent to. public Server Server => Channel.Server; /// Returns if this message was sent from the logged-in accounts. diff --git a/src/Discord.Net/Models/Profile.cs b/src/Discord.Net/Models/Profile.cs index c0aaa6f25..89cdde640 100644 --- a/src/Discord.Net/Models/Profile.cs +++ b/src/Discord.Net/Models/Profile.cs @@ -28,7 +28,7 @@ namespace Discord public UserStatus Status => Client.PrivateUser.Status; /// Returns the string used to mention this user. public string Mention => $"<@{Id}>"; - + /// Gets the email for this user. public string Email { get; private set; } /// Gets if the email for this user has been verified. diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index 1bdec0d3c..4fd5c4b7a 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -33,6 +33,8 @@ namespace Discord /// Gets the color of this role. public Color Color { get; private set; } + /// Gets the path to this object. + internal string Path => $"{Server?.Name ?? "[Private]"}/{Name}"; /// Gets true if this is the role representing all users in a server. public bool IsEveryone => Id == Server.Id; /// Gets a list of all members in this role. diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index aaed6aac8..ec5f58519 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -79,6 +79,8 @@ namespace Discord /// Gets all custom emojis on this server. public IEnumerable CustomEmojis { get; private set; } + /// Gets the path to this object. + internal string Path => Name; /// Gets the user that created this server. public User Owner => GetUser(_ownerId); /// Returns true if the current user owns this server. diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index b2f5488cf..dd381d014 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -73,6 +73,8 @@ namespace Discord // /// Gets this user's voice token. // public string Token { get; private set; } + /// Gets the path to this object. + internal string Path => $"{Server?.Name ?? "[Private]"}/{Name}"; /// Gets the current private channel for this user if one exists. public Channel PrivateChannel => Client.GetPrivateChannel(Id); /// Returns the string used to mention this user. From bd9fc36720a281b454562507f7c6b6813c9491c7 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 19:27:25 -0400 Subject: [PATCH 07/51] Temp. moved built-ing event logging to Debug temporarily, improved login speed. --- src/Discord.Net/DiscordClient.cs | 119 ++++++++++++++---------------- src/Discord.Net/DiscordConfig.cs | 6 +- src/Discord.Net/Models/Channel.cs | 15 ++-- src/Discord.Net/Models/Server.cs | 12 +-- 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 871838b38..a295cfc97 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -28,9 +28,9 @@ namespace Discord private readonly ManualResetEventSlim _connectedEvent; private readonly TaskManager _taskManager; private readonly ServiceCollection _services; - private readonly ConcurrentDictionary _servers; - private readonly ConcurrentDictionary _channels; - private readonly ConcurrentDictionary _privateChannels; //Key = RecipientId + private ConcurrentDictionary _servers; + private ConcurrentDictionary _channels; + private ConcurrentDictionary _privateChannels; //Key = RecipientId private Dictionary _regions; internal Logger Logger { get; } @@ -118,9 +118,10 @@ namespace Discord CancelToken = new CancellationToken(true); //Cache - _servers = new ConcurrentDictionary(); - _channels = new ConcurrentDictionary(); - _privateChannels = new ConcurrentDictionary(); + //ConcurrentLevel = 2 (only REST and WebSocket can add/remove) + _servers = new ConcurrentDictionary(2, 0); + _channels = new ConcurrentDictionary(2, 0); + _privateChannels = new ConcurrentDictionary(2, 0); //Serialization Serializer = new JsonSerializer(); @@ -237,8 +238,8 @@ namespace Discord } } - ClientAPI.Token = token; - var request = new LoginRequest() { Email = email, Password = password }; + ClientAPI.Token = token; + var request = new LoginRequest();// { Email = email, Password = password }; var response = await ClientAPI.Send(request).ConfigureAwait(false); token = response.Token; if (Config.CacheDir != null && token != oldToken && tokenPath != null) @@ -475,15 +476,26 @@ namespace Discord if (Config.LogLevel >= LogSeverity.Verbose) stopwatch = Stopwatch.StartNew(); var data = e.Payload.ToObject(Serializer); + + int channelCount = 0; + for (int i = 0; i < data.Guilds.Length; i++) + channelCount += data.Guilds[i].Channels.Length; + + //ConcurrencyLevel = 2 (only REST and WebSocket can add/remove) + _servers = new ConcurrentDictionary(2, (int)(data.Guilds.Length * 1.05)); + _channels = new ConcurrentDictionary(2, (int)(channelCount * 1.05)); + _privateChannels = new ConcurrentDictionary(2, (int)(data.PrivateChannels.Length * 1.05)); + List largeServers = new List(); + SessionId = data.SessionId; PrivateUser = new User(this, data.User.Id, null); PrivateUser.Update(data.User); CurrentUser = new Profile(this, data.User.Id); CurrentUser.Update(data.User); - List largeServers = new List(); - foreach (var model in data.Guilds) + for (int i = 0; i < data.Guilds.Length; i++) { + var model = data.Guilds[i]; if (model.Unavailable != true) { var server = AddServer(model.Id); @@ -492,13 +504,15 @@ namespace Discord largeServers.Add(server.Id); } } - foreach (var model in data.PrivateChannels) + for (int i = 0; i < data.PrivateChannels.Length; i++) { + var model = data.PrivateChannels[i]; var channel = AddPrivateChannel(model.Id, model.Recipient.Id); channel.Update(model); } if (largeServers.Count > 0) GatewaySocket.SendRequestMembers(largeServers, "", 0); + if (Config.LogLevel >= LogSeverity.Verbose) { stopwatch.Stop(); @@ -516,14 +530,11 @@ namespace Discord { var server = AddServer(data.Id); server.Update(data); - - if (Config.LogEvents) - { - if (data.Unavailable != false) - Logger.Info($"Server Created: {server.Name}"); - else - Logger.Info($"Server Available: {server.Name}"); - } + + if (data.Unavailable != false) + Logger.Debug($"GUILD_CREATE: {server.Path}"); + else + Logger.Debug($"GUILD_AVAILABLE: {server.Path}"); if (data.Unavailable != false) OnJoinedServer(server); @@ -539,8 +550,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? server.Clone() : null; server.Update(data); - if (Config.LogEvents) - Logger.Info($"Server Updated: {server.Name}"); + Logger.Debug($"GUILD_UPDATE: {server.Path}"); OnServerUpdated(before, server); } else @@ -553,13 +563,10 @@ namespace Discord Server server = RemoveServer(data.Id); if (server != null) { - if (Config.LogEvents) - { - if (data.Unavailable != true) - Logger.Info($"Server Destroyed: {server.Name}"); - else - Logger.Info($"Server Unavailable: {server.Name}"); - } + if (data.Unavailable != true) + Logger.Debug($"GUILD_DELETE: {server.Path}"); + else + Logger.Debug($"GUILD_UNAVAILABLE: {server.Path}"); OnServerUnavailable(server); if (data.Unavailable != true) @@ -589,8 +596,7 @@ namespace Discord if (channel != null) { channel.Update(data); - if (Config.LogEvents) - Logger.Info($"Channel Created: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); + Logger.Debug($"CHANNEL_CREATE: {channel.Path}"); OnChannelCreated(channel); } } @@ -603,8 +609,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? channel.Clone() : null; channel.Update(data); - if (Config.LogEvents) - Logger.Info($"Channel Updated: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); + Logger.Debug($"CHANNEL_UPDATE: {channel.Path}"); OnChannelUpdated(before, channel); } else @@ -617,8 +622,7 @@ namespace Discord var channel = RemoveChannel(data.Id); if (channel != null) { - if (Config.LogEvents) - Logger.Info($"Channel Destroyed: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); + Logger.Debug($"CHANNEL_DELETE: {channel.Path}"); OnChannelDestroyed(channel); } else @@ -636,8 +640,7 @@ namespace Discord var user = server.AddUser(data.User.Id); user.Update(data); user.UpdateActivity(); - if (Config.LogEvents) - Logger.Info($"User Joined: {server.Name}/{user.Name}"); + Logger.Debug($"GUILD_MEMBER_ADD: {user.Path}"); OnUserJoined(user); } else @@ -655,8 +658,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); - if (Config.LogEvents) - Logger.Info($"User Updated: {server.Name}/{user.Name}"); + Logger.Debug($"GUILD_MEMBER_UPDATE: {user.Path}"); OnUserUpdated(before, user); } else @@ -675,8 +677,7 @@ namespace Discord var user = server.RemoveUser(data.User.Id); if (user != null) { - if (Config.LogEvents) - Logger.Info($"User Left: {server.Name}/{user.Name}"); + Logger.Debug($"GUILD_MEMBER_REMOVE: {user.Path}"); OnUserLeft(user); } else @@ -698,6 +699,7 @@ namespace Discord user.Update(memberData); //OnUserAdded(user); } + Logger.Debug($"GUILD_MEMBERS_CHUNK: {data.Members.Length} users"); } else Logger.Warning("GUILD_MEMBERS_CHUNK referenced an unknown guild."); @@ -713,8 +715,7 @@ namespace Discord { var role = server.AddRole(data.Data.Id); role.Update(data.Data); - if (Config.LogEvents) - Logger.Info($"Role Created: {server.Name}/{role.Name}"); + Logger.Debug($"GUILD_ROLE_CREATE: {role.Path}"); OnRoleCreated(role); } else @@ -732,8 +733,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? role.Clone() : null; role.Update(data.Data); - if (Config.LogEvents) - Logger.Info($"Role Updated: {server.Name}/{role.Name}"); + Logger.Debug($"GUILD_ROLE_UPDATE: {role.Path}"); OnRoleUpdated(before, role); } else @@ -752,8 +752,7 @@ namespace Discord var role = server.RemoveRole(data.RoleId); if (role != null) { - if (Config.LogEvents) - Logger.Info($"Role Deleted: {server.Name}/{role.Name}"); + Logger.Debug($"GUILD_ROLE_DELETE: {role.Path}"); OnRoleDeleted(role); } else @@ -774,8 +773,7 @@ namespace Discord var user = server.GetUser(data.User.Id); if (user != null) { - if (Config.LogEvents) - Logger.Info($"User Banned: {server.Name}/{user.Name}"); + Logger.Debug($"GUILD_BAN_ADD: {user.Path}"); OnUserBanned(user); } else @@ -793,8 +791,7 @@ namespace Discord { var user = new User(this, data.User.Id, server); user.Update(data.User); - if (Config.LogEvents) - Logger.Info($"User Unbanned: {server.Name}/{user.Name}"); + Logger.Debug($"GUILD_BAN_REMOVE: {user.Path}"); OnUserUnbanned(user); } else @@ -838,9 +835,8 @@ namespace Discord msg.Update(data); user.UpdateActivity(); - - if (Config.LogEvents) - Logger.Verbose($"Message Received: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); + + Logger.Debug($"MESSAGE_CREATE: {channel.Path} ({user.Name ?? "Unknown"})"); OnMessageReceived(msg); } else @@ -859,8 +855,7 @@ namespace Discord var msg = channel.GetMessage(data.Id, data.Author?.Id); var before = Config.EnablePreUpdateEvents ? msg.Clone() : null; msg.Update(data); - if (Config.LogEvents) - Logger.Verbose($"Message Update: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); + Logger.Debug($"MESSAGE_UPDATE: {channel.Path} ({data.Author?.Username ?? "Unknown"})"); OnMessageUpdated(before, msg); } else @@ -874,8 +869,7 @@ namespace Discord if (channel != null) { var msg = channel.RemoveMessage(data.Id); - if (Config.LogEvents) - Logger.Verbose($"Message Deleted: {channel.Server?.Name ?? "[Private]"}/{channel.Name}"); + Logger.Debug($"MESSAGE_DELETE: {channel.Path} ({msg.User?.Name ?? "Unknown"})"); OnMessageDeleted(msg); } else @@ -908,9 +902,9 @@ namespace Discord if (user != null) { + Logger.Debug($"PRESENCE_UPDATE: {user.Path}"); var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); - //Logger.Verbose($"Presence Updated: {server.Name}/{user.Name}"); OnUserUpdated(before, user); } /*else //Occurs when a user leaves a server @@ -929,13 +923,13 @@ namespace Discord if (channel.Recipient.Id == data.UserId) user = channel.Recipient; else - return; ; + break; } else user = channel.Server.GetUser(data.UserId); if (user != null) { - //Logger.Verbose($"Is Typing: {channel.Server?.Name ?? "[Private]"}/{channel.Name}/{user.Name}"); + Logger.Debug($"TYPING_START: {channel.Path} ({user.Name})"); OnUserIsTypingUpdated(channel, user); user.UpdateActivity(); } @@ -955,6 +949,7 @@ namespace Discord var user = server.GetUser(data.UserId); if (user != null) { + Logger.Debug($"VOICE_STATE_UPDATE: {user.Path}"); var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); //Logger.Verbose($"Voice Updated: {server.Name}/{user.Name}"); @@ -979,8 +974,7 @@ namespace Discord PrivateUser.Update(data); foreach (var server in _servers) server.Value.CurrentUser.Update(data); - if (Config.LogEvents) - Logger.Info("Profile Updated"); + Logger.Debug($"USER_UPDATE"); OnProfileUpdated(before, CurrentUser); } } @@ -996,6 +990,7 @@ namespace Discord case "VOICE_SERVER_UPDATE": case "GUILD_EMOJIS_UPDATE": case "MESSAGE_ACK": + Logger.Debug($"{e.Type} [Ignored]"); break; //Others diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net/DiscordConfig.cs index 453f0952d..5add8dcda 100644 --- a/src/Discord.Net/DiscordConfig.cs +++ b/src/Discord.Net/DiscordConfig.cs @@ -16,10 +16,8 @@ namespace Discord /// Gets or sets the version of your application, used in the user agent. public string AppVersion { get; set; } = null; - /// Gets or sets the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. + /// Gets or sets the minimum log level severity that will be sent to the LogMessage event. public LogSeverity LogLevel { get; set; } = LogSeverity.Info; - /// Enables or disables the default event logger. - public bool LogEvents { get; set; } = true; //WebSocket @@ -66,7 +64,6 @@ namespace Discord public const string InviteUrl = "https://discord.gg/"; public LogSeverity LogLevel { get; } - public bool LogEvents { get; } public int ConnectionTimeout { get; } public int ReconnectDelay { get; } @@ -83,7 +80,6 @@ namespace Discord internal DiscordConfig(DiscordConfigBuilder builder) { LogLevel = builder.LogLevel; - LogEvents = builder.LogEvents; ConnectionTimeout = builder.ConnectionTimeout; ReconnectDelay = builder.ReconnectDelay; diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 438646bdf..a461035e5 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -111,7 +111,7 @@ namespace Discord : this(client, id) { Server = server; - if (server != null) + if (server != null && Client.Config.UsePermissionsCache) { foreach (var user in server.Users) AddUser(user); @@ -131,9 +131,15 @@ namespace Discord Id = id; _permissionOverwrites = new Dictionary(); - _users = new ConcurrentDictionary(); + if (client.Config.UsePermissionsCache) + { + if (IsPrivate) + _users = new ConcurrentDictionary(2, 2); + else + _users = new ConcurrentDictionary(2, (int)(Server.UserCount * 1.05)); + } if (client.Config.MessageCacheSize > 0) - _messages = new ConcurrentDictionary(); + _messages = new ConcurrentDictionary(2, (int)(client.Config.MessageCacheSize * 1.05)); } internal void Update(ChannelReference model) @@ -542,8 +548,7 @@ namespace Discord #region Users internal void AddUser(User user) { - if (!Client.Config.UsePermissionsCache) - return; + if (!Client.Config.UsePermissionsCache) return; var perms = new ChannelPermissions(); UpdatePermissions(user, ref perms); diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index ec5f58519..5123d9bbc 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -46,9 +46,9 @@ namespace Discord } } - private readonly ConcurrentDictionary _roles; - private readonly ConcurrentDictionary _users; - private readonly ConcurrentDictionary _channels; + private ConcurrentDictionary _roles; + private ConcurrentDictionary _users; + private ConcurrentDictionary _channels; private ulong _ownerId; private ulong? _afkChannelId; private int _userCount; @@ -117,9 +117,6 @@ namespace Discord Client = client; Id = id; - _channels = new ConcurrentDictionary(); - _roles = new ConcurrentDictionary(); - _users = new ConcurrentDictionary(); DefaultChannel = AddChannel(id); EveryoneRole = AddRole(id); } @@ -157,6 +154,7 @@ namespace Discord } if (model.Roles != null) { + _roles = new ConcurrentDictionary(2, model.Roles.Length); foreach (var x in model.Roles) AddRole(x.Id).Update(x); } @@ -171,11 +169,13 @@ namespace Discord if (model.Channels != null) { + _channels = new ConcurrentDictionary(2, (int)(model.Channels.Length * 1.05)); foreach (var subModel in model.Channels) AddChannel(subModel.Id).Update(subModel); } if (model.Members != null) { + _users = new ConcurrentDictionary(2, (int)(model.Members.Length * 1.05)); foreach (var subModel in model.Members) AddUser(subModel.User.Id).Update(subModel); } From 19e2528affe6084cdd0459e23ae572083b7064d7 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 19:27:54 -0400 Subject: [PATCH 08/51] Removed redundant casts in MessageQueue and improved error logging --- src/Discord.Net/MessageQueue.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs index dc3d27327..b5de885b0 100644 --- a/src/Discord.Net/MessageQueue.cs +++ b/src/Discord.Net/MessageQueue.cs @@ -103,7 +103,7 @@ namespace Discord.Net } private Task RunSendQueue(CancellationToken cancelToken) { - return Task.Run((Func)(async () => + return Task.Run(async () => { try { @@ -134,7 +134,7 @@ namespace Discord.Net catch (Exception ex) { msg.State = MessageState.Failed; - _logger.Error("Failed to send message", ex); + _logger.Error($"Failed to send message to {msg.Channel.Path}", ex); } } } @@ -142,11 +142,11 @@ namespace Discord.Net } } catch (OperationCanceledException) { } - })); + }); } private Task RunEditQueue(CancellationToken cancelToken) { - return Task.Run((Func)(async () => + return Task.Run(async () => { try { @@ -168,7 +168,7 @@ namespace Discord.Net }; await _rest.Send(request).ConfigureAwait(false); } - catch (Exception ex) { _logger.Error("Failed to edit message", ex); } + catch (Exception ex) { _logger.Error($"Failed to edit message {edit.Message.Path}", ex); } } } } @@ -176,11 +176,11 @@ namespace Discord.Net } } catch (OperationCanceledException) { } - })); + }); } private Task RunDeleteQueue(CancellationToken cancelToken) { - return Task.Run((Func)(async () => + return Task.Run(async () => { try { @@ -200,7 +200,7 @@ namespace Discord.Net await _rest.Send(request).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } //Ignore - catch (Exception ex) { _logger.Error("Failed to delete message", ex); } + catch (Exception ex) { _logger.Error($"Failed to delete message {msg.Path}", ex); } } } } @@ -209,7 +209,7 @@ namespace Discord.Net } } catch (OperationCanceledException) { } - })); + }); } private void IncrementCount() From d68e6995561d8e5ca3ed1677a393ccda8c8c40c8 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 20:35:18 -0400 Subject: [PATCH 09/51] Fixed a login crash bug, fixed permissions resolving twice during READY --- src/Discord.Net/DiscordClient.cs | 4 +-- src/Discord.Net/Models/Channel.cs | 9 ++----- src/Discord.Net/Models/Role.cs | 18 ++++++++----- src/Discord.Net/Models/Server.cs | 45 +++++++++++++++---------------- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index a295cfc97..42bb86214 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -714,7 +714,7 @@ namespace Discord if (server != null) { var role = server.AddRole(data.Data.Id); - role.Update(data.Data); + role.Update(data.Data, false); Logger.Debug($"GUILD_ROLE_CREATE: {role.Path}"); OnRoleCreated(role); } @@ -732,7 +732,7 @@ namespace Discord if (role != null) { var before = Config.EnablePreUpdateEvents ? role.Clone() : null; - role.Update(data.Data); + role.Update(data.Data, true); Logger.Debug($"GUILD_ROLE_UPDATE: {role.Path}"); OnRoleUpdated(before, role); } diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index a461035e5..ddf85dd0b 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -141,18 +141,13 @@ namespace Discord if (client.Config.MessageCacheSize > 0) _messages = new ConcurrentDictionary(2, (int)(client.Config.MessageCacheSize * 1.05)); } - - internal void Update(ChannelReference model) + + internal void Update(APIChannel model) { if (!IsPrivate && model.Name != null) Name = model.Name; if (model.Type != null) Type = model.Type; - } - internal void Update(APIChannel model) - { - Update(model as ChannelReference); - if (model.Position != null) Position = model.Position.Value; if (model.Topic != null) diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index 4fd5c4b7a..d3e9f9094 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -52,9 +52,9 @@ namespace Discord Color = new Color(0); } - internal void Update(APIRole model) + internal void Update(APIRole model, bool updatePermissions) { - if (model.Name != null) + if (model.Name != null) Name = model.Name; if (model.Hoist != null) IsHoisted = model.Hoist.Value; @@ -64,11 +64,15 @@ namespace Discord Position = model.Position.Value; if (model.Color != null) Color = new Color(model.Color.Value); - if (model.Permissions != null) - Permissions = new ServerPermissions(model.Permissions.Value); - - foreach (var member in Members) - Server.UpdatePermissions(member); + if (model.Permissions != null) + { + Permissions = new ServerPermissions(model.Permissions.Value); + if (updatePermissions) //Dont update these during READY + { + foreach (var member in Members) + Server.UpdatePermissions(member); + } + } } public async Task Edit(string name = null, ServerPermissions? permissions = null, Color color = null, bool? isHoisted = null, int? position = null) diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 5123d9bbc..2c0e9f288 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -57,10 +57,6 @@ namespace Discord /// Gets the unique identifier for this server. public ulong Id { get; } - /// Gets the default channel for this server. - public Channel DefaultChannel { get; } - /// Gets the the role representing all users in a server. - public Role EveryoneRole { get; } /// Gets the name of this server. public string Name { get; private set; } @@ -74,6 +70,10 @@ namespace Discord public int AFKTimeout { get; private set; } /// Gets the date and time you joined this server. public DateTime JoinedAt { get; private set; } + /// Gets the default channel for this server. + public Channel DefaultChannel { get; private set; } + /// Gets the the role representing all users in a server. + public Role EveryoneRole { get; private set; } /// Gets all extra features added to this server. public IEnumerable Features { get; private set; } /// Gets all custom emojis on this server. @@ -116,20 +116,12 @@ namespace Discord { Client = client; Id = id; - - DefaultChannel = AddChannel(id); - EveryoneRole = AddRole(id); } - internal void Update(GuildReference model) + internal void Update(Guild model) { if (model.Name != null) Name = model.Name; - } - internal void Update(Guild model) - { - Update(model as GuildReference); - if (model.AFKTimeout != null) AFKTimeout = model.AFKTimeout.Value; if (model.JoinedAt != null) @@ -142,7 +134,17 @@ namespace Discord IconId = model.Icon; if (model.Features != null) Features = model.Features; - if (model.Emojis != null) + if (model.Roles != null) + { + _roles = new ConcurrentDictionary(2, model.Roles.Length); + foreach (var x in model.Roles) + { + var role = AddRole(x.Id); + role.Update(x, false); + } + EveryoneRole = _roles[Id]; + } + if (model.Emojis != null) //Needs Roles { CustomEmojis = model.Emojis.Select(x => new Emoji(x.Id) { @@ -152,12 +154,6 @@ namespace Discord Roles = x.RoleIds.Select(y => GetRole(y)).Where(y => y != null).ToArray() }).ToArray(); } - if (model.Roles != null) - { - _roles = new ConcurrentDictionary(2, model.Roles.Length); - foreach (var x in model.Roles) - AddRole(x.Id).Update(x); - } //Can be null _afkChannelId = model.AFKChannelId; @@ -165,14 +161,15 @@ namespace Discord } internal void Update(ExtendedGuild model) { - Update(model as Guild); - if (model.Channels != null) { _channels = new ConcurrentDictionary(2, (int)(model.Channels.Length * 1.05)); foreach (var subModel in model.Channels) AddChannel(subModel.Id).Update(subModel); + DefaultChannel = _channels[Id]; } + Update(model as Guild); //Needs channels + if (model.Members != null) { _users = new ConcurrentDictionary(2, (int)(model.Members.Length * 1.05)); @@ -368,7 +365,7 @@ namespace Discord var createRequest = new CreateRoleRequest(Id); var createResponse = await Client.ClientAPI.Send(createRequest).ConfigureAwait(false); var role = AddRole(createResponse.Id); - role.Update(createResponse); + role.Update(createResponse, false); var editRequest = new UpdateRoleRequest(role.Server.Id, role.Id) { @@ -378,7 +375,7 @@ namespace Discord IsHoisted = isHoisted }; var editResponse = await Client.ClientAPI.Send(editRequest).ConfigureAwait(false); - role.Update(editResponse); + role.Update(editResponse, true); return role; } From 13358c05b51c3f0864c3a2a970a928efc412e717 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 15 Feb 2016 20:39:02 -0400 Subject: [PATCH 10/51] Removed some test code --- src/Discord.Net/DiscordClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 42bb86214..ed500784c 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -239,7 +239,7 @@ namespace Discord } ClientAPI.Token = token; - var request = new LoginRequest();// { Email = email, Password = password }; + var request = new LoginRequest() { Email = email, Password = password }; var response = await ClientAPI.Send(request).ConfigureAwait(false); token = response.Token; if (Config.CacheDir != null && token != oldToken && tokenPath != null) From 7cbefe6fa28096792b29e0b7d9acc23a23a5703d Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 03:32:47 -0400 Subject: [PATCH 11/51] Cleaned up ETFWriter --- src/Discord.Net/ETF/ETFWriter.cs | 57 +++++++++++++++++--------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/Discord.Net/ETF/ETFWriter.cs b/src/Discord.Net/ETF/ETFWriter.cs index 43255780e..37d1553db 100644 --- a/src/Discord.Net/ETF/ETFWriter.cs +++ b/src/Discord.Net/ETF/ETFWriter.cs @@ -12,21 +12,23 @@ namespace Discord.ETF { public unsafe class ETFWriter : IDisposable { - private readonly static byte[] _nilBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 3, (byte)'n', (byte)'i', (byte)'l' }; - private readonly static byte[] _falseBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 5, (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; - private readonly static byte[] _trueBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 4, (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + private static readonly ConcurrentDictionary _serializers = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _indirectSerializers = new ConcurrentDictionary(); - private readonly static MethodInfo _writeTMethod = GetGenericWriteMethod(null); - private readonly static MethodInfo _writeNullableTMethod = GetGenericWriteMethod(typeof(Nullable<>)); - private readonly static MethodInfo _writeDictionaryTMethod = GetGenericWriteMethod(typeof(IDictionary<,>)); - private readonly static MethodInfo _writeEnumerableTMethod = GetGenericWriteMethod(typeof(IEnumerable<>)); - private readonly static DateTime _epochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static readonly byte[] _nilBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 3, (byte)'n', (byte)'i', (byte)'l' }; + private static readonly byte[] _falseBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 5, (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + private static readonly byte[] _trueBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 4, (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + + private static readonly MethodInfo _writeTMethod = GetGenericWriteMethod(null); + private static readonly MethodInfo _writeNullableTMethod = GetGenericWriteMethod(typeof(Nullable<>)); + private static readonly MethodInfo _writeDictionaryTMethod = GetGenericWriteMethod(typeof(IDictionary<,>)); + private static readonly MethodInfo _writeEnumerableTMethod = GetGenericWriteMethod(typeof(IEnumerable<>)); + private static readonly DateTime _epochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private readonly Stream _stream; private readonly byte[] _buffer; private readonly bool _leaveOpen; private readonly Encoding _encoding; - private readonly ConcurrentDictionary _serializers, _indirectSerializers; public virtual Stream BaseStream { @@ -45,8 +47,6 @@ namespace Discord.ETF _leaveOpen = leaveOpen; _buffer = new byte[11]; _encoding = Encoding.UTF8; - _serializers = new ConcurrentDictionary(); - _indirectSerializers = new ConcurrentDictionary(); } public void Write(bool value) @@ -72,6 +72,7 @@ namespace Discord.ETF } else if (value >= int.MinValue && value <= int.MaxValue) { + //TODO: Does this encode negatives correctly? _buffer[0] = (byte)ETFType.INTEGER_EXT; _buffer[1] = (byte)(value >> 24); _buffer[2] = (byte)(value >> 16); @@ -145,7 +146,7 @@ namespace Discord.ETF public void Write(DateTime value) => Write((ulong)((value.Ticks - _epochTime.Ticks) / TimeSpan.TicksPerSecond)); - public void Write(bool? value) { if (value.HasValue) Write((bool)value.Value); else WriteNil(); } + public void Write(bool? value) { if (value.HasValue) Write(value.Value); else WriteNil(); } public void Write(sbyte? value) { if (value.HasValue) Write((long)value.Value); else WriteNil(); } public void Write(byte? value) { if (value.HasValue) Write((ulong)value.Value); else WriteNil(); } public void Write(short? value) { if (value.HasValue) Write((long)value.Value); else WriteNil(); } @@ -158,35 +159,35 @@ namespace Discord.ETF public void Write(float? value) { if (value.HasValue) Write((double)value.Value); else WriteNil(); } public void Write(DateTime? value) { if (value.HasValue) Write(value.Value); else WriteNil(); } - public void Write(byte[] value) + public void Write(string value) { if (value != null) { - int count = value.Length; + var bytes = _encoding.GetBytes(value); + int count = bytes.Length; _buffer[0] = (byte)ETFType.BINARY_EXT; _buffer[1] = (byte)(count >> 24); _buffer[2] = (byte)(count >> 16); _buffer[3] = (byte)(count >> 8); _buffer[4] = (byte)count; _stream.Write(_buffer, 0, 5); - _stream.Write(value, 0, value.Length); + _stream.Write(bytes, 0, bytes.Length); } else WriteNil(); } - public void Write(string value) + public void Write(byte[] value) { if (value != null) { - var bytes = _encoding.GetBytes(value); - int count = bytes.Length; + int count = value.Length; _buffer[0] = (byte)ETFType.BINARY_EXT; _buffer[1] = (byte)(count >> 24); _buffer[2] = (byte)(count >> 16); _buffer[3] = (byte)(count >> 8); _buffer[4] = (byte)count; _stream.Write(_buffer, 0, 5); - _stream.Write(bytes, 0, bytes.Length); + _stream.Write(value, 0, value.Length); } else WriteNil(); @@ -269,7 +270,7 @@ namespace Discord.ETF public virtual long Seek(int offset, SeekOrigin origin) => _stream.Seek(offset, origin); #region Emit - private Action CreateSerializer(Type type, TypeInfo typeInfo, bool isDirect) + private static Action CreateSerializer(Type type, TypeInfo typeInfo, bool isDirect) { var method = new DynamicMethod(isDirect ? "SerializeETF" : "SerializeIndirectETF", null, new[] { typeof(ETFWriter), isDirect ? type : typeof(object) }, true); @@ -291,7 +292,7 @@ namespace Discord.ETF generator.Emit(OpCodes.Ret); return method.CreateDelegate(typeof(Action)) as Action; } - private void EmitWriteValue(ILGenerator generator, Type type, TypeInfo typeInfo, bool isTop) + private static void EmitWriteValue(ILGenerator generator, Type type, TypeInfo typeInfo, bool isTop) { //Convert enum types to their base type if (typeInfo.IsEnum) @@ -335,18 +336,21 @@ namespace Discord.ETF //Dictionaries else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces .Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>))) + { generator.EmitCall(OpCodes.Call, _writeDictionaryTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); - + } //Enumerable else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces .Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))) + { generator.EmitCall(OpCodes.Call, _writeEnumerableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); - + } //Nullable Structs - else if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>) && + else if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>) && typeInfo.GenericTypeParameters[0].GetTypeInfo().IsValueType) + { generator.EmitCall(OpCodes.Call, _writeNullableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); - + } //Structs/Classes else if (typeInfo.IsClass || (typeInfo.IsValueType && !typeInfo.IsPrimitive)) { @@ -387,7 +391,6 @@ namespace Discord.ETF generator.EmitCall(OpCodes.Call, _writeTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); } } - //Unsupported (decimal, char) else throw new InvalidOperationException($"Serializing {type.Name} is not supported."); @@ -429,7 +432,7 @@ namespace Discord.ETF { return typeof(ETFWriter).GetTypeInfo().GetDeclaredMethods(nameof(Write)) .Where(x => x.GetParameters()[0].ParameterType == paramType) - .FirstOrDefault(); + .Single(); } private static MethodInfo GetGenericWriteMethod(Type genericType) { From 1f1e6e6ed4cfb10249353f5791372de6dfed7bb4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 03:33:36 -0400 Subject: [PATCH 12/51] Added Bool, Integer and Nullable support to ETFReader --- src/Discord.Net/ETF/ETFReader.cs | 492 ++++++++++++++++++++++++------- 1 file changed, 392 insertions(+), 100 deletions(-) diff --git a/src/Discord.Net/ETF/ETFReader.cs b/src/Discord.Net/ETF/ETFReader.cs index d7617c344..4641d8234 100644 --- a/src/Discord.Net/ETF/ETFReader.cs +++ b/src/Discord.Net/ETF/ETFReader.cs @@ -1,26 +1,49 @@ -/*using System; +using Newtonsoft.Json; +using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using System.Text; namespace Discord.ETF { - public class ETFReader + public class ETFReader : IDisposable { - + private static readonly ConcurrentDictionary _deserializers; + private static readonly Dictionary _readMethods = GetPrimitiveReadMethods(); + private readonly Stream _stream; private readonly byte[] _buffer; private readonly bool _leaveOpen; private readonly Encoding _encoding; - private readonly ConcurrentDictionary _serializers, _indirectSerializers; + + public ETFReader(Stream stream, bool leaveOpen = false) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); - private void ReadNil(bool allow) + _stream = stream; + _leaveOpen = leaveOpen; + _buffer = new byte[11]; + _encoding = Encoding.UTF8; + } + + private bool ReadNil(bool ignoreLength = false) { - if (!allow) throw new InvalidDataException(); + if (!ignoreLength) + { + _stream.Read(_buffer, 0, 1); + byte length = _buffer[0]; + if (length != 3) return false; + } + _stream.Read(_buffer, 0, 3); - if (_buffer[0] != 'n' || _buffer[1] != 'i' || _buffer[2] != 'l') - throw new InvalidDataException(); + if (_buffer[0] == 'n' && _buffer[1] == 'i' && _buffer[2] == 'l') + return true; + + return false; } private void ReadTrue() { @@ -35,138 +58,407 @@ namespace Discord.ETF throw new InvalidDataException(); } - public bool? ReadBool(bool allowNil) + public bool? ReadNullableBool() { - _stream.Read(_buffer, 0, 2); - switch ((ETFType)_buffer[0]) + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT) { - case ETFType.SMALL_ATOM_EXT: - switch (_buffer[1]) //Length - { - case 3: - ReadNil(allowNil); + _stream.Read(_buffer, 0, 1); + switch (_buffer[0]) //Length + { + case 3: + if (ReadNil()) return null; - case 4: - ReadTrue(); - return true; - case 5: - ReadFalse(); - return false; - } - break; + break; + case 4: + ReadTrue(); + return true; + case 5: + ReadFalse(); + return false; + } + } + throw new InvalidDataException(); + } + public bool ReadBool() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT) + { + _stream.Read(_buffer, 0, 1); + switch (_buffer[0]) //Length + { + case 4: + ReadTrue(); + return true; + case 5: + ReadFalse(); + return false; + } } throw new InvalidDataException(); } - public long? ReadInteger(bool allowNil) + public int ReadSByte() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (sbyte)ReadLongInternal(type); + } + public int? ReadNullableSByte() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (sbyte)ReadLongInternal(type); + } + public uint ReadByte() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (byte)ReadLongInternal(type); + } + public uint? ReadNullableByte() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (byte)ReadLongInternal(type); + } + public int ReadShort() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (short)ReadLongInternal(type); + } + public int? ReadNullableShort() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (short)ReadLongInternal(type); + } + public uint ReadUShort() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (ushort)ReadLongInternal(type); + } + public uint? ReadNullableUShort() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (ushort)ReadLongInternal(type); + } + public int ReadInt() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (int)ReadLongInternal(type); + } + public int? ReadNullableInt() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (int)ReadLongInternal(type); + } + public uint ReadUInt() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (uint)ReadLongInternal(type); + } + public uint? ReadNullableUInt() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (uint)ReadLongInternal(type); + } + public long ReadLong() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return ReadLongInternal(type); + } + public long? ReadNullableLong() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return ReadLongInternal(type); + } + public ulong ReadULong() { _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)reader.ReadByte(); + ETFType type = (ETFType)_buffer[0]; + return (ulong)ReadLongInternal(type); + } + public ulong? ReadNullableULong() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (ulong)ReadLongInternal(type); + } + public long ReadLongInternal(ETFType type) + { switch (type) { - case ETFType.SMALL_ATOM_EXT: - ReadNil(allowNil); - return null; case ETFType.SMALL_INTEGER_EXT: _stream.Read(_buffer, 0, 1); - return (_buffer[0] << 24) | (_buffer[1] << 16) | - (_buffer[2] << 8) | (_buffer[3]); + return _buffer[0]; case ETFType.INTEGER_EXT: _stream.Read(_buffer, 0, 4); - return ??; + return (_buffer[0] << 24) | (_buffer[1] << 16) | (_buffer[2] << 8) | (_buffer[3]); case ETFType.SMALL_BIG_EXT: - return ??; + _stream.Read(_buffer, 0, 2); + bool isPositive = _buffer[0] == 0; + byte count = _buffer[1]; + + int shiftValue = (count - 1) * 8; + ulong value = 0; + _stream.Read(_buffer, 0, count); + for (int i = 0; i < count; i++, shiftValue -= 8) + value = value + _buffer[i] << shiftValue; + if (!isPositive) + return -(long)value; + else + return (long)value; } throw new InvalidDataException(); } - public void Write(sbyte value) => Write((long)value); - public void Write(byte value) => Write((ulong)value); - public void Write(short value) => Write((long)value); - public void Write(ushort value) => Write((ulong)value); - public void Write(int value) => Write((long)value); - public void Write(uint value) => Write((ulong)value); - public void Write(long value) + public float ReadSingle() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return (float)ReadDoubleInternal(type); + } + public float? ReadNullableSingle() { - if (value >= byte.MinValue && value <= byte.MaxValue) + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (float)ReadDoubleInternal(type); + } + public double ReadDouble() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return ReadDoubleInternal(type); + } + public double? ReadNullableDouble() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return ReadDoubleInternal(type); + } + public double ReadDoubleInternal(ETFType type) + { + throw new NotImplementedException(); + } + + public string ReadString() + { + throw new NotImplementedException(); + } + public byte[] ReadByteArray() + { + throw new NotImplementedException(); + } + + #region Emit + private static Func CreateDeserializer(Type type, TypeInfo typeInfo) + where T : new() + { + var method = new DynamicMethod("DeserializeETF", type, new[] { typeof(ETFReader) }, true); + var generator = method.GetILGenerator(); + + generator.Emit(OpCodes.Ldarg_0); //ETFReader(this) + EmitReadValue(generator, type, typeInfo, true); + + generator.Emit(OpCodes.Ret); + return method.CreateDelegate(typeof(Func)) as Func; + } + private static void EmitReadValue(ILGenerator generator, Type type, TypeInfo typeInfo, bool isTop) + { + //Convert enum types to their base type + if (typeInfo.IsEnum) { - _buffer[0] = (byte)ETFType.SMALL_INTEGER_EXT; - _buffer[1] = (byte)value; - _stream.Write(_buffer, 0, 2); + type = Enum.GetUnderlyingType(type); + typeInfo = type.GetTypeInfo(); } - else if (value >= int.MinValue && value <= int.MaxValue) + //Primitives/Enums + if (!typeInfo.IsEnum && IsType(type, typeof(sbyte), typeof(byte), typeof(short), + typeof(ushort), typeof(int), typeof(uint), typeof(long), + typeof(ulong), typeof(double), typeof(bool), typeof(string), + typeof(sbyte?), typeof(byte?), typeof(short?), typeof(ushort?), + typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?), + typeof(bool?), typeof(float?), typeof(double?) + /*typeof(object), typeof(DateTime)*/)) { - _buffer[0] = (byte)ETFType.INTEGER_EXT; - _buffer[1] = (byte)(value >> 24); - _buffer[2] = (byte)(value >> 16); - _buffer[3] = (byte)(value >> 8); - _buffer[4] = (byte)value; - _stream.Write(_buffer, 0, 5); + //No conversion needed + generator.EmitCall(OpCodes.Call, GetReadMethod(type), null); } - else + //Dictionaries + /*else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces + .Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>))) + { + generator.EmitCall(OpCodes.Call, _writeDictionaryTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); + } + //Enumerable + else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces + .Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))) { - _buffer[0] = (byte)ETFType.SMALL_BIG_EXT; - if (value < 0) + generator.EmitCall(OpCodes.Call, _writeEnumerableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); + } + //Nullable Structs + else if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>) && + typeInfo.GenericTypeParameters[0].GetTypeInfo().IsValueType) + { + generator.EmitCall(OpCodes.Call, _writeNullableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); + } + //Structs/Classes + else if (typeInfo.IsClass || (typeInfo.IsValueType && !typeInfo.IsPrimitive)) + { + if (isTop) { - _buffer[2] = 1; //Is negative - value = -value; - } + typeInfo.ForEachField(f => + { + string name; + if (!f.IsPublic || !IsETFProperty(f, out name)) return; - byte bytes = 0; - while (value > 0) - _buffer[3 + bytes++] = (byte)(value >>= 8); - _buffer[1] = bytes; //Encoded bytes + generator.Emit(OpCodes.Ldarg_0); //ETFReader(this) + generator.Emit(OpCodes.Ldstr, name); //ETFReader(this), name + generator.EmitCall(OpCodes.Call, GetWriteMethod(typeof(string)), null); + generator.Emit(OpCodes.Ldarg_0); //ETFReader(this) + generator.Emit(OpCodes.Ldarg_1); //ETFReader(this), obj + generator.Emit(OpCodes.Ldfld, f); //ETFReader(this), obj.fieldValue + EmitWriteValue(generator, f.FieldType, f.FieldType.GetTypeInfo(), false); + }); - _stream.Write(_buffer, 0, 3 + bytes); - } + typeInfo.ForEachProperty(p => + { + string name; + if (!p.CanRead || !p.GetMethod.IsPublic || !IsETFProperty(p, out name)) return; + + generator.Emit(OpCodes.Ldarg_0); //ETFReader(this) + generator.Emit(OpCodes.Ldstr, name); //ETFReader(this), name + generator.EmitCall(OpCodes.Call, GetWriteMethod(typeof(string)), null); + generator.Emit(OpCodes.Ldarg_0); //ETFReader(this) + generator.Emit(OpCodes.Ldarg_1); //ETFReader(this), obj + generator.EmitCall(OpCodes.Callvirt, p.GetMethod, null); //ETFReader(this), obj.propValue + EmitWriteValue(generator, p.PropertyType, p.PropertyType.GetTypeInfo(), false); + }); + } + else + { + //While we could drill deeper and make a large serializer that also serializes all subclasses, + //it's more efficient to serialize on a per-type basis via another Write call. + generator.EmitCall(OpCodes.Call, _writeTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); + } + }*/ + //Unsupported (decimal, char) + else + throw new InvalidOperationException($"Deserializing {type.Name} is not supported."); } - public void Write(ulong value) + + private static bool IsType(Type type, params Type[] types) { - if (value <= byte.MaxValue) + for (int i = 0; i < types.Length; i++) { - _buffer[0] = (byte)ETFType.SMALL_INTEGER_EXT; - _buffer[1] = (byte)value; - _stream.Write(_buffer, 0, 2); + if (type == types[i]) + return true; } - else if (value <= int.MaxValue) + return false; + } + private static bool IsETFProperty(FieldInfo f, out string name) + { + var attrib = f.CustomAttributes.Where(x => x.AttributeType == typeof(JsonPropertyAttribute)).FirstOrDefault(); + if (attrib != null) { - _buffer[0] = (byte)ETFType.INTEGER_EXT; - _buffer[1] = (byte)(value >> 24); - _buffer[2] = (byte)(value >> 16); - _buffer[3] = (byte)(value >> 8); - _buffer[4] = (byte)value; - _stream.Write(_buffer, 0, 5); + name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? f.Name; + return true; } - else + name = null; + return false; + } + private static bool IsETFProperty(PropertyInfo p, out string name) + { + var attrib = p.CustomAttributes.Where(x => x.AttributeType == typeof(JsonPropertyAttribute)).FirstOrDefault(); + if (attrib != null) { - _buffer[0] = (byte)ETFType.SMALL_BIG_EXT; - _buffer[2] = 0; //Always positive - - byte bytes = 0; - while (value > 0) - _buffer[3 + bytes++] = (byte)(value >>= 8); - _buffer[1] = bytes; //Encoded bytes - - _stream.Write(_buffer, 0, 3 + bytes); + name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? p.Name; + return true; } + name = null; + return false; + } + + private static MethodInfo GetReadMethod(string name) + => typeof(ETFReader).GetTypeInfo().GetDeclaredMethods(name).Single(); + private static MethodInfo GetReadMethod(Type type) + { + MethodInfo method; + if (_readMethods.TryGetValue(type, out method)) + return method; + return null; } + private static Dictionary GetPrimitiveReadMethods() + { + return new Dictionary + { + { typeof(bool), GetReadMethod(nameof(ReadBool)) }, + { typeof(bool?), GetReadMethod(nameof(ReadNullableBool)) }, + { typeof(byte), GetReadMethod(nameof(ReadByte)) }, + { typeof(byte?), GetReadMethod(nameof(ReadNullableByte)) }, + { typeof(sbyte), GetReadMethod(nameof(ReadSByte)) }, + { typeof(sbyte?), GetReadMethod(nameof(ReadNullableSByte)) }, + { typeof(short), GetReadMethod(nameof(ReadShort)) }, + { typeof(short?), GetReadMethod(nameof(ReadNullableShort)) }, + { typeof(ushort), GetReadMethod(nameof(ReadUShort)) }, + { typeof(ushort?), GetReadMethod(nameof(ReadNullableUShort)) }, + { typeof(int), GetReadMethod(nameof(ReadInt)) }, + { typeof(int?), GetReadMethod(nameof(ReadNullableInt)) }, + { typeof(uint), GetReadMethod(nameof(ReadUInt)) }, + { typeof(uint?), GetReadMethod(nameof(ReadNullableUInt)) }, + { typeof(long), GetReadMethod(nameof(ReadLong)) }, + { typeof(long?), GetReadMethod(nameof(ReadNullableLong)) }, + { typeof(ulong), GetReadMethod(nameof(ReadULong)) }, + { typeof(ulong?), GetReadMethod(nameof(ReadNullableULong)) }, + { typeof(float), GetReadMethod(nameof(ReadSingle)) }, + { typeof(float?), GetReadMethod(nameof(ReadNullableSingle)) }, + { typeof(double), GetReadMethod(nameof(ReadDouble)) }, + { typeof(double?), GetReadMethod(nameof(ReadNullableDouble)) }, + }; + } + #endregion + + #region IDisposable + private bool _isDisposed = false; - public void Write(float value) => Write((double)value); - public unsafe void Write(double value) + protected virtual void Dispose(bool disposing) { - ulong value2 = *(ulong*)&value; - _buffer[0] = (byte)ETFType.NEW_FLOAT_EXT; - _buffer[1] = (byte)(value2 >> 56); - _buffer[2] = (byte)(value2 >> 48); - _buffer[3] = (byte)(value2 >> 40); - _buffer[4] = (byte)(value2 >> 32); - _buffer[5] = (byte)(value2 >> 24); - _buffer[6] = (byte)(value2 >> 16); - _buffer[7] = (byte)(value2 >> 8); - _buffer[8] = (byte)value2; - _stream.Write(_buffer, 0, 9); + if (!_isDisposed) + { + if (disposing) + { + if (_leaveOpen) + _stream.Flush(); + else + _stream.Dispose(); + } + _isDisposed = true; + } } - public void Write(DateTime value) => Write((ulong)((value.Ticks - _epochTime.Ticks) / TimeSpan.TicksPerSecond)); + public void Dispose() => Dispose(true); + #endregion } -}*/ \ No newline at end of file +} \ No newline at end of file From 433ead6f3f201e87a62d8d7c1b50bddbfd35ad17 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 03:34:11 -0400 Subject: [PATCH 13/51] Added DeleteGuildRequest --- src/Discord.Net.Net45/Discord.Net.csproj | 3 +++ .../API/Client/Rest/DeleteGuild.cs | 20 +++++++++++++++++++ src/Discord.Net/Models/Server.cs | 6 +----- 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 src/Discord.Net/API/Client/Rest/DeleteGuild.cs diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index d8023d6bd..b26180c07 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -256,6 +256,9 @@ API\Client\Rest\DeleteChannel.cs + + API\Client\Rest\DeleteGuild.cs + API\Client\Rest\DeleteInvite.cs diff --git a/src/Discord.Net/API/Client/Rest/DeleteGuild.cs b/src/Discord.Net/API/Client/Rest/DeleteGuild.cs new file mode 100644 index 000000000..d55c6d989 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/DeleteGuild.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public class DeleteGuildRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"guilds/{GuildId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; set; } + + public DeleteGuildRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 2c0e9f288..7db898804 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -208,17 +208,13 @@ namespace Discord /// Leaves this server. This function will fail if you're the owner - use Delete instead. public async Task Leave() { - if (_ownerId == CurrentUser.Id) - throw new InvalidOperationException("Unable to leave a server you own, use Server.Delete instead"); try { await Client.ClientAPI.Send(new LeaveGuildRequest(Id)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } /// Deletes this server. This function will fail if you're not the owner - use Leave instead. public async Task Delete() { - if (_ownerId != CurrentUser.Id) - throw new InvalidOperationException("Unable to delete a server you don't own, use Server.Leave instead"); - try { await Client.ClientAPI.Send(new LeaveGuildRequest(Id)).ConfigureAwait(false); } + try { await Client.ClientAPI.Send(new DeleteGuildRequest(Id)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } From 1e6e965775b6b37561c789c9ca11b63b726e8db4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 03:35:01 -0400 Subject: [PATCH 14/51] Changed LeaveGuildRequest to new endpoint --- src/Discord.Net/API/Client/Rest/LeaveGuild.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs index 6a8b3c0cf..df210ad70 100644 --- a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs +++ b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs @@ -6,7 +6,7 @@ namespace Discord.API.Client.Rest public class LeaveGuildRequest : IRestRequest { string IRestRequest.Method => "DELETE"; - string IRestRequest.Endpoint => $"guilds/{GuildId}"; + string IRestRequest.Endpoint => $"users/@me/guilds/{GuildId}"; object IRestRequest.Payload => null; bool IRestRequest.IsPrivate => false; From 04645bb32795c784ae8effd5420549699fd1c439 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 03:42:17 -0400 Subject: [PATCH 15/51] Reorganized ETFReader to match ETFWriter's layout --- src/Discord.Net/ETF/ETFReader.cs | 129 ++++++++++++++++--------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/src/Discord.Net/ETF/ETFReader.cs b/src/Discord.Net/ETF/ETFReader.cs index 4641d8234..051337f86 100644 --- a/src/Discord.Net/ETF/ETFReader.cs +++ b/src/Discord.Net/ETF/ETFReader.cs @@ -107,104 +107,48 @@ namespace Discord.ETF ETFType type = (ETFType)_buffer[0]; return (sbyte)ReadLongInternal(type); } - public int? ReadNullableSByte() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (sbyte)ReadLongInternal(type); - } public uint ReadByte() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return (byte)ReadLongInternal(type); } - public uint? ReadNullableByte() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (byte)ReadLongInternal(type); - } public int ReadShort() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return (short)ReadLongInternal(type); } - public int? ReadNullableShort() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (short)ReadLongInternal(type); - } public uint ReadUShort() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return (ushort)ReadLongInternal(type); } - public uint? ReadNullableUShort() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (ushort)ReadLongInternal(type); - } public int ReadInt() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return (int)ReadLongInternal(type); } - public int? ReadNullableInt() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (int)ReadLongInternal(type); - } public uint ReadUInt() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return (uint)ReadLongInternal(type); } - public uint? ReadNullableUInt() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (uint)ReadLongInternal(type); - } public long ReadLong() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return ReadLongInternal(type); } - public long? ReadNullableLong() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return ReadLongInternal(type); - } public ulong ReadULong() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; return (ulong)ReadLongInternal(type); } - public ulong? ReadNullableULong() - { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (ulong)ReadLongInternal(type); - } public long ReadLongInternal(ETFType type) { switch (type) @@ -239,29 +183,86 @@ namespace Discord.ETF ETFType type = (ETFType)_buffer[0]; return (float)ReadDoubleInternal(type); } - public float? ReadNullableSingle() + public double ReadDouble() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + return ReadDoubleInternal(type); + } + public double ReadDoubleInternal(ETFType type) + { + throw new NotImplementedException(); + } + + public int? ReadNullableSByte() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return (float)ReadDoubleInternal(type); + return (sbyte)ReadLongInternal(type); } - public double ReadDouble() + public uint? ReadNullableByte() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; - return ReadDoubleInternal(type); + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (byte)ReadLongInternal(type); } - public double? ReadNullableDouble() + public int? ReadNullableShort() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; - return ReadDoubleInternal(type); + return (short)ReadLongInternal(type); } - public double ReadDoubleInternal(ETFType type) + public uint? ReadNullableUShort() { - throw new NotImplementedException(); + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (ushort)ReadLongInternal(type); + } + public int? ReadNullableInt() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (int)ReadLongInternal(type); + } + public uint? ReadNullableUInt() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (uint)ReadLongInternal(type); + } + public long? ReadNullableLong() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return ReadLongInternal(type); + } + public ulong? ReadNullableULong() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (ulong)ReadLongInternal(type); + } + public float? ReadNullableSingle() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return (float)ReadDoubleInternal(type); + } + public double? ReadNullableDouble() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null; + return ReadDoubleInternal(type); } public string ReadString() From 964ba8292864df2b6cbc4ea8fc802201f85584c7 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 03:49:17 -0400 Subject: [PATCH 16/51] Added a few more placeholders to ETFReader --- src/Discord.Net/ETF/ETFReader.cs | 128 +++++++++++++++++++------------ 1 file changed, 77 insertions(+), 51 deletions(-) diff --git a/src/Discord.Net/ETF/ETFReader.cs b/src/Discord.Net/ETF/ETFReader.cs index 051337f86..9cbbbfa74 100644 --- a/src/Discord.Net/ETF/ETFReader.cs +++ b/src/Discord.Net/ETF/ETFReader.cs @@ -29,36 +29,8 @@ namespace Discord.ETF _buffer = new byte[11]; _encoding = Encoding.UTF8; } - - private bool ReadNil(bool ignoreLength = false) - { - if (!ignoreLength) - { - _stream.Read(_buffer, 0, 1); - byte length = _buffer[0]; - if (length != 3) return false; - } - - _stream.Read(_buffer, 0, 3); - if (_buffer[0] == 'n' && _buffer[1] == 'i' && _buffer[2] == 'l') - return true; - - return false; - } - private void ReadTrue() - { - _stream.Read(_buffer, 0, 4); - if (_buffer[0] != 't' || _buffer[1] != 'r' || _buffer[2] != 'u' || _buffer[3] != 'e') - throw new InvalidDataException(); - } - private void ReadFalse() - { - _stream.Read(_buffer, 0, 5); - if (_buffer[0] != 'f' || _buffer[1] != 'a' || _buffer[2] != 'l' || _buffer[3] != 's' || _buffer[4] != 'e') - throw new InvalidDataException(); - } - - public bool? ReadNullableBool() + + public bool ReadBool() { _stream.Read(_buffer, 0, 1); ETFType type = (ETFType)_buffer[0]; @@ -67,10 +39,6 @@ namespace Discord.ETF _stream.Read(_buffer, 0, 1); switch (_buffer[0]) //Length { - case 3: - if (ReadNil()) - return null; - break; case 4: ReadTrue(); return true; @@ -81,24 +49,17 @@ namespace Discord.ETF } throw new InvalidDataException(); } - public bool ReadBool() + private void ReadTrue() { - _stream.Read(_buffer, 0, 1); - ETFType type = (ETFType)_buffer[0]; - if (type == ETFType.SMALL_ATOM_EXT) - { - _stream.Read(_buffer, 0, 1); - switch (_buffer[0]) //Length - { - case 4: - ReadTrue(); - return true; - case 5: - ReadFalse(); - return false; - } - } - throw new InvalidDataException(); + _stream.Read(_buffer, 0, 4); + if (_buffer[0] != 't' || _buffer[1] != 'r' || _buffer[2] != 'u' || _buffer[3] != 'e') + throw new InvalidDataException(); + } + private void ReadFalse() + { + _stream.Read(_buffer, 0, 5); + if (_buffer[0] != 'f' || _buffer[1] != 'a' || _buffer[2] != 'l' || _buffer[3] != 's' || _buffer[4] != 'e') + throw new InvalidDataException(); } public int ReadSByte() @@ -194,6 +155,29 @@ namespace Discord.ETF throw new NotImplementedException(); } + public bool? ReadNullableBool() + { + _stream.Read(_buffer, 0, 1); + ETFType type = (ETFType)_buffer[0]; + if (type == ETFType.SMALL_ATOM_EXT) + { + _stream.Read(_buffer, 0, 1); + switch (_buffer[0]) //Length + { + case 3: + if (ReadNil()) + return null; + break; + case 4: + ReadTrue(); + return true; + case 5: + ReadFalse(); + return false; + } + } + throw new InvalidDataException(); + } public int? ReadNullableSByte() { _stream.Read(_buffer, 0, 1); @@ -274,6 +258,48 @@ namespace Discord.ETF throw new NotImplementedException(); } + public T Read() + where T : new() + { + var type = typeof(T); + var typeInfo = type.GetTypeInfo(); + var action = _deserializers.GetOrAdd(type, _ => CreateDeserializer(type, typeInfo)) as Func; + return action(this); + } + /*public void Read() + where T : Nullable + where U : struct, new() + { + }*/ + public T[] ReadArray() + { + throw new NotImplementedException(); + } + public IDictionary ReadDictionary() + { + throw new NotImplementedException(); + } + /*public object Read(object obj) + { + throw new NotImplementedException(); + }*/ + + private bool ReadNil(bool ignoreLength = false) + { + if (!ignoreLength) + { + _stream.Read(_buffer, 0, 1); + byte length = _buffer[0]; + if (length != 3) return false; + } + + _stream.Read(_buffer, 0, 3); + if (_buffer[0] == 'n' && _buffer[1] == 'i' && _buffer[2] == 'l') + return true; + + return false; + } + #region Emit private static Func CreateDeserializer(Type type, TypeInfo typeInfo) where T : new() From ca792a798a61a12312ce43217809ee13970c20da Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 16:05:17 -0400 Subject: [PATCH 17/51] Fixed compile warning --- src/Discord.Net/ETF/ETFReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net/ETF/ETFReader.cs b/src/Discord.Net/ETF/ETFReader.cs index 9cbbbfa74..9e1e4e8ef 100644 --- a/src/Discord.Net/ETF/ETFReader.cs +++ b/src/Discord.Net/ETF/ETFReader.cs @@ -12,7 +12,7 @@ namespace Discord.ETF { public class ETFReader : IDisposable { - private static readonly ConcurrentDictionary _deserializers; + private static readonly ConcurrentDictionary _deserializers = new ConcurrentDictionary(); private static readonly Dictionary _readMethods = GetPrimitiveReadMethods(); private readonly Stream _stream; From 989243f88c42291d639a030c4786d030393a6b0b Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 16:10:21 -0400 Subject: [PATCH 18/51] Fixed Channel.Users permissions cache creation --- src/Discord.Net/Models/Channel.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index ddf85dd0b..79e908dd8 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -116,6 +116,8 @@ namespace Discord foreach (var user in server.Users) AddUser(user); } + if (client.Config.UsePermissionsCache) + _users = new ConcurrentDictionary(2, (int)(server.UserCount * 1.05)); } internal Channel(DiscordClient client, ulong id, User recipient) : this(client, id) @@ -124,6 +126,8 @@ namespace Discord AddUser(client.PrivateUser); AddUser(recipient); Type = ChannelType.Text; //Discord doesn't give us a type for private channels + if (client.Config.UsePermissionsCache) + _users = new ConcurrentDictionary(2, 2); } private Channel(DiscordClient client, ulong id) { @@ -131,13 +135,6 @@ namespace Discord Id = id; _permissionOverwrites = new Dictionary(); - if (client.Config.UsePermissionsCache) - { - if (IsPrivate) - _users = new ConcurrentDictionary(2, 2); - else - _users = new ConcurrentDictionary(2, (int)(Server.UserCount * 1.05)); - } if (client.Config.MessageCacheSize > 0) _messages = new ConcurrentDictionary(2, (int)(client.Config.MessageCacheSize * 1.05)); } From 8e519d19259c0bd8f821f4aba5c27db41c4838fe Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 16:34:52 -0400 Subject: [PATCH 19/51] Several permission cache fixes --- src/Discord.Net/DiscordClient.cs | 6 ++--- src/Discord.Net/Models/Channel.cs | 41 +++++++++++++------------------ src/Discord.Net/Models/Server.cs | 28 +++++++++++++-------- src/Discord.Net/Models/User.cs | 18 +++++++++++--- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index ed500784c..9d6d82a6a 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -587,7 +587,7 @@ namespace Discord { var server = GetServer(data.GuildId.Value); if (server != null) - channel = server.AddChannel(data.Id); + channel = server.AddChannel(data.Id, true); else Logger.Warning("CHANNEL_CREATE referenced an unknown guild."); } @@ -637,7 +637,7 @@ namespace Discord var server = GetServer(data.GuildId.Value); if (server != null) { - var user = server.AddUser(data.User.Id); + var user = server.AddUser(data.User.Id, true); user.Update(data); user.UpdateActivity(); Logger.Debug($"GUILD_MEMBER_ADD: {user.Path}"); @@ -695,7 +695,7 @@ namespace Discord { foreach (var memberData in data.Members) { - var user = server.AddUser(memberData.User.Id); + var user = server.AddUser(memberData.User.Id, true); user.Update(memberData); //OnUserAdded(user); } diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 79e908dd8..3023c76e3 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -80,18 +80,20 @@ namespace Discord { get { - if (IsPrivate) - return _users.Values.Select(x => x.User); if (Client.Config.UsePermissionsCache) { - if (Type == ChannelType.Text) + if (IsPrivate) + return new User[] { Client.PrivateUser, Recipient }; + else if (Type == ChannelType.Text) return _users.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); else if (Type == ChannelType.Voice) return _users.Values.Select(x => x.User).Where(x => x.VoiceChannel == this); } else { - if (Type == ChannelType.Text) + if (IsPrivate) + return new User[] { Client.PrivateUser, Recipient }; + else if (Type == ChannelType.Text) { ChannelPermissions perms = new ChannelPermissions(); return Server.Users.Where(x => @@ -111,11 +113,6 @@ namespace Discord : this(client, id) { Server = server; - if (server != null && Client.Config.UsePermissionsCache) - { - foreach (var user in server.Users) - AddUser(user); - } if (client.Config.UsePermissionsCache) _users = new ConcurrentDictionary(2, (int)(server.UserCount * 1.05)); } @@ -123,11 +120,7 @@ namespace Discord : this(client, id) { Recipient = recipient; - AddUser(client.PrivateUser); - AddUser(recipient); Type = ChannelType.Text; //Discord doesn't give us a type for private channels - if (client.Config.UsePermissionsCache) - _users = new ConcurrentDictionary(2, 2); } private Channel(DiscordClient client, ulong id) { @@ -328,7 +321,7 @@ namespace Discord public IEnumerable FindUsers(string name, bool exactMatch = false) { if (name == null) throw new ArgumentNullException(nameof(name)); - return _users.Select(x => x.Value.User).Find(name, exactMatch: exactMatch); + return Users.Find(name, exactMatch: exactMatch); } public Task SendMessage(string text) => SendMessageInternal(text, false); @@ -370,8 +363,7 @@ namespace Discord #region Permissions internal void UpdatePermissions() { - if (!Client.Config.UsePermissionsCache) - return; + if (!Client.Config.UsePermissionsCache) return; foreach (var pair in _users) { @@ -383,8 +375,7 @@ namespace Discord } internal void UpdatePermissions(User user) { - if (!Client.Config.UsePermissionsCache) - return; + if (!Client.Config.UsePermissionsCache) return; Member member; if (_users.TryGetValue(user.Id, out member)) @@ -549,8 +540,7 @@ namespace Discord } internal void RemoveUser(ulong id) { - if (!Client.Config.UsePermissionsCache) - return; + if (!Client.Config.UsePermissionsCache) return; Member ignored; _users.TryRemove(id, out ignored); @@ -579,10 +569,13 @@ namespace Discord } return null; } - - Member result; - _users.TryGetValue(id, out result); - return result.User; + else + { + Member result; + if (_users.TryGetValue(id, out result)) + return result.User; + return null; + } } #endregion diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 7db898804..17c90cc8a 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -161,21 +161,21 @@ namespace Discord } internal void Update(ExtendedGuild model) { + Update(model as Guild); //Needs channels if (model.Channels != null) { _channels = new ConcurrentDictionary(2, (int)(model.Channels.Length * 1.05)); foreach (var subModel in model.Channels) - AddChannel(subModel.Id).Update(subModel); + AddChannel(subModel.Id, false).Update(subModel); DefaultChannel = _channels[Id]; } - Update(model as Guild); //Needs channels - if (model.Members != null) { _users = new ConcurrentDictionary(2, (int)(model.Members.Length * 1.05)); foreach (var subModel in model.Members) - AddUser(subModel.User.Id).Update(subModel); + AddUser(subModel.User.Id, false).Update(subModel); } + if (model.VoiceStates != null) { foreach (var subModel in model.VoiceStates) @@ -248,9 +248,14 @@ namespace Discord #endregion #region Channels - internal Channel AddChannel(ulong id) + internal Channel AddChannel(ulong id, bool cachePerms) { var channel = new Channel(Client, id, this); + if (cachePerms && Client.Config.UsePermissionsCache) + { + foreach (var user in Users) + channel.AddUser(user); + } Client.AddChannel(channel); return _channels.GetOrAdd(id, x => channel); } @@ -287,7 +292,7 @@ namespace Discord var request = new CreateChannelRequest(Id) { Name = name, Type = type.Value }; var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); - var channel = AddChannel(response.Id); + var channel = AddChannel(response.Id, true); channel.Update(response); return channel; } @@ -439,7 +444,7 @@ namespace Discord #endregion #region Users - internal User AddUser(ulong id) + internal User AddUser(ulong id, bool cachePerms) { _userCount++; Member member = new Member(new User(Client, id, this), ServerPermissions.None); @@ -448,11 +453,14 @@ namespace Discord member.User.CurrentGame = Client.CurrentGame; member.User.Status = Client.Status; } - + if (_users.TryGetOrAdd(id, member, out member)) { - foreach (var channel in _channels) - channel.Value.AddUser(member.User); + if (cachePerms && Client.Config.UsePermissionsCache) + { + foreach (var channel in _channels) + channel.Value.AddUser(member.User); + } } return member.User; } diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index dd381d014..8c45e5dcc 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -309,13 +309,20 @@ namespace Discord #region Roles private void UpdateRoles(IEnumerable roles) { + bool updated = false; var newRoles = new Dictionary(); + + var oldRoles = _roles; if (roles != null) { foreach (var r in roles) { if (r != null) + { newRoles[r.Id] = r; + if (!oldRoles.ContainsKey(r.Id)) + updated = true; //Check for adds + } } } @@ -324,10 +331,15 @@ namespace Discord var everyone = Server.EveryoneRole; newRoles[everyone.Id] = everyone; } - _roles = newRoles; + if (oldRoles.Count != newRoles.Count) + updated = true; //Check for removes - if (Server != null) - Server.UpdatePermissions(this); + if (updated) + { + _roles = newRoles; + if (Server != null) + Server.UpdatePermissions(this); + } } public bool HasRole(Role role) { From c6af9fa70dc55d6cc2d2635fa12cf9e517d1c234 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 16:38:20 -0400 Subject: [PATCH 20/51] Fixed channel's user cache not populating on READY --- src/Discord.Net/Models/Server.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 17c90cc8a..69d754c0b 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -161,7 +161,9 @@ namespace Discord } internal void Update(ExtendedGuild model) { - Update(model as Guild); //Needs channels + Update(model as Guild); + + //Only channels or members should have AddXXX(cachePerms: true), not both if (model.Channels != null) { _channels = new ConcurrentDictionary(2, (int)(model.Channels.Length * 1.05)); @@ -173,7 +175,7 @@ namespace Discord { _users = new ConcurrentDictionary(2, (int)(model.Members.Length * 1.05)); foreach (var subModel in model.Members) - AddUser(subModel.User.Id, false).Update(subModel); + AddUser(subModel.User.Id, true).Update(subModel); } if (model.VoiceStates != null) From 41125c41db7dae874dc7ad62d3b064342db4c14f Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 16:40:24 -0400 Subject: [PATCH 21/51] Fixed Channel.GetUser --- src/Discord.Net/Models/Channel.cs | 34 ++++++++++++++----------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 3023c76e3..02bd78b0b 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -547,35 +547,31 @@ namespace Discord } public User GetUser(ulong id) { - if (!Client.Config.UsePermissionsCache) + if (IsPrivate) { - if (Server != null) - { - var user = Server.GetUser(id); - if (user != null) - { - ChannelPermissions perms = new ChannelPermissions(); - UpdatePermissions(user, ref perms); - if (perms.ReadMessages) - return user; - } - } - else + if (id == Recipient.Id) + return Recipient; + else if (id == Client.PrivateUser.Id) + return Client.PrivateUser; + } + else if (!Client.Config.UsePermissionsCache) + { + var user = Server.GetUser(id); + if (user != null) { - if (id == Recipient.Id) - return Recipient; - else if (id == Client.PrivateUser.Id) - return Client.PrivateUser; + ChannelPermissions perms = new ChannelPermissions(); + UpdatePermissions(user, ref perms); + if (perms.ReadMessages) + return user; } - return null; } else { Member result; if (_users.TryGetValue(id, out result)) return result.User; - return null; } + return null; } #endregion From d7ecefb5cbc45bf93c15d3369df623710be2c678 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 16:50:07 -0400 Subject: [PATCH 22/51] Improved performance where Channel.GetUser is used --- src/Discord.Net/DiscordClient.cs | 3 ++- src/Discord.Net/Models/Channel.cs | 20 +++++++++++++++++--- src/Discord.Net/Models/Message.cs | 4 ++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 9d6d82a6a..f7baa9eee 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -807,7 +807,8 @@ namespace Discord Channel channel = GetChannel(data.ChannelId); if (channel != null) { - var user = channel.GetUser(data.Author.Id); + var user = channel.GetUserFast(data.Author.Id); + if (user != null) { Message msg = null; diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 02bd78b0b..bd2b4a9cc 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -275,7 +275,7 @@ namespace Discord if (_messages.TryGetValue(id, out result)) return result; } - return new Message(id, this, userId != null ? GetUser(userId.Value) : null); + return new Message(id, this, userId != null ? GetUserFast(userId.Value) : null); } public async Task DownloadMessages(int limit = 100, ulong? relativeMessageId = null, @@ -298,13 +298,13 @@ namespace Discord Message msg = null; if (useCache) { - msg = AddMessage(x.Id, GetUser(x.Author.Id), x.Timestamp.Value); + msg = AddMessage(x.Id, GetUserFast(x.Author.Id), x.Timestamp.Value); var user = msg.User; if (user != null) user.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp); } else - msg = new Message(x.Id, this, GetUser(x.Author.Id)); + msg = new Message(x.Id, this, GetUserFast(x.Author.Id)); msg.Update(x); return msg; }) @@ -573,6 +573,20 @@ namespace Discord } return null; } + internal User GetUserFast(ulong id) + { + //Can return users not in this channel, but that's usually okay + if (IsPrivate) + { + if (id == Recipient.Id) + return Recipient; + else if (id == Client.PrivateUser.Id) + return Client.PrivateUser; + } + else + return Server.GetUser(id); + return null; + } #endregion internal Channel Clone() diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index 324acd3ae..0d5ca74ae 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -36,7 +36,7 @@ namespace Discord ulong id; if (e.Value.Substring(2, e.Value.Length - 3).TryToId(out id)) { - var user = channel.GetUser(id); + var user = channel.GetUserFast(id); if (user != null) { if (users != null) @@ -276,7 +276,7 @@ namespace Discord if (model.Mentions != null) { MentionedUsers = model.Mentions - .Select(x => Channel.GetUser(x.Id)) + .Select(x => Channel.GetUserFast(x.Id)) .Where(x => x != null) .ToArray(); } From d6f22d11dc0dac82fd362ecbcdbdd4d85fb35fc6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 16 Feb 2016 22:42:06 -0400 Subject: [PATCH 23/51] Wait for all offline users before raising LoggedIn --- src/Discord.Net/DiscordClient.cs | 111 ++++++++++++++++++------------- src/Discord.Net/Models/Server.cs | 11 +-- 2 files changed, 72 insertions(+), 50 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index f7baa9eee..f00b02eab 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -32,6 +32,7 @@ namespace Discord private ConcurrentDictionary _channels; private ConcurrentDictionary _privateChannels; //Key = RecipientId private Dictionary _regions; + private Stopwatch _connectionStopwatch; internal Logger Logger { get; } @@ -109,6 +110,8 @@ namespace Discord //Logging Log = new LogManager(this); Logger = Log.CreateLogger("Discord"); + if (config.LogLevel >= LogSeverity.Verbose) + _connectionStopwatch = new Stopwatch(); //Async _taskManager = new TaskManager(Cleanup); @@ -143,11 +146,7 @@ namespace Discord ClientAPI = new JsonRestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI")); StatusAPI = new JsonRestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI")); GatewaySocket = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway")); - GatewaySocket.Connected += (s, e) => - { - if (State == ConnectionState.Connecting) - EndConnect(); - }; + //GatewaySocket.Disconnected += (s, e) => OnDisconnected(e.WasUnexpected, e.Exception); GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e); @@ -186,7 +185,10 @@ namespace Discord Stopwatch stopwatch = null; if (Config.LogLevel >= LogSeverity.Verbose) + { + _connectionStopwatch.Restart(); stopwatch = Stopwatch.StartNew(); + } State = ConnectionState.Connecting; _disconnectedEvent.Reset(); @@ -208,7 +210,7 @@ namespace Discord { stopwatch.Stop(); double seconds = Math.Round(stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerSecond, 2); - Logger.Verbose($"Connection took {seconds} sec"); + Logger.Verbose($"Handshake + Ready took {seconds} sec"); } } } @@ -253,11 +255,21 @@ namespace Discord } private void EndConnect() { - State = ConnectionState.Connected; - _connectedEvent.Set(); + if (State == ConnectionState.Connecting) + { + State = ConnectionState.Connected; + _connectedEvent.Set(); - SendStatus(); - OnLoggedIn(); + if (Config.LogLevel >= LogSeverity.Verbose) + { + _connectionStopwatch.Stop(); + double seconds = Math.Round(_connectionStopwatch.ElapsedTicks / (double)TimeSpan.TicksPerSecond, 2); + Logger.Verbose($"Connection took {seconds} sec"); + } + + SendStatus(); + OnLoggedIn(); + } } /// Disconnects from the Discord server, canceling any pending requests. @@ -472,9 +484,6 @@ namespace Discord //Global case "READY": { - Stopwatch stopwatch = null; - if (Config.LogLevel >= LogSeverity.Verbose) - stopwatch = Stopwatch.StartNew(); var data = e.Payload.ToObject(Serializer); int channelCount = 0; @@ -512,13 +521,8 @@ namespace Discord } if (largeServers.Count > 0) GatewaySocket.SendRequestMembers(largeServers, "", 0); - - if (Config.LogLevel >= LogSeverity.Verbose) - { - stopwatch.Stop(); - double seconds = Math.Round(stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerSecond, 2); - Logger.Verbose($"READY took {seconds} sec"); - } + else + EndConnect(); } break; @@ -532,9 +536,9 @@ namespace Discord server.Update(data); if (data.Unavailable != false) - Logger.Debug($"GUILD_CREATE: {server.Path}"); + Logger.Info($"GUILD_CREATE: {server.Path}"); else - Logger.Debug($"GUILD_AVAILABLE: {server.Path}"); + Logger.Info($"GUILD_AVAILABLE: {server.Path}"); if (data.Unavailable != false) OnJoinedServer(server); @@ -550,7 +554,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? server.Clone() : null; server.Update(data); - Logger.Debug($"GUILD_UPDATE: {server.Path}"); + Logger.Info($"GUILD_UPDATE: {server.Path}"); OnServerUpdated(before, server); } else @@ -564,9 +568,9 @@ namespace Discord if (server != null) { if (data.Unavailable != true) - Logger.Debug($"GUILD_DELETE: {server.Path}"); + Logger.Info($"GUILD_DELETE: {server.Path}"); else - Logger.Debug($"GUILD_UNAVAILABLE: {server.Path}"); + Logger.Info($"GUILD_UNAVAILABLE: {server.Path}"); OnServerUnavailable(server); if (data.Unavailable != true) @@ -596,7 +600,7 @@ namespace Discord if (channel != null) { channel.Update(data); - Logger.Debug($"CHANNEL_CREATE: {channel.Path}"); + Logger.Info($"CHANNEL_CREATE: {channel.Path}"); OnChannelCreated(channel); } } @@ -609,7 +613,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? channel.Clone() : null; channel.Update(data); - Logger.Debug($"CHANNEL_UPDATE: {channel.Path}"); + Logger.Info($"CHANNEL_UPDATE: {channel.Path}"); OnChannelUpdated(before, channel); } else @@ -622,7 +626,7 @@ namespace Discord var channel = RemoveChannel(data.Id); if (channel != null) { - Logger.Debug($"CHANNEL_DELETE: {channel.Path}"); + Logger.Info($"CHANNEL_DELETE: {channel.Path}"); OnChannelDestroyed(channel); } else @@ -637,10 +641,10 @@ namespace Discord var server = GetServer(data.GuildId.Value); if (server != null) { - var user = server.AddUser(data.User.Id, true); + var user = server.AddUser(data.User.Id, true, true); user.Update(data); user.UpdateActivity(); - Logger.Debug($"GUILD_MEMBER_ADD: {user.Path}"); + Logger.Info($"GUILD_MEMBER_ADD: {user.Path}"); OnUserJoined(user); } else @@ -658,7 +662,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); - Logger.Debug($"GUILD_MEMBER_UPDATE: {user.Path}"); + Logger.Info($"GUILD_MEMBER_UPDATE: {user.Path}"); OnUserUpdated(before, user); } else @@ -677,7 +681,7 @@ namespace Discord var user = server.RemoveUser(data.User.Id); if (user != null) { - Logger.Debug($"GUILD_MEMBER_REMOVE: {user.Path}"); + Logger.Info($"GUILD_MEMBER_REMOVE: {user.Path}"); OnUserLeft(user); } else @@ -695,11 +699,23 @@ namespace Discord { foreach (var memberData in data.Members) { - var user = server.AddUser(memberData.User.Id, true); + var user = server.AddUser(memberData.User.Id, true, false); user.Update(memberData); //OnUserAdded(user); } - Logger.Debug($"GUILD_MEMBERS_CHUNK: {data.Members.Length} users"); + Logger.Verbose($"GUILD_MEMBERS_CHUNK: {data.Members.Length} users"); + + if (server.CurrentUserCount >= server.UserCount) //Finished downloading for there + { + bool isConnectComplete = true; + foreach (var server2 in _servers.Select(x => x.Value)) + { + if (server2.CurrentUserCount < server2.UserCount) + isConnectComplete = false; + } + if (isConnectComplete) + EndConnect(); + } } else Logger.Warning("GUILD_MEMBERS_CHUNK referenced an unknown guild."); @@ -715,7 +731,7 @@ namespace Discord { var role = server.AddRole(data.Data.Id); role.Update(data.Data, false); - Logger.Debug($"GUILD_ROLE_CREATE: {role.Path}"); + Logger.Info($"GUILD_ROLE_CREATE: {role.Path}"); OnRoleCreated(role); } else @@ -733,7 +749,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? role.Clone() : null; role.Update(data.Data, true); - Logger.Debug($"GUILD_ROLE_UPDATE: {role.Path}"); + Logger.Info($"GUILD_ROLE_UPDATE: {role.Path}"); OnRoleUpdated(before, role); } else @@ -752,7 +768,7 @@ namespace Discord var role = server.RemoveRole(data.RoleId); if (role != null) { - Logger.Debug($"GUILD_ROLE_DELETE: {role.Path}"); + Logger.Info($"GUILD_ROLE_DELETE: {role.Path}"); OnRoleDeleted(role); } else @@ -773,7 +789,7 @@ namespace Discord var user = server.GetUser(data.User.Id); if (user != null) { - Logger.Debug($"GUILD_BAN_ADD: {user.Path}"); + Logger.Info($"GUILD_BAN_ADD: {user.Path}"); OnUserBanned(user); } else @@ -791,7 +807,7 @@ namespace Discord { var user = new User(this, data.User.Id, server); user.Update(data.User); - Logger.Debug($"GUILD_BAN_REMOVE: {user.Path}"); + Logger.Info($"GUILD_BAN_REMOVE: {user.Path}"); OnUserUnbanned(user); } else @@ -837,7 +853,7 @@ namespace Discord msg.Update(data); user.UpdateActivity(); - Logger.Debug($"MESSAGE_CREATE: {channel.Path} ({user.Name ?? "Unknown"})"); + Logger.Verbose($"MESSAGE_CREATE: {channel.Path} ({user.Name ?? "Unknown"})"); OnMessageReceived(msg); } else @@ -856,7 +872,7 @@ namespace Discord var msg = channel.GetMessage(data.Id, data.Author?.Id); var before = Config.EnablePreUpdateEvents ? msg.Clone() : null; msg.Update(data); - Logger.Debug($"MESSAGE_UPDATE: {channel.Path} ({data.Author?.Username ?? "Unknown"})"); + Logger.Verbose($"MESSAGE_UPDATE: {channel.Path} ({data.Author?.Username ?? "Unknown"})"); OnMessageUpdated(before, msg); } else @@ -870,7 +886,7 @@ namespace Discord if (channel != null) { var msg = channel.RemoveMessage(data.Id); - Logger.Debug($"MESSAGE_DELETE: {channel.Path} ({msg.User?.Name ?? "Unknown"})"); + Logger.Verbose($"MESSAGE_DELETE: {channel.Path} ({msg.User?.Name ?? "Unknown"})"); OnMessageDeleted(msg); } else @@ -903,7 +919,8 @@ namespace Discord if (user != null) { - Logger.Debug($"PRESENCE_UPDATE: {user.Path}"); + /*if (Config.LogLevel == LogSeverity.Debug) + Logger.Debug($"PRESENCE_UPDATE: {user.Path}");*/ var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); OnUserUpdated(before, user); @@ -930,7 +947,8 @@ namespace Discord user = channel.Server.GetUser(data.UserId); if (user != null) { - Logger.Debug($"TYPING_START: {channel.Path} ({user.Name})"); + /*if (Config.LogLevel == LogSeverity.Debug) + Logger.Debug($"TYPING_START: {channel.Path} ({user.Name})");*/ OnUserIsTypingUpdated(channel, user); user.UpdateActivity(); } @@ -950,7 +968,8 @@ namespace Discord var user = server.GetUser(data.UserId); if (user != null) { - Logger.Debug($"VOICE_STATE_UPDATE: {user.Path}"); + /*if (Config.LogLevel == LogSeverity.Debug) + Logger.Debug($"VOICE_STATE_UPDATE: {user.Path}");*/ var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); //Logger.Verbose($"Voice Updated: {server.Name}/{user.Name}"); @@ -975,7 +994,7 @@ namespace Discord PrivateUser.Update(data); foreach (var server in _servers) server.Value.CurrentUser.Update(data); - Logger.Debug($"USER_UPDATE"); + Logger.Info($"USER_UPDATE"); OnProfileUpdated(before, CurrentUser); } } diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 69d754c0b..e4c036c60 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -107,10 +107,12 @@ namespace Discord /// Gets the number of channels in this server. public int ChannelCount => _channels.Count; + /// Gets the number of users downloaded for this server so far. + internal int CurrentUserCount => _users.Count; /// Gets the number of users in this server. public int UserCount => _userCount; /// Gets the number of roles in this server. - public int RoleCount => _channels.Count; + public int RoleCount => _roles.Count; internal Server(DiscordClient client, ulong id) { @@ -175,7 +177,7 @@ namespace Discord { _users = new ConcurrentDictionary(2, (int)(model.Members.Length * 1.05)); foreach (var subModel in model.Members) - AddUser(subModel.User.Id, true).Update(subModel); + AddUser(subModel.User.Id, true, true).Update(subModel); } if (model.VoiceStates != null) @@ -446,9 +448,10 @@ namespace Discord #endregion #region Users - internal User AddUser(ulong id, bool cachePerms) + internal User AddUser(ulong id, bool cachePerms, bool incrementCount) { - _userCount++; + if (incrementCount) + _userCount++; Member member = new Member(new User(Client, id, this), ServerPermissions.None); if (id == Client.CurrentUser.Id) { From 6402b274583dc038f0c131003d51d32e835f1739 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 17 Feb 2016 01:21:11 -0400 Subject: [PATCH 24/51] Fixed getting_started's nuget links --- docs/getting_started.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 1e8ee492e..f9dfd857d 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -18,14 +18,16 @@ You can get Discord.Net from NuGet: * `Discord.Net`_ * `Discord.Net.Commands`_ * `Discord.Net.Modules`_ +* `Discord.Net.Audio`_ If you have trouble installing from NuGet, try installing dependencies manually. You can also pull the latest source from `GitHub`_ -.. _Discord.Net: https://www.nuget.org/packages/Discord.Net/0.8.1-beta2 -.. _Discord.Net.Commands: https://www.nuget.org/packages/Discord.Net.Commands/0.8.1-beta2 -.. _Discord.Net.Modules: https://www.nuget.org/packages/Discord.Net.Modules/0.8.1-beta2 +.. _Discord.Net: https://www.nuget.org/packages/Discord.Net +.. _Discord.Net.Commands: https://www.nuget.org/packages/Discord.Net.Commands +.. _Discord.Net.Modules: https://www.nuget.org/packages/Discord.Net.Modules +.. _Discord.Net.Modules: https://www.nuget.org/packages/Discord.Net.Audio .. _GitHub: https://github.com/RogueException/Discord.Net/ Async From f7b2eb7f6fe4d0f1d0f9c7ca348fb00e727c2f50 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 17 Feb 2016 01:25:12 -0400 Subject: [PATCH 25/51] Avoid processing online users twice during connection --- src/Discord.Net/DiscordClient.cs | 1 - src/Discord.Net/Models/Server.cs | 54 ++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index f00b02eab..b43278ca8 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -701,7 +701,6 @@ namespace Discord { var user = server.AddUser(memberData.User.Id, true, false); user.Update(memberData); - //OnUserAdded(user); } Logger.Verbose($"GUILD_MEMBERS_CHUNK: {data.Members.Length} users"); diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index e4c036c60..b7a9933dc 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -173,25 +173,30 @@ namespace Discord AddChannel(subModel.Id, false).Update(subModel); DefaultChannel = _channels[Id]; } - if (model.Members != null) - { - _users = new ConcurrentDictionary(2, (int)(model.Members.Length * 1.05)); - foreach (var subModel in model.Members) - AddUser(subModel.User.Id, true, true).Update(subModel); - } - - if (model.VoiceStates != null) + if (model.MemberCount != null) { - foreach (var subModel in model.VoiceStates) - GetUser(subModel.UserId)?.Update(subModel); + if (_users == null) + _users = new ConcurrentDictionary(2, (int)(model.MemberCount * 1.05)); + _userCount = model.MemberCount.Value; } - if (model.Presences != null) + if (!model.IsLarge) { - foreach (var subModel in model.Presences) - GetUser(subModel.User.Id)?.Update(subModel); + if (model.Members != null) + { + foreach (var subModel in model.Members) + AddUser(subModel.User.Id, true, false).Update(subModel); + } + if (model.VoiceStates != null) + { + foreach (var subModel in model.VoiceStates) + GetUser(subModel.UserId)?.Update(subModel); + } + if (model.Presences != null) + { + foreach (var subModel in model.Presences) + GetUser(subModel.User.Id)?.Update(subModel); + } } - if (model.MemberCount != null) - _userCount = model.MemberCount.Value; } /// Edits this server, changing only non-null attributes. @@ -450,17 +455,20 @@ namespace Discord #region Users internal User AddUser(ulong id, bool cachePerms, bool incrementCount) { - if (incrementCount) + if (incrementCount) _userCount++; - Member member = new Member(new User(Client, id, this), ServerPermissions.None); - if (id == Client.CurrentUser.Id) - { - member.User.CurrentGame = Client.CurrentGame; - member.User.Status = Client.Status; - } - if (_users.TryGetOrAdd(id, member, out member)) + Member member; + if (!_users.TryGetValue(id, out member)) //Users can only be added from websocket thread, ignore threadsafety { + member = new Member(new User(Client, id, this), ServerPermissions.None); + if (id == Client.CurrentUser.Id) + { + member.User.CurrentGame = Client.CurrentGame; + member.User.Status = Client.Status; + } + + _users[id] = member; if (cachePerms && Client.Config.UsePermissionsCache) { foreach (var channel in _channels) From b9d68b723a96918638f675c510922f27f04dd11a Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 17 Feb 2016 01:30:05 -0400 Subject: [PATCH 26/51] Renamed LoggedIn event to Ready --- src/Discord.Net/DiscordClient.Events.cs | 6 +++--- src/Discord.Net/DiscordClient.cs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net/DiscordClient.Events.cs b/src/Discord.Net/DiscordClient.Events.cs index 5d2577627..5e9b5bc09 100644 --- a/src/Discord.Net/DiscordClient.Events.cs +++ b/src/Discord.Net/DiscordClient.Events.cs @@ -5,7 +5,7 @@ namespace Discord { public partial class DiscordClient { - public event EventHandler LoggedIn = delegate { }; + public event EventHandler Ready = delegate { }; //public event EventHandler LoggedOut = delegate { }; public event EventHandler ChannelCreated = delegate { }; public event EventHandler ChannelDestroyed = delegate { }; @@ -31,8 +31,8 @@ namespace Discord public event EventHandler UserUpdated = delegate { }; public event EventHandler UserUnbanned = delegate { }; - private void OnLoggedIn() - => OnEvent(LoggedIn); + private void OnReady() + => OnEvent(Ready); /*private void OnLoggedOut(bool wasUnexpected, Exception ex) => OnEvent(LoggedOut, new DisconnectedEventArgs(wasUnexpected, ex));*/ diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index b43278ca8..51d35ed90 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -268,7 +268,7 @@ namespace Discord } SendStatus(); - OnLoggedIn(); + OnReady(); } } @@ -484,6 +484,8 @@ namespace Discord //Global case "READY": { + //TODO: None of this is really threadsafe - should only replace the cache collections when they have been fully populated + var data = e.Payload.ToObject(Serializer); int channelCount = 0; From 212890a147d52aecdb78122b5cff75699440e0ef Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 17 Feb 2016 01:44:26 -0400 Subject: [PATCH 27/51] Removed raw input/output from Debug logging --- .../API/Client/Rest/AcceptInvite.cs | 1 - src/Discord.Net/API/Client/Rest/AckMessage.cs | 1 - .../API/Client/Rest/AddChannelPermission.cs | 1 - .../API/Client/Rest/AddGuildBan.cs | 1 - .../API/Client/Rest/CreateChannel.cs | 1 - .../API/Client/Rest/CreateGuild.cs | 1 - .../API/Client/Rest/CreateInvite.cs | 1 - .../API/Client/Rest/CreatePrivateChannel.cs | 1 - src/Discord.Net/API/Client/Rest/CreateRole.cs | 1 - .../API/Client/Rest/DeleteChannel.cs | 1 - .../API/Client/Rest/DeleteGuild.cs | 1 - .../API/Client/Rest/DeleteInvite.cs | 1 - .../API/Client/Rest/DeleteMessage.cs | 1 - src/Discord.Net/API/Client/Rest/DeleteRole.cs | 1 - src/Discord.Net/API/Client/Rest/Gateway.cs | 1 - src/Discord.Net/API/Client/Rest/GetBans.cs | 1 - src/Discord.Net/API/Client/Rest/GetInvite.cs | 1 - src/Discord.Net/API/Client/Rest/GetInvites.cs | 1 - .../API/Client/Rest/GetMessages.cs | 1 - .../API/Client/Rest/GetVoiceRegions.cs | 1 - src/Discord.Net/API/Client/Rest/GetWidget.cs | 1 - src/Discord.Net/API/Client/Rest/KickMember.cs | 1 - src/Discord.Net/API/Client/Rest/LeaveGuild.cs | 1 - src/Discord.Net/API/Client/Rest/Login.cs | 1 - src/Discord.Net/API/Client/Rest/Logout.cs | 1 - .../API/Client/Rest/PruneMembers.cs | 1 - .../Client/Rest/RemoveChannelPermission.cs | 1 - .../API/Client/Rest/RemoveGuildBan.cs | 1 - .../API/Client/Rest/ReorderChannels.cs | 1 - .../API/Client/Rest/ReorderRoles.cs | 1 - src/Discord.Net/API/Client/Rest/SendFile.cs | 1 - .../API/Client/Rest/SendIsTyping.cs | 1 - .../API/Client/Rest/SendMessage.cs | 1 - .../API/Client/Rest/UpdateChannel.cs | 1 - .../API/Client/Rest/UpdateGuild.cs | 1 - .../API/Client/Rest/UpdateMember.cs | 1 - .../API/Client/Rest/UpdateMessage.cs | 1 - .../API/Client/Rest/UpdateProfile.cs | 1 - src/Discord.Net/API/Client/Rest/UpdateRole.cs | 1 - src/Discord.Net/API/IRestRequest.cs | 1 - .../API/Status/Rest/ActiveMaintenances.cs | 1 - .../API/Status/Rest/AllIncidents.cs | 1 - .../API/Status/Rest/UnresolvedIncidents.cs | 1 - .../API/Status/Rest/UpcomingMaintenances.cs | 1 - src/Discord.Net/DiscordClient.cs | 12 +++++----- src/Discord.Net/Net/Rest/RestClient.cs | 22 ++----------------- src/Discord.Net/Net/WebSockets/WebSocket.cs | 4 ---- 47 files changed, 8 insertions(+), 74 deletions(-) diff --git a/src/Discord.Net/API/Client/Rest/AcceptInvite.cs b/src/Discord.Net/API/Client/Rest/AcceptInvite.cs index 865e37c2d..2940c98ac 100644 --- a/src/Discord.Net/API/Client/Rest/AcceptInvite.cs +++ b/src/Discord.Net/API/Client/Rest/AcceptInvite.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"invite/{InviteId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public string InviteId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/AckMessage.cs b/src/Discord.Net/API/Client/Rest/AckMessage.cs index 1678ed34b..4cf238b72 100644 --- a/src/Discord.Net/API/Client/Rest/AckMessage.cs +++ b/src/Discord.Net/API/Client/Rest/AckMessage.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"channels/{ChannelId}/messages/{MessageId}/ack"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } public ulong MessageId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs b/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs index c7a9f57ac..44d08b4bf 100644 --- a/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs +++ b/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs @@ -9,7 +9,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PUT"; string IRestRequest.Endpoint => $"channels/{ChannelId}/permissions/{TargetId}"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/AddGuildBan.cs b/src/Discord.Net/API/Client/Rest/AddGuildBan.cs index 7699a74e4..3e0b165f5 100644 --- a/src/Discord.Net/API/Client/Rest/AddGuildBan.cs +++ b/src/Discord.Net/API/Client/Rest/AddGuildBan.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PUT"; string IRestRequest.Endpoint => $"guilds/{GuildId}/bans/{UserId}?delete-message-days={PruneDays}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } public ulong UserId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/CreateChannel.cs b/src/Discord.Net/API/Client/Rest/CreateChannel.cs index 0dc45bc43..0d3309701 100644 --- a/src/Discord.Net/API/Client/Rest/CreateChannel.cs +++ b/src/Discord.Net/API/Client/Rest/CreateChannel.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"guilds/{GuildId}/channels"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/CreateGuild.cs b/src/Discord.Net/API/Client/Rest/CreateGuild.cs index baa1f455e..a18d2bee9 100644 --- a/src/Discord.Net/API/Client/Rest/CreateGuild.cs +++ b/src/Discord.Net/API/Client/Rest/CreateGuild.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"guilds"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; [JsonProperty("name")] public string Name { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/CreateInvite.cs b/src/Discord.Net/API/Client/Rest/CreateInvite.cs index a55b9c7e9..73f15c248 100644 --- a/src/Discord.Net/API/Client/Rest/CreateInvite.cs +++ b/src/Discord.Net/API/Client/Rest/CreateInvite.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"channels/{ChannelId}/invites"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs b/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs index 2d413a8d9..e1087dc36 100644 --- a/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs +++ b/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs @@ -9,7 +9,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"users/@me/channels"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; [JsonProperty("recipient_id"), JsonConverter(typeof(LongStringConverter))] public ulong RecipientId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/CreateRole.cs b/src/Discord.Net/API/Client/Rest/CreateRole.cs index 87715490d..3978c6aaa 100644 --- a/src/Discord.Net/API/Client/Rest/CreateRole.cs +++ b/src/Discord.Net/API/Client/Rest/CreateRole.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"guilds/{GuildId}/roles"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/DeleteChannel.cs b/src/Discord.Net/API/Client/Rest/DeleteChannel.cs index 6443c2387..ae56934b5 100644 --- a/src/Discord.Net/API/Client/Rest/DeleteChannel.cs +++ b/src/Discord.Net/API/Client/Rest/DeleteChannel.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"channels/{ChannelId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/DeleteGuild.cs b/src/Discord.Net/API/Client/Rest/DeleteGuild.cs index d55c6d989..44df5892e 100644 --- a/src/Discord.Net/API/Client/Rest/DeleteGuild.cs +++ b/src/Discord.Net/API/Client/Rest/DeleteGuild.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"guilds/{GuildId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/DeleteInvite.cs b/src/Discord.Net/API/Client/Rest/DeleteInvite.cs index 5de8b348b..4bfe1e0d7 100644 --- a/src/Discord.Net/API/Client/Rest/DeleteInvite.cs +++ b/src/Discord.Net/API/Client/Rest/DeleteInvite.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"invite/{InviteCode}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public string InviteCode { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/DeleteMessage.cs b/src/Discord.Net/API/Client/Rest/DeleteMessage.cs index 33921cd1a..3f781a756 100644 --- a/src/Discord.Net/API/Client/Rest/DeleteMessage.cs +++ b/src/Discord.Net/API/Client/Rest/DeleteMessage.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"channels/{ChannelId}/messages/{MessageId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } public ulong MessageId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/DeleteRole.cs b/src/Discord.Net/API/Client/Rest/DeleteRole.cs index 650ece9f2..56faf3d33 100644 --- a/src/Discord.Net/API/Client/Rest/DeleteRole.cs +++ b/src/Discord.Net/API/Client/Rest/DeleteRole.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"guilds/{GuildId}/roles/{RoleId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } public ulong RoleId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/Gateway.cs b/src/Discord.Net/API/Client/Rest/Gateway.cs index ef9486ca1..02dd71008 100644 --- a/src/Discord.Net/API/Client/Rest/Gateway.cs +++ b/src/Discord.Net/API/Client/Rest/Gateway.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"gateway"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } public class GatewayResponse diff --git a/src/Discord.Net/API/Client/Rest/GetBans.cs b/src/Discord.Net/API/Client/Rest/GetBans.cs index ee07cb242..714cdbaf8 100644 --- a/src/Discord.Net/API/Client/Rest/GetBans.cs +++ b/src/Discord.Net/API/Client/Rest/GetBans.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"guilds/{GuildId}/bans"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/GetInvite.cs b/src/Discord.Net/API/Client/Rest/GetInvite.cs index 27de264f0..2531ac26a 100644 --- a/src/Discord.Net/API/Client/Rest/GetInvite.cs +++ b/src/Discord.Net/API/Client/Rest/GetInvite.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"invite/{InviteCode}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public string InviteCode { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/GetInvites.cs b/src/Discord.Net/API/Client/Rest/GetInvites.cs index 079c54ef5..2b4f2f5fe 100644 --- a/src/Discord.Net/API/Client/Rest/GetInvites.cs +++ b/src/Discord.Net/API/Client/Rest/GetInvites.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"guilds/{GuildId}/invites"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/GetMessages.cs b/src/Discord.Net/API/Client/Rest/GetMessages.cs index b72b05c8b..1beadb9a9 100644 --- a/src/Discord.Net/API/Client/Rest/GetMessages.cs +++ b/src/Discord.Net/API/Client/Rest/GetMessages.cs @@ -19,7 +19,6 @@ namespace Discord.API.Client.Rest } } object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs b/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs index 7dc97ef31..df21cc203 100644 --- a/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs +++ b/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"voice/regions"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } public class GetVoiceRegionsResponse diff --git a/src/Discord.Net/API/Client/Rest/GetWidget.cs b/src/Discord.Net/API/Client/Rest/GetWidget.cs index 3b1006358..0437a8b6b 100644 --- a/src/Discord.Net/API/Client/Rest/GetWidget.cs +++ b/src/Discord.Net/API/Client/Rest/GetWidget.cs @@ -9,7 +9,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"servers/{GuildId}/widget.json"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/KickMember.cs b/src/Discord.Net/API/Client/Rest/KickMember.cs index 96804ff6b..4808f8543 100644 --- a/src/Discord.Net/API/Client/Rest/KickMember.cs +++ b/src/Discord.Net/API/Client/Rest/KickMember.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"guilds/{GuildId}/members/{UserId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } public ulong UserId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs index df210ad70..99fd8cbe7 100644 --- a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs +++ b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"users/@me/guilds/{GuildId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/Login.cs b/src/Discord.Net/API/Client/Rest/Login.cs index ab7efc31b..f9c89c717 100644 --- a/src/Discord.Net/API/Client/Rest/Login.cs +++ b/src/Discord.Net/API/Client/Rest/Login.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => Email != null ? "POST" : "GET"; string IRestRequest.Endpoint => $"auth/login"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)] public string Email { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/Logout.cs b/src/Discord.Net/API/Client/Rest/Logout.cs index 78f8059e5..9f4443c51 100644 --- a/src/Discord.Net/API/Client/Rest/Logout.cs +++ b/src/Discord.Net/API/Client/Rest/Logout.cs @@ -8,6 +8,5 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"auth/logout"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } } diff --git a/src/Discord.Net/API/Client/Rest/PruneMembers.cs b/src/Discord.Net/API/Client/Rest/PruneMembers.cs index 41771f7d6..e80498bb1 100644 --- a/src/Discord.Net/API/Client/Rest/PruneMembers.cs +++ b/src/Discord.Net/API/Client/Rest/PruneMembers.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => IsSimulation ? "GET" : "POST"; string IRestRequest.Endpoint => $"guilds/{GuildId}/prune?days={Days}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs b/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs index c704eadbc..b453cba49 100644 --- a/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs +++ b/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"channels/{ChannelId}/permissions/{TargetId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } public ulong TargetId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs b/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs index c6d48c944..5a8f4f796 100644 --- a/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs +++ b/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "DELETE"; string IRestRequest.Endpoint => $"guilds/{GuildId}/bans/{UserId}"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } public ulong UserId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/ReorderChannels.cs b/src/Discord.Net/API/Client/Rest/ReorderChannels.cs index c481eda43..c13f8b21c 100644 --- a/src/Discord.Net/API/Client/Rest/ReorderChannels.cs +++ b/src/Discord.Net/API/Client/Rest/ReorderChannels.cs @@ -17,7 +17,6 @@ namespace Discord.API.Client.Rest return ChannelIds.Select(x => new Channel(x, pos++)); } } - bool IRestRequest.IsPrivate => false; public class Channel { diff --git a/src/Discord.Net/API/Client/Rest/ReorderRoles.cs b/src/Discord.Net/API/Client/Rest/ReorderRoles.cs index 23d73541f..300176a76 100644 --- a/src/Discord.Net/API/Client/Rest/ReorderRoles.cs +++ b/src/Discord.Net/API/Client/Rest/ReorderRoles.cs @@ -17,7 +17,6 @@ namespace Discord.API.Client.Rest return RoleIds.Select(x => new Role(x, pos++)); } } - bool IRestRequest.IsPrivate => false; public class Role { diff --git a/src/Discord.Net/API/Client/Rest/SendFile.cs b/src/Discord.Net/API/Client/Rest/SendFile.cs index 8d072d0e3..4b59e1a11 100644 --- a/src/Discord.Net/API/Client/Rest/SendFile.cs +++ b/src/Discord.Net/API/Client/Rest/SendFile.cs @@ -9,7 +9,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"channels/{ChannelId}/messages"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; string IRestFileRequest.Filename => Filename; Stream IRestFileRequest.Stream => Stream; diff --git a/src/Discord.Net/API/Client/Rest/SendIsTyping.cs b/src/Discord.Net/API/Client/Rest/SendIsTyping.cs index aab017c67..4c56da0be 100644 --- a/src/Discord.Net/API/Client/Rest/SendIsTyping.cs +++ b/src/Discord.Net/API/Client/Rest/SendIsTyping.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"channels/{ChannelId}/typing"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/SendMessage.cs b/src/Discord.Net/API/Client/Rest/SendMessage.cs index 6c6d1ae10..9caca991d 100644 --- a/src/Discord.Net/API/Client/Rest/SendMessage.cs +++ b/src/Discord.Net/API/Client/Rest/SendMessage.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "POST"; string IRestRequest.Endpoint => $"channels/{ChannelId}/messages"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs index cccd4b096..86a35a605 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PATCH"; string IRestRequest.Endpoint => $"channels/{ChannelId}"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/UpdateGuild.cs b/src/Discord.Net/API/Client/Rest/UpdateGuild.cs index 4ff530554..f36b18d9f 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateGuild.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateGuild.cs @@ -9,7 +9,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PATCH"; string IRestRequest.Endpoint => $"guilds/{GuildId}"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/UpdateMember.cs b/src/Discord.Net/API/Client/Rest/UpdateMember.cs index 019c4ee99..ce1649bdd 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateMember.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateMember.cs @@ -9,7 +9,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PATCH"; string IRestRequest.Endpoint => $"guilds/{GuildId}/members/{UserId}"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } public ulong UserId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/UpdateMessage.cs b/src/Discord.Net/API/Client/Rest/UpdateMessage.cs index df3ca46c0..fc055b2bc 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateMessage.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateMessage.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PATCH"; string IRestRequest.Endpoint => $"channels/{ChannelId}/messages/{MessageId}"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong ChannelId { get; set; } public ulong MessageId { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/UpdateProfile.cs b/src/Discord.Net/API/Client/Rest/UpdateProfile.cs index 08f28d868..0f0cdb313 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateProfile.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateProfile.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PATCH"; string IRestRequest.Endpoint => $"users/@me"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; [JsonProperty("password")] public string CurrentPassword { get; set; } diff --git a/src/Discord.Net/API/Client/Rest/UpdateRole.cs b/src/Discord.Net/API/Client/Rest/UpdateRole.cs index 7aac774b7..4bea0b52b 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateRole.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateRole.cs @@ -8,7 +8,6 @@ namespace Discord.API.Client.Rest string IRestRequest.Method => "PATCH"; string IRestRequest.Endpoint => $"guilds/{GuildId}/roles/{RoleId}"; object IRestRequest.Payload => this; - bool IRestRequest.IsPrivate => false; public ulong GuildId { get; set; } public ulong RoleId { get; set; } diff --git a/src/Discord.Net/API/IRestRequest.cs b/src/Discord.Net/API/IRestRequest.cs index b8c7b818c..af520370d 100644 --- a/src/Discord.Net/API/IRestRequest.cs +++ b/src/Discord.Net/API/IRestRequest.cs @@ -7,7 +7,6 @@ namespace Discord.API string Method { get; } string Endpoint { get; } object Payload { get; } - bool IsPrivate { get; } } public interface IRestRequest : IRestRequest where ResponseT : class diff --git a/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs b/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs index 638c176a5..639f85f08 100644 --- a/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs +++ b/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs @@ -8,6 +8,5 @@ namespace Discord.API.Status.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"scheduled-maintenances/active.json"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } } diff --git a/src/Discord.Net/API/Status/Rest/AllIncidents.cs b/src/Discord.Net/API/Status/Rest/AllIncidents.cs index 81a82ce51..9575bbd43 100644 --- a/src/Discord.Net/API/Status/Rest/AllIncidents.cs +++ b/src/Discord.Net/API/Status/Rest/AllIncidents.cs @@ -8,6 +8,5 @@ namespace Discord.API.Status.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"incidents.json"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } } diff --git a/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs b/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs index 1665dde75..3cff11c23 100644 --- a/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs +++ b/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs @@ -8,6 +8,5 @@ namespace Discord.API.Status.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"incidents/unresolved.json"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } } diff --git a/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs b/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs index afc812cc9..803a8a630 100644 --- a/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs +++ b/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs @@ -8,6 +8,5 @@ namespace Discord.API.Status.Rest string IRestRequest.Method => "GET"; string IRestRequest.Endpoint => $"scheduled-maintenances/upcoming.json"; object IRestRequest.Payload => null; - bool IRestRequest.IsPrivate => false; } } diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 51d35ed90..1b474ca5d 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -920,8 +920,8 @@ namespace Discord if (user != null) { - /*if (Config.LogLevel == LogSeverity.Debug) - Logger.Debug($"PRESENCE_UPDATE: {user.Path}");*/ + if (Config.LogLevel == LogSeverity.Debug) + Logger.Debug($"PRESENCE_UPDATE: {user.Path}"); var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); OnUserUpdated(before, user); @@ -948,8 +948,8 @@ namespace Discord user = channel.Server.GetUser(data.UserId); if (user != null) { - /*if (Config.LogLevel == LogSeverity.Debug) - Logger.Debug($"TYPING_START: {channel.Path} ({user.Name})");*/ + if (Config.LogLevel == LogSeverity.Debug) + Logger.Debug($"TYPING_START: {channel.Path} ({user.Name})"); OnUserIsTypingUpdated(channel, user); user.UpdateActivity(); } @@ -969,8 +969,8 @@ namespace Discord var user = server.GetUser(data.UserId); if (user != null) { - /*if (Config.LogLevel == LogSeverity.Debug) - Logger.Debug($"VOICE_STATE_UPDATE: {user.Path}");*/ + if (Config.LogLevel == LogSeverity.Debug) + Logger.Debug($"VOICE_STATE_UPDATE: {user.Path}"); var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); //Logger.Verbose($"Voice Updated: {server.Name}/{user.Name}"); diff --git a/src/Discord.Net/Net/Rest/RestClient.cs b/src/Discord.Net/Net/Rest/RestClient.cs index cb404a2f0..47853f8a4 100644 --- a/src/Discord.Net/Net/Rest/RestClient.cs +++ b/src/Discord.Net/Net/Rest/RestClient.cs @@ -63,26 +63,8 @@ namespace Discord.Net.Rest _engine = new BuiltInEngine(config, baseUrl, logger); #endif - if (_logger != null && _logger.Level >= LogSeverity.Verbose) - { - this.SentRequest += (s, e) => - { - string log = $"{e.Request.Method} {e.Request.Endpoint}: {e.Milliseconds} ms"; - if (_config.LogLevel >= LogSeverity.Debug) - { - if (e.Request is IRestFileRequest) - log += $" [{(e.Request as IRestFileRequest).Filename}]"; - else if (e.Response != null) - { - if (e.Request.IsPrivate) - log += $" [Hidden]"; - else - log += $" {e.ResponseJson}"; - } - } - _logger.Verbose(log); - }; - } + if (logger != null && logger.Level >= LogSeverity.Verbose) + SentRequest += (s, e) => _logger.Verbose($"{e.Request.Method} {e.Request.Endpoint}: {e.Milliseconds} ms"); } public async Task Send(IRestRequest request) diff --git a/src/Discord.Net/Net/WebSockets/WebSocket.cs b/src/Discord.Net/Net/WebSockets/WebSocket.cs index f0052f29a..aa2c9a98b 100644 --- a/src/Discord.Net/Net/WebSockets/WebSocket.cs +++ b/src/Discord.Net/Net/WebSockets/WebSocket.cs @@ -138,15 +138,11 @@ namespace Discord.Net.WebSockets protected virtual Task ProcessMessage(string json) { - if (Logger.Level >= LogSeverity.Debug) - Logger.Debug( $"In: {json}"); return TaskHelper.CompletedTask; } protected void QueueMessage(IWebSocketMessage message) { string json = JsonConvert.SerializeObject(new WebSocketMessage(message)); - if (Logger.Level >= LogSeverity.Debug) - Logger.Debug( $"Out: {json}"); _engine.QueueMessage(json); } From 3101e232287efc51d78e458bc2ef5e2706d230b2 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 17 Feb 2016 02:27:21 -0400 Subject: [PATCH 28/51] Store AppName/Url/Version in DiscordConfig, generate config for AudioClient --- src/Discord.Net.Audio/AudioClient.cs | 23 +++++++++++++++++- src/Discord.Net/DiscordConfig.cs | 35 ++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs index 436c7e8c8..bbe6833f9 100644 --- a/src/Discord.Net.Audio/AudioClient.cs +++ b/src/Discord.Net.Audio/AudioClient.cs @@ -70,7 +70,6 @@ namespace Discord.Audio public AudioClient(DiscordClient client, Server server, int id) { Id = id; - _config = client.Config; Service = client.GetService(); Config = Service.Config; Serializer = client.Serializer; @@ -87,6 +86,25 @@ namespace Discord.Audio //Networking if (Config.EnableMultiserver) { + //TODO: We can remove this hack when official API launches + var baseConfig = client.Config; + var builder = new DiscordConfigBuilder + { + AppName = baseConfig.AppName, + AppUrl = baseConfig.AppUrl, + AppVersion = baseConfig.AppVersion, + CacheToken = baseConfig.CacheDir != null, + ConnectionTimeout = baseConfig.ConnectionTimeout, + EnablePreUpdateEvents = false, + FailedReconnectDelay = baseConfig.FailedReconnectDelay, + LargeThreshold = 1, + LogLevel = baseConfig.LogLevel, + MessageCacheSize = 0, + ReconnectDelay = baseConfig.ReconnectDelay, + UsePermissionsCache = false + }; + _config = builder.Build(); + ClientAPI = new JsonRestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}")); GatewaySocket = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}")); GatewaySocket.Connected += (s, e) => @@ -96,7 +114,10 @@ namespace Discord.Audio }; } else + { + _config = client.Config; GatewaySocket = client.GatewaySocket; + } GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e); VoiceSocket = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}")); VoiceSocket.Server = server; diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net/DiscordConfig.cs index 5add8dcda..ab6f92eba 100644 --- a/src/Discord.Net/DiscordConfig.cs +++ b/src/Discord.Net/DiscordConfig.cs @@ -34,10 +34,18 @@ namespace Discord public bool CacheToken { get; set; } = true; /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. public int MessageCacheSize { get; set; } = 100; - /// Gets or sets whether the permissions cache should be used. This makes operations such as User.GetPermissions(Channel), User.ServerPermissions and Channel.Members + /// + /// Gets or sets whether the permissions cache should be used. + /// This makes operations such as User.GetPermissions(Channel), User.ServerPermissions, Channel.GetUser, and Channel.Members much faster while increasing memory usage. + /// public bool UsePermissionsCache { get; set; } = true; - /// Gets or sets whether the a copy of a model is generated on an update event to allow a user to check which properties changed. + /// Gets or sets whether the a copy of a model is generated on an update event to allow you to check which properties changed. public bool EnablePreUpdateEvents { get; set; } = true; + /// + /// Gets or sets the max number of users a server may have for offline users to be included in the READY packet. Max is 250. + /// Decreasing this may reduce CPU usage while increasing login time and network usage. + /// + public int LargeThreshold { get; set; } = 250; //Events @@ -65,22 +73,32 @@ namespace Discord public LogSeverity LogLevel { get; } + public string AppName { get; } + public string AppUrl { get; } + public string AppVersion { get; } + public string UserAgent { get; } + public string CacheDir { get; } + public int ConnectionTimeout { get; } public int ReconnectDelay { get; } public int FailedReconnectDelay { get; } - public int LargeThreshold { get; } = 250; + public int LargeThreshold { get; } public int MessageCacheSize { get; } public bool UsePermissionsCache { get; } public bool EnablePreUpdateEvents { get; } - public string UserAgent { get; } - public string CacheDir { get; } internal DiscordConfig(DiscordConfigBuilder builder) { LogLevel = builder.LogLevel; + AppName = builder.AppName; + AppUrl = builder.AppUrl; + AppVersion = builder.AppVersion; + UserAgent = GetUserAgent(builder); + CacheDir = GetCacheDir(builder); + ConnectionTimeout = builder.ConnectionTimeout; ReconnectDelay = builder.ReconnectDelay; FailedReconnectDelay = builder.FailedReconnectDelay; @@ -88,12 +106,9 @@ namespace Discord MessageCacheSize = builder.MessageCacheSize; UsePermissionsCache = builder.UsePermissionsCache; EnablePreUpdateEvents = builder.EnablePreUpdateEvents; - - UserAgent = GetUserAgent(builder); - CacheDir = GetCacheDir(builder); } - private string GetUserAgent(DiscordConfigBuilder builder) + private static string GetUserAgent(DiscordConfigBuilder builder) { StringBuilder sb = new StringBuilder(); if (!string.IsNullOrEmpty(builder.AppName)) @@ -115,7 +130,7 @@ namespace Discord sb.Append($"DiscordBot ({LibUrl}, v{LibVersion})"); return sb.ToString(); } - private string GetCacheDir(DiscordConfigBuilder builder) + private static string GetCacheDir(DiscordConfigBuilder builder) { if (builder.CacheToken) return Path.Combine(Path.GetTempPath(), builder.AppName ?? "Discord.Net"); From 03920b9d489e2844bdd5fcb32f18fbb35c73f073 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 17 Feb 2016 16:54:47 -0400 Subject: [PATCH 29/51] Added non-generic Modules.Add method --- src/Discord.Net.Modules/ModuleExtensions.cs | 12 ++++++--- src/Discord.Net.Modules/ModuleService.cs | 30 ++++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Discord.Net.Modules/ModuleExtensions.cs b/src/Discord.Net.Modules/ModuleExtensions.cs index 7d51ac547..a96517c06 100644 --- a/src/Discord.Net.Modules/ModuleExtensions.cs +++ b/src/Discord.Net.Modules/ModuleExtensions.cs @@ -8,15 +8,19 @@ return client; } - public static void AddModule(this DiscordClient client, T instance, string name = null, ModuleFilter filter = ModuleFilter.None) - where T : class, IModule + public static void AddModule(this DiscordClient client, IModule instance, string name = null, ModuleFilter filter = ModuleFilter.None) { - client.GetService().Add(instance, name ?? nameof(T), filter); + client.GetService().Add(instance, name, filter); } public static void AddModule(this DiscordClient client, string name = null, ModuleFilter filter = ModuleFilter.None) where T : class, IModule, new() { - client.GetService().Add(new T(), name ?? nameof(T), filter); + client.GetService().Add(name, filter); + } + public static void AddModule(this DiscordClient client, T instance, string name = null, ModuleFilter filter = ModuleFilter.None) + where T : class, IModule + { + client.GetService().Add(instance, name, filter); } public static ModuleManager GetModule(this DiscordClient client) where T : class, IModule diff --git a/src/Discord.Net.Modules/ModuleService.cs b/src/Discord.Net.Modules/ModuleService.cs index 0eadd0694..1f405a222 100644 --- a/src/Discord.Net.Modules/ModuleService.cs +++ b/src/Discord.Net.Modules/ModuleService.cs @@ -1,16 +1,21 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Discord.Modules { public class ModuleService : IService { public DiscordClient Client { get; private set; } - - public IEnumerable Modules => _modules.Values; + + private static readonly MethodInfo addMethod = typeof(ModuleService).GetTypeInfo().GetDeclaredMethods(nameof(Add)) + .Single(x => x.IsGenericMethodDefinition && x.GetParameters().Length == 3); + + public IEnumerable Modules => _modules.Values; private readonly Dictionary _modules; - public ModuleService() + public ModuleService() { _modules = new Dictionary(); } @@ -20,22 +25,29 @@ namespace Discord.Modules Client = client; } - public T Add(T module, string name, ModuleFilter filterType) + public void Add(IModule instance, string name, ModuleFilter filter) + { + Type type = instance.GetType(); + addMethod.MakeGenericMethod(type).Invoke(this, new object[] { instance, name, filter }); + } + public void Add(string name, ModuleFilter filter) + where T : class, IModule, new() + => Add(new T(), name, filter); + public void Add(T instance, string name, ModuleFilter filter) where T : class, IModule { - if (module == null) throw new ArgumentNullException(nameof(module)); - if (name == null) throw new ArgumentNullException(nameof(name)); + if (instance == null) throw new ArgumentNullException(nameof(instance)); if (Client == null) throw new InvalidOperationException("Service needs to be added to a DiscordClient before modules can be installed."); Type type = typeof(T); + if (name == null) name = type.Name; if (_modules.ContainsKey(type)) throw new InvalidOperationException("This module has already been added."); - var manager = new ModuleManager(Client, module, name, filterType); + var manager = new ModuleManager(Client, instance, name, filter); _modules.Add(type, manager); - module.Install(manager); - return module; + instance.Install(manager); } public ModuleManager Get() where T : class, IModule From 564e6ca2cc4c5b652bf9b426995fc8c1e9016435 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 18 Feb 2016 13:15:20 -0400 Subject: [PATCH 30/51] Added a cleanup note for Message.Resolve --- src/Discord.Net/Models/Message.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index 0d5ca74ae..510d1a341 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -81,6 +81,7 @@ namespace Discord })); }*/ + //TODO: Move this somewhere private static string Resolve(Channel channel, string text) { if (text == null) throw new ArgumentNullException(nameof(text)); From 00f8c7a7330e850d913f23dec7a512c0513df089 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 18 Feb 2016 13:16:01 -0400 Subject: [PATCH 31/51] Fixed TTS --- src/Discord.Net/MessageQueue.cs | 1 + src/Discord.Net/Models/Message.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs index b5de885b0..bc659e4d6 100644 --- a/src/Discord.Net/MessageQueue.cs +++ b/src/Discord.Net/MessageQueue.cs @@ -55,6 +55,7 @@ namespace Discord.Net internal Message QueueSend(Channel channel, string text, bool isTTS) { Message msg = new Message(0, channel, channel.IsPrivate ? channel.Client.PrivateUser : channel.Server.CurrentUser); + msg.IsTTS = isTTS; msg.RawText = text; msg.Text = msg.Resolve(text); msg.Nonce = GenerateNonce(); diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index 510d1a341..a3e0ed699 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -177,7 +177,7 @@ namespace Discord public User User { get; } /// Returns true if the message was sent as text-to-speech by someone with permissions to do so. - public bool IsTTS { get; private set; } + public bool IsTTS { get; internal set; } /// Returns the state of this message. Only useful if UseMessageQueue is true. public MessageState State { get; internal set; } /// Returns the raw content of this message as it was received from the server. From 3b71a329b823fe0f74ba96390d41f28d47f452b1 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 18 Feb 2016 19:40:46 -0400 Subject: [PATCH 32/51] AudioExtensions.GetAudioClient should be an extension method --- src/Discord.Net.Audio/AudioExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Audio/AudioExtensions.cs b/src/Discord.Net.Audio/AudioExtensions.cs index 198542d8f..705cc893f 100644 --- a/src/Discord.Net.Audio/AudioExtensions.cs +++ b/src/Discord.Net.Audio/AudioExtensions.cs @@ -21,6 +21,6 @@ namespace Discord.Audio public static Task JoinAudio(this Channel channel) => channel.Client.GetService().Join(channel); public static Task LeaveAudio(this Channel channel) => channel.Client.GetService().Leave(channel); public static Task LeaveAudio(this Server server) => server.Client.GetService().Leave(server); - public static IAudioClient GetAudioClient(Server server) => server.Client.GetService().GetClient(server); + public static IAudioClient GetAudioClient(this Server server) => server.Client.GetService().GetClient(server); } } From c387e04c09a70de3a3f8935bbf7766ff204f31c9 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 21 Feb 2016 23:09:36 -0400 Subject: [PATCH 33/51] Reorganize Logger.cs --- src/Discord.Net/Logging/Logger.cs | 40 ++++++++++--------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/src/Discord.Net/Logging/Logger.cs b/src/Discord.Net/Logging/Logger.cs index e3e917a24..59d591163 100644 --- a/src/Discord.Net/Logging/Logger.cs +++ b/src/Discord.Net/Logging/Logger.cs @@ -17,54 +17,40 @@ namespace Discord.Logging public void Log(LogSeverity severity, string message, Exception exception = null) => _manager.Log(severity, Name, message, exception); -#if DOTNET5_4 - public void Log(LogSeverity severity, FormattableString message, Exception exception = null) - => _manager.Log(severity, Name, message, exception); -#endif - public void Error(string message, Exception exception = null) => _manager.Error(Name, message, exception); -#if DOTNET5_4 - public void Error(FormattableString message, Exception exception = null) - => _manager.Error(Name, message, exception); -#endif public void Error(Exception exception) => _manager.Error(Name, exception); - public void Warning(string message, Exception exception = null) => _manager.Warning(Name, message, exception); -#if DOTNET5_4 - public void Warning(FormattableString message, Exception exception = null) - => _manager.Warning(Name, message, exception); -#endif public void Warning(Exception exception) => _manager.Warning(Name, exception); - public void Info(string message, Exception exception = null) => _manager.Info(Name, message, exception); -#if DOTNET5_4 - public void Info(FormattableString message, Exception exception = null) - => _manager.Info(Name, message, exception); -#endif public void Info(Exception exception) => _manager.Info(Name, exception); - public void Verbose(string message, Exception exception = null) => _manager.Verbose(Name, message, exception); -#if DOTNET5_4 - public void Verbose(FormattableString message, Exception exception = null) - => _manager.Verbose(Name, message, exception); -#endif public void Verbose(Exception exception) => _manager.Verbose(Name, exception); - public void Debug(string message, Exception exception = null) => _manager.Debug(Name, message, exception); + public void Debug(Exception exception) + => _manager.Debug(Name, exception); + #if DOTNET5_4 + public void Log(LogSeverity severity, FormattableString message, Exception exception = null) + => _manager.Log(severity, Name, message, exception); + public void Error(FormattableString message, Exception exception = null) + => _manager.Error(Name, message, exception); + public void Warning(FormattableString message, Exception exception = null) + => _manager.Warning(Name, message, exception); + public void Info(FormattableString message, Exception exception = null) + => _manager.Info(Name, message, exception); + public void Verbose(FormattableString message, Exception exception = null) + => _manager.Verbose(Name, message, exception); public void Debug(FormattableString message, Exception exception = null) => _manager.Debug(Name, message, exception); #endif - public void Debug(Exception exception) - => _manager.Debug(Name, exception); } } From 0ec7218ad010c843ccf8e366ab4fb9236b1bffc1 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 21 Feb 2016 23:09:52 -0400 Subject: [PATCH 34/51] Provide more information during MessageQueue warnings --- src/Discord.Net/MessageQueue.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs index bc659e4d6..eaad53bd3 100644 --- a/src/Discord.Net/MessageQueue.cs +++ b/src/Discord.Net/MessageQueue.cs @@ -33,11 +33,16 @@ namespace Discord.Net private readonly ConcurrentQueue _pendingEdits; private readonly ConcurrentQueue _pendingDeletes; private readonly ConcurrentDictionary _pendingSendsByNonce; - private int _nextWarning; - private int _count; + private int _count, _nextWarning; /// Gets the current number of queued actions. public int Count => _count; + /// Gets the current number of queued sends. + public int SendCount => _pendingSends.Count; + /// Gets the current number of queued edits. + public int EditCount => _pendingEdits.Count; + /// Gets the current number of queued deletes. + public int DeleteCount => _pendingDeletes.Count; internal MessageQueue(RestClient rest, Logger logger) { @@ -218,8 +223,12 @@ namespace Discord.Net int count = Interlocked.Increment(ref _count); if (count >= _nextWarning) { - _nextWarning *= 2; - _logger.Warning($"Queue is backed up, currently at {count} actions."); + _nextWarning <<= 1; + int sendCount = _pendingSends.Count; + int editCount = _pendingEdits.Count; + int deleteCount = _pendingDeletes.Count; + count = sendCount + editCount + deleteCount; //May not add up due to async + _logger.Warning($"Queue is backed up, currently at {count} actions ({sendCount} sends, {editCount} edits, {deleteCount} deletes)."); } else if (count < WarningStart) //Reset once the problem is solved _nextWarning = WarningStart; From ce8e69d83c30dd562fe350041b107639126aaf43 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 21 Feb 2016 23:15:53 -0400 Subject: [PATCH 35/51] Added a few missing ConfigureAwait(false) --- src/Discord.Net.Audio/AudioClient.cs | 2 +- src/Discord.Net.Audio/Net/VoiceSocket.cs | 4 ++-- src/Discord.Net/MessageQueue.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs index bbe6833f9..2f21ce7f8 100644 --- a/src/Discord.Net.Audio/AudioClient.cs +++ b/src/Discord.Net.Audio/AudioClient.cs @@ -230,7 +230,7 @@ namespace Discord.Audio SendVoiceUpdate(channel.Server.Id, channel.Id); using (await _connectionLock.LockAsync().ConfigureAwait(false)) - await Task.Run(() => VoiceSocket.WaitForConnection(CancelToken)); + await Task.Run(() => VoiceSocket.WaitForConnection(CancelToken)).ConfigureAwait(false); } private async void OnReceivedEvent(WebSocketEventEventArgs e) diff --git a/src/Discord.Net.Audio/Net/VoiceSocket.cs b/src/Discord.Net.Audio/Net/VoiceSocket.cs index adb583f1d..544a7d710 100644 --- a/src/Discord.Net.Audio/Net/VoiceSocket.cs +++ b/src/Discord.Net.Audio/Net/VoiceSocket.cs @@ -371,7 +371,7 @@ namespace Discord.Net.WebSockets break; } } - await _udp.SendAsync(pingPacket, pingPacket.Length); + await _udp.SendAsync(pingPacket, pingPacket.Length).ConfigureAwait(false); nextPingTicks = currentTicks + 5 * ticksPerSeconds; } } @@ -395,7 +395,7 @@ namespace Discord.Net.WebSockets //Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken private async Task WatcherAsync() { - await CancelToken.Wait(); + await CancelToken.Wait().ConfigureAwait(false); _udp.Close(); } #endif diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs index eaad53bd3..f9dcfc445 100644 --- a/src/Discord.Net/MessageQueue.cs +++ b/src/Discord.Net/MessageQueue.cs @@ -144,7 +144,7 @@ namespace Discord.Net } } } - await Task.Delay((int)Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); + await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } } catch (OperationCanceledException) { } @@ -178,7 +178,7 @@ namespace Discord.Net } } } - await Task.Delay((int)Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); + await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } } catch (OperationCanceledException) { } @@ -211,7 +211,7 @@ namespace Discord.Net } } - await Task.Delay((int)Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); + await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } } catch (OperationCanceledException) { } From 1ef5e4a1ca2cadd8cc1dc935c1c47fbee5ce7d40 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 22 Feb 2016 00:22:26 -0400 Subject: [PATCH 36/51] Cleaned up LogManager --- src/Discord.Net/Logging/LogManager.cs | 46 +++++++++++---------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/Discord.Net/Logging/LogManager.cs b/src/Discord.Net/Logging/LogManager.cs index 425053abe..2abc4f10d 100644 --- a/src/Discord.Net/Logging/LogManager.cs +++ b/src/Discord.Net/Logging/LogManager.cs @@ -16,69 +16,59 @@ namespace Discord.Logging Level = client.Config.LogLevel; } -#if DOTNET5_4 - public void Log(LogSeverity severity, string source, FormattableString message, Exception exception = null) + public void Log(LogSeverity severity, string source, string message, Exception exception = null) { if (severity <= Level) { - try { Message(this, new LogMessageEventArgs(severity, source, message.ToString(), exception)); } + try { Message(this, new LogMessageEventArgs(severity, source, message, exception)); } catch { } //We dont want to log on log errors } } -#endif - public void Log(LogSeverity severity, string source, string message, Exception exception = null) + +#if DOTNET5_4 + public void Log(LogSeverity severity, string source, FormattableString message, Exception exception = null) { if (severity <= Level) { - try { Message(this, new LogMessageEventArgs(severity, source, message, exception)); } + try { Message(this, new LogMessageEventArgs(severity, source, message.ToString(), exception)); } catch { } //We dont want to log on log errors } } +#endif public void Error(string source, string message, Exception ex = null) => Log(LogSeverity.Error, source, message, ex); -#if DOTNET5_4 - public void Error(string source, FormattableString message, Exception ex = null) - => Log(LogSeverity.Error, source, message, ex); -#endif public void Error(string source, Exception ex) => Log(LogSeverity.Error, source, (string)null, ex); - public void Warning(string source, string message, Exception ex = null) => Log(LogSeverity.Warning, source, message, ex); -#if DOTNET5_4 - public void Warning(string source, FormattableString message, Exception ex = null) - => Log(LogSeverity.Warning, source, message, ex); -#endif public void Warning(string source, Exception ex) => Log(LogSeverity.Warning, source, (string)null, ex); - public void Info(string source, string message, Exception ex = null) => Log(LogSeverity.Info, source, message, ex); -#if DOTNET5_4 - public void Info(string source, FormattableString message, Exception ex = null) - => Log(LogSeverity.Info, source, message, ex); -#endif public void Info(string source, Exception ex) => Log(LogSeverity.Info, source, (string)null, ex); - public void Verbose(string source, string message, Exception ex = null) => Log(LogSeverity.Verbose, source, message, ex); -#if DOTNET5_4 - public void Verbose(string source, FormattableString message, Exception ex = null) - => Log(LogSeverity.Verbose, source, message, ex); -#endif public void Verbose(string source, Exception ex) => Log(LogSeverity.Verbose, source, (string)null, ex); - public void Debug(string source, string message, Exception ex = null) => Log(LogSeverity.Debug, source, message, ex); + public void Debug(string source, Exception ex) + => Log(LogSeverity.Debug, source, (string)null, ex); + #if DOTNET5_4 + public void Error(string source, FormattableString message, Exception ex = null) + => Log(LogSeverity.Error, source, message, ex); + public void Warning(string source, FormattableString message, Exception ex = null) + => Log(LogSeverity.Warning, source, message, ex); + public void Info(string source, FormattableString message, Exception ex = null) + => Log(LogSeverity.Info, source, message, ex); + public void Verbose(string source, FormattableString message, Exception ex = null) + => Log(LogSeverity.Verbose, source, message, ex); public void Debug(string source, FormattableString message, Exception ex = null) => Log(LogSeverity.Debug, source, message, ex); #endif - public void Debug(string source, Exception ex) - => Log(LogSeverity.Debug, source, (string)null, ex); public Logger CreateLogger(string name) => new Logger(this, name); } From 5decad09a12dff3e96dd2f30cf06e35912db946d Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 22 Feb 2016 00:22:58 -0400 Subject: [PATCH 37/51] Be more strict with resetting queue warnings, to prevent spam --- src/Discord.Net/MessageQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs index f9dcfc445..99cfd6c3f 100644 --- a/src/Discord.Net/MessageQueue.cs +++ b/src/Discord.Net/MessageQueue.cs @@ -236,7 +236,7 @@ namespace Discord.Net private void DecrementCount() { int count = Interlocked.Decrement(ref _count); - if (count < WarningStart) //Reset once the problem is solved + if (count < (WarningStart / 2)) //Reset once the problem is solved _nextWarning = WarningStart; } From 27d7e9915b3d3deafae809ab957c11348c2f0f6c Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 24 Feb 2016 07:00:49 -0400 Subject: [PATCH 38/51] Initial 1.0-alpha1 design changes --- src/Discord.Net.Audio/AudioClient.cs | 38 +- src/Discord.Net.Audio/AudioExtensions.cs | 4 +- src/Discord.Net.Audio/AudioService.cs | 6 +- src/Discord.Net.Audio/IAudioClient.cs | 6 +- src/Discord.Net.Audio/Net/VoiceSocket.cs | 2 +- src/Discord.Net.Audio/VirtualClient.cs | 5 +- src/Discord.Net.Audio/project.json | 4 +- src/Discord.Net.Commands/Command.cs | 2 +- src/Discord.Net.Commands/CommandBuilder.cs | 4 +- src/Discord.Net.Commands/CommandEventArgs.cs | 3 +- src/Discord.Net.Commands/CommandMap.cs | 2 +- src/Discord.Net.Commands/CommandService.cs | 36 +- .../GenericPermissionChecker.cs | 6 +- .../{Permissions => }/IPermissionChecker.cs | 2 +- .../Levels/PermissionLevelChecker.cs | 24 - .../Levels/PermissionLevelExtensions.cs | 29 - .../Levels/PermissionLevelService.cs | 23 - .../Permissions/Userlist/BlacklistChecker.cs | 18 - .../Userlist/BlacklistExtensions.cs | 48 -- .../Permissions/Userlist/BlacklistService.cs | 13 - .../Permissions/Userlist/UserlistService.cs | 52 -- .../Permissions/Userlist/WhitelistChecker.cs | 18 - .../Userlist/WhitelistExtensions.cs | 48 -- .../Permissions/Userlist/WhitelistService.cs | 13 - .../Permissions/Visibility/PrivateChecker.cs | 21 - .../Visibility/PrivateExtensions.cs | 21 - .../Permissions/Visibility/PublicChecker.cs | 21 - .../Visibility/PublicExtensions.cs | 21 - src/Discord.Net.Commands/project.json | 4 +- src/Discord.Net.Modules/ModuleChecker.cs | 6 +- src/Discord.Net.Modules/ModuleManager.cs | 142 ++--- src/Discord.Net.Modules/project.json | 6 +- src/Discord.Net.Net45/Discord.Net.csproj | 12 + src/Discord.Net/API/Client/Common/Channel.cs | 2 + .../API/Client/Rest/AddChannelPermission.cs | 4 +- .../API/Client/Rest/CreateChannel.cs | 2 +- .../API/Client/Rest/UpdateChannel.cs | 2 + src/Discord.Net/DiscordClient.Events.cs | 12 +- src/Discord.Net/DiscordClient.cs | 183 +++--- src/Discord.Net/EnumConverters.cs | 42 ++ src/Discord.Net/Enums/ChannelType.cs | 38 +- src/Discord.Net/Enums/PermissionTarget.cs | 33 +- src/Discord.Net/Events/ChannelEventArgs.cs | 6 +- .../Events/ChannelUpdatedEventArgs.cs | 8 +- src/Discord.Net/Events/MessageEventArgs.cs | 2 +- .../Events/MessageUpdatedEventArgs.cs | 2 +- ...nelUserEventArgs.cs => TypingEventArgs.cs} | 6 +- src/Discord.Net/IModel.cs | 11 + src/Discord.Net/InternalExtensions.cs | 81 --- src/Discord.Net/MessageQueue.cs | 119 ++-- src/Discord.Net/Models/Channel.cs | 601 +----------------- src/Discord.Net/Models/Color.cs | 22 - src/Discord.Net/Models/IChannel.cs | 21 + src/Discord.Net/Models/IPrivateChannel.cs | 6 + src/Discord.Net/Models/IPublicChannel.cs | 6 + src/Discord.Net/Models/ITextChannel.cs | 17 + src/Discord.Net/Models/IVoiceChannel.cs | 6 + src/Discord.Net/Models/Invite.cs | 10 +- .../Models/Managers/MessageManager.cs | 154 +++++ .../Models/Managers/PermissionManager.cs | 223 +++++++ src/Discord.Net/Models/Message.cs | 103 ++- src/Discord.Net/Models/Permissions.cs | 32 +- src/Discord.Net/Models/PrivateChannel.cs | 66 ++ src/Discord.Net/Models/Profile.cs | 2 +- src/Discord.Net/Models/PublicChannel.cs | 109 ++++ src/Discord.Net/Models/Region.cs | 2 + src/Discord.Net/Models/Role.cs | 6 +- src/Discord.Net/Models/Server.cs | 114 ++-- src/Discord.Net/Models/TextChannel.cs | 88 +++ src/Discord.Net/Models/User.cs | 88 +-- src/Discord.Net/Models/VoiceChannel.cs | 60 ++ src/Discord.Net/project.json | 6 +- 72 files changed, 1245 insertions(+), 1710 deletions(-) rename src/Discord.Net.Commands/{Permissions => }/GenericPermissionChecker.cs (54%) rename src/Discord.Net.Commands/{Permissions => }/IPermissionChecker.cs (54%) delete mode 100644 src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Visibility/PrivateChecker.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Visibility/PublicChecker.cs delete mode 100644 src/Discord.Net.Commands/Permissions/Visibility/PublicExtensions.cs create mode 100644 src/Discord.Net/EnumConverters.cs rename src/Discord.Net/Events/{ChannelUserEventArgs.cs => TypingEventArgs.cs} (50%) create mode 100644 src/Discord.Net/IModel.cs create mode 100644 src/Discord.Net/Models/IChannel.cs create mode 100644 src/Discord.Net/Models/IPrivateChannel.cs create mode 100644 src/Discord.Net/Models/IPublicChannel.cs create mode 100644 src/Discord.Net/Models/ITextChannel.cs create mode 100644 src/Discord.Net/Models/IVoiceChannel.cs create mode 100644 src/Discord.Net/Models/Managers/MessageManager.cs create mode 100644 src/Discord.Net/Models/Managers/PermissionManager.cs create mode 100644 src/Discord.Net/Models/PrivateChannel.cs create mode 100644 src/Discord.Net/Models/PublicChannel.cs create mode 100644 src/Discord.Net/Models/TextChannel.cs create mode 100644 src/Discord.Net/Models/VoiceChannel.cs diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs index 2f21ce7f8..00aacea12 100644 --- a/src/Discord.Net.Audio/AudioClient.cs +++ b/src/Discord.Net.Audio/AudioClient.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json; using Nito.AsyncEx; using System; using System.Diagnostics; -using System.IO; using System.Threading; using System.Threading.Tasks; @@ -15,35 +14,6 @@ namespace Discord.Audio { internal class AudioClient : IAudioClient { - private class OutStream : Stream - { - public override bool CanRead => false; - public override bool CanSeek => false; - public override bool CanWrite => true; - - private readonly AudioClient _client; - - internal OutStream(AudioClient client) - { - _client = client; - } - - public override long Length { get { throw new InvalidOperationException(); } } - public override long Position - { - get { throw new InvalidOperationException(); } - set { throw new InvalidOperationException(); } - } - public override void Flush() { throw new InvalidOperationException(); } - public override long Seek(long offset, SeekOrigin origin) { throw new InvalidOperationException(); } - public override void SetLength(long value) { throw new InvalidOperationException(); } - public override int Read(byte[] buffer, int offset, int count) { throw new InvalidOperationException(); } - public override void Write(byte[] buffer, int offset, int count) - { - _client.Send(buffer, offset, count); - } - } - private readonly DiscordConfig _config; private readonly AsyncLock _connectionLock; private readonly TaskManager _taskManager; @@ -58,14 +28,13 @@ namespace Discord.Audio public GatewaySocket GatewaySocket { get; } public VoiceSocket VoiceSocket { get; } public JsonSerializer Serializer { get; } - public Stream OutputStream { get; } public CancellationToken CancelToken { get; private set; } public string SessionId => GatewaySocket.SessionId; public ConnectionState State => VoiceSocket.State; public Server Server => VoiceSocket.Server; - public Channel Channel => VoiceSocket.Channel; + public VoiceChannel Channel => VoiceSocket.Channel; public AudioClient(DiscordClient client, Server server, int id) { @@ -121,7 +90,6 @@ namespace Discord.Audio GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e); VoiceSocket = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}")); VoiceSocket.Server = server; - OutputStream = new OutStream(this); } public async Task Connect() @@ -216,7 +184,7 @@ namespace Discord.Audio _gatewayState = (int)ConnectionState.Disconnected; } - public async Task Join(Channel channel) + public async Task Join(VoiceChannel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (channel.Type != ChannelType.Voice) @@ -248,7 +216,7 @@ namespace Discord.Audio await Disconnect().ConfigureAwait(false); else { - var channel = Service.Client.GetChannel(data.ChannelId.Value); + var channel = Service.Client.GetChannel(data.ChannelId.Value) as VoiceChannel; if (channel != null) VoiceSocket.Channel = channel; else diff --git a/src/Discord.Net.Audio/AudioExtensions.cs b/src/Discord.Net.Audio/AudioExtensions.cs index 705cc893f..7def445a6 100644 --- a/src/Discord.Net.Audio/AudioExtensions.cs +++ b/src/Discord.Net.Audio/AudioExtensions.cs @@ -18,8 +18,8 @@ namespace Discord.Audio return client; } - public static Task JoinAudio(this Channel channel) => channel.Client.GetService().Join(channel); - public static Task LeaveAudio(this Channel channel) => channel.Client.GetService().Leave(channel); + public static Task JoinAudio(this VoiceChannel channel) => channel.Client.GetService().Join(channel); + public static Task LeaveAudio(this VoiceChannel channel) => channel.Client.GetService().Leave(channel); public static Task LeaveAudio(this Server server) => server.Client.GetService().Leave(server); public static IAudioClient GetAudioClient(this Server server) => server.Client.GetService().GetClient(server); } diff --git a/src/Discord.Net.Audio/AudioService.cs b/src/Discord.Net.Audio/AudioService.cs index 3de3531ae..e44a4a1ce 100644 --- a/src/Discord.Net.Audio/AudioService.cs +++ b/src/Discord.Net.Audio/AudioService.cs @@ -113,7 +113,7 @@ namespace Discord.Audio } } - public async Task Join(Channel channel) + public async Task Join(VoiceChannel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); @@ -163,8 +163,8 @@ namespace Discord.Audio } public Task Leave(Server server) => Leave(server, null); - public Task Leave(Channel channel) => Leave(channel.Server, channel); - private async Task Leave(Server server, Channel channel) + public Task Leave(VoiceChannel channel) => Leave(channel.Server, channel); + private async Task Leave(Server server, VoiceChannel channel) { if (server == null) throw new ArgumentNullException(nameof(server)); diff --git a/src/Discord.Net.Audio/IAudioClient.cs b/src/Discord.Net.Audio/IAudioClient.cs index 71c8783d5..a986fad7d 100644 --- a/src/Discord.Net.Audio/IAudioClient.cs +++ b/src/Discord.Net.Audio/IAudioClient.cs @@ -15,11 +15,9 @@ namespace Discord.Audio /// Gets the current state of this client. ConnectionState State { get; } /// Gets the channel this client is currently a member of. - Channel Channel { get; } + VoiceChannel Channel { get; } /// Gets the server this client is bound to. Server Server { get; } - /// Gets a stream object that wraps the Send() function. - Stream OutputStream { get; } /// Gets a cancellation token that triggers when the client is manually disconnected. CancellationToken CancelToken { get; } @@ -31,7 +29,7 @@ namespace Discord.Audio VoiceSocket VoiceSocket { get; } /// Moves the client to another channel on the same server. - Task Join(Channel channel); + Task Join(VoiceChannel channel); /// Disconnects from the Discord server, canceling any pending requests. Task Disconnect(); diff --git a/src/Discord.Net.Audio/Net/VoiceSocket.cs b/src/Discord.Net.Audio/Net/VoiceSocket.cs index 544a7d710..9ee5c60e0 100644 --- a/src/Discord.Net.Audio/Net/VoiceSocket.cs +++ b/src/Discord.Net.Audio/Net/VoiceSocket.cs @@ -46,7 +46,7 @@ namespace Discord.Net.WebSockets public string Token { get; internal set; } public Server Server { get; internal set; } - public Channel Channel { get; internal set; } + public VoiceChannel Channel { get; internal set; } public int Ping => _ping; internal VoiceBuffer OutputBuffer => _sendBuffer; diff --git a/src/Discord.Net.Audio/VirtualClient.cs b/src/Discord.Net.Audio/VirtualClient.cs index 12d285a0d..9c8100e47 100644 --- a/src/Discord.Net.Audio/VirtualClient.cs +++ b/src/Discord.Net.Audio/VirtualClient.cs @@ -16,8 +16,7 @@ namespace Discord.Audio public string SessionId => _client.Server == Server ? _client.SessionId : null; public ConnectionState State => _client.Server == Server ? _client.State : ConnectionState.Disconnected; - public Channel Channel => _client.Server == Server ? _client.Channel : null; - public Stream OutputStream => _client.Server == Server ? _client.OutputStream : null; + public VoiceChannel Channel => _client.Server == Server ? _client.Channel : null; public CancellationToken CancelToken => _client.Server == Server ? _client.CancelToken : CancellationToken.None; public RestClient ClientAPI => _client.Server == Server ? _client.ClientAPI : null; @@ -31,7 +30,7 @@ namespace Discord.Audio } public Task Disconnect() => _client.Service.Leave(Server); - public Task Join(Channel channel) => _client.Join(channel); + public Task Join(VoiceChannel channel) => _client.Join(channel); public void Send(byte[] data, int offset, int count) => _client.Send(data, offset, count); public void Clear() => _client.Clear(); diff --git a/src/Discord.Net.Audio/project.json b/src/Discord.Net.Audio/project.json index 664767ba7..c41a619c7 100644 --- a/src/Discord.Net.Audio/project.json +++ b/src/Discord.Net.Audio/project.json @@ -1,5 +1,5 @@ { - "version": "0.9.0-rc3", + "version": "1.0.0-alpha1", "description": "A Discord.Net extension adding voice support.", "authors": [ "RogueException" ], "tags": [ "discord", "discordapp" ], @@ -18,7 +18,7 @@ }, "dependencies": { - "Discord.Net": "0.9.0-rc3-3" + "Discord.Net": "1.0.0-alpha1" }, "frameworks": { "net45": { }, diff --git a/src/Discord.Net.Commands/Command.cs b/src/Discord.Net.Commands/Command.cs index a8addc1b1..ccd62798b 100644 --- a/src/Discord.Net.Commands/Command.cs +++ b/src/Discord.Net.Commands/Command.cs @@ -52,7 +52,7 @@ namespace Discord.Commands _checks = checks; } - internal bool CanRun(User user, Channel channel, out string error) + internal bool CanRun(User user, ITextChannel channel, out string error) { for (int i = 0; i < _checks.Length; i++) { diff --git a/src/Discord.Net.Commands/CommandBuilder.cs b/src/Discord.Net.Commands/CommandBuilder.cs index 129dc24ab..6b945841c 100644 --- a/src/Discord.Net.Commands/CommandBuilder.cs +++ b/src/Discord.Net.Commands/CommandBuilder.cs @@ -79,7 +79,7 @@ namespace Discord.Commands _checks.Add(check); return this; } - public CommandBuilder AddCheck(Func checkFunc, string errorMsg = null) + public CommandBuilder AddCheck(Func checkFunc, string errorMsg = null) { _checks.Add(new GenericPermissionChecker(checkFunc, errorMsg)); return this; @@ -145,7 +145,7 @@ namespace Discord.Commands { _checks.Add(checker); } - public void AddCheck(Func checkFunc, string errorMsg = null) + public void AddCheck(Func checkFunc, string errorMsg = null) { _checks.Add(new GenericPermissionChecker(checkFunc, errorMsg)); } diff --git a/src/Discord.Net.Commands/CommandEventArgs.cs b/src/Discord.Net.Commands/CommandEventArgs.cs index 818f5fa23..70793f5e1 100644 --- a/src/Discord.Net.Commands/CommandEventArgs.cs +++ b/src/Discord.Net.Commands/CommandEventArgs.cs @@ -10,8 +10,7 @@ namespace Discord.Commands public Command Command { get; } public User User => Message.User; - public Channel Channel => Message.Channel; - public Server Server => Message.Channel.Server; + public ITextChannel Channel => Message.Channel; public CommandEventArgs(Message message, Command command, string[] args) { diff --git a/src/Discord.Net.Commands/CommandMap.cs b/src/Discord.Net.Commands/CommandMap.cs index 98decd833..ad280b335 100644 --- a/src/Discord.Net.Commands/CommandMap.cs +++ b/src/Discord.Net.Commands/CommandMap.cs @@ -116,7 +116,7 @@ namespace Discord.Commands } } - public bool CanRun(User user, Channel channel, out string error) + public bool CanRun(User user, ITextChannel channel, out string error) { error = null; if (_commands.Count > 0) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 8e6f42c51..ea530d27b 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -63,7 +63,7 @@ namespace Discord.Commands .Description("Returns information about commands.") .Do(async e => { - Channel replyChannel = Config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreatePMChannel().ConfigureAwait(false); + ITextChannel replyChannel = Config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreatePMChannel().ConfigureAwait(false); if (e.Args.Length > 0) //Show command help { var map = _map.GetItem(string.Join(" ", e.Args)); @@ -175,7 +175,7 @@ namespace Discord.Commands }; } - public Task ShowGeneralHelp(User user, Channel channel, Channel replyChannel = null) + public Task ShowGeneralHelp(User user, ITextChannel channel, ITextChannel replyChannel = null) { StringBuilder output = new StringBuilder(); bool isFirstCategory = true; @@ -219,32 +219,12 @@ namespace Discord.Commands if (output.Length == 0) output.Append("There are no commands you have permission to run."); else - { - output.Append("\n\n"); - - //TODO: Should prefix be stated in the help message or not? - /*StringBuilder builder = new StringBuilder(); - if (Config.PrefixChar != null) - { - builder.Append('`'); - builder.Append(Config.PrefixChar.Value); - builder.Append('`'); - } - if (Config.AllowMentionPrefix) - { - if (builder.Length > 0) - builder.Append(" or "); - builder.Append(Client.CurrentUser.Mention); - } - if (builder.Length > 0) - output.AppendLine($"Start your message with {builder.ToString()} to run a command.");*/ - output.AppendLine($"Run `help ` for more information."); - } + output.AppendLine("\n\nRun `help ` for more information."); return (replyChannel ?? channel).SendMessage(output.ToString()); } - private Task ShowCommandHelp(CommandMap map, User user, Channel channel, Channel replyChannel = null) + private Task ShowCommandHelp(CommandMap map, User user, ITextChannel channel, ITextChannel replyChannel = null) { StringBuilder output = new StringBuilder(); @@ -255,9 +235,7 @@ namespace Discord.Commands { foreach (var cmd in cmds) { - if (!cmd.CanRun(user, channel, out error)) { } - //output.AppendLine(error ?? DefaultPermissionError); - else + if (cmd.CanRun(user, channel, out error)) { if (isFirstCmd) isFirstCmd = false; @@ -299,7 +277,7 @@ namespace Discord.Commands return (replyChannel ?? channel).SendMessage(output.ToString()); } - public Task ShowCommandHelp(Command command, User user, Channel channel, Channel replyChannel = null) + public Task ShowCommandHelp(Command command, User user, ITextChannel channel, ITextChannel replyChannel = null) { StringBuilder output = new StringBuilder(); string error; @@ -309,7 +287,7 @@ namespace Discord.Commands ShowCommandHelpInternal(command, user, channel, output); return (replyChannel ?? channel).SendMessage(output.ToString()); } - private void ShowCommandHelpInternal(Command command, User user, Channel channel, StringBuilder output) + private void ShowCommandHelpInternal(Command command, User user, ITextChannel channel, StringBuilder output) { output.Append('`'); output.Append(command.Text); diff --git a/src/Discord.Net.Commands/Permissions/GenericPermissionChecker.cs b/src/Discord.Net.Commands/GenericPermissionChecker.cs similarity index 54% rename from src/Discord.Net.Commands/Permissions/GenericPermissionChecker.cs rename to src/Discord.Net.Commands/GenericPermissionChecker.cs index 05e95ac64..10d665811 100644 --- a/src/Discord.Net.Commands/Permissions/GenericPermissionChecker.cs +++ b/src/Discord.Net.Commands/GenericPermissionChecker.cs @@ -4,16 +4,16 @@ namespace Discord.Commands.Permissions { internal class GenericPermissionChecker : IPermissionChecker { - private readonly Func _checkFunc; + private readonly Func _checkFunc; private readonly string _error; - public GenericPermissionChecker(Func checkFunc, string error = null) + public GenericPermissionChecker(Func checkFunc, string error = null) { _checkFunc = checkFunc; _error = error; } - public bool CanRun(Command command, User user, Channel channel, out string error) + public bool CanRun(Command command, User user, ITextChannel channel, out string error) { error = _error; return _checkFunc(command, user, channel); diff --git a/src/Discord.Net.Commands/Permissions/IPermissionChecker.cs b/src/Discord.Net.Commands/IPermissionChecker.cs similarity index 54% rename from src/Discord.Net.Commands/Permissions/IPermissionChecker.cs rename to src/Discord.Net.Commands/IPermissionChecker.cs index f400c3420..0f317ffef 100644 --- a/src/Discord.Net.Commands/Permissions/IPermissionChecker.cs +++ b/src/Discord.Net.Commands/IPermissionChecker.cs @@ -2,6 +2,6 @@ { public interface IPermissionChecker { - bool CanRun(Command command, User user, Channel channel, out string error); + bool CanRun(Command command, User user, ITextChannel channel, out string error); } } diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs deleted file mode 100644 index 0092c4edf..000000000 --- a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Discord.Commands.Permissions.Levels -{ - public class PermissionLevelChecker : IPermissionChecker - { - private readonly PermissionLevelService _service; - private readonly int _minPermissions; - - public PermissionLevelService Service => _service; - public int MinPermissions => _minPermissions; - - internal PermissionLevelChecker(DiscordClient client, int minPermissions) - { - _service = client.GetService(true); - _minPermissions = minPermissions; - } - - public bool CanRun(Command command, User user, Channel channel, out string error) - { - error = null; //Use default error text. - int permissions = _service.GetPermissionLevel(user, channel); - return permissions >= _minPermissions; - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs deleted file mode 100644 index 10f153215..000000000 --- a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Discord.Commands.Permissions.Levels -{ - public static class PermissionLevelExtensions - { - public static DiscordClient UsingPermissionLevels(this DiscordClient client, Func permissionResolver) - { - client.AddService(new PermissionLevelService(permissionResolver)); - return client; - } - - public static CommandBuilder MinPermissions(this CommandBuilder builder, int minPermissions) - { - builder.AddCheck(new PermissionLevelChecker(builder.Service.Client, minPermissions)); - return builder; - } - public static CommandGroupBuilder MinPermissions(this CommandGroupBuilder builder, int minPermissions) - { - builder.AddCheck(new PermissionLevelChecker(builder.Service.Client, minPermissions)); - return builder; - } - public static CommandService MinPermissions(this CommandService service, int minPermissions) - { - service.Root.AddCheck(new PermissionLevelChecker(service.Client, minPermissions)); - return service; - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs deleted file mode 100644 index 6bab13b97..000000000 --- a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Discord.Commands.Permissions.Levels -{ - public class PermissionLevelService : IService - { - private readonly Func _getPermissionsFunc; - - private DiscordClient _client; - public DiscordClient Client => _client; - - public PermissionLevelService(Func getPermissionsFunc) - { - _getPermissionsFunc = getPermissionsFunc; - } - - public void Install(DiscordClient client) - { - _client = client; - } - public int GetPermissionLevel(User user, Channel channel) => _getPermissionsFunc(user, channel); - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs deleted file mode 100644 index 23c48cba9..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Discord.Commands.Permissions.Userlist -{ - public class BlacklistChecker : IPermissionChecker - { - private readonly BlacklistService _service; - - internal BlacklistChecker(DiscordClient client) - { - _service = client.GetService(true); - } - - public bool CanRun(Command command, User user, Channel channel, out string error) - { - error = null; //Use default error text. - return _service.CanRun(user); - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs deleted file mode 100644 index 1ff51ee58..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; - -namespace Discord.Commands.Permissions.Userlist -{ - public static class BlacklistExtensions - { - public static DiscordClient UsingGlobalBlacklist(this DiscordClient client, params ulong[] initialUserIds) - { - client.AddService(new BlacklistService(initialUserIds)); - return client; - } - - public static CommandBuilder UseGlobalBlacklist(this CommandBuilder builder) - { - builder.AddCheck(new BlacklistChecker(builder.Service.Client)); - return builder; - } - public static CommandGroupBuilder UseGlobalBlacklist(this CommandGroupBuilder builder) - { - builder.AddCheck(new BlacklistChecker(builder.Service.Client)); - return builder; - } - public static CommandService UseGlobalBlacklist(this CommandService service) - { - service.Root.AddCheck(new BlacklistChecker(service.Client)); - return service; - } - - public static IEnumerable GetBlacklistedUserIds(this DiscordClient client) - => client.GetService().UserIds; - public static void BlacklistUser(this DiscordClient client, User user) - { - client.GetService().Add(user.Id); - } - public static void BlacklistUser(this DiscordClient client, ulong userId) - { - client.GetService().Add(userId); - } - public static void UnBlacklistUser(this DiscordClient client, User user) - { - client.GetService().Remove(user.Id); - } - public static void UnBlacklistUser(this DiscordClient client, ulong userId) - { - client.GetService().Remove(userId); - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs deleted file mode 100644 index ced4c3fdc..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Discord.Commands.Permissions.Userlist -{ - public class BlacklistService : UserlistService - { - public BlacklistService(params ulong[] initialList) - : base(initialList) - { - } - - public bool CanRun(User user) - => !_userList.ContainsKey(user.Id); - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs deleted file mode 100644 index da8264312..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace Discord.Commands.Permissions.Userlist -{ - public class UserlistService : IService - { - protected readonly ConcurrentDictionary _userList; - private DiscordClient _client; - - public DiscordClient Client => _client; - public IEnumerable UserIds => _userList.Select(x => x.Key); - - public UserlistService(params ulong[] initialUserIds) - { - _userList = new ConcurrentDictionary(); - for (int i = 0; i < initialUserIds.Length; i++) - _userList.TryAdd(initialUserIds[i], true); - } - - public void Add(User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - - _userList[user.Id] = true; - } - public void Add(ulong userId) - { - _userList[userId] = true; - } - - public bool Remove(User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - - bool ignored; - return _userList.TryRemove(user.Id, out ignored); - } - public bool Remove(ulong userId) - { - bool ignored; - return _userList.TryRemove(userId, out ignored); - } - - void IService.Install(DiscordClient client) - { - _client = client; - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs deleted file mode 100644 index fa441644f..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Discord.Commands.Permissions.Userlist -{ - public class WhitelistChecker : IPermissionChecker - { - private readonly WhitelistService _service; - - internal WhitelistChecker(DiscordClient client) - { - _service = client.GetService(true); - } - - public bool CanRun(Command command, User user, Channel channel, out string error) - { - error = null; //Use default error text. - return _service.CanRun(user); - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs deleted file mode 100644 index 391668298..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; - -namespace Discord.Commands.Permissions.Userlist -{ - public static class WhitelistExtensions - { - public static DiscordClient UsingGlobalWhitelist(this DiscordClient client, params ulong[] initialUserIds) - { - client.AddService(new WhitelistService(initialUserIds)); - return client; - } - - public static CommandBuilder UseGlobalWhitelist(this CommandBuilder builder) - { - builder.AddCheck(new WhitelistChecker(builder.Service.Client)); - return builder; - } - public static CommandGroupBuilder UseGlobalWhitelist(this CommandGroupBuilder builder) - { - builder.AddCheck(new WhitelistChecker(builder.Service.Client)); - return builder; - } - public static CommandService UseGlobalWhitelist(this CommandService service) - { - service.Root.AddCheck(new BlacklistChecker(service.Client)); - return service; - } - - public static IEnumerable GetWhitelistedUserIds(this DiscordClient client) - => client.GetService().UserIds; - public static void WhitelistUser(this DiscordClient client, User user) - { - client.GetService().Add(user.Id); - } - public static void WhitelistUser(this DiscordClient client, ulong userId) - { - client.GetService().Add(userId); - } - public static void UnWhitelistUser(this DiscordClient client, User user) - { - client.GetService().Remove(user.Id); - } - public static void RemoveFromWhitelist(this DiscordClient client, ulong userId) - { - client.GetService().Remove(userId); - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs deleted file mode 100644 index ae25d3fd1..000000000 --- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Discord.Commands.Permissions.Userlist -{ - public class WhitelistService : UserlistService - { - public WhitelistService(params ulong[] initialList) - : base(initialList) - { - } - - public bool CanRun(User user) - => _userList.ContainsKey(user.Id); - } -} diff --git a/src/Discord.Net.Commands/Permissions/Visibility/PrivateChecker.cs b/src/Discord.Net.Commands/Permissions/Visibility/PrivateChecker.cs deleted file mode 100644 index dd336042d..000000000 --- a/src/Discord.Net.Commands/Permissions/Visibility/PrivateChecker.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Discord.Commands.Permissions.Visibility -{ - public class PrivateChecker : IPermissionChecker - { - internal PrivateChecker() { } - - public bool CanRun(Command command, User user, Channel channel, out string error) - { - if (user.Server != null) - { - error = "This command may only be run in a private chat."; - return false; - } - else - { - error = null; - return true; - } - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs b/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs deleted file mode 100644 index cb3579983..000000000 --- a/src/Discord.Net.Commands/Permissions/Visibility/PrivateExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Discord.Commands.Permissions.Visibility -{ - public static class PrivateExtensions - { - public static CommandBuilder PrivateOnly(this CommandBuilder builder) - { - builder.AddCheck(new PrivateChecker()); - return builder; - } - public static CommandGroupBuilder PrivateOnly(this CommandGroupBuilder builder) - { - builder.AddCheck(new PrivateChecker()); - return builder; - } - public static CommandService PrivateOnly(this CommandService service) - { - service.Root.AddCheck(new PrivateChecker()); - return service; - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Visibility/PublicChecker.cs b/src/Discord.Net.Commands/Permissions/Visibility/PublicChecker.cs deleted file mode 100644 index 9e70b647b..000000000 --- a/src/Discord.Net.Commands/Permissions/Visibility/PublicChecker.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Discord.Commands.Permissions.Visibility -{ - public class PublicChecker : IPermissionChecker - { - internal PublicChecker() { } - - public bool CanRun(Command command, User user, Channel channel, out string error) - { - if (user.Server == null) - { - error = "This command can't be run in a private chat."; - return false; - } - else - { - error = null; - return true; - } - } - } -} diff --git a/src/Discord.Net.Commands/Permissions/Visibility/PublicExtensions.cs b/src/Discord.Net.Commands/Permissions/Visibility/PublicExtensions.cs deleted file mode 100644 index 8cd78a4fe..000000000 --- a/src/Discord.Net.Commands/Permissions/Visibility/PublicExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Discord.Commands.Permissions.Visibility -{ - public static class PublicExtensions - { - public static CommandBuilder PublicOnly(this CommandBuilder builder) - { - builder.AddCheck(new PublicChecker()); - return builder; - } - public static CommandGroupBuilder PublicOnly(this CommandGroupBuilder builder) - { - builder.AddCheck(new PublicChecker()); - return builder; - } - public static CommandService PublicOnly(this CommandService service) - { - service.Root.AddCheck(new PublicChecker()); - return service; - } - } -} diff --git a/src/Discord.Net.Commands/project.json b/src/Discord.Net.Commands/project.json index c460619e4..124a29dfe 100644 --- a/src/Discord.Net.Commands/project.json +++ b/src/Discord.Net.Commands/project.json @@ -1,5 +1,5 @@ { - "version": "0.9.0-rc3-1", + "version": "1.0.0-alpha1", "description": "A Discord.Net extension adding basic command support.", "authors": [ "RogueException" ], "tags": [ "discord", "discordapp" ], @@ -16,7 +16,7 @@ }, "dependencies": { - "Discord.Net": "0.9.0-rc3-3" + "Discord.Net": "1.0.0-alpha1" }, "frameworks": { "net45": { }, diff --git a/src/Discord.Net.Modules/ModuleChecker.cs b/src/Discord.Net.Modules/ModuleChecker.cs index 7b54c8a2d..5f9b8e116 100644 --- a/src/Discord.Net.Modules/ModuleChecker.cs +++ b/src/Discord.Net.Modules/ModuleChecker.cs @@ -14,9 +14,11 @@ namespace Discord.Modules _filterType = manager.FilterType; } - public bool CanRun(Command command, User user, Channel channel, out string error) + public bool CanRun(Command command, User user, ITextChannel channel, out string error) { - if (_filterType == ModuleFilter.None || _filterType == ModuleFilter.AlwaysAllowPrivate || _manager.HasChannel(channel)) + if (_filterType == ModuleFilter.None || + _filterType == ModuleFilter.AlwaysAllowPrivate || + (channel.IsPublic && _manager.HasChannel(channel))) { error = null; return true; diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs index b00dc244f..71b5b0c08 100644 --- a/src/Discord.Net.Modules/ModuleManager.cs +++ b/src/Discord.Net.Modules/ModuleManager.cs @@ -20,11 +20,6 @@ namespace Discord.Modules public class ModuleManager { - public event EventHandler ServerEnabled = delegate { }; - public event EventHandler ServerDisabled = delegate { }; - public event EventHandler ChannelEnabled = delegate { }; - public event EventHandler ChannelDisabled = delegate { }; - public event EventHandler JoinedServer = delegate { }; public event EventHandler LeftServer = delegate { }; public event EventHandler ServerUpdated = delegate { }; @@ -43,10 +38,8 @@ namespace Discord.Modules public event EventHandler UserJoined = delegate { }; public event EventHandler UserLeft = delegate { }; public event EventHandler UserUpdated = delegate { }; - //public event EventHandler UserPresenceUpdated = delegate { }; - //public event EventHandler UserVoiceStateUpdated = delegate { }; public event EventHandler UserUnbanned = delegate { }; - public event EventHandler UserIsTyping = delegate { }; + public event EventHandler UserIsTyping = delegate { }; public event EventHandler MessageReceived = delegate { }; public event EventHandler MessageSent = delegate { }; @@ -56,7 +49,7 @@ namespace Discord.Modules private readonly bool _useServerWhitelist, _useChannelWhitelist, _allowAll, _allowPrivate; private readonly ConcurrentDictionary _enabledServers; - private readonly ConcurrentDictionary _enabledChannels; + private readonly ConcurrentDictionary _enabledChannels; private readonly ConcurrentDictionary _indirectServers; private readonly AsyncLock _lock; @@ -67,7 +60,7 @@ namespace Discord.Modules public ModuleFilter FilterType { get; } public IEnumerable EnabledServers => _enabledServers.Select(x => x.Value); - public IEnumerable EnabledChannels => _enabledChannels.Select(x => x.Value); + public IEnumerable EnabledChannels => _enabledChannels.Select(x => x.Value); internal ModuleManager(DiscordClient client, IModule instance, string name, ModuleFilter filterType) { @@ -85,12 +78,17 @@ namespace Discord.Modules _allowPrivate = filterType.HasFlag(ModuleFilter.AlwaysAllowPrivate); _enabledServers = new ConcurrentDictionary(); - _enabledChannels = new ConcurrentDictionary(); + _enabledChannels = new ConcurrentDictionary(); _indirectServers = new ConcurrentDictionary(); if (_allowAll || _useServerWhitelist) //Server-only events { - client.ChannelCreated += (s, e) => { if (e.Server != null && HasServer(e.Server)) ChannelCreated(s, e); }; + client.ChannelCreated += (s, e) => + { + var server = (e.Channel as PublicChannel)?.Server; + if (HasServer(server)) + ChannelCreated(s, e); + }; //TODO: This *is* a channel update if the before/after voice channel is whitelisted //client.UserVoiceStateUpdated += (s, e) => { if (HasServer(e.Server)) UserVoiceStateUpdated(s, e); }; } @@ -116,10 +114,9 @@ namespace Discord.Modules client.UserJoined += (s, e) => { if (HasIndirectServer(e.Server)) UserJoined(s, e); }; client.UserLeft += (s, e) => { if (HasIndirectServer(e.Server)) UserLeft(s, e); }; + //TODO: We aren't getting events from UserPresence if AllowPrivate is enabled, but the server we know that user through isn't on the whitelist client.UserUpdated += (s, e) => { if (HasIndirectServer(e.Server)) UserUpdated(s, e); }; client.UserIsTyping += (s, e) => { if (HasChannel(e.Channel)) UserIsTyping(s, e); }; - //TODO: We aren't getting events from UserPresence if AllowPrivate is enabled, but the server we know that user through isn't on the whitelist - //client.UserPresenceUpdated += (s, e) => { if (HasIndirectServer(e.Server)) UserPresenceUpdated(s, e); }; client.UserBanned += (s, e) => { if (HasIndirectServer(e.Server)) UserBanned(s, e); }; client.UserUnbanned += (s, e) => { if (HasIndirectServer(e.Server)) UserUnbanned(s, e); }; } @@ -155,16 +152,7 @@ namespace Discord.Modules EnableServerInternal(server); } } - private bool EnableServerInternal(Server server) - { - if (_enabledServers.TryAdd(server.Id, server)) - { - if (ServerEnabled != null) - ServerEnabled(this, new ServerEventArgs(server)); - return true; - } - return false; - } + private bool EnableServerInternal(Server server) => _enabledServers.TryAdd(server.Id, server); public bool DisableServer(Server server) { @@ -172,34 +160,18 @@ namespace Discord.Modules if (!_useServerWhitelist) return false; using (_lock.Lock()) - { - if (_enabledServers.TryRemove(server.Id, out server)) - { - if (ServerDisabled != null) - ServerDisabled(this, new ServerEventArgs(server)); - return true; - } - return false; - } + return _enabledServers.TryRemove(server.Id, out server); } public void DisableAllServers() { if (!_useServerWhitelist) throw new InvalidOperationException("This module is not configured to use a server whitelist."); if (!_useServerWhitelist) return; - using (_lock.Lock()) - { - if (ServerDisabled != null) - { - foreach (var server in _enabledServers) - ServerDisabled(this, new ServerEventArgs(server.Value)); - } - + using (_lock.Lock()) _enabledServers.Clear(); - } } - public bool EnableChannel(Channel channel) + public bool EnableChannel(ITextChannel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (!_useChannelWhitelist) throw new InvalidOperationException("This module is not configured to use a channel whitelist."); @@ -207,7 +179,7 @@ namespace Discord.Modules using (_lock.Lock()) return EnableChannelInternal(channel); } - public void EnableChannels(IEnumerable channels) + public void EnableChannels(IEnumerable channels) { if (channels == null) throw new ArgumentNullException(nameof(channels)); if (channels.Contains(null)) throw new ArgumentException("Collection cannot contain null.", nameof(channels)); @@ -219,65 +191,55 @@ namespace Discord.Modules EnableChannelInternal(channel); } } - private bool EnableChannelInternal(Channel channel) + private bool EnableChannelInternal(ITextChannel channel) { if (_enabledChannels.TryAdd(channel.Id, channel)) - { - var server = channel.Server; - if (server != null) - { - int value = 0; - _indirectServers.TryGetValue(server.Id, out value); - value++; - _indirectServers[server.Id] = value; - } - if (ChannelEnabled != null) - ChannelEnabled(this, new ChannelEventArgs(channel)); + { + if (channel.Type != ChannelType.Private) + { + var server = (channel as PublicChannel)?.Server; + int value = 0; + _indirectServers.TryGetValue(server.Id, out value); + value++; + _indirectServers[server.Id] = value; + } return true; } return false; } - public bool DisableChannel(Channel channel) + public bool DisableChannel(IChannel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (!_useChannelWhitelist) return false; - using (_lock.Lock()) + IChannel ignored; + if (_enabledChannels.TryRemove(channel.Id, out ignored)) { - Channel ignored; - if (_enabledChannels.TryRemove(channel.Id, out ignored)) - { - var server = channel.Server; - if (server != null) - { - int value = 0; - _indirectServers.TryGetValue(server.Id, out value); - value--; - if (value <= 0) - _indirectServers.TryRemove(server.Id, out value); - else - _indirectServers[server.Id] = value; - } - if (ChannelDisabled != null) - ChannelDisabled(this, new ChannelEventArgs(channel)); - return true; - } - return false; - } - } + using (_lock.Lock()) + { + if (channel.Type != ChannelType.Private) + { + var server = (channel as PublicChannel)?.Server; + int value = 0; + _indirectServers.TryGetValue(server.Id, out value); + value--; + if (value <= 0) + _indirectServers.TryRemove(server.Id, out value); + else + _indirectServers[server.Id] = value; + } + return true; + } + } + return false; + } public void DisableAllChannels() { if (!_useChannelWhitelist) return; using (_lock.Lock()) { - if (ChannelDisabled != null) - { - foreach (var channel in _enabledChannels) - ChannelDisabled(this, new ChannelEventArgs(channel.Value)); - } - _enabledChannels.Clear(); _indirectServers.Clear(); } @@ -293,20 +255,20 @@ namespace Discord.Modules internal bool HasServer(Server server) => _allowAll || - _useServerWhitelist && _enabledServers.ContainsKey(server.Id); + (_useServerWhitelist && _enabledServers.ContainsKey(server.Id)); internal bool HasIndirectServer(Server server) => _allowAll || (_useServerWhitelist && _enabledServers.ContainsKey(server.Id)) || (_useChannelWhitelist && _indirectServers.ContainsKey(server.Id)); - internal bool HasChannel(Channel channel) + internal bool HasChannel(IChannel channel) { if (_allowAll) return true; - if (channel.IsPrivate) return _allowPrivate; + if (channel.Type == ChannelType.Private) return _allowPrivate; if (_useChannelWhitelist && _enabledChannels.ContainsKey(channel.Id)) return true; - if (_useServerWhitelist) + if (_useServerWhitelist && channel.IsPublic) { - var server = channel.Server; + var server = (channel as PublicChannel).Server; if (server == null) return false; if (_enabledServers.ContainsKey(server.Id)) return true; } diff --git a/src/Discord.Net.Modules/project.json b/src/Discord.Net.Modules/project.json index 884339080..e8e897cac 100644 --- a/src/Discord.Net.Modules/project.json +++ b/src/Discord.Net.Modules/project.json @@ -1,5 +1,5 @@ { - "version": "0.9.0-rc3", + "version": "1.0.0-alpha1", "description": "A Discord.Net extension adding basic plugin support.", "authors": [ "RogueException" ], "tags": [ "discord", "discordapp" ], @@ -16,8 +16,8 @@ }, "dependencies": { - "Discord.Net": "0.9.0-rc3-3", - "Discord.Net.Commands": "0.9.0-rc3-1" + "Discord.Net": "1.0.0-alpha1", + "Discord.Net.Commands": "1.0.0-alpha1" }, "frameworks": { "net45": { }, diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index b26180c07..93c4eb664 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -526,9 +526,15 @@ Models\Permissions.cs + + Models\PrivateChannel.cs + Models\Profile.cs + + Models\PublicChannel.cs + Models\Region.cs @@ -538,9 +544,15 @@ Models\Server.cs + + Models\TextChannel.cs + Models\User.cs + + Models\VoiceChannel.cs + Net\HttpException.cs diff --git a/src/Discord.Net/API/Client/Common/Channel.cs b/src/Discord.Net/API/Client/Common/Channel.cs index 90ed8bf38..37eac8e48 100644 --- a/src/Discord.Net/API/Client/Common/Channel.cs +++ b/src/Discord.Net/API/Client/Common/Channel.cs @@ -29,5 +29,7 @@ namespace Discord.API.Client public PermissionOverwrite[] PermissionOverwrites { get; set; } [JsonProperty("recipient")] public UserReference Recipient { get; set; } + [JsonProperty("bitrate")] + public int Bitrate { get; set; } } } diff --git a/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs b/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs index 44d08b4bf..bf725bcaf 100644 --- a/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs +++ b/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Client.Rest { [JsonObject(MemberSerialization.OptIn)] - public class AddChannelPermissionsRequest : IRestRequest + public class AddOrUpdateChannelPermissionsRequest : IRestRequest { string IRestRequest.Method => "PUT"; string IRestRequest.Endpoint => $"channels/{ChannelId}/permissions/{TargetId}"; @@ -21,7 +21,7 @@ namespace Discord.API.Client.Rest [JsonProperty("deny")] public uint Deny { get; set; } - public AddChannelPermissionsRequest(ulong channelId) + public AddOrUpdateChannelPermissionsRequest(ulong channelId) { ChannelId = channelId; } diff --git a/src/Discord.Net/API/Client/Rest/CreateChannel.cs b/src/Discord.Net/API/Client/Rest/CreateChannel.cs index 0d3309701..90d9afec0 100644 --- a/src/Discord.Net/API/Client/Rest/CreateChannel.cs +++ b/src/Discord.Net/API/Client/Rest/CreateChannel.cs @@ -14,7 +14,7 @@ namespace Discord.API.Client.Rest [JsonProperty("name")] public string Name { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public ChannelType Type { get; set; } public CreateChannelRequest(ulong guildId) { diff --git a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs index 86a35a605..8a82caefd 100644 --- a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs +++ b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs @@ -17,6 +17,8 @@ namespace Discord.API.Client.Rest public string Topic { get; set; } [JsonProperty("position")] public int Position { get; set; } + [JsonProperty("bitrate")] + public int Bitrate { get; set; } public UpdateChannelRequest(ulong channelId) { diff --git a/src/Discord.Net/DiscordClient.Events.cs b/src/Discord.Net/DiscordClient.Events.cs index 5e9b5bc09..b05725f9b 100644 --- a/src/Discord.Net/DiscordClient.Events.cs +++ b/src/Discord.Net/DiscordClient.Events.cs @@ -25,7 +25,7 @@ namespace Discord public event EventHandler ServerUpdated = delegate { }; public event EventHandler ServerUnavailable = delegate { }; public event EventHandler UserBanned = delegate { }; - public event EventHandler UserIsTyping = delegate { }; + public event EventHandler UserIsTyping = delegate { }; public event EventHandler UserJoined = delegate { }; public event EventHandler UserLeft = delegate { }; public event EventHandler UserUpdated = delegate { }; @@ -36,11 +36,11 @@ namespace Discord /*private void OnLoggedOut(bool wasUnexpected, Exception ex) => OnEvent(LoggedOut, new DisconnectedEventArgs(wasUnexpected, ex));*/ - private void OnChannelCreated(Channel channel) + private void OnChannelCreated(IChannel channel) => OnEvent(ChannelCreated, new ChannelEventArgs(channel)); - private void OnChannelDestroyed(Channel channel) + private void OnChannelDestroyed(IChannel channel) => OnEvent(ChannelDestroyed, new ChannelEventArgs(channel)); - private void OnChannelUpdated(Channel before, Channel after) + private void OnChannelUpdated(IChannel before, IChannel after) => OnEvent(ChannelUpdated, new ChannelUpdatedEventArgs(before, after)); private void OnMessageAcknowledged(Message msg) @@ -77,8 +77,8 @@ namespace Discord private void OnUserBanned(User user) => OnEvent(UserBanned, new UserEventArgs(user)); - private void OnUserIsTypingUpdated(Channel channel, User user) - => OnEvent(UserIsTyping, new ChannelUserEventArgs(channel, user)); + private void OnUserIsTypingUpdated(ITextChannel channel, User user) + => OnEvent(UserIsTyping, new TypingEventArgs(channel, user)); private void OnUserJoined(User user) => OnEvent(UserJoined, new UserEventArgs(user)); private void OnUserLeft(User user) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 1b474ca5d..ac29228dc 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -1,4 +1,5 @@ -using Discord.API.Client.GatewaySocket; +using APIChannel = Discord.API.Client.Channel; +using Discord.API.Client.GatewaySocket; using Discord.API.Client.Rest; using Discord.Logging; using Discord.Net; @@ -29,8 +30,8 @@ namespace Discord private readonly TaskManager _taskManager; private readonly ServiceCollection _services; private ConcurrentDictionary _servers; - private ConcurrentDictionary _channels; - private ConcurrentDictionary _privateChannels; //Key = RecipientId + private ConcurrentDictionary _channels; + private ConcurrentDictionary _privateChannels; //Key = RecipientId private Dictionary _regions; private Stopwatch _connectionStopwatch; @@ -71,7 +72,7 @@ namespace Discord /// Gets a collection of all servers this client is a member of. public IEnumerable Servers => _servers.Select(x => x.Value); /// Gets a collection of all private channels this client is a member of. - public IEnumerable PrivateChannels => _privateChannels.Select(x => x.Value); + public IEnumerable PrivateChannels => _privateChannels.Select(x => x.Value); /// Gets a collection of all voice regions currently offered by Discord. public IEnumerable Regions => _regions.Select(x => x.Value); @@ -123,8 +124,8 @@ namespace Discord //Cache //ConcurrentLevel = 2 (only REST and WebSocket can add/remove) _servers = new ConcurrentDictionary(2, 0); - _channels = new ConcurrentDictionary(2, 0); - _privateChannels = new ConcurrentDictionary(2, 0); + _channels = new ConcurrentDictionary(2, 0); + _privateChannels = new ConcurrentDictionary(2, 0); //Serialization Serializer = new JsonSerializer(); @@ -335,45 +336,47 @@ namespace Discord } #region Channels - internal void AddChannel(Channel channel) + internal void AddChannel(IChannel channel) { _channels.GetOrAdd(channel.Id, channel); } - private Channel RemoveChannel(ulong id) + private IChannel RemoveChannel(ulong id) { - Channel channel; + IChannel channel; if (_channels.TryRemove(id, out channel)) { if (channel.IsPrivate) - _privateChannels.TryRemove(channel.Recipient.Id, out channel); + { + PrivateChannel removed; + _privateChannels.TryRemove((channel as PrivateChannel).Recipient.Id, out removed); + } else - channel.Server.RemoveChannel(id); + (channel as PublicChannel).Server.RemoveChannel(id); } return channel; } - public Channel GetChannel(ulong id) + public IChannel GetChannel(ulong id) { - Channel channel; + IChannel channel; _channels.TryGetValue(id, out channel); return channel; } - private Channel AddPrivateChannel(ulong id, ulong recipientId) + private PrivateChannel AddPrivateChannel(APIChannel model) { - Channel channel; - if (_channels.TryGetOrAdd(id, x => new Channel(this, x, new User(this, recipientId, null)), out channel)) - _privateChannels[recipientId] = channel; - return channel; + IChannel channel; + if (_channels.TryGetOrAdd(model.Id, x => new PrivateChannel(x, new User(this, model.Recipient.Id, null), model), out channel)) + _privateChannels[model.Recipient.Id] = channel as PrivateChannel; + return channel as PrivateChannel; } - internal Channel GetPrivateChannel(ulong recipientId) + internal PrivateChannel GetPrivateChannel(ulong recipientId) { - Channel channel; + PrivateChannel channel; _privateChannels.TryGetValue(recipientId, out channel); return channel; } - internal Task CreatePMChannel(User user) - => CreatePrivateChannel(user.Id); - public async Task CreatePrivateChannel(ulong userId) + public Task CreatePrivateChannel(User user) => CreatePrivateChannel(user.Id); + public async Task CreatePrivateChannel(ulong userId) { var channel = GetPrivateChannel(userId); if (channel != null) return channel; @@ -381,9 +384,7 @@ namespace Discord var request = new CreatePrivateChannelRequest() { RecipientId = userId }; var response = await ClientAPI.Send(request).ConfigureAwait(false); - channel = AddPrivateChannel(response.Id, userId); - channel.Update(response); - return channel; + return AddPrivateChannel(response); } #endregion @@ -429,8 +430,7 @@ namespace Discord #endregion #region Servers - private Server AddServer(ulong id) - => _servers.GetOrAdd(id, x => new Server(this, x)); + private Server AddServer(ulong id) => _servers.GetOrAdd(id, x => new Server(this, x)); private Server RemoveServer(ulong id) { Server server; @@ -448,11 +448,6 @@ namespace Discord _servers.TryGetValue(id, out server); return server; } - public IEnumerable FindServers(string name) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - return _servers.Select(x => x.Value).Find(name); - } /// Creates a new server with the provided name and region. public async Task CreateServer(string name, Region region, ImageType iconType = ImageType.None, Stream icon = null) @@ -494,8 +489,8 @@ namespace Discord //ConcurrencyLevel = 2 (only REST and WebSocket can add/remove) _servers = new ConcurrentDictionary(2, (int)(data.Guilds.Length * 1.05)); - _channels = new ConcurrentDictionary(2, (int)(channelCount * 1.05)); - _privateChannels = new ConcurrentDictionary(2, (int)(data.PrivateChannels.Length * 1.05)); + _channels = new ConcurrentDictionary(2, (int)(channelCount * 1.05)); + _privateChannels = new ConcurrentDictionary(2, (int)(data.PrivateChannels.Length * 1.05)); List largeServers = new List(); SessionId = data.SessionId; @@ -516,11 +511,7 @@ namespace Discord } } for (int i = 0; i < data.PrivateChannels.Length; i++) - { - var model = data.PrivateChannels[i]; - var channel = AddPrivateChannel(model.Id, model.Recipient.Id); - channel.Update(model); - } + AddPrivateChannel(data.PrivateChannels[i]); if (largeServers.Count > 0) GatewaySocket.SendRequestMembers(largeServers, "", 0); else @@ -538,9 +529,9 @@ namespace Discord server.Update(data); if (data.Unavailable != false) - Logger.Info($"GUILD_CREATE: {server.Path}"); + Logger.Info($"GUILD_CREATE: {server}"); else - Logger.Info($"GUILD_AVAILABLE: {server.Path}"); + Logger.Info($"GUILD_AVAILABLE: {server}"); if (data.Unavailable != false) OnJoinedServer(server); @@ -556,7 +547,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? server.Clone() : null; server.Update(data); - Logger.Info($"GUILD_UPDATE: {server.Path}"); + Logger.Info($"GUILD_UPDATE: {server}"); OnServerUpdated(before, server); } else @@ -570,9 +561,9 @@ namespace Discord if (server != null) { if (data.Unavailable != true) - Logger.Info($"GUILD_DELETE: {server.Path}"); + Logger.Info($"GUILD_DELETE: {server}"); else - Logger.Info($"GUILD_UNAVAILABLE: {server.Path}"); + Logger.Info($"GUILD_UNAVAILABLE: {server}"); OnServerUnavailable(server); if (data.Unavailable != true) @@ -588,23 +579,22 @@ namespace Discord { var data = e.Payload.ToObject(Serializer); - Channel channel = null; + IChannel channel = null; if (data.GuildId != null) { var server = GetServer(data.GuildId.Value); if (server != null) - channel = server.AddChannel(data.Id, true); + channel = server.AddChannel(data, true); else + { Logger.Warning("CHANNEL_CREATE referenced an unknown guild."); + break; + } } else - channel = AddPrivateChannel(data.Id, data.Recipient.Id); - if (channel != null) - { - channel.Update(data); - Logger.Info($"CHANNEL_CREATE: {channel.Path}"); - OnChannelCreated(channel); - } + channel = AddPrivateChannel(data); + Logger.Info($"CHANNEL_CREATE: {channel}"); + OnChannelCreated(channel); } break; case "CHANNEL_UPDATE": @@ -613,9 +603,9 @@ namespace Discord var channel = GetChannel(data.Id); if (channel != null) { - var before = Config.EnablePreUpdateEvents ? channel.Clone() : null; - channel.Update(data); - Logger.Info($"CHANNEL_UPDATE: {channel.Path}"); + var before = Config.EnablePreUpdateEvents ? (channel as Channel).Clone() : null; + (channel as Channel).Update(data); + Logger.Info($"CHANNEL_UPDATE: {channel}"); OnChannelUpdated(before, channel); } else @@ -628,7 +618,7 @@ namespace Discord var channel = RemoveChannel(data.Id); if (channel != null) { - Logger.Info($"CHANNEL_DELETE: {channel.Path}"); + Logger.Info($"CHANNEL_DELETE: {channel}"); OnChannelDestroyed(channel); } else @@ -646,7 +636,7 @@ namespace Discord var user = server.AddUser(data.User.Id, true, true); user.Update(data); user.UpdateActivity(); - Logger.Info($"GUILD_MEMBER_ADD: {user.Path}"); + Logger.Info($"GUILD_MEMBER_ADD: {user}"); OnUserJoined(user); } else @@ -664,7 +654,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); - Logger.Info($"GUILD_MEMBER_UPDATE: {user.Path}"); + Logger.Info($"GUILD_MEMBER_UPDATE: {user}"); OnUserUpdated(before, user); } else @@ -683,7 +673,7 @@ namespace Discord var user = server.RemoveUser(data.User.Id); if (user != null) { - Logger.Info($"GUILD_MEMBER_REMOVE: {user.Path}"); + Logger.Info($"GUILD_MEMBER_REMOVE: {user}"); OnUserLeft(user); } else @@ -732,7 +722,7 @@ namespace Discord { var role = server.AddRole(data.Data.Id); role.Update(data.Data, false); - Logger.Info($"GUILD_ROLE_CREATE: {role.Path}"); + Logger.Info($"GUILD_ROLE_CREATE: {role}"); OnRoleCreated(role); } else @@ -750,7 +740,7 @@ namespace Discord { var before = Config.EnablePreUpdateEvents ? role.Clone() : null; role.Update(data.Data, true); - Logger.Info($"GUILD_ROLE_UPDATE: {role.Path}"); + Logger.Info($"GUILD_ROLE_UPDATE: {role}"); OnRoleUpdated(before, role); } else @@ -769,7 +759,7 @@ namespace Discord var role = server.RemoveRole(data.RoleId); if (role != null) { - Logger.Info($"GUILD_ROLE_DELETE: {role.Path}"); + Logger.Info($"GUILD_ROLE_DELETE: {role}"); OnRoleDeleted(role); } else @@ -790,7 +780,7 @@ namespace Discord var user = server.GetUser(data.User.Id); if (user != null) { - Logger.Info($"GUILD_BAN_ADD: {user.Path}"); + Logger.Info($"GUILD_BAN_ADD: {user}"); OnUserBanned(user); } else @@ -808,7 +798,7 @@ namespace Discord { var user = new User(this, data.User.Id, server); user.Update(data.User); - Logger.Info($"GUILD_BAN_REMOVE: {user.Path}"); + Logger.Info($"GUILD_BAN_REMOVE: {user}"); OnUserUnbanned(user); } else @@ -821,40 +811,20 @@ namespace Discord { var data = e.Payload.ToObject(Serializer); - Channel channel = GetChannel(data.ChannelId); + var channel = GetChannel(data.ChannelId) as ITextChannel; if (channel != null) { - var user = channel.GetUserFast(data.Author.Id); + var user = (channel as Channel).GetUser(data.Author.Id); if (user != null) { Message msg = null; - bool isAuthor = data.Author.Id == CurrentUser.Id; - //ulong nonce = 0; - - /*if (data.Author.Id == _privateUser.Id && Config.UseMessageQueue) - { - if (data.Nonce != null && ulong.TryParse(data.Nonce, out nonce)) - msg = _messages[nonce]; - }*/ - if (msg == null) - { - msg = channel.AddMessage(data.Id, user, data.Timestamp.Value); - //nonce = 0; - } - - //Remapped queued message - /*if (nonce != 0) - { - msg = _messages.Remap(nonce, data.Id); - msg.Id = data.Id; - RaiseMessageSent(msg); - }*/ + msg = (channel as Channel).MessageManager.Add(data.Id, user, data.Timestamp.Value); msg.Update(data); user.UpdateActivity(); - Logger.Verbose($"MESSAGE_CREATE: {channel.Path} ({user.Name ?? "Unknown"})"); + Logger.Verbose($"MESSAGE_CREATE: {channel} ({user})"); OnMessageReceived(msg); } else @@ -867,13 +837,13 @@ namespace Discord case "MESSAGE_UPDATE": { var data = e.Payload.ToObject(Serializer); - var channel = GetChannel(data.ChannelId); + var channel = GetChannel(data.ChannelId) as ITextChannel; if (channel != null) { - var msg = channel.GetMessage(data.Id, data.Author?.Id); + var msg = (channel as Channel).MessageManager.Get(data.Id, data.Author?.Id); var before = Config.EnablePreUpdateEvents ? msg.Clone() : null; msg.Update(data); - Logger.Verbose($"MESSAGE_UPDATE: {channel.Path} ({data.Author?.Username ?? "Unknown"})"); + Logger.Verbose($"MESSAGE_UPDATE: {channel} ({data.Author?.Username ?? "Unknown"})"); OnMessageUpdated(before, msg); } else @@ -883,11 +853,11 @@ namespace Discord case "MESSAGE_DELETE": { var data = e.Payload.ToObject(Serializer); - var channel = GetChannel(data.ChannelId); + var channel = GetChannel(data.ChannelId) as ITextChannel; if (channel != null) { - var msg = channel.RemoveMessage(data.Id); - Logger.Verbose($"MESSAGE_DELETE: {channel.Path} ({msg.User?.Name ?? "Unknown"})"); + var msg = (channel as Channel).MessageManager.Remove(data.Id); + Logger.Verbose($"MESSAGE_DELETE: {channel} ({msg.User?.Name ?? "Unknown"})"); OnMessageDeleted(msg); } else @@ -921,7 +891,7 @@ namespace Discord if (user != null) { if (Config.LogLevel == LogSeverity.Debug) - Logger.Debug($"PRESENCE_UPDATE: {user.Path}"); + Logger.Debug($"PRESENCE_UPDATE: {user}"); var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); OnUserUpdated(before, user); @@ -933,29 +903,18 @@ namespace Discord case "TYPING_START": { var data = e.Payload.ToObject(Serializer); - var channel = GetChannel(data.ChannelId); + var channel = GetChannel(data.ChannelId) as ITextChannel; if (channel != null) { - User user; - if (channel.IsPrivate) - { - if (channel.Recipient.Id == data.UserId) - user = channel.Recipient; - else - break; - } - else - user = channel.Server.GetUser(data.UserId); + User user = (channel as Channel).GetUser(data.UserId); if (user != null) { if (Config.LogLevel == LogSeverity.Debug) - Logger.Debug($"TYPING_START: {channel.Path} ({user.Name})"); + Logger.Debug($"TYPING_START: {user.ToString(channel)}"); OnUserIsTypingUpdated(channel, user); user.UpdateActivity(); } } - else - Logger.Warning("TYPING_START referenced an unknown channel."); } break; @@ -970,7 +929,7 @@ namespace Discord if (user != null) { if (Config.LogLevel == LogSeverity.Debug) - Logger.Debug($"VOICE_STATE_UPDATE: {user.Path}"); + Logger.Debug($"VOICE_STATE_UPDATE: {user}"); var before = Config.EnablePreUpdateEvents ? user.Clone() : null; user.Update(data); //Logger.Verbose($"Voice Updated: {server.Name}/{user.Name}"); diff --git a/src/Discord.Net/EnumConverters.cs b/src/Discord.Net/EnumConverters.cs new file mode 100644 index 000000000..7448055f6 --- /dev/null +++ b/src/Discord.Net/EnumConverters.cs @@ -0,0 +1,42 @@ +using System; + +namespace Discord +{ + public static class EnumConverters + { + public static ChannelType ToChannelType(string value) + { + switch (value) + { + case "text": return ChannelType.Text; + case "voice": return ChannelType.Voice; + default: throw new ArgumentException("Unknown channel type", nameof(value)); + } + } + public static string ToString(ChannelType value) + { + if ((value & ChannelType.Text) != 0) return "text"; + if ((value & ChannelType.Voice) != 0) return "voice"; + throw new ArgumentException("Invalid channel tType", nameof(value)); + } + + public static PermissionTarget ToPermissionTarget(string value) + { + switch (value) + { + case "member": return PermissionTarget.User; + case "role": return PermissionTarget.Role; + default: throw new ArgumentException("Unknown permission target", nameof(value)); + } + } + public static string ToString(PermissionTarget value) + { + switch (value) + { + case PermissionTarget.User: return "member"; + case PermissionTarget.Role: return "role"; + default: throw new ArgumentException("Invalid permission target", nameof(value)); + } + } + } +} diff --git a/src/Discord.Net/Enums/ChannelType.cs b/src/Discord.Net/Enums/ChannelType.cs index 9ed49a701..77b0614ea 100644 --- a/src/Discord.Net/Enums/ChannelType.cs +++ b/src/Discord.Net/Enums/ChannelType.cs @@ -2,36 +2,12 @@ namespace Discord { - public class ChannelType : StringEnum, IEquatable - { - /// A text-only channel. - public static ChannelType Text { get; } = new ChannelType("text"); - /// A voice-only channel. - public static ChannelType Voice { get; } = new ChannelType("voice"); - - private ChannelType(string value) - : base(value) { } - - public static ChannelType FromString(string value) - { - switch (value) - { - case null: - return null; - case "text": - return Text; - case "voice": - return Voice; - default: - return new ChannelType(value); - } - } - - public static implicit operator ChannelType(string value) => FromString(value); - public static bool operator ==(ChannelType a, ChannelType b) => ((object)a == null && (object)b == null) || (a?.Equals(b) ?? false); - public static bool operator !=(ChannelType a, ChannelType b) => !(a == b); - public override int GetHashCode() => Value.GetHashCode(); - public override bool Equals(object obj) => (obj as ChannelType)?.Equals(this) ?? false; - public bool Equals(ChannelType type) => type != null && type.Value == Value; + [Flags] + public enum ChannelType : byte + { + Public = 0x01, + Private = 0x02, + Text = 0x10, + Voice = 0x20 } } diff --git a/src/Discord.Net/Enums/PermissionTarget.cs b/src/Discord.Net/Enums/PermissionTarget.cs index 38a70e013..d1381a5ec 100644 --- a/src/Discord.Net/Enums/PermissionTarget.cs +++ b/src/Discord.Net/Enums/PermissionTarget.cs @@ -1,35 +1,8 @@ namespace Discord { - public class PermissionTarget : StringEnum + public enum PermissionTarget : byte { - /// A text-only channel. - public static PermissionTarget Role { get; } = new PermissionTarget("role"); - /// A voice-only channel. - public static PermissionTarget User { get; } = new PermissionTarget("member"); - - private PermissionTarget(string value) - : base(value) { } - - public static PermissionTarget FromString(string value) - { - switch (value) - { - case null: - return null; - case "role": - return Role; - case "member": - return User; - default: - return new PermissionTarget(value); - } - } - - public static implicit operator PermissionTarget(string value) => FromString(value); - public static bool operator ==(PermissionTarget a, PermissionTarget b) => ((object)a == null && (object)b == null) || (a?.Equals(b) ?? false); - public static bool operator !=(PermissionTarget a, PermissionTarget b) => !(a == b); - public override int GetHashCode() => Value.GetHashCode(); - public override bool Equals(object obj) => (obj as PermissionTarget)?.Equals(this) ?? false; - public bool Equals(PermissionTarget type) => type != null && type.Value == Value; + User, + Role } } diff --git a/src/Discord.Net/Events/ChannelEventArgs.cs b/src/Discord.Net/Events/ChannelEventArgs.cs index 4aecf34f1..ac31a27f5 100644 --- a/src/Discord.Net/Events/ChannelEventArgs.cs +++ b/src/Discord.Net/Events/ChannelEventArgs.cs @@ -4,10 +4,8 @@ namespace Discord { public class ChannelEventArgs : EventArgs { - public Channel Channel { get; } + public IChannel Channel { get; } - public Server Server => Channel.Server; - - public ChannelEventArgs(Channel channel) { Channel = channel; } + public ChannelEventArgs(IChannel channel) { Channel = channel; } } } diff --git a/src/Discord.Net/Events/ChannelUpdatedEventArgs.cs b/src/Discord.Net/Events/ChannelUpdatedEventArgs.cs index fa8da98ea..138f2dd8e 100644 --- a/src/Discord.Net/Events/ChannelUpdatedEventArgs.cs +++ b/src/Discord.Net/Events/ChannelUpdatedEventArgs.cs @@ -4,12 +4,10 @@ namespace Discord { public class ChannelUpdatedEventArgs : EventArgs { - public Channel Before { get; } - public Channel After { get; } + public IChannel Before { get; } + public IChannel After { get; } - public Server Server => After.Server; - - public ChannelUpdatedEventArgs(Channel before, Channel after) + public ChannelUpdatedEventArgs(IChannel before, IChannel after) { Before = before; After = after; diff --git a/src/Discord.Net/Events/MessageEventArgs.cs b/src/Discord.Net/Events/MessageEventArgs.cs index 7edb23347..76c9455dc 100644 --- a/src/Discord.Net/Events/MessageEventArgs.cs +++ b/src/Discord.Net/Events/MessageEventArgs.cs @@ -7,7 +7,7 @@ namespace Discord public Message Message { get; } public User User => Message.User; - public Channel Channel => Message.Channel; + public ITextChannel Channel => Message.Channel; public Server Server => Message.Server; public MessageEventArgs(Message msg) { Message = msg; } diff --git a/src/Discord.Net/Events/MessageUpdatedEventArgs.cs b/src/Discord.Net/Events/MessageUpdatedEventArgs.cs index 849f234e1..1583dc981 100644 --- a/src/Discord.Net/Events/MessageUpdatedEventArgs.cs +++ b/src/Discord.Net/Events/MessageUpdatedEventArgs.cs @@ -8,7 +8,7 @@ namespace Discord public Message After { get; } public User User => After.User; - public Channel Channel => After.Channel; + public ITextChannel Channel => After.Channel; public Server Server => After.Server; public MessageUpdatedEventArgs(Message before, Message after) diff --git a/src/Discord.Net/Events/ChannelUserEventArgs.cs b/src/Discord.Net/Events/TypingEventArgs.cs similarity index 50% rename from src/Discord.Net/Events/ChannelUserEventArgs.cs rename to src/Discord.Net/Events/TypingEventArgs.cs index 819c7fcfa..73d47f688 100644 --- a/src/Discord.Net/Events/ChannelUserEventArgs.cs +++ b/src/Discord.Net/Events/TypingEventArgs.cs @@ -1,11 +1,11 @@ namespace Discord { - public class ChannelUserEventArgs + public class TypingEventArgs { - public Channel Channel { get; } + public ITextChannel Channel { get; } public User User { get; } - public ChannelUserEventArgs(Channel channel, User user) + public TypingEventArgs(ITextChannel channel, User user) { Channel = channel; User = user; diff --git a/src/Discord.Net/IModel.cs b/src/Discord.Net/IModel.cs new file mode 100644 index 000000000..8a86ceb3d --- /dev/null +++ b/src/Discord.Net/IModel.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public interface IModel + { + ulong Id { get; } + + Task Save(); + } +} diff --git a/src/Discord.Net/InternalExtensions.cs b/src/Discord.Net/InternalExtensions.cs index be9d90b3f..3dec1c7bf 100644 --- a/src/Discord.Net/InternalExtensions.cs +++ b/src/Discord.Net/InternalExtensions.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; using System.Runtime.CompilerServices; namespace Discord @@ -63,85 +61,6 @@ namespace Discord } } - public static IEnumerable Find(this IEnumerable channels, string name, ChannelType type = null, bool exactMatch = false) - { - //Search by name - var query = channels - .Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); - - if (!exactMatch) - { - if (name.Length >= 2 && name[0] == '<' && name[1] == '#' && name[name.Length - 1] == '>') //Search by raw mention - { - ulong id; - if (name.Substring(2, name.Length - 3).TryToId(out id)) - { - var channel = channels.Where(x => x.Id == id).FirstOrDefault(); - if (channel != null) - query = query.Concat(new Channel[] { channel }); - } - } - if (name.Length >= 1 && name[0] == '#' && (type == null || type == ChannelType.Text)) //Search by clean mention - { - string name2 = name.Substring(1); - query = query.Concat(channels - .Where(x => x.Type == ChannelType.Text && string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase))); - } - } - - if (type != null) - query = query.Where(x => x.Type == type); - return query; - } - - public static IEnumerable Find(this IEnumerable users, - string name, ushort? discriminator = null, bool exactMatch = false) - { - //Search by name - var query = users - .Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); - - if (!exactMatch) - { - if (name.Length >= 2 && name[0] == '<' && name[1] == '@' && name[name.Length - 1] == '>') //Search by raw mention - { - ulong id; - if (name.Substring(2, name.Length - 3).TryToId(out id)) - { - var user = users.Where(x => x.Id == id).FirstOrDefault(); - if (user != null) - query = query.Concat(new User[] { user }); - } - } - if (name.Length >= 1 && name[0] == '@') //Search by clean mention - { - string name2 = name.Substring(1); - query = query.Concat(users - .Where(x => string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase))); - } - } - - if (discriminator != null) - query = query.Where(x => x.Discriminator == discriminator.Value); - return query; - } - - public static IEnumerable Find(this IEnumerable roles, string name, bool exactMatch = false) - { - // if (name.StartsWith("@")) - // { - // string name2 = name.Substring(1); - // return _roles.Where(x => x.Server.Id == server.Id && - // string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) || - // string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase)); - // } - // else - return roles.Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); - } - - public static IEnumerable Find(this IEnumerable servers, string name, bool exactMatch = false) - => servers.Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); - public static string Base64(this Stream stream, ImageType type, string existingId) { if (type == ImageType.None) diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs index 99cfd6c3f..c1a0948b0 100644 --- a/src/Discord.Net/MessageQueue.cs +++ b/src/Discord.Net/MessageQueue.cs @@ -57,12 +57,12 @@ namespace Discord.Net _pendingSendsByNonce = new ConcurrentDictionary(); } - internal Message QueueSend(Channel channel, string text, bool isTTS) + internal Message QueueSend(ITextChannel channel, string text, bool isTTS) { - Message msg = new Message(0, channel, channel.IsPrivate ? channel.Client.PrivateUser : channel.Server.CurrentUser); + Message msg = new Message(0, channel, (channel as Channel).CurrentUser); msg.IsTTS = isTTS; msg.RawText = text; - msg.Text = msg.Resolve(text); + msg.Text = Message.ResolveMentions(msg.Channel, msg.Text); msg.Nonce = GenerateNonce(); if (_pendingSendsByNonce.TryAdd(msg.Nonce, text)) { @@ -111,110 +111,99 @@ namespace Discord.Net { return Task.Run(async () => { - try + while (!cancelToken.IsCancellationRequested) { - while (!cancelToken.IsCancellationRequested) + Message msg; + while (_pendingSends.TryDequeue(out msg)) { - Message msg; - while (_pendingSends.TryDequeue(out msg)) + DecrementCount(); + string text; + if (_pendingSendsByNonce.TryRemove(msg.Nonce, out text)) //If it was deleted from queue, this will fail { - DecrementCount(); - string text; - if (_pendingSendsByNonce.TryRemove(msg.Nonce, out text)) //If it was deleted from queue, this will fail + try { - try + //msg.RawText = text; + //msg.Text = Message.ResolveMentions(msg.Channel, text); + var request = new SendMessageRequest(msg.Channel.Id) { - msg.RawText = text; - msg.Text = msg.Resolve(text); - var request = new SendMessageRequest(msg.Channel.Id) - { - Content = msg.RawText, - Nonce = msg.Nonce.ToString(), - IsTTS = msg.IsTTS - }; - var response = await _rest.Send(request).ConfigureAwait(false); - msg.State = MessageState.Normal; - msg.Id = response.Id; - msg.Update(response); - } - catch (Exception ex) - { - msg.State = MessageState.Failed; - _logger.Error($"Failed to send message to {msg.Channel.Path}", ex); - } + Content = text, + Nonce = msg.Nonce.ToString(), + IsTTS = msg.IsTTS + }; + var response = await _rest.Send(request).ConfigureAwait(false); + msg.Id = response.Id; + msg.State = MessageState.Normal; + msg.Update(response); + } + catch (Exception ex) + { + msg.State = MessageState.Failed; + _logger.Error($"Failed to send message to {msg.Channel}", ex); } } - await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } + await Task.Delay(DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } - catch (OperationCanceledException) { } }); } private Task RunEditQueue(CancellationToken cancelToken) { return Task.Run(async () => { - try + while (!cancelToken.IsCancellationRequested) { - while (!cancelToken.IsCancellationRequested) + MessageEdit edit; + while (_pendingEdits.TryPeek(out edit) && edit.Message.State != MessageState.Queued) { - MessageEdit edit; - while (_pendingEdits.TryPeek(out edit) && edit.Message.State != MessageState.Queued) + if (_pendingEdits.TryDequeue(out edit)) { - if (_pendingEdits.TryDequeue(out edit)) + DecrementCount(); + if (edit.Message.State == MessageState.Normal) { - DecrementCount(); - if (edit.Message.State == MessageState.Normal) + try { - try + var request = new UpdateMessageRequest(edit.Message.Channel.Id, edit.Message.Id) { - var request = new UpdateMessageRequest(edit.Message.Channel.Id, edit.Message.Id) - { - Content = edit.NewText - }; - await _rest.Send(request).ConfigureAwait(false); - } - catch (Exception ex) { _logger.Error($"Failed to edit message {edit.Message.Path}", ex); } + Content = edit.NewText + }; + await _rest.Send(request).ConfigureAwait(false); } + catch (Exception ex) { _logger.Error($"Failed to edit message {edit.Message}", ex); } } } - await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } + await Task.Delay(DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } - catch (OperationCanceledException) { } }); } private Task RunDeleteQueue(CancellationToken cancelToken) { return Task.Run(async () => { - try + while (!cancelToken.IsCancellationRequested) { - while (!cancelToken.IsCancellationRequested) + Message msg; + while (_pendingDeletes.TryPeek(out msg) && msg.State != MessageState.Queued) { - Message msg; - while (_pendingDeletes.TryPeek(out msg) && msg.State != MessageState.Queued) + if (_pendingDeletes.TryDequeue(out msg)) { - if (_pendingDeletes.TryDequeue(out msg)) + DecrementCount(); + if (msg.State == MessageState.Normal) { - DecrementCount(); - if (msg.State == MessageState.Normal) + try { - try - { - var request = new DeleteMessageRequest(msg.Channel.Id, msg.Id); - await _rest.Send(request).ConfigureAwait(false); - } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } //Ignore - catch (Exception ex) { _logger.Error($"Failed to delete message {msg.Path}", ex); } + var request = new DeleteMessageRequest(msg.Channel.Id, msg.Id); + await _rest.Send(request).ConfigureAwait(false); + msg.State = MessageState.Deleted; } + catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } //Ignore + catch (Exception ex) { _logger.Error($"Failed to delete message {msg}", ex); } } } - - await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } + + await Task.Delay(Discord.DiscordConfig.MessageQueueInterval).ConfigureAwait(false); } - catch (OperationCanceledException) { } }); } diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index bd2b4a9cc..5894ade45 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -1,602 +1,55 @@ -using Discord.API.Client; -using Discord.API.Client.Rest; -using Discord.Net; -using System; -using System.Collections.Concurrent; +using APIChannel = Discord.API.Client.Channel; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using APIChannel = Discord.API.Client.Channel; namespace Discord { - public class Channel : IMentionable + public abstract class Channel : IChannel { - private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); - - private struct Member - { - public User User { get; } - public ChannelPermissions Permissions { get; } - - public Member(User user, ChannelPermissions permissions) - { - User = user; - Permissions = permissions; - } - } - - public class PermissionOverwrite + /// An entry in a public channel's permissions that gives or takes permissions from a specific role or user. + public class PermissionRule { + /// The type of object TargetId is referring to. public PermissionTarget TargetType { get; } + /// The Id of an object, whos type is specified by TargetType, that is the target of permissions being added or taken away. public ulong TargetId { get; } - public ChannelPermissionOverrides Permissions { get; } + /// A collection of permissions that are added or taken away from the target. + public ChannelTriStatePermissions Permissions { get; } - internal PermissionOverwrite(PermissionTarget targetType, ulong targetId, uint allow, uint deny) + internal PermissionRule(PermissionTarget targetType, ulong targetId, uint allow, uint deny) { TargetType = targetType; TargetId = targetId; - Permissions = new ChannelPermissionOverrides(allow, deny); + Permissions = new ChannelTriStatePermissions(allow, deny); } } - private readonly ConcurrentDictionary _users; - private readonly ConcurrentDictionary _messages; - private Dictionary _permissionOverwrites; - - public DiscordClient Client { get; } - /// Gets the unique identifier for this channel. public ulong Id { get; } - /// Gets the server owning this channel, if this is a public chat. - public Server Server { get; } - /// Gets the target user, if this is a private chat. - public User Recipient { get; } - - /// Gets the name of this channel. - public string Name { get; private set; } - /// Gets the topic of this channel. - public string Topic { get; private set; } - /// Gets the position of this channel relative to other channels in this server. - public int Position { get; private set; } - /// Gets the type of this channel). - public ChannelType Type { get; private set; } - - /// Gets the path to this object. - internal string Path => $"{Server?.Name ?? "[Private]"}/{Name}"; - /// Gets true if this is a private chat with another user. - public bool IsPrivate => Recipient != null; - /// Gets the string used to mention this channel. - public string Mention => $"<#{Id}>"; - /// Gets a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. - public IEnumerable Messages => _messages?.Values ?? Enumerable.Empty(); - /// Gets a collection of all custom permissions used for this channel. - public IEnumerable PermissionOverwrites => _permissionOverwrites.Select(x => x.Value); - - /// Gets a collection of all users with read access to this channel. - public IEnumerable Users - { - get - { - if (Client.Config.UsePermissionsCache) - { - if (IsPrivate) - return new User[] { Client.PrivateUser, Recipient }; - else if (Type == ChannelType.Text) - return _users.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); - else if (Type == ChannelType.Voice) - return _users.Values.Select(x => x.User).Where(x => x.VoiceChannel == this); - } - else - { - if (IsPrivate) - return new User[] { Client.PrivateUser, Recipient }; - else if (Type == ChannelType.Text) - { - ChannelPermissions perms = new ChannelPermissions(); - return Server.Users.Where(x => - { - UpdatePermissions(x, ref perms); - return perms.ReadMessages == true; - }); - } - else if (Type == ChannelType.Voice) - return Server.Users.Where(x => x.VoiceChannel == this); - } - return Enumerable.Empty(); - } - } - - internal Channel(DiscordClient client, ulong id, Server server) - : this(client, id) - { - Server = server; - if (client.Config.UsePermissionsCache) - _users = new ConcurrentDictionary(2, (int)(server.UserCount * 1.05)); - } - internal Channel(DiscordClient client, ulong id, User recipient) - : this(client, id) - { - Recipient = recipient; - Type = ChannelType.Text; //Discord doesn't give us a type for private channels - } - private Channel(DiscordClient client, ulong id) - { - Client = client; - Id = id; - - _permissionOverwrites = new Dictionary(); - if (client.Config.MessageCacheSize > 0) - _messages = new ConcurrentDictionary(2, (int)(client.Config.MessageCacheSize * 1.05)); - } - - internal void Update(APIChannel model) - { - if (!IsPrivate && model.Name != null) - Name = model.Name; - if (model.Type != null) - Type = model.Type; - if (model.Position != null) - Position = model.Position.Value; - if (model.Topic != null) - Topic = model.Topic; - if (model.Recipient != null) - { - Recipient.Update(model.Recipient); - Name = $"@{Recipient}"; - } - - if (model.PermissionOverwrites != null) - { - _permissionOverwrites = model.PermissionOverwrites - .Select(x => new PermissionOverwrite(PermissionTarget.FromString(x.Type), x.Id, x.Allow, x.Deny)) - .ToDictionary(x => x.TargetId); - UpdatePermissions(); - } - } - - /// Edits this channel, changing only non-null attributes. - public async Task Edit(string name = null, string topic = null, int? position = null) - { - if (name != null || topic != null) - { - var request = new UpdateChannelRequest(Id) - { - Name = name ?? Name, - Topic = topic ?? Topic, - Position = Position - }; - await Client.ClientAPI.Send(request).ConfigureAwait(false); - } - if (position != null) - { - Channel[] channels = Server.AllChannels.Where(x => x.Type == Type).OrderBy(x => x.Position).ToArray(); - int oldPos = Array.IndexOf(channels, this); - var newPosChannel = channels.Where(x => x.Position > position).FirstOrDefault(); - int newPos = (newPosChannel != null ? Array.IndexOf(channels, newPosChannel) : channels.Length) - 1; - if (newPos < 0) - newPos = 0; - int minPos; + public abstract DiscordClient Client { get; } + /// Gets the type of this channel. + public abstract ChannelType Type { get; } + public bool IsText => (Type & ChannelType.Text) != 0; + public bool IsVoice => (Type & ChannelType.Voice) != 0; + public bool IsPrivate => (Type & ChannelType.Private) != 0; + public bool IsPublic => (Type & ChannelType.Public) != 0; - if (oldPos < newPos) //Moving Down - { - minPos = oldPos; - for (int i = oldPos; i < newPos; i++) - channels[i] = channels[i + 1]; - channels[newPos] = this; - } - else //(oldPos > newPos) Moving Up - { - minPos = newPos; - for (int i = oldPos; i > newPos; i--) - channels[i] = channels[i - 1]; - channels[newPos] = this; - } - Channel after = minPos > 0 ? channels.Skip(minPos - 1).FirstOrDefault() : null; - await Server.ReorderChannels(channels.Skip(minPos), after).ConfigureAwait(false); - } - } - - public async Task Delete() - { - try { await Client.ClientAPI.Send(new DeleteChannelRequest(Id)).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } - } + public abstract User CurrentUser { get; } + /// Gets a collection of all users in this channel. + public abstract IEnumerable Users { get; } - #region Invites - /// Gets all active (non-expired) invites to this server. - public async Task> GetInvites() - => (await Server.GetInvites().ConfigureAwait(false)).Where(x => x.Channel.Id == Id); + internal abstract MessageManager MessageManager { get; } + internal abstract PermissionManager PermissionManager { get; } - /// Creates a new invite to this channel. - /// Time (in seconds) until the invite expires. Set to null to never expire. - /// The max amount of times this invite may be used. Set to null to have unlimited uses. - /// If true, a user accepting this invite will be kicked from the server after closing their client. - /// If true, creates a human-readable link. Not supported if maxAge is set to null. - public async Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) + protected Channel(ulong id) { - if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge)); - if (maxUses < 0) throw new ArgumentOutOfRangeException(nameof(maxUses)); - - var request = new CreateInviteRequest(Id) - { - MaxAge = maxAge ?? 0, - MaxUses = maxUses ?? 0, - IsTemporary = tempMembership, - WithXkcdPass = withXkcd - }; - - var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); - var invite = new Invite(Client, response.Code, response.XkcdPass); - return invite; - } - #endregion - - #region Messages - internal Message AddMessage(ulong id, User user, DateTime timestamp) - { - Message message = new Message(id, this, user); - message.State = MessageState.Normal; - var cacheLength = Client.Config.MessageCacheSize; - if (cacheLength > 0) - { - var oldestIds = _messages - .Where(x => x.Value.Timestamp < timestamp) - .Select(x => x.Key).OrderBy(x => x) - .Take(_messages.Count - cacheLength); - Message removed; - foreach (var removeId in oldestIds) - _messages.TryRemove(removeId, out removed); - return _messages.GetOrAdd(message.Id, message); - } - return message; - } - internal Message RemoveMessage(ulong id) - { - if (Client.Config.MessageCacheSize > 0) - { - Message msg; - if (_messages.TryRemove(id, out msg)) - return msg; - } - return new Message(id, this, null); - } - - public Message GetMessage(ulong id) - => GetMessage(id, null); - internal Message GetMessage(ulong id, ulong? userId) - { - if (Client.Config.MessageCacheSize > 0) - { - Message result; - if (_messages.TryGetValue(id, out result)) - return result; - } - return new Message(id, this, userId != null ? GetUserFast(userId.Value) : null); - } - - public async Task DownloadMessages(int limit = 100, ulong? relativeMessageId = null, - Relative relativeDir = Relative.Before, bool useCache = true) - { - if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); - if (limit == 0 || Type != ChannelType.Text) return new Message[0]; - - try - { - var request = new GetMessagesRequest(Id) - { - Limit = limit, - RelativeDir = relativeMessageId.HasValue ? relativeDir == Relative.Before ? "before" : "after" : null, - RelativeId = relativeMessageId ?? 0 - }; - var msgs = await Client.ClientAPI.Send(request).ConfigureAwait(false); - return msgs.Select(x => - { - Message msg = null; - if (useCache) - { - msg = AddMessage(x.Id, GetUserFast(x.Author.Id), x.Timestamp.Value); - var user = msg.User; - if (user != null) - user.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp); - } - else - msg = new Message(x.Id, this, GetUserFast(x.Author.Id)); - msg.Update(x); - return msg; - }) - .ToArray(); - } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.Forbidden) - { - return new Message[0]; - } - } - - /// Returns all members of this channel with the specified name. - /// Name formats supported: Name, @Name and <@Id>. Search is case-insensitive if exactMatch is false. - public IEnumerable FindUsers(string name, bool exactMatch = false) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - return Users.Find(name, exactMatch: exactMatch); - } - - public Task SendMessage(string text) => SendMessageInternal(text, false); - public Task SendTTSMessage(string text) => SendMessageInternal(text, true); - private Task SendMessageInternal(string text, bool isTTS) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (text == "") throw new ArgumentException("Value cannot be blank", nameof(text)); - if (text.Length > DiscordConfig.MaxMessageSize) - throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less."); - return Task.FromResult(Client.MessageQueue.QueueSend(this, text, isTTS)); - } - - public async Task SendFile(string filePath) - { - using (var stream = File.OpenRead(filePath)) - return await SendFile(System.IO.Path.GetFileName(filePath), stream).ConfigureAwait(false); - } - public async Task SendFile(string filename, Stream stream) - { - if (filename == null) throw new ArgumentNullException(nameof(filename)); - if (stream == null) throw new ArgumentNullException(nameof(stream)); - - var request = new SendFileRequest(Id) - { - Filename = filename, - Stream = stream - }; - var model = await Client.ClientAPI.Send(request).ConfigureAwait(false); - - var msg = AddMessage(model.Id, IsPrivate ? Client.PrivateUser : Server.CurrentUser, model.Timestamp.Value); - msg.Update(model); - return msg; - } - - public Task SendIsTyping() => Client.ClientAPI.Send(new SendIsTypingRequest(Id)); - #endregion - - #region Permissions - internal void UpdatePermissions() - { - if (!Client.Config.UsePermissionsCache) return; - - foreach (var pair in _users) - { - var member = pair.Value; - var perms = member.Permissions; - if (UpdatePermissions(member.User, ref perms)) - _users[pair.Key] = new Member(member.User, perms); - } - } - internal void UpdatePermissions(User user) - { - if (!Client.Config.UsePermissionsCache) return; - - Member member; - if (_users.TryGetValue(user.Id, out member)) - { - var perms = member.Permissions; - if (UpdatePermissions(member.User, ref perms)) - _users[user.Id] = new Member(member.User, perms); - } - } - internal bool UpdatePermissions(User user, ref ChannelPermissions permissions) - { - uint newPermissions = 0; - var server = Server; - - //Load the mask of all permissions supported by this channel type - var mask = ChannelPermissions.All(this).RawValue; - - if (server != null) - { - //Start with this user's server permissions - newPermissions = server.GetPermissions(user).RawValue; - - if (IsPrivate || user == Server.Owner) - newPermissions = mask; //Owners always have all permissions - else - { - var channelOverwrites = PermissionOverwrites; - - var roles = user.Roles; - foreach (var denyRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Permissions.DenyValue != 0 && roles.Any(y => y.Id == x.TargetId))) - newPermissions &= ~denyRole.Permissions.DenyValue; - foreach (var allowRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Permissions.AllowValue != 0 && roles.Any(y => y.Id == x.TargetId))) - newPermissions |= allowRole.Permissions.AllowValue; - foreach (var denyUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id && x.Permissions.DenyValue != 0)) - newPermissions &= ~denyUser.Permissions.DenyValue; - foreach (var allowUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id && x.Permissions.AllowValue != 0)) - newPermissions |= allowUser.Permissions.AllowValue; - - if (newPermissions.HasBit((byte)PermissionBits.ManageRolesOrPermissions)) - newPermissions = mask; //ManageRolesOrPermissions gives all permisions - else if (Type == ChannelType.Text && !newPermissions.HasBit((byte)PermissionBits.ReadMessages)) - newPermissions = 0; //No read permission on a text channel removes all other permissions - else if (Type == ChannelType.Voice && !newPermissions.HasBit((byte)PermissionBits.Connect)) - newPermissions = 0; //No connect permissions on a voice channel removes all other permissions - else - newPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from serverPerms, for example) - } - } - else - newPermissions = mask; //Private messages always have all permissions - - if (newPermissions != permissions.RawValue) - { - permissions = new ChannelPermissions(newPermissions); - return true; - } - return false; - } - internal ChannelPermissions GetPermissions(User user) - { - if (Client.Config.UsePermissionsCache) - { - Member member; - if (_users.TryGetValue(user.Id, out member)) - return member.Permissions; - else - return ChannelPermissions.None; - } - else - { - ChannelPermissions perms = new ChannelPermissions(); - UpdatePermissions(user, ref perms); - return perms; - } - } - - public ChannelPermissionOverrides GetPermissionsRule(User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - - return PermissionOverwrites - .Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id) - .Select(x => x.Permissions) - .FirstOrDefault(); - } - public ChannelPermissionOverrides GetPermissionsRule(Role role) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - - return PermissionOverwrites - .Where(x => x.TargetType == PermissionTarget.Role && x.TargetId == role.Id) - .Select(x => x.Permissions) - .FirstOrDefault(); - } - - public Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - - return AddPermissionsRule(user.Id, PermissionTarget.User, allow.RawValue, deny.RawValue); - } - public Task AddPermissionsRule(User user, ChannelPermissionOverrides permissions) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - - return AddPermissionsRule(user.Id, PermissionTarget.User, permissions.AllowValue, permissions.DenyValue); - } - public Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - - return AddPermissionsRule(role.Id, PermissionTarget.Role, allow.RawValue, deny.RawValue); - } - public Task AddPermissionsRule(Role role, ChannelPermissionOverrides permissions) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - - return AddPermissionsRule(role.Id, PermissionTarget.Role, permissions.AllowValue, permissions.DenyValue); - } - private Task AddPermissionsRule(ulong targetId, PermissionTarget targetType, uint allow, uint deny) - { - var request = new AddChannelPermissionsRequest(Id) - { - TargetId = targetId, - TargetType = targetType.Value, - Allow = allow, - Deny = deny - }; - return Client.ClientAPI.Send(request); - } - - public Task RemovePermissionsRule(User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return RemovePermissionsRule(user.Id, PermissionTarget.User); - } - public Task RemovePermissionsRule(Role role) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - return RemovePermissionsRule(role.Id, PermissionTarget.Role); - } - private async Task RemovePermissionsRule(ulong userOrRoleId, PermissionTarget targetType) - { - try - { - var perms = PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).FirstOrDefault(); - await Client.ClientAPI.Send(new RemoveChannelPermissionsRequest(Id, userOrRoleId)).ConfigureAwait(false); - } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } - } - #endregion - - #region Users - internal void AddUser(User user) - { - if (!Client.Config.UsePermissionsCache) return; - - var perms = new ChannelPermissions(); - UpdatePermissions(user, ref perms); - var member = new Member(user, ChannelPermissions.None); - _users[user.Id] = new Member(user, ChannelPermissions.None); + Id = id; } - internal void RemoveUser(ulong id) - { - if (!Client.Config.UsePermissionsCache) return; - Member ignored; - _users.TryRemove(id, out ignored); - } - public User GetUser(ulong id) - { - if (IsPrivate) - { - if (id == Recipient.Id) - return Recipient; - else if (id == Client.PrivateUser.Id) - return Client.PrivateUser; - } - else if (!Client.Config.UsePermissionsCache) - { - var user = Server.GetUser(id); - if (user != null) - { - ChannelPermissions perms = new ChannelPermissions(); - UpdatePermissions(user, ref perms); - if (perms.ReadMessages) - return user; - } - } - else - { - Member result; - if (_users.TryGetValue(id, out result)) - return result.User; - } - return null; - } - internal User GetUserFast(ulong id) - { - //Can return users not in this channel, but that's usually okay - if (IsPrivate) - { - if (id == Recipient.Id) - return Recipient; - else if (id == Client.PrivateUser.Id) - return Client.PrivateUser; - } - else - return Server.GetUser(id); - return null; - } - #endregion + internal abstract void Update(APIChannel model); - internal Channel Clone() - { - var result = new Channel(); - _cloner(this, result); - return result; - } - private Channel() { } + internal abstract User GetUser(ulong id); - public override string ToString() => Name ?? Id.ToIdString(); + internal abstract Channel Clone(); } } diff --git a/src/Discord.Net/Models/Color.cs b/src/Discord.Net/Models/Color.cs index 3555c5d8d..325aa2f39 100644 --- a/src/Discord.Net/Models/Color.cs +++ b/src/Discord.Net/Models/Color.cs @@ -3,28 +3,6 @@ public class Color { public static readonly Color Default = new Color(0); - - public static readonly Color Teal = new Color(0x1ABC9C); - public static readonly Color DarkTeal = new Color(0x11806A); - public static readonly Color Green = new Color(0x2ECC71); - public static readonly Color DarkGreen = new Color(0x1F8B4C); - public static readonly Color Blue = new Color(0x3498DB); - public static readonly Color DarkBlue = new Color(0x206694); - public static readonly Color Purple = new Color(0x9B59B6); - public static readonly Color DarkPurple = new Color(0x71368A); - public static readonly Color Magenta = new Color(0xE91E63); - public static readonly Color DarkMagenta = new Color(0xAD1457); - public static readonly Color Gold = new Color(0xF1C40F); - public static readonly Color DarkGold = new Color(0xC27C0E); - public static readonly Color Orange = new Color(0xE67E22); - public static readonly Color DarkOrange = new Color(0xA84300); - public static readonly Color Red = new Color(0xE74C3C); - public static readonly Color DarkRed = new Color(0x992D22); - - public static readonly Color LighterGrey = new Color(0x95A5A6); - public static readonly Color DarkGrey = new Color(0x607D8B); - public static readonly Color LightGrey = new Color(0x979C9F); - public static readonly Color DarkerGrey = new Color(0x546E7A); public uint RawValue { get; } diff --git a/src/Discord.Net/Models/IChannel.cs b/src/Discord.Net/Models/IChannel.cs new file mode 100644 index 000000000..f39678abb --- /dev/null +++ b/src/Discord.Net/Models/IChannel.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace Discord +{ + public interface IChannel + { + /// Gets the unique identifier for this channel. + ulong Id { get; } + DiscordClient Client { get; } + + /// Gets the type of this channel. + ChannelType Type { get; } + bool IsText { get; } + bool IsVoice { get; } + bool IsPrivate { get; } + bool IsPublic { get; } + + /// Gets a collection of all users in this channel. + IEnumerable Users { get; } + } +} diff --git a/src/Discord.Net/Models/IPrivateChannel.cs b/src/Discord.Net/Models/IPrivateChannel.cs new file mode 100644 index 000000000..02c4c1b9c --- /dev/null +++ b/src/Discord.Net/Models/IPrivateChannel.cs @@ -0,0 +1,6 @@ +namespace Discord +{ + public interface IPrivateChannel : IChannel + { + } +} diff --git a/src/Discord.Net/Models/IPublicChannel.cs b/src/Discord.Net/Models/IPublicChannel.cs new file mode 100644 index 000000000..32b8c610e --- /dev/null +++ b/src/Discord.Net/Models/IPublicChannel.cs @@ -0,0 +1,6 @@ +namespace Discord +{ + public interface IPublicChannel : IChannel + { + } +} diff --git a/src/Discord.Net/Models/ITextChannel.cs b/src/Discord.Net/Models/ITextChannel.cs new file mode 100644 index 000000000..af665ab0f --- /dev/null +++ b/src/Discord.Net/Models/ITextChannel.cs @@ -0,0 +1,17 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public interface ITextChannel : IChannel + { + Message GetMessage(ulong id); + Task DownloadMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before); + + Task SendMessage(string text, bool isTTS = false); + Task SendFile(string filePath); + Task SendFile(string filename, Stream stream); + + Task SendIsTyping(); + } +} diff --git a/src/Discord.Net/Models/IVoiceChannel.cs b/src/Discord.Net/Models/IVoiceChannel.cs new file mode 100644 index 000000000..7c3f2c194 --- /dev/null +++ b/src/Discord.Net/Models/IVoiceChannel.cs @@ -0,0 +1,6 @@ +namespace Discord +{ + public interface IVoiceChannel : IChannel + { + } +} diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs index a6a0407b1..f8ee235ba 100644 --- a/src/Discord.Net/Models/Invite.cs +++ b/src/Discord.Net/Models/Invite.cs @@ -1,10 +1,10 @@ -using Discord.API.Client; +using APIInvite = Discord.API.Client.Invite; +using Discord.API.Client; using Discord.API.Client.Rest; using Discord.Net; using System; using System.Net; using System.Threading.Tasks; -using APIInvite = Discord.API.Client.Invite; namespace Discord { @@ -84,9 +84,7 @@ namespace Discord public bool IsTemporary { get; private set; } /// Gets when this invite was created. public DateTime CreatedAt { get; private set; } - - /// Gets the path to this object. - internal string Path => $"{Server?.Name ?? "[Private]"}/{Code}"; + /// Returns a URL for this invite using XkcdCode if available or Id if not. public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; @@ -138,6 +136,6 @@ namespace Discord } private Invite() { } //Used for cloning - public override string ToString() => XkcdCode ?? Code; + public override string ToString() => $"{Server}/{XkcdCode ?? Code}"; } } diff --git a/src/Discord.Net/Models/Managers/MessageManager.cs b/src/Discord.Net/Models/Managers/MessageManager.cs new file mode 100644 index 000000000..88a95dc48 --- /dev/null +++ b/src/Discord.Net/Models/Managers/MessageManager.cs @@ -0,0 +1,154 @@ +using Discord.API.Client.Rest; +using Discord.Net; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace Discord +{ + internal class MessageManager : IEnumerable + { + private readonly ITextChannel _channel; + private readonly int _size; + private readonly ConcurrentDictionary _messages; + private readonly ConcurrentQueue _orderedMessages; + + public MessageManager(ITextChannel channel, int size = 0) + { + _channel = channel; + _size = size; + if (size > 0) + { + _messages = new ConcurrentDictionary(2, size); + _orderedMessages = new ConcurrentQueue(); + } + } + + internal Message Add(ulong id, User user, DateTime timestamp) + { + Message message = new Message(id, _channel, user); + message.State = MessageState.Normal; + if (_size > 0) + { + if (_messages.TryAdd(id, message)) + { + _orderedMessages.Enqueue(id); + + ulong msgId; + while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId)) + { + Message msg; + if (_messages.TryRemove(msgId, out msg)) + msg.State = MessageState.Detached; + } + } + } + return message; + } + internal Message Remove(ulong id) + { + if (_size > 0) + { + Message msg; + if (_messages.TryRemove(id, out msg)) + return msg; + } + return new Message(id, _channel, null) { State = MessageState.Deleted }; + } + + public Message Get(ulong id, ulong? userId = null) + { + if (_messages != null) + { + Message result; + if (_messages.TryGetValue(id, out result)) + return result; + } + return new Message(id, _channel, userId != null ? (_channel as Channel).GetUser(userId.Value) : null) { State = MessageState.Detached }; + } + + public async Task Download(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) + { + if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); + if (limit == 0) return new Message[0]; + + try + { + var request = new GetMessagesRequest(_channel.Id) + { + Limit = limit, + RelativeDir = relativeMessageId.HasValue ? relativeDir == Relative.Before ? "before" : "after" : null, + RelativeId = relativeMessageId ?? 0 + }; + var msgs = await _channel.Client.ClientAPI.Send(request).ConfigureAwait(false); + var server = (_channel as PublicChannel)?.Server; + + return msgs.Select(x => + { + Message msg = null; + ulong id = x.Author.Id; + var user = server?.GetUser(id) ?? (_channel as Channel).GetUser(id); + /*if (useCache) + { + msg = Add(x.Id, user, x.Timestamp.Value); + if (user != null) + user.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp); + } + else*/ + msg = new Message(x.Id, _channel, user); + msg.Update(x); + return msg; + }).ToArray(); + } + catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.Forbidden) + { + return new Message[0]; + } + } + + public Task Send(string text, bool isTTS) + { + if (text == "") throw new ArgumentException("Value cannot be blank", nameof(text)); + if (text.Length > DiscordConfig.MaxMessageSize) + throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less."); + return Task.FromResult(_channel.Client.MessageQueue.QueueSend(_channel, text, isTTS)); + } + public async Task SendFile(string filePath) + { + using (var stream = File.OpenRead(filePath)) + return await SendFile(Path.GetFileName(filePath), stream).ConfigureAwait(false); + } + public async Task SendFile(string filename, Stream stream) + { + var request = new SendFileRequest(_channel.Id) + { + Filename = filename, + Stream = stream + }; + var model = await _channel.Client.ClientAPI.Send(request).ConfigureAwait(false); + + var msg = Add(model.Id, (_channel as Channel).CurrentUser, model.Timestamp.Value); + msg.Update(model); + return msg; + } + + public IEnumerator GetEnumerator() + { + return _orderedMessages + .Select(x => + { + Message msg; + if (_messages.TryGetValue(x, out msg)) + return msg; + return null; + }) + .Where(x => x != null).GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Discord.Net/Models/Managers/PermissionManager.cs b/src/Discord.Net/Models/Managers/PermissionManager.cs new file mode 100644 index 000000000..4b8eb5c8c --- /dev/null +++ b/src/Discord.Net/Models/Managers/PermissionManager.cs @@ -0,0 +1,223 @@ +using Discord.API.Client.Rest; +using Discord.Net; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using APIChannel = Discord.API.Client.Channel; + +namespace Discord +{ + internal class PermissionManager + { + public struct Member + { + public User User { get; } + public ChannelPermissions Permissions { get; } + + public Member(User user, ChannelPermissions permissions) + { + User = user; + Permissions = permissions; + } + } + + private readonly PublicChannel _channel; + private readonly ConcurrentDictionary _users; + private Dictionary _rules; + + public IEnumerable Users => _users.Select(x => x.Value); + public IEnumerable Rules => _rules.Values; + + public PermissionManager(PublicChannel channel, APIChannel model, int initialSize = -1) + { + _channel = channel; + if (initialSize >= 0) + _users = new ConcurrentDictionary(2, initialSize); + Update(model); + } + + public void Update(APIChannel model) + { + _rules = model.PermissionOverwrites + .Select(x => new Channel.PermissionRule(EnumConverters.ToPermissionTarget(x.Type), x.Id, x.Allow, x.Deny)) + .ToDictionary(x => x.TargetId); + UpdatePermissions(); + } + + public ChannelTriStatePermissions? GetOverwrite(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + Channel.PermissionRule rule; + if (_rules.TryGetValue(user.Id, out rule)) + return rule.Permissions; + return null; + } + public ChannelTriStatePermissions? GetOverwrite(Role role) + { + if (role == null) throw new ArgumentNullException(nameof(role)); + + Channel.PermissionRule rule; + if (_rules.TryGetValue(role.Id, out rule)) + return rule.Permissions; + return null; + } + public Task AddOrUpdateOverwrite(User user, ChannelTriStatePermissions permissions) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + return AddOrUpdateOverwrite(user.Id, PermissionTarget.User, permissions); + } + public Task AddOrUpdateOverwrite(Role role, ChannelTriStatePermissions permissions) + { + if (role == null) throw new ArgumentNullException(nameof(role)); + return AddOrUpdateOverwrite(role.Id, PermissionTarget.Role, permissions); + } + private Task AddOrUpdateOverwrite(ulong id, PermissionTarget type, ChannelTriStatePermissions permissions) + { + var request = new AddOrUpdateChannelPermissionsRequest(id) + { + TargetId = id, + TargetType = EnumConverters.ToString(type), + Allow = permissions.AllowValue, + Deny = permissions.DenyValue + }; + return _channel.Client.ClientAPI.Send(request); + } + public Task RemoveOverwrite(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + return RemoveOverwrite(user.Id); + } + public Task RemoveOverwrite(Role role) + { + if (role == null) throw new ArgumentNullException(nameof(role)); + return RemoveOverwrite(role.Id); + } + private async Task RemoveOverwrite(ulong id) + { + try { await _channel.Client.ClientAPI.Send(new RemoveChannelPermissionsRequest(_channel.Id, id)).ConfigureAwait(false); } + catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } + } + + public ChannelPermissions GetPermissions(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + if (_users != null) + { + Member member; + if (_users.TryGetValue(user.Id, out member)) + return member.Permissions; + else + return ChannelPermissions.None; + } + else + { + ChannelPermissions perms = new ChannelPermissions(); + ResolvePermissions(user, ref perms); + return perms; + } + } + public void UpdatePermissions() + { + if (_users != null) + { + foreach (var pair in _users) + { + var member = pair.Value; + var perms = member.Permissions; + if (ResolvePermissions(member.User, ref perms)) + _users[pair.Key] = new Member(member.User, perms); + } + } + } + public void UpdatePermissions(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + if (_users != null) + { + Member member; + if (_users.TryGetValue(user.Id, out member)) + { + var perms = member.Permissions; + if (ResolvePermissions(member.User, ref perms)) + _users[user.Id] = new Member(member.User, perms); + } + } + } + public bool ResolvePermissions(User user, ref ChannelPermissions permissions) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + uint newPermissions = 0; + var server = user.Server; + + var mask = ChannelPermissions.All(_channel.Type).RawValue; + if (_channel.IsPrivate || user.IsOwner) + newPermissions = mask; //Private messages and owners always have all permissions + else + { + //Start with this user's server permissions + newPermissions = server.GetPermissions(user).RawValue; + var rules = _rules; + + Channel.PermissionRule rule; + var roles = user.Roles.ToArray(); + if (roles.Length > 0) + { + for (int i = 0; i < roles.Length; i++) + { + if (rules.TryGetValue(roles[i].Id, out rule)) + newPermissions &= ~rule.Permissions.DenyValue; + } + for (int i = 0; i < roles.Length; i++) + { + if (rules.TryGetValue(roles[i].Id, out rule)) + newPermissions |= rule.Permissions.AllowValue; + } + } + if (rules.TryGetValue(user.Id, out rule)) + newPermissions = (newPermissions & ~rule.Permissions.DenyValue) | rule.Permissions.AllowValue; + + if (newPermissions.HasBit((byte)PermissionBits.ManageRolesOrPermissions)) + newPermissions = mask; //ManageRolesOrPermissions gives all permisions + else if (_channel.IsText && !newPermissions.HasBit((byte)PermissionBits.ReadMessages)) + newPermissions = 0; //No read permission on a text channel removes all other permissions + else if (_channel.IsVoice && !newPermissions.HasBit((byte)PermissionBits.Connect)) + newPermissions = 0; //No connect permissions on a voice channel removes all other permissions + else + newPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from serverPerms, for example) + } + + if (newPermissions != permissions.RawValue) + { + permissions = new ChannelPermissions(newPermissions); + return true; + } + return false; + } + + public void AddUser(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + + if (_users != null) + { + var perms = new ChannelPermissions(); + ResolvePermissions(user, ref perms); + var member = new Member(user, ChannelPermissions.None); + _users[user.Id] = new Member(user, ChannelPermissions.None); + } + } + public void RemoveUser(ulong id) + { + Member ignored; + if (_users != null) + _users.TryRemove(id, out ignored); + } + } +} diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index a3e0ed699..6a45b2ec1 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -13,10 +13,14 @@ namespace Discord Normal = 0, /// Message is current queued. Queued, + /// Message was deleted. + Deleted, /// Message was deleted before it was sent. Aborted, /// Message failed to be sent. - Failed + Failed, + /// Message has been removed from cache and will no longer receive updates. + Detached } public class Message @@ -29,14 +33,14 @@ namespace Discord private static readonly Attachment[] _initialAttachments = new Attachment[0]; private static readonly Embed[] _initialEmbeds = new Embed[0]; - internal static string CleanUserMentions(Channel channel, string text, List users = null) + internal static string CleanUserMentions(PublicChannel channel, string text, List users = null) { return _userRegex.Replace(text, new MatchEvaluator(e => { ulong id; if (e.Value.Substring(2, e.Value.Length - 3).TryToId(out id)) { - var user = channel.GetUserFast(id); + var user = channel.GetUser(id); if (user != null) { if (users != null) @@ -47,7 +51,7 @@ namespace Discord return e.Value; //User not found or parse failed })); } - internal static string CleanChannelMentions(Channel channel, string text, List channels = null) + internal static string CleanChannelMentions(PublicChannel channel, string text, List channels = null) { var server = channel.Server; if (server == null) return text; @@ -81,34 +85,21 @@ namespace Discord })); }*/ - //TODO: Move this somewhere - private static string Resolve(Channel channel, string text) + internal static string ResolveMentions(IChannel channel, string text) { + if (channel == null) throw new ArgumentNullException(nameof(channel)); if (text == null) throw new ArgumentNullException(nameof(text)); - var client = channel.Client; - text = CleanUserMentions(channel, text); - text = CleanChannelMentions(channel, text); - //text = CleanRoleMentions(Channel, text); + var publicChannel = channel as PublicChannel; + if (publicChannel != null) + { + text = CleanUserMentions(publicChannel, text); + text = CleanChannelMentions(publicChannel, text); + //text = CleanRoleMentions(publicChannel, text); + } return text; } - /*internal class ImportResolver : DefaultContractResolver - { - protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) - { - var property = base.CreateProperty(member, memberSerialization); - if (member is PropertyInfo) - { - if (member.Name == nameof(ChannelId) || !(member as PropertyInfo).CanWrite) - return null; - - property.Writable = true; //Handles private setters - } - return property; - } - }*/ - public class Attachment : File { /// Unique identifier for this file. @@ -172,7 +163,7 @@ namespace Discord /// Returns the unique identifier for this message. public ulong Id { get; internal set; } /// Returns the channel this message was sent to. - public Channel Channel { get; } + public ITextChannel Channel { get; } /// Returns the author of this message. public User User { get; } @@ -196,20 +187,18 @@ namespace Discord /// Returns a collection of all users mentioned in this message. public IEnumerable MentionedUsers { get; internal set; } /// Returns a collection of all channels mentioned in this message. - public IEnumerable MentionedChannels { get; internal set; } + public IEnumerable MentionedChannels { get; internal set; } /// Returns a collection of all roles mentioned in this message. public IEnumerable MentionedRoles { get; internal set; } internal int Nonce { get; set; } - - /// Gets the path to this object. - internal string Path => $"{Server?.Name ?? "[Private]"}/{Id}"; + /// Returns the server containing the channel this message was sent to. - public Server Server => Channel.Server; + public Server Server => (Channel as PublicChannel)?.Server; /// Returns if this message was sent from the logged-in accounts. public bool IsAuthor => User != null && User.Id == Client.CurrentUser?.Id; - internal Message(ulong id, Channel channel, User user) + internal Message(ulong id, ITextChannel channel, User user) { Id = id; Channel = channel; @@ -222,7 +211,6 @@ namespace Discord internal void Update(APIMessage model) { var channel = Channel; - var server = channel.Server; if (model.Attachments != null) { Attachments = model.Attachments @@ -277,36 +265,34 @@ namespace Discord if (model.Mentions != null) { MentionedUsers = model.Mentions - .Select(x => Channel.GetUserFast(x.Id)) + .Select(x => (Channel as Channel).GetUser(x.Id)) .Where(x => x != null) .ToArray(); } if (model.IsMentioningEveryone != null) - { - if (model.IsMentioningEveryone.Value && User != null && User.GetPermissions(channel).MentionEveryone) - MentionedRoles = new Role[] { Server.EveryoneRole }; - else - MentionedRoles = new Role[0]; + { + var server = (channel as PublicChannel).Server; + if (model.IsMentioningEveryone.Value && server != null) + MentionedRoles = new Role[] { server.EveryoneRole }; + else + MentionedRoles = Enumerable.Empty(); } if (model.Content != null) { string text = model.Content; RawText = text; - //var mentionedUsers = new List(); - var mentionedChannels = new List(); - //var mentionedRoles = new List(); - text = CleanUserMentions(Channel, text/*, mentionedUsers*/); - if (server != null) - { - text = CleanChannelMentions(Channel, text, mentionedChannels); - //text = CleanRoleMentions(_client, User, channel, text, mentionedRoles); - } - Text = text; - - //MentionedUsers = mentionedUsers; - MentionedChannels = mentionedChannels; - //MentionedRoles = mentionedRoles; + List mentionedChannels = null; + if (Channel.IsPublic) + mentionedChannels = new List(); + + text = CleanUserMentions(Channel as PublicChannel, text); + text = CleanChannelMentions(Channel as PublicChannel, text, mentionedChannels); + + if (Channel.IsPublic) + MentionedChannels = mentionedChannels; + + Text = text; } } @@ -341,13 +327,6 @@ namespace Discord return MentionedUsers?.Contains(me) ?? false; } - /// Resolves all mentions in a provided string to those users, channels or roles' names. - public string Resolve(string text) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - return Resolve(Channel, text); - } - internal Message Clone() { var result = new Message(); @@ -356,6 +335,6 @@ namespace Discord } private Message() { } //Used for cloning - public override string ToString() => $"{User?.Name ?? "Unknown User"}: {RawText}"; + public override string ToString() => $"{User}: {RawText}"; } } diff --git a/src/Discord.Net/Models/Permissions.cs b/src/Discord.Net/Models/Permissions.cs index 609524dfe..badccc116 100644 --- a/src/Discord.Net/Models/Permissions.cs +++ b/src/Discord.Net/Models/Permissions.cs @@ -97,6 +97,8 @@ namespace Discord RawValue = value; } public ServerPermissions(uint rawValue) { RawValue = rawValue; } + + public override string ToString() => Convert.ToString(RawValue, 2); } public struct ChannelPermissions @@ -105,13 +107,15 @@ namespace Discord public static ChannelPermissions TextOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000000000000111111110000011001", 2)); public static ChannelPermissions PrivateOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000000000000011100110000000000", 2)); public static ChannelPermissions VoiceOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000011111100000000000000011001", 2)); - public static ChannelPermissions All(Channel channel) => All(channel.Type, channel.IsPrivate); - public static ChannelPermissions All(ChannelType channelType, bool isPrivate) + public static ChannelPermissions All(ChannelType channelType) { - if (isPrivate) return PrivateOnly; - else if (channelType == ChannelType.Text) return TextOnly; - else if (channelType == ChannelType.Voice) return VoiceOnly; - else return None; + switch (channelType) + { + case ChannelType.Text: return TextOnly; + case ChannelType.Voice: return VoiceOnly; + case ChannelType.Private: return PrivateOnly; + default: return None; + } } public uint RawValue { get; } @@ -191,11 +195,13 @@ namespace Discord RawValue = value; } public ChannelPermissions(uint rawValue) { RawValue = rawValue; } + + public override string ToString() => Convert.ToString(RawValue, 2); } - public struct ChannelPermissionOverrides + public struct ChannelTriStatePermissions { - public static ChannelPermissionOverrides InheritAll { get; } = new ChannelPermissionOverrides(); + public static ChannelTriStatePermissions InheritAll { get; } = new ChannelTriStatePermissions(); public uint AllowValue { get; } public uint DenyValue { get; } @@ -236,16 +242,16 @@ namespace Discord /// If True, a user may use voice activation rather than push-to-talk. public PermValue UseVoiceActivation => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.UseVoiceActivation); - public ChannelPermissionOverrides(PermValue? createInstantInvite = null, PermValue? managePermissions = null, + public ChannelTriStatePermissions(PermValue? createInstantInvite = null, PermValue? managePermissions = null, PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null) - : this(new ChannelPermissionOverrides(), createInstantInvite, managePermissions, manageChannel, readMessages, sendMessages, sendTTSMessages, + : this(new ChannelTriStatePermissions(), createInstantInvite, managePermissions, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation) { } - public ChannelPermissionOverrides(ChannelPermissionOverrides basePerms, PermValue? createInstantInvite = null, PermValue? managePermissions = null, + public ChannelTriStatePermissions(ChannelTriStatePermissions basePerms, PermValue? createInstantInvite = null, PermValue? managePermissions = null, PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, @@ -274,11 +280,13 @@ namespace Discord AllowValue = allow; DenyValue = deny; } - public ChannelPermissionOverrides(uint allow = 0, uint deny = 0) + public ChannelTriStatePermissions(uint allow = 0, uint deny = 0) { AllowValue = allow; DenyValue = deny; } + + public override string ToString() => $"Allow: {Convert.ToString(AllowValue, 2)}, Deny: {Convert.ToString(DenyValue, 2)}"; } internal static class PermissionsHelper { diff --git a/src/Discord.Net/Models/PrivateChannel.cs b/src/Discord.Net/Models/PrivateChannel.cs new file mode 100644 index 000000000..b3f3f1faf --- /dev/null +++ b/src/Discord.Net/Models/PrivateChannel.cs @@ -0,0 +1,66 @@ +using APIChannel = Discord.API.Client.Channel; +using Discord.API.Client.Rest; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public class PrivateChannel : Channel, IPrivateChannel, ITextChannel + { + private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); + + private readonly MessageManager _messages; + + /// Gets the target user, if this is a private chat. + public User Recipient { get; } + + public override DiscordClient Client => Recipient.Client; + + public override ChannelType Type => ChannelType.Private; + public override User CurrentUser => Client.PrivateUser; + public override IEnumerable Users => new User[] { Client.PrivateUser, Recipient }; + + internal override MessageManager MessageManager => _messages; + internal override PermissionManager PermissionManager => null; + + internal PrivateChannel(ulong id, User recipient, APIChannel model) + : this(id, recipient) + { + _messages = new MessageManager(this, Client.Config.MessageCacheSize); + Update(model); + } + private PrivateChannel(ulong id, User recipient) + :base(id) + { + Recipient = recipient; + } + + internal override User GetUser(ulong id) + { + if (id == Recipient.Id) return Recipient; + else if (id == Client.CurrentUser.Id) return Client.PrivateUser; + else return null; + } + + public Message GetMessage(ulong id) => _messages.Get(id); + public Task DownloadMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) + => _messages.Download(limit, relativeMessageId, relativeDir); + + public Task SendMessage(string text, bool isTTS = false) => _messages.Send(text, isTTS); + public Task SendFile(string filePath) => _messages.SendFile(filePath); + public Task SendFile(string filename, Stream stream) => _messages.SendFile(filename, stream); + public Task SendIsTyping() => Client.ClientAPI.Send(new SendIsTypingRequest(Id)); + + public override string ToString() => $"@{Recipient.Name}"; + + internal override void Update(APIChannel model) { } + internal override Channel Clone() + { + var result = new PrivateChannel(Id, Recipient); + _cloner(this, result); + return result; + } + } +} diff --git a/src/Discord.Net/Models/Profile.cs b/src/Discord.Net/Models/Profile.cs index 89cdde640..4e499325a 100644 --- a/src/Discord.Net/Models/Profile.cs +++ b/src/Discord.Net/Models/Profile.cs @@ -83,6 +83,6 @@ namespace Discord } private Profile() { } //Used for cloning - public override string ToString() => Id.ToIdString(); + public override string ToString() => Name; } } diff --git a/src/Discord.Net/Models/PublicChannel.cs b/src/Discord.Net/Models/PublicChannel.cs new file mode 100644 index 000000000..112a250ce --- /dev/null +++ b/src/Discord.Net/Models/PublicChannel.cs @@ -0,0 +1,109 @@ +using APIChannel = Discord.API.Client.Channel; +using Discord.API.Client.Rest; +using Discord.Net; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Discord +{ + /// A public Discord channel + public abstract class PublicChannel : Channel, IModel, IMentionable + { + internal readonly PermissionManager _permissions; + + /// Gets the server owning this channel. + public Server Server { get; } + + /// Gets or sets the name of this channel. + public string Name { get; set; } + /// Getsor sets the position of this channel relative to other channels of the same type in this server. + public int Position { get; set; } + + /// Gets the DiscordClient that created this model. + public override DiscordClient Client => Server.Client; + public override User CurrentUser => Server.CurrentUser; + /// Gets the string used to mention this channel. + public string Mention => $"<#{Id}>"; + /// Gets a collection of all custom permissions used for this channel. + public IEnumerable PermissionRules => _permissions.Rules; + + internal PublicChannel(APIChannel model, Server server) + : this(model.Id, server) + { + _permissions = new PermissionManager(this, model, server.Client.Config.UsePermissionsCache ? (int)(server.UserCount * 1.05) : -1); + Update(model); + } + protected PublicChannel(ulong id, Server server) + : base(id) + { + Server = server; + } + + internal override void Update(APIChannel model) + { + if (model.Name != null) Name = model.Name; + if (model.Position != null) Position = model.Position.Value; + + if (model.PermissionOverwrites != null) + _permissions.Update(model); + } + + public async Task Delete() + { + try { await Client.ClientAPI.Send(new DeleteChannelRequest(Id)).ConfigureAwait(false); } + catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } + } + public abstract Task Save(); + + internal override User GetUser(ulong id) => Server.GetUser(id); + + public ChannelTriStatePermissions? GetPermissionsRule(User user) => _permissions.GetOverwrite(user); + public ChannelTriStatePermissions? GetPermissionsRule(Role role) => _permissions.GetOverwrite(role); + public Task AddOrUpdatePermissionsRule(User user, ChannelTriStatePermissions permissions) => _permissions.AddOrUpdateOverwrite(user, permissions); + public Task AddOrUpdatePermissionsRule(Role role, ChannelTriStatePermissions permissions) => _permissions.AddOrUpdateOverwrite(role, permissions); + public Task RemovePermissionsRule(User user) => _permissions.RemoveOverwrite(user); + public async Task RemovePermissionsRule(Role role) + { + if (role == null) throw new ArgumentNullException(nameof(role)); + try { await Client.ClientAPI.Send(new RemoveChannelPermissionsRequest(Id, role.Id)).ConfigureAwait(false); } + catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } + } + + internal ChannelPermissions GetPermissions(User user) => _permissions.GetPermissions(user); + internal void UpdatePermissions() => _permissions.UpdatePermissions(); + internal void UpdatePermissions(User user) => _permissions.UpdatePermissions(user); + internal bool ResolvePermissions(User user, ref ChannelPermissions permissions) => _permissions.ResolvePermissions(user, ref permissions); + + internal override PermissionManager PermissionManager => null; + + /// Creates a new invite to this channel. + /// Time (in seconds) until the invite expires. Set to null to never expire. + /// The max amount of times this invite may be used. Set to null to have unlimited uses. + /// If true, a user accepting this invite will be kicked from the server after closing their client. + /// If true, creates a human-readable link. Not supported if maxAge is set to null. + public async Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) + { + if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge)); + if (maxUses < 0) throw new ArgumentOutOfRangeException(nameof(maxUses)); + + var request = new CreateInviteRequest(Id) + { + MaxAge = maxAge ?? 0, + MaxUses = maxUses ?? 0, + IsTemporary = tempMembership, + WithXkcdPass = withXkcd + }; + + var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); + var invite = new Invite(Client, response.Code, response.XkcdPass); + return invite; + } + + internal void AddUser(User user) => _permissions.AddUser(user); + internal void RemoveUser(ulong id) => _permissions.RemoveUser(id); + + public override string ToString() => $"{Server}/{Name ?? Id.ToIdString()}"; + } +} diff --git a/src/Discord.Net/Models/Region.cs b/src/Discord.Net/Models/Region.cs index 5b0354048..cf144a223 100644 --- a/src/Discord.Net/Models/Region.cs +++ b/src/Discord.Net/Models/Region.cs @@ -16,5 +16,7 @@ Port = port; Vip = vip; } + + public override string ToString() => Name; } } diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index d3e9f9094..6fb989fec 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -32,9 +32,7 @@ namespace Discord public ServerPermissions Permissions { get; private set; } /// Gets the color of this role. public Color Color { get; private set; } - - /// Gets the path to this object. - internal string Path => $"{Server?.Name ?? "[Private]"}/{Name}"; + /// Gets true if this is the role representing all users in a server. public bool IsEveryone => Id == Server.Id; /// Gets a list of all members in this role. @@ -132,6 +130,6 @@ namespace Discord } private Role() { } //Used for cloning - public override string ToString() => Name ?? Id.ToIdString(); + public override string ToString() => $"{Server}/{Name ?? Id.ToString()}"; } } diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index b7a9933dc..85e74b4ac 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -1,4 +1,5 @@ -using Discord.API.Client; +using APIChannel = Discord.API.Client.Channel; +using Discord.API.Client; using Discord.API.Client.Rest; using Discord.Net; using System; @@ -48,7 +49,7 @@ namespace Discord private ConcurrentDictionary _roles; private ConcurrentDictionary _users; - private ConcurrentDictionary _channels; + private ConcurrentDictionary _channels; private ulong _ownerId; private ulong? _afkChannelId; private int _userCount; @@ -59,34 +60,31 @@ namespace Discord public ulong Id { get; } /// Gets the name of this server. - public string Name { get; private set; } + public string Name { get; set; } /// Gets the voice region for this server. - public Region Region { get; private set; } - /// Gets the unique identifier for this user's current avatar. - public string IconId { get; private set; } - /// Gets the unique identifier for this server's custom splash image. - public string SplashId { get; private set; } + public Region Region { get; set; } + /// Gets the AFK voice channel for this server. + public VoiceChannel AFKChannel { get; set; } /// Gets the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK voice channel, if one is set. - public int AFKTimeout { get; private set; } + public int AFKTimeout { get; set; } + /// Gets the date and time you joined this server. public DateTime JoinedAt { get; private set; } - /// Gets the default channel for this server. - public Channel DefaultChannel { get; private set; } /// Gets the the role representing all users in a server. public Role EveryoneRole { get; private set; } /// Gets all extra features added to this server. public IEnumerable Features { get; private set; } /// Gets all custom emojis on this server. public IEnumerable CustomEmojis { get; private set; } - - /// Gets the path to this object. - internal string Path => Name; + /// Gets the unique identifier for this user's current avatar. + public string IconId { get; private set; } + /// Gets the unique identifier for this server's custom splash image. + public string SplashId { get; private set; } + /// Gets the user that created this server. public User Owner => GetUser(_ownerId); - /// Returns true if the current user owns this server. - public bool IsOwner => _ownerId == Client.CurrentUser.Id; - /// Gets the AFK voice channel for this server. - public Channel AFKChannel => _afkChannelId != null ? GetChannel(_afkChannelId.Value) : null; + /// Gets the default channel for this server. + public TextChannel DefaultChannel => _channels[Id] as TextChannel; /// Gets the current user in this server. public User CurrentUser => GetUser(Client.CurrentUser.Id); /// Gets the URL to this server's current icon. @@ -95,11 +93,11 @@ namespace Discord public string SplashUrl => GetSplashUrl(Id, SplashId); /// Gets a collection of all channels in this server. - public IEnumerable AllChannels => _channels.Select(x => x.Value); + public IEnumerable AllChannels => _channels.Select(x => x.Value); /// Gets a collection of text channels in this server. - public IEnumerable TextChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Text); + public IEnumerable TextChannels => _channels.Where(x => x.Value.IsText).Select(x => x.Value as TextChannel); /// Gets a collection of voice channels in this server. - public IEnumerable VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); + public IEnumerable VoiceChannels => _channels.Where(x => x.Value.IsVoice).Select(x => x.Value as VoiceChannel); /// Gets a collection of all members in this server. public IEnumerable Users => _users.Select(x => x.Value.User); /// Gets a collection of all roles in this server. @@ -168,10 +166,9 @@ namespace Discord //Only channels or members should have AddXXX(cachePerms: true), not both if (model.Channels != null) { - _channels = new ConcurrentDictionary(2, (int)(model.Channels.Length * 1.05)); + _channels = new ConcurrentDictionary(2, (int)(model.Channels.Length * 1.05)); foreach (var subModel in model.Channels) - AddChannel(subModel.Id, false).Update(subModel); - DefaultChannel = _channels[Id]; + AddChannel(subModel, false); } if (model.MemberCount != null) { @@ -257,67 +254,53 @@ namespace Discord #endregion #region Channels - internal Channel AddChannel(ulong id, bool cachePerms) + internal PublicChannel AddChannel(APIChannel model, bool cachePerms) { - var channel = new Channel(Client, id, this); + PublicChannel channel; + ChannelType type = EnumConverters.ToChannelType(model.Type); + if (type == ChannelType.Voice) + channel = new VoiceChannel(model, this); + else + channel = new TextChannel(model, this); + if (cachePerms && Client.Config.UsePermissionsCache) { foreach (var user in Users) channel.AddUser(user); } Client.AddChannel(channel); - return _channels.GetOrAdd(id, x => channel); + return _channels.GetOrAdd(model.Id, x => channel); } - internal Channel RemoveChannel(ulong id) + internal PublicChannel RemoveChannel(ulong id) { - Channel channel; + PublicChannel channel; _channels.TryRemove(id, out channel); return channel; } /// Gets the channel with the provided id and owned by this server, or null if not found. - public Channel GetChannel(ulong id) + public PublicChannel GetChannel(ulong id) { - Channel result; + PublicChannel result; _channels.TryGetValue(id, out result); return result; } - - /// Returns all channels with the specified server and name. - /// Name formats supported: Name, #Name and <#Id>. Search is case-insensitive if exactMatch is false. - public IEnumerable FindChannels(string name, ChannelType type = null, bool exactMatch = false) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - - return _channels.Select(x => x.Value).Find(name, type, exactMatch); - } + public TextChannel GetTextChannel(ulong id) => GetChannel(id) as TextChannel; + public VoiceChannel GetVoiceChannel(ulong id) => GetChannel(id) as VoiceChannel; /// Creates a new channel. - public async Task CreateChannel(string name, ChannelType type) + public async Task CreateChannel(string name, ChannelType type) { if (name == null) throw new ArgumentNullException(nameof(name)); - if (type == null) throw new ArgumentNullException(nameof(type)); + if (type != ChannelType.Text && type != ChannelType.Voice) throw new ArgumentException("Invalid channel type", nameof(type)); - var request = new CreateChannelRequest(Id) { Name = name, Type = type.Value }; + var request = new CreateChannelRequest(Id) { Name = name, Type = type }; var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); - var channel = AddChannel(response.Id, true); + var channel = AddChannel(response, true); channel.Update(response); return channel; } - - /// Reorders the provided channels and optionally places them after a certain channel. - public Task ReorderChannels(IEnumerable channels, Channel after = null) - { - if (channels == null) throw new ArgumentNullException(nameof(channels)); - - var request = new ReorderChannelsRequest(Id) - { - ChannelIds = channels.Select(x => x.Id).ToArray(), - StartPos = after != null ? after.Position + 1 : channels.Min(x => x.Position) - }; - return Client.ClientAPI.Send(request); - } #endregion #region Invites @@ -359,13 +342,6 @@ namespace Discord _roles.TryGetValue(id, out result); return result; } - /// Returns all roles with the specified server and name. - /// Search is case-insensitive if exactMatch is false. - public IEnumerable FindRoles(string name, bool exactMatch = false) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - return _roles.Select(x => x.Value).Find(name, exactMatch); - } /// Creates a new role. public async Task CreateRole(string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) @@ -504,15 +480,7 @@ namespace Discord { if (name == null) throw new ArgumentNullException(nameof(name)); - return _users.Select(x => x.Value.User).Find(name, discriminator: discriminator, exactMatch: false).FirstOrDefault(); - } - /// Returns all members of this server with the specified name. - /// Name formats supported: Name, @Name and <@Id>. Search is case-insensitive if exactMatch is false. - public IEnumerable FindUsers(string name, bool exactMatch = false) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - - return _users.Select(x => x.Value.User).Find(name, exactMatch: exactMatch); + return _users.Select(x => x.Value.User).Where(x => x.Discriminator == discriminator && x.Name == name).SingleOrDefault(); } /// Kicks all users with an inactivity greater or equal to the provided number of days. diff --git a/src/Discord.Net/Models/TextChannel.cs b/src/Discord.Net/Models/TextChannel.cs new file mode 100644 index 000000000..8ee97aad1 --- /dev/null +++ b/src/Discord.Net/Models/TextChannel.cs @@ -0,0 +1,88 @@ +using Discord.API.Client.Rest; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using APIChannel = Discord.API.Client.Channel; + +namespace Discord +{ + public class TextChannel : PublicChannel, IPublicChannel, ITextChannel + { + private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); + + private readonly MessageManager _messages; + + /// Gets or sets the topic of this channel. + public string Topic { get; set; } + + public override ChannelType Type => ChannelType.Text; + /// Gets a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. + public IEnumerable Messages => _messages != null ? _messages : Enumerable.Empty(); + /// Gets a collection of all users with read access to this channel. + public override IEnumerable Users + { + get + { + if (Client.Config.UsePermissionsCache) + return _permissions.Users.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); + else + { + ChannelPermissions perms = new ChannelPermissions(); + return Server.Users.Where(x => + { + _permissions.ResolvePermissions(x, ref perms); + return perms.ReadMessages == true; + }); + } + } + } + + internal override MessageManager MessageManager => _messages; + + internal TextChannel(APIChannel model, Server server) + : base(model, server) + { + if (Client.Config.MessageCacheSize > 0) + _messages = new MessageManager(this, (int)(Client.Config.MessageCacheSize * 1.05)); + } + private TextChannel(ulong id, Server server) + : base(id, server) + { + } + + internal override void Update(APIChannel model) + { + base.Update(model); + if (model.Topic != null) Topic = model.Topic; + } + /// Save all changes to this channel. + public override async Task Save() + { + var request = new UpdateChannelRequest(Id) + { + Name = Name, + Topic = Topic, + Position = Position + }; + await Client.ClientAPI.Send(request).ConfigureAwait(false); + } + + public Message GetMessage(ulong id) => _messages.Get(id); + public Task DownloadMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) + => _messages.Download(limit, relativeMessageId, relativeDir); + + public Task SendMessage(string text, bool isTTS = false) => _messages.Send(text, isTTS); + public Task SendFile(string filePath) => _messages.SendFile(filePath); + public Task SendFile(string filename, Stream stream) => _messages.SendFile(filename, stream); + public Task SendIsTyping() => Client.ClientAPI.Send(new SendIsTypingRequest(Id)); + + internal override Channel Clone() + { + var result = new TextChannel(Id, Server); + _cloner(this, result); + return result; + } + } +} diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index 8c45e5dcc..ac2bbe17b 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -73,12 +73,11 @@ namespace Discord // /// Gets this user's voice token. // public string Token { get; private set; } - /// Gets the path to this object. - internal string Path => $"{Server?.Name ?? "[Private]"}/{Name}"; /// Gets the current private channel for this user if one exists. - public Channel PrivateChannel => Client.GetPrivateChannel(Id); + public PrivateChannel PrivateChannel => Client.GetPrivateChannel(Id); /// Returns the string used to mention this user. public string Mention => $"<@{Id}>"; + public bool IsOwner => Server == null ? false : this == Server.Owner; /// Returns true if this user has marked themselves as muted. public bool IsSelfMuted => (_voiceState & VoiceState.SelfMuted) != 0; /// Returns true if this user has marked themselves as deafened. @@ -92,14 +91,15 @@ namespace Discord /// Returns the time this user was last seen online in this server. public DateTime? LastOnlineAt => Status != UserStatus.Offline ? DateTime.UtcNow : _lastOnline; /// Gets this user's current voice channel. - public Channel VoiceChannel => _voiceChannelId != null ? Server.GetChannel(_voiceChannelId.Value) : null; + public VoiceChannel VoiceChannel => _voiceChannelId != null ? Server.GetVoiceChannel(_voiceChannelId.Value) : null; /// Gets the URL to this user's current avatar. public string AvatarUrl => GetAvatarUrl(Id, AvatarId); /// Gets all roles that have been assigned to this user, including the everyone role. public IEnumerable Roles => _roles.Select(x => x.Value); + public ServerPermissions ServerPermissions => Server.GetPermissions(this); /// Returns a collection of all channels this user has permissions to join on this server. - public IEnumerable Channels + public IEnumerable Channels { get { @@ -108,8 +108,8 @@ namespace Discord if (Client.Config.UsePermissionsCache) { return Server.AllChannels.Where(x => - (x.Type == ChannelType.Text && x.GetPermissions(this).ReadMessages) || - (x.Type == ChannelType.Voice && x.GetPermissions(this).Connect)); + (x.IsText && x.GetPermissions(this).ReadMessages) || + (x.IsVoice && x.GetPermissions(this).Connect)); } else { @@ -117,7 +117,7 @@ namespace Discord return Server.AllChannels .Where(x => { - x.UpdatePermissions(this, ref perms); + x.ResolvePermissions(this, ref perms); return (x.Type == ChannelType.Text && perms.ReadMessages) || (x.Type == ChannelType.Voice && perms.Connect); }); @@ -131,9 +131,9 @@ namespace Discord { var privateChannel = Client.GetPrivateChannel(Id); if (privateChannel != null) - return new Channel[] { privateChannel }; + return new IChannel[] { privateChannel }; else - return new Channel[0]; + return new IChannel[0]; } } } @@ -266,47 +266,15 @@ namespace Discord var request = new KickMemberRequest(Server.Id, Id); return Client.ClientAPI.Send(request); } - - #region Permissions - public ServerPermissions ServerPermissions => Server.GetPermissions(this); - public ChannelPermissions GetPermissions(Channel channel) + + public ChannelPermissions GetPermissions(PublicChannel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); return channel.GetPermissions(this); } - #endregion - - #region Channels - public Task CreatePMChannel() - => Client.CreatePMChannel(this); - #endregion - - #region Messages - public async Task SendMessage(string text) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - - var channel = await CreatePMChannel().ConfigureAwait(false); - return await channel.SendMessage(text).ConfigureAwait(false); - } - public async Task SendFile(string filePath) - { - if (filePath == null) throw new ArgumentNullException(nameof(filePath)); - - var channel = await CreatePMChannel().ConfigureAwait(false); - return await channel.SendFile(filePath).ConfigureAwait(false); - } - public async Task SendFile(string filename, Stream stream) - { - if (filename == null) throw new ArgumentNullException(nameof(filename)); - if (stream == null) throw new ArgumentNullException(nameof(stream)); - - var channel = await CreatePMChannel().ConfigureAwait(false); - return await channel.SendFile(filename, stream).ConfigureAwait(false); - } - #endregion - - #region Roles + + public Task CreatePMChannel() => Client.CreatePrivateChannel(this); + private void UpdateRoles(IEnumerable roles) { bool updated = false; @@ -348,11 +316,10 @@ namespace Discord return _roles.ContainsKey(role.Id); } - public Task AddRoles(params Role[] roles) - => Edit(roles: Roles.Concat(roles)); - public Task RemoveRoles(params Role[] roles) - => Edit(roles: Roles.Except(roles)); - #endregion + public Task AddRoles(params Role[] roles) => Edit(roles: Roles.Concat(roles)); + public Task AddRoles(IEnumerable roles) => Edit(roles: Roles.Concat(roles)); + public Task RemoveRoles(params Role[] roles) => Edit(roles: Roles.Except(roles)); + public Task RemoveRoles(IEnumerable roles) => Edit(roles: Roles.Except(roles)); internal User Clone() { @@ -362,6 +329,19 @@ namespace Discord } private User() { } //Used for cloning - public override string ToString() => Name != null ? $"{Name}#{Discriminator}" : Id.ToIdString(); - } + public override string ToString() + { + if (Name != null) + return $"{Server?.Name ?? "[Private]"}/{Name}#{Discriminator}"; + else + return $"{Server?.Name ?? "[Private]"}/{Id}"; + } + internal string ToString(IChannel channel) + { + if (Name != null) + return $"{channel}/{Name}#{Discriminator}"; + else + return $"{channel}/{Id}"; + } + } } \ No newline at end of file diff --git a/src/Discord.Net/Models/VoiceChannel.cs b/src/Discord.Net/Models/VoiceChannel.cs new file mode 100644 index 000000000..5b40a6aad --- /dev/null +++ b/src/Discord.Net/Models/VoiceChannel.cs @@ -0,0 +1,60 @@ +using APIChannel = Discord.API.Client.Channel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Discord.API.Client.Rest; + +namespace Discord +{ + public class VoiceChannel : PublicChannel, IPublicChannel, IVoiceChannel + { + private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); + + public int Bitrate { get; set; } + + public override ChannelType Type => ChannelType.Public | ChannelType.Voice; + /// Gets a collection of all users currently in this voice channel. + public override IEnumerable Users + { + get + { + if (Client.Config.UsePermissionsCache) + return _permissions.Users.Select(x => x.User).Where(x => x.VoiceChannel == this); + else + return Server.Users.Where(x => x.VoiceChannel == this); + } + } + + internal override MessageManager MessageManager => null; + + internal VoiceChannel(APIChannel model, Server server) + : base(model, server) + { + } + private VoiceChannel(ulong id, Server server) + : base(id, server) + { + } + + + /// Save all changes to this channel. + public override async Task Save() + { + var request = new UpdateChannelRequest(Id) + { + Name = Name, + Position = Position, + Bitrate = Bitrate + }; + await Client.ClientAPI.Send(request).ConfigureAwait(false); + } + + internal override Channel Clone() + { + var result = new VoiceChannel(Id, Server); + _cloner(this, result); + return result; + } + } +} diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json index 565bc2e86..80a6fb51d 100644 --- a/src/Discord.Net/project.json +++ b/src/Discord.Net/project.json @@ -1,9 +1,7 @@ { - "version": "0.9.0-rc3-3", + "version": "1.0.0-alpha1", "description": "An unofficial .Net API wrapper for the Discord client.", - "authors": [ - "RogueException" - ], + "authors": [ "RogueException" ], "tags": [ "discord", "discordapp" From 36ea8b8c3a75f024c8ab0d5023371278f5356e32 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 24 Feb 2016 19:36:18 -0400 Subject: [PATCH 39/51] Added reference project --- ref/Discord.Net.xproj | 21 +++++ ref/DiscordClient.Events.cs | 32 ++++++++ ref/DiscordClient.cs | 59 ++++++++++++++ ref/DiscordConfig.cs | 36 +++++++++ ref/Entities/Channels/Channel.cs | 22 +++++ ref/Entities/Channels/IChannel.cs | 17 ++++ .../Entities/Channels}/IPrivateChannel.cs | 1 + ref/Entities/Channels/IPublicChannel.cs | 30 +++++++ ref/Entities/Channels/ITextChannel.cs | 18 +++++ ref/Entities/Channels/IVoiceChannel.cs | 7 ++ ref/Entities/Channels/PrivateChannel.cs | 31 +++++++ ref/Entities/Channels/TextChannel.cs | 50 ++++++++++++ ref/Entities/Channels/VoiceChannel.cs | 49 +++++++++++ ref/Entities/Color.cs | 17 ++++ ref/Entities/IMentionable.cs | 7 ++ ref/Entities/IModel.cs | 13 +++ ref/Entities/Invite.cs | 48 +++++++++++ ref/Entities/Message.cs | 68 ++++++++++++++++ .../Permissions/ChannelPermissions.cs | 54 +++++++++++++ .../Permissions/PermissionOverwrite.cs | 9 +++ ref/Entities/Permissions/ServerPermissions.cs | 55 +++++++++++++ .../Permissions/TriStateChannelPermissions.cs | 50 ++++++++++++ ref/Entities/Profile.cs | 23 ++++++ ref/Entities/Region.cs | 20 +++++ ref/Entities/Role.cs | 29 +++++++ ref/Entities/Server.cs | 70 ++++++++++++++++ ref/Entities/User.cs | 55 +++++++++++++ ref/Enums/ChannelType.cs | 8 ++ ref/Enums/ConnectionState.cs | 10 +++ ref/Enums/ImageType.cs | 9 +++ ref/Enums/LogSeverity.cs | 11 +++ ref/Enums/MessageState.cs | 18 +++++ ref/Enums/PermValue.cs | 9 +++ ref/Enums/PermissionBits.cs | 31 +++++++ ref/Enums/PermissionTarget.cs | 8 ++ ref/Enums/Relative.cs | 8 ++ ref/Enums/UserStatus.cs | 9 +++ ref/Events/ChannelEventArgs.cs | 10 +++ ref/Events/ChannelUpdatedEventArgs.cs | 11 +++ ref/Events/ChannelUserEventArgs.cs | 8 ++ ref/Events/DisconnectedEventArgs.cs | 10 +++ ref/Events/LogMessageEventArgs.cs | 12 +++ ref/Events/MessageEventArgs.cs | 12 +++ ref/Events/MessageUpdatedEventArgs.cs | 13 +++ ref/Events/ProfileUpdatedEventArgs.cs | 10 +++ ref/Events/RoleEventArgs.cs | 10 +++ ref/Events/RoleUpdatedEventArgs.cs | 11 +++ ref/Events/ServerEventArgs.cs | 9 +++ ref/Events/ServerUpdatedEventArgs.cs | 10 +++ ref/Events/UserEventArgs.cs | 9 +++ ref/Events/UserUpdatedEventArgs.cs | 10 +++ ref/Format.cs | 14 ++++ ref/ILogger.cs | 30 +++++++ ref/MessageQueue.cs | 9 +++ ref/Net/HttpException.cs | 16 ++++ ref/Net/Rest/CompletedRequestEventArgs.cs | 17 ++++ ref/Net/Rest/IRestRequest.cs | 23 ++++++ ref/Net/Rest/RequestEventArgs.cs | 12 +++ ref/Net/Rest/RestClient.cs | 25 ++++++ ref/Net/TimeoutException.cs | 9 +++ ref/Net/WebSocketException.cs | 12 +++ ref/Net/WebSockets/BinaryMessageEventArgs.cs | 11 +++ ref/Net/WebSockets/GatewaySocket.cs | 31 +++++++ ref/Net/WebSockets/IWebSocketEngine.cs | 18 +++++ ref/Net/WebSockets/TextMessageEventArgs.cs | 11 +++ ref/Net/WebSockets/WebSocket.cs | 22 +++++ ref/Net/WebSockets/WebSocketEventEventArgs.cs | 17 ++++ ref/project.json | 81 +++++++++++++++++++ 68 files changed, 1515 insertions(+) create mode 100644 ref/Discord.Net.xproj create mode 100644 ref/DiscordClient.Events.cs create mode 100644 ref/DiscordClient.cs create mode 100644 ref/DiscordConfig.cs create mode 100644 ref/Entities/Channels/Channel.cs create mode 100644 ref/Entities/Channels/IChannel.cs rename {src/Discord.Net/Models => ref/Entities/Channels}/IPrivateChannel.cs (72%) create mode 100644 ref/Entities/Channels/IPublicChannel.cs create mode 100644 ref/Entities/Channels/ITextChannel.cs create mode 100644 ref/Entities/Channels/IVoiceChannel.cs create mode 100644 ref/Entities/Channels/PrivateChannel.cs create mode 100644 ref/Entities/Channels/TextChannel.cs create mode 100644 ref/Entities/Channels/VoiceChannel.cs create mode 100644 ref/Entities/Color.cs create mode 100644 ref/Entities/IMentionable.cs create mode 100644 ref/Entities/IModel.cs create mode 100644 ref/Entities/Invite.cs create mode 100644 ref/Entities/Message.cs create mode 100644 ref/Entities/Permissions/ChannelPermissions.cs create mode 100644 ref/Entities/Permissions/PermissionOverwrite.cs create mode 100644 ref/Entities/Permissions/ServerPermissions.cs create mode 100644 ref/Entities/Permissions/TriStateChannelPermissions.cs create mode 100644 ref/Entities/Profile.cs create mode 100644 ref/Entities/Region.cs create mode 100644 ref/Entities/Role.cs create mode 100644 ref/Entities/Server.cs create mode 100644 ref/Entities/User.cs create mode 100644 ref/Enums/ChannelType.cs create mode 100644 ref/Enums/ConnectionState.cs create mode 100644 ref/Enums/ImageType.cs create mode 100644 ref/Enums/LogSeverity.cs create mode 100644 ref/Enums/MessageState.cs create mode 100644 ref/Enums/PermValue.cs create mode 100644 ref/Enums/PermissionBits.cs create mode 100644 ref/Enums/PermissionTarget.cs create mode 100644 ref/Enums/Relative.cs create mode 100644 ref/Enums/UserStatus.cs create mode 100644 ref/Events/ChannelEventArgs.cs create mode 100644 ref/Events/ChannelUpdatedEventArgs.cs create mode 100644 ref/Events/ChannelUserEventArgs.cs create mode 100644 ref/Events/DisconnectedEventArgs.cs create mode 100644 ref/Events/LogMessageEventArgs.cs create mode 100644 ref/Events/MessageEventArgs.cs create mode 100644 ref/Events/MessageUpdatedEventArgs.cs create mode 100644 ref/Events/ProfileUpdatedEventArgs.cs create mode 100644 ref/Events/RoleEventArgs.cs create mode 100644 ref/Events/RoleUpdatedEventArgs.cs create mode 100644 ref/Events/ServerEventArgs.cs create mode 100644 ref/Events/ServerUpdatedEventArgs.cs create mode 100644 ref/Events/UserEventArgs.cs create mode 100644 ref/Events/UserUpdatedEventArgs.cs create mode 100644 ref/Format.cs create mode 100644 ref/ILogger.cs create mode 100644 ref/MessageQueue.cs create mode 100644 ref/Net/HttpException.cs create mode 100644 ref/Net/Rest/CompletedRequestEventArgs.cs create mode 100644 ref/Net/Rest/IRestRequest.cs create mode 100644 ref/Net/Rest/RequestEventArgs.cs create mode 100644 ref/Net/Rest/RestClient.cs create mode 100644 ref/Net/TimeoutException.cs create mode 100644 ref/Net/WebSocketException.cs create mode 100644 ref/Net/WebSockets/BinaryMessageEventArgs.cs create mode 100644 ref/Net/WebSockets/GatewaySocket.cs create mode 100644 ref/Net/WebSockets/IWebSocketEngine.cs create mode 100644 ref/Net/WebSockets/TextMessageEventArgs.cs create mode 100644 ref/Net/WebSockets/WebSocket.cs create mode 100644 ref/Net/WebSockets/WebSocketEventEventArgs.cs create mode 100644 ref/project.json diff --git a/ref/Discord.Net.xproj b/ref/Discord.Net.xproj new file mode 100644 index 000000000..d3559797d --- /dev/null +++ b/ref/Discord.Net.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 5b2afee6-fff6-4ba2-be12-61b283b72ac0 + Discord + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + True + + + \ No newline at end of file diff --git a/ref/DiscordClient.Events.cs b/ref/DiscordClient.Events.cs new file mode 100644 index 000000000..42b2601e3 --- /dev/null +++ b/ref/DiscordClient.Events.cs @@ -0,0 +1,32 @@ +using System; + +namespace Discord +{ + public partial class DiscordClient + { + public event EventHandler Ready = delegate { }; + public event EventHandler ChannelCreated = delegate { }; + public event EventHandler ChannelDestroyed = delegate { }; + public event EventHandler ChannelUpdated = delegate { }; + public event EventHandler MessageAcknowledged = delegate { }; + public event EventHandler MessageDeleted = delegate { }; + public event EventHandler MessageReceived = delegate { }; + public event EventHandler MessageSent = delegate { }; + public event EventHandler MessageUpdated = delegate { }; + public event EventHandler ProfileUpdated = delegate { }; + public event EventHandler RoleCreated = delegate { }; + public event EventHandler RoleUpdated = delegate { }; + public event EventHandler RoleDeleted = delegate { }; + public event EventHandler JoinedServer = delegate { }; + public event EventHandler LeftServer = delegate { }; + public event EventHandler ServerAvailable = delegate { }; + public event EventHandler ServerUpdated = delegate { }; + public event EventHandler ServerUnavailable = delegate { }; + public event EventHandler UserBanned = delegate { }; + public event EventHandler UserIsTyping = delegate { }; + public event EventHandler UserJoined = delegate { }; + public event EventHandler UserLeft = delegate { }; + public event EventHandler UserUpdated = delegate { }; + public event EventHandler UserUnbanned = delegate { }; + } +} diff --git a/ref/DiscordClient.cs b/ref/DiscordClient.cs new file mode 100644 index 000000000..f2a1cb95d --- /dev/null +++ b/ref/DiscordClient.cs @@ -0,0 +1,59 @@ +using Discord.Net; +using Discord.Net.Rest; +using Discord.Net.WebSockets; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord +{ + /// Provides a connection to the DiscordApp service. + public partial class DiscordClient : IDisposable + { + public DiscordConfig Config { get; } + public RestClient ClientAPI { get; } + public RestClient StatusAPI { get; } + public GatewaySocket GatewaySocket { get; } + public MessageQueue MessageQueue { get; } + public JsonSerializer Serializer { get; } + + public ConnectionState State { get; } + public CancellationToken CancelToken { get; } + public Profile CurrentUser { get; } + public string SessionId { get; } + public UserStatus Status { get; } + public string CurrentGame { get; } + + public IEnumerable Servers { get; } + public IEnumerable PrivateChannels { get; } + public IEnumerable Regions { get; } + + public DiscordClient() { } + public DiscordClient(DiscordConfig config) { } + public DiscordClient(Action configFunc) { } + + public Task Connect(string email, string password, string token = null) => null; + public Task Connect(string token) => null; + public Task Disconnect() => null; + + public void SetStatus(UserStatus status) { } + public void SetGame(string game) { } + + public Channel GetChannel(ulong id) => null; + public Task CreatePrivateChannel(ulong userId) => null; + + public Task GetInvite(string inviteIdOrXkcd) => null; + + public Region GetRegion(string id) => null; + + public Server GetServer(ulong id) => null; + public IEnumerable FindServers(string name) => null; + public Task CreateServer(string name, Region region, ImageType iconType = ImageType.None, Stream icon = null) => null; + + public void Dispose() { } + } +} \ No newline at end of file diff --git a/ref/DiscordConfig.cs b/ref/DiscordConfig.cs new file mode 100644 index 000000000..657a58437 --- /dev/null +++ b/ref/DiscordConfig.cs @@ -0,0 +1,36 @@ +using System; + +namespace Discord +{ + public class DiscordConfig + { + public const int MaxMessageSize = 2000; + public const string LibName = "Discord.Net"; + public static readonly string LibVersion = null; + public const string LibUrl = "https://github.com/RogueException/Discord.Net"; + + public const string ClientAPIUrl = "https://discordapp.com/api/"; + public const string StatusAPIUrl = "https://srhpyqt94yxb.statuspage.io/api/v2/"; //"https://status.discordapp.com/api/v2/"; + public const string CDNUrl = "https://cdn.discordapp.com/"; + public const string InviteUrl = "https://discord.gg/"; + + public string AppName { get; set; } = null; + public string AppUrl { get; set; } = null; + public string AppVersion { get; set; } = null; + public LogSeverity LogLevel { get; set; } = LogSeverity.Info; + + public int ConnectionTimeout { get; set; } = 30000; + public int ReconnectDelay { get; set; } = 1000; + public int FailedReconnectDelay { get; set; } = 15000; + + public bool CacheToken { get; set; } = true; + public int MessageCacheSize { get; set; } = 100; + + public bool UsePermissionsCache { get; set; } = true; + public bool EnablePreUpdateEvents { get; set; } = true; + public int LargeThreshold { get; set; } = 250; + + public EventHandler LogHandler { get; set; } + public string UserAgent { get; } + } +} diff --git a/ref/Entities/Channels/Channel.cs b/ref/Entities/Channels/Channel.cs new file mode 100644 index 000000000..8cb4a1afb --- /dev/null +++ b/ref/Entities/Channels/Channel.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public abstract class Channel : IChannel + { + public ulong Id { get; } + + public abstract DiscordClient Client { get; } + public abstract ChannelType Type { get; } + public bool IsText { get; } + public bool IsVoice { get; } + public bool IsPrivate { get; } + public bool IsPublic { get; } + + public abstract User CurrentUser { get; } + public abstract IEnumerable Users { get; } + + public abstract Task Save(); + } +} diff --git a/ref/Entities/Channels/IChannel.cs b/ref/Entities/Channels/IChannel.cs new file mode 100644 index 000000000..7fbe67208 --- /dev/null +++ b/ref/Entities/Channels/IChannel.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Discord +{ + public interface IChannel : IModel + { + DiscordClient Client { get; } + + ChannelType Type { get; } + bool IsText { get; } + bool IsVoice { get; } + bool IsPrivate { get; } + bool IsPublic { get; } + + IEnumerable Users { get; } + } +} diff --git a/src/Discord.Net/Models/IPrivateChannel.cs b/ref/Entities/Channels/IPrivateChannel.cs similarity index 72% rename from src/Discord.Net/Models/IPrivateChannel.cs rename to ref/Entities/Channels/IPrivateChannel.cs index 02c4c1b9c..44d55a67f 100644 --- a/src/Discord.Net/Models/IPrivateChannel.cs +++ b/ref/Entities/Channels/IPrivateChannel.cs @@ -2,5 +2,6 @@ { public interface IPrivateChannel : IChannel { + User Recipient { get; } } } diff --git a/ref/Entities/Channels/IPublicChannel.cs b/ref/Entities/Channels/IPublicChannel.cs new file mode 100644 index 000000000..8b8f87b45 --- /dev/null +++ b/ref/Entities/Channels/IPublicChannel.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IPublicChannel : IChannel + { + Server Server { get; } + + string Name { get; set; } + int Position { get; set; } + + IEnumerable PermissionOverwrites { get; } + + PermissionOverwrite? GetPermissionsRule(User user); + PermissionOverwrite? GetPermissionsRule(Role role); + Task> DownloadInvites(); + + Task Delete(); + + Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false); + + Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny); + Task AddPermissionsRule(User user, TriStateChannelPermissions permissions); + Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny); + Task AddPermissionsRule(Role role, TriStateChannelPermissions permissions); + Task RemovePermissionsRule(User user); + Task RemovePermissionsRule(Role role); + } +} diff --git a/ref/Entities/Channels/ITextChannel.cs b/ref/Entities/Channels/ITextChannel.cs new file mode 100644 index 000000000..aa7e09435 --- /dev/null +++ b/ref/Entities/Channels/ITextChannel.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public interface ITextChannel : IChannel + { + Message GetMessage(ulong id); + Task> DownloadMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before); + + Task SendMessage(string text, bool isTTS = false); + Task SendFile(string filePath, string text = null, bool isTTS = false); + Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false); + + Task SendIsTyping(); + } +} diff --git a/ref/Entities/Channels/IVoiceChannel.cs b/ref/Entities/Channels/IVoiceChannel.cs new file mode 100644 index 000000000..85d093209 --- /dev/null +++ b/ref/Entities/Channels/IVoiceChannel.cs @@ -0,0 +1,7 @@ +namespace Discord +{ + public interface IVoiceChannel : IChannel + { + int Bitrate { get; set; } + } +} diff --git a/ref/Entities/Channels/PrivateChannel.cs b/ref/Entities/Channels/PrivateChannel.cs new file mode 100644 index 000000000..ca25dadab --- /dev/null +++ b/ref/Entities/Channels/PrivateChannel.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public class PrivateChannel : Channel, ITextChannel, IPrivateChannel + { + public User Recipient { get; } + public IEnumerable Messages { get; } + + public override DiscordClient Client => null; + public override ChannelType Type => default(ChannelType); + + public override User CurrentUser => null; + public override IEnumerable Users => null; + + public Message GetMessage(ulong id) => null; + public Task> DownloadMessages(int limit) => null; + public Task> DownloadMessages(int limit, ulong? relativeMessageId, Relative relativeDir) => null; + + public Task SendMessage(string text, bool isTTS = false) => null; + public Task SendFile(string path, string text = null, bool isTTS = false) => null; + public Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false) => null; + + public Task SendIsTyping() => null; + + public override Task Save() => null; + } +} diff --git a/ref/Entities/Channels/TextChannel.cs b/ref/Entities/Channels/TextChannel.cs new file mode 100644 index 000000000..e09528e01 --- /dev/null +++ b/ref/Entities/Channels/TextChannel.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public class TextChannel : Channel, ITextChannel, IPublicChannel, IMentionable + { + public Server Server { get; } + public string Mention { get; } + public IEnumerable PermissionOverwrites { get; } + public IEnumerable Messages { get; } + + public string Topic { get; set; } + public bool IsTyping { get; set; } + public string Name { get; set; } + public int Position { get; set; } + + public override DiscordClient Client => null; + public override ChannelType Type => default(ChannelType); + public override User CurrentUser => null; + public override IEnumerable Users => null; + + public Message GetMessage(ulong id) => null; + public PermissionOverwrite? GetPermissionsRule(User user) => null; + public PermissionOverwrite? GetPermissionsRule(Role role) => null; + + public Task> DownloadMessages(int limit) => null; + public Task> DownloadMessages(int limit, ulong? relativeMessageId, Relative relativeDir) => null; + public Task> DownloadInvites() => null; + + public Task SendMessage(string text, bool isTTS = false) => null; + public Task SendFile(string path, string text = null, bool isTTS = false) => null; + public Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false) => null; + + public Task SendIsTyping() => null; + + public Task CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool tempMembership = false, bool withXkcd = false) => null; + + public Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny) => null; + public Task AddPermissionsRule(User user, TriStateChannelPermissions permissions) => null; + public Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; + public Task AddPermissionsRule(Role role, TriStateChannelPermissions permissions) => null; + public Task RemovePermissionsRule(User user) => null; + public Task RemovePermissionsRule(Role role) => null; + + public Task Delete() => null; + public override Task Save() => null; + } +} diff --git a/ref/Entities/Channels/VoiceChannel.cs b/ref/Entities/Channels/VoiceChannel.cs new file mode 100644 index 000000000..c38d6c40b --- /dev/null +++ b/ref/Entities/Channels/VoiceChannel.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public class VoiceChannel : IPublicChannel, IVoiceChannel + { + public ulong Id { get; } + public DiscordClient Client { get; } + public Server Server { get; } + public ChannelType Type { get; } + public bool IsText { get; } + public bool IsVoice { get; } + public bool IsPrivate { get; } + public bool IsPublic { get; } + public IEnumerable PermissionOverwrites { get; } + public IEnumerable Users { get; } + + public string Name { get; set; } + public int Position { get; set; } + public int Bitrate { get; set; } + + public Message GetMessage(ulong id) => null; + public PermissionOverwrite? GetPermissionsRule(User user) => null; + public PermissionOverwrite? GetPermissionsRule(Role role) => null; + + public Task> DownloadMessages(int limit) => null; + public Task> DownloadMessages(int limit, ulong? relativeMessageId, Relative relativeDir) => null; + public Task> DownloadInvites() => null; + + public Task SendMessage(string text, bool isTTS = false) => null; + public Task SendFile(string path, string text = null, bool isTTS = false) => null; + public Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false) => null; + + public Task CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool tempMembership = false, bool withXkcd = false) => null; + + public Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny) => null; + public Task AddPermissionsRule(User user, TriStateChannelPermissions permissions) => null; + public Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; + public Task AddPermissionsRule(Role role, TriStateChannelPermissions permissions) => null; + public Task RemovePermissionsRule(User user) => null; + public Task RemovePermissionsRule(Role role) => null; + + public Task Delete() => null; + public Task Save() => null; + } +} diff --git a/ref/Entities/Color.cs b/ref/Entities/Color.cs new file mode 100644 index 000000000..b3c78debf --- /dev/null +++ b/ref/Entities/Color.cs @@ -0,0 +1,17 @@ +namespace Discord +{ + public class Color + { + public static readonly Color Default = new Color(0); + + public uint RawValue { get; } + + public Color(uint rawValue) { } + public Color(byte r, byte g, byte b) { } + public Color(float r, float g, float b) { } + + public byte R { get; } + public byte G { get; } + public byte B { get; } + } +} diff --git a/ref/Entities/IMentionable.cs b/ref/Entities/IMentionable.cs new file mode 100644 index 000000000..0a4bf439c --- /dev/null +++ b/ref/Entities/IMentionable.cs @@ -0,0 +1,7 @@ +namespace Discord +{ + public interface IMentionable + { + string Mention { get; } + } +} diff --git a/ref/Entities/IModel.cs b/ref/Entities/IModel.cs new file mode 100644 index 000000000..58c016a27 --- /dev/null +++ b/ref/Entities/IModel.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public interface IModel : IModel + { + TId Id { get; } + } + public interface IModel + { + Task Save(); + } +} diff --git a/ref/Entities/Invite.cs b/ref/Entities/Invite.cs new file mode 100644 index 000000000..041ef2e97 --- /dev/null +++ b/ref/Entities/Invite.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; + +namespace Discord +{ + public class Invite : IModel + { + public class ServerInfo + { + public ulong Id { get; } + public string Name { get; } + } + public class ChannelInfo + { + public ulong Id { get; } + public string Name { get; } + } + public class InviterInfo + { + public ulong Id { get; } + public string Name { get; } + public ushort Discriminator { get; } + public string AvatarId { get; } + public string AvatarUrl { get; } + } + + public DiscordClient Client { get; } + + string IModel.Id => Code; + public string Code { get; } + public string XkcdCode { get; } + + public ServerInfo Server { get; } + public ChannelInfo Channel { get; } + public int? MaxAge { get; } + public int Uses { get; } + public int? MaxUses { get; } + public bool IsRevoked { get; } + public bool IsTemporary { get; } + public DateTime CreatedAt { get; } + public string Url { get; } + + public Task Delete() => null; + public Task Accept() => null; + + public Task Save() => null; + } +} diff --git a/ref/Entities/Message.cs b/ref/Entities/Message.cs new file mode 100644 index 000000000..f315315fc --- /dev/null +++ b/ref/Entities/Message.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public class Message : IModel + { + public class Attachment : File + { + public string Id { get; } + public int Size { get; } + public string Filename { get; } + } + + public class Embed + { + public string Url { get; } + public string Type { get; } + public string Title { get; } + public string Description { get; } + public EmbedLink Author { get; } + public EmbedLink Provider { get; } + public File Thumbnail { get; } + public File Video { get; } + } + + public class EmbedLink + { + public string Url { get; } + public string Name { get; } + } + + public class File + { + public string Url { get; } + public string ProxyUrl { get; } + public int? Width { get; } + public int? Height { get; } + } + + public DiscordClient Client { get; } + public ulong Id { get; } + public ITextChannel Channel { get; } + public User User { get; } + public bool IsTTS { get; } + public MessageState State { get; } + public string RawText { get; } + public string Text { get; } + public DateTime Timestamp { get; } + public DateTime? EditedTimestamp { get; } + public Attachment[] Attachments { get; } + public Embed[] Embeds { get; } + + public IEnumerable MentionedUsers { get; } + public IEnumerable MentionedChannels { get; } + public IEnumerable MentionedRoles { get; } + + public Server Server => null; + public bool IsAuthor => false; + + public Task Delete() => null; + + public Task Save() => null; + + public bool IsMentioningMe(bool includeRoles = false) => false; + } +} diff --git a/ref/Entities/Permissions/ChannelPermissions.cs b/ref/Entities/Permissions/ChannelPermissions.cs new file mode 100644 index 000000000..9a4248923 --- /dev/null +++ b/ref/Entities/Permissions/ChannelPermissions.cs @@ -0,0 +1,54 @@ +namespace Discord +{ + public struct ChannelPermissions + { + public static ChannelPermissions None { get; } + public static ChannelPermissions TextOnly { get; } + public static ChannelPermissions PrivateOnly { get; } + public static ChannelPermissions VoiceOnly { get; } + public static ChannelPermissions All(Channel channel) => default(ChannelPermissions); + public static ChannelPermissions All(ChannelType channelType, bool isPrivate) => default(ChannelPermissions); + + public uint RawValue { get; } + + public bool CreateInstantInvit { get; } + public bool ManagePermission { get; } + public bool ManageChannel { get; } + + public bool ReadMessages { get; } + public bool SendMessages { get; } + public bool SendTTSMessages { get; } + public bool ManageMessages { get; } + public bool EmbedLinks { get; } + public bool AttachFiles { get; } + public bool ReadMessageHistory { get; } + public bool MentionEveryone { get; } + + public bool Connect { get; } + public bool Speak { get; } + public bool MuteMembers { get; } + public bool DeafenMembers { get; } + public bool MoveMembers { get; } + public bool UseVoiceActivation { get; } + + public ChannelPermissions(bool? createInstantInvite = null, bool? managePermissions = null, + bool? manageChannel = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, + bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, + bool? mentionEveryone = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + : this() + { + } + public ChannelPermissions(uint rawValue) + : this() + { + } + + public ChannelPermissions Modify(ChannelPermissions basePerms, bool? createInstantInvite = null, bool? managePermissions = null, + bool? manageChannel = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, + bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, + bool? mentionEveryone = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + => default(ChannelPermissions); + } +} diff --git a/ref/Entities/Permissions/PermissionOverwrite.cs b/ref/Entities/Permissions/PermissionOverwrite.cs new file mode 100644 index 000000000..94658160a --- /dev/null +++ b/ref/Entities/Permissions/PermissionOverwrite.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public struct PermissionOverwrite + { + public PermissionTarget TargetType { get; } + public ulong TargetId { get; } + public TriStateChannelPermissions Permissions { get; } + } +} diff --git a/ref/Entities/Permissions/ServerPermissions.cs b/ref/Entities/Permissions/ServerPermissions.cs new file mode 100644 index 000000000..67f208b97 --- /dev/null +++ b/ref/Entities/Permissions/ServerPermissions.cs @@ -0,0 +1,55 @@ +namespace Discord +{ + public struct ServerPermissions + { + public static ServerPermissions None { get; } + public static ServerPermissions All { get; } + + public uint RawValue { get; } + + public bool CreateInstantInvite { get; } + public bool BanMembers { get; } + public bool KickMembers { get; } + public bool ManageRoles { get; } + public bool ManageChannels { get; } + public bool ManageServer { get; } + + public bool ReadMessages { get; } + public bool SendMessages { get; } + public bool SendTTSMessages { get; } + public bool ManageMessages { get; } + public bool EmbedLinks { get; } + public bool AttachFiles { get; } + public bool ReadMessageHistory { get; } + public bool MentionEveryone { get; } + + public bool Connect { get; } + public bool Speak { get; } + public bool MuteMembers { get; } + public bool DeafenMembers { get; } + public bool MoveMembers { get; } + public bool UseVoiceActivation { get; } + + public ServerPermissions(bool? createInstantInvite = null, bool? manageRoles = null, + bool? kickMembers = null, bool? banMembers = null, bool? manageChannel = null, bool? manageServer = null, + bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, + bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, + bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + : this() + { + } + public ServerPermissions(uint rawValue) + : this() + { + } + + public ServerPermissions Modify(ServerPermissions basePerms, bool? createInstantInvite = null, bool? manageRoles = null, + bool? kickMembers = null, bool? banMembers = null, bool? manageChannel = null, bool? manageServer = null, + bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, + bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, + bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + => default(ServerPermissions); + } +} diff --git a/ref/Entities/Permissions/TriStateChannelPermissions.cs b/ref/Entities/Permissions/TriStateChannelPermissions.cs new file mode 100644 index 000000000..7a26bd185 --- /dev/null +++ b/ref/Entities/Permissions/TriStateChannelPermissions.cs @@ -0,0 +1,50 @@ +namespace Discord +{ + public struct TriStateChannelPermissions + { + public static TriStateChannelPermissions InheritAll { get; } + + public uint AllowValue { get; } + public uint DenyValue { get; } + + public PermValue CreateInstantInvite { get; } + public PermValue ManagePermissions { get; } + public PermValue ManageChannel { get; } + public PermValue ReadMessages { get; } + public PermValue SendMessages { get; } + public PermValue SendTTSMessages { get; } + public PermValue ManageMessages { get; } + public PermValue EmbedLinks { get; } + public PermValue AttachFiles { get; } + public PermValue ReadMessageHistory { get; } + public PermValue MentionEveryone { get; } + + public PermValue Connect { get; } + public PermValue Speak { get; } + public PermValue MuteMembers { get; } + public PermValue DeafenMembers { get; } + public PermValue MoveMembers { get; } + public PermValue UseVoiceActivation { get; } + + public TriStateChannelPermissions(PermValue? createInstantInvite = null, PermValue? managePermissions = null, + PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, + PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, + PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, + PermValue? moveMembers = null, PermValue? useVoiceActivation = null) + : this() + { + } + + public TriStateChannelPermissions(uint allow = 0, uint deny = 0) + : this() + { + } + + public TriStateChannelPermissions Modify(PermValue? createInstantInvite = null, PermValue? managePermissions = null, + PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, + PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, + PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, + PermValue? moveMembers = null, PermValue? useVoiceActivation = null) + => default(TriStateChannelPermissions); + } +} diff --git a/ref/Entities/Profile.cs b/ref/Entities/Profile.cs new file mode 100644 index 000000000..067a58988 --- /dev/null +++ b/ref/Entities/Profile.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public class Profile : IModel + { + public DiscordClient Client { get; } + + public ulong Id { get; } + public string AvatarId { get; } + public string AvatarUrl { get; } + public ushort Discriminator { get; } + public string CurrentGame { get; } + public UserStatus Status { get; } + public string Mention { get; } + public string Email { get; } + public bool? IsVerified { get; } + + public string Name { get; set; } + + public Task Save() => null; + } +} diff --git a/ref/Entities/Region.cs b/ref/Entities/Region.cs new file mode 100644 index 000000000..5b0354048 --- /dev/null +++ b/ref/Entities/Region.cs @@ -0,0 +1,20 @@ +namespace Discord +{ + public class Region + { + public string Id { get; } + public string Name { get; } + public string Hostname { get; } + public int Port { get; } + public bool Vip { get; } + + internal Region(string id, string name, string hostname, int port, bool vip) + { + Id = id; + Name = name; + Hostname = hostname; + Port = port; + Vip = vip; + } + } +} diff --git a/ref/Entities/Role.cs b/ref/Entities/Role.cs new file mode 100644 index 000000000..ae2c92988 --- /dev/null +++ b/ref/Entities/Role.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public class Role : IModel, IMentionable + { + public DiscordClient Client { get; } + + public ulong Id { get; } + public Server Server { get; } + + public string Name { get; } + public bool IsHoisted { get; } + public int Position { get; } + public bool IsManaged { get; } + public ServerPermissions Permissions { get; } + public Color Color { get; } + + public bool IsEveryone { get; } + public IEnumerable Members { get; } + + public string Mention { get; } + + public Task Delete() => null; + + public Task Save() => null; + } +} diff --git a/ref/Entities/Server.cs b/ref/Entities/Server.cs new file mode 100644 index 000000000..75b98d979 --- /dev/null +++ b/ref/Entities/Server.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public class Server : IModel + { + public class Emoji + { + public string Id { get; } + public string Name { get; } + public bool IsManaged { get; } + public bool RequireColons { get; } + public IEnumerable Roles { get; } + } + + public ulong Id { get; } + public User CurrentUser { get; } + public string IconId { get; } + public string SplashId { get; } + public string IconUrl { get; } + public string SplashUrl { get; } + public int ChannelCount { get; } + public int UserCount { get; } + public int RoleCount { get; } + public TextChannel DefaultChannel { get; } + public Role EveryoneRole { get; } + public IEnumerable Features { get; } + public IEnumerable CustomEmojis { get; } + public IEnumerable Channels { get; } + public IEnumerable TextChannels { get; } + public IEnumerable VoiceChannels { get; } + public IEnumerable Users { get; } + public IEnumerable Roles { get; } + + public string Name { get; set; } + public Region Region { get; set; } + public int AFKTimeout { get; set; } + public DateTime JoinedAt { get; set; } + public User Owner { get; set; } + public VoiceChannel AFKChannel { get; set; } + + public IPublicChannel GetChannel(ulong id) => null; + public IPublicChannel GetChannel(string mention) => null; + public Role GetRole(ulong id) => null; + public User GetUser(ulong id) => null; + public User GetUser(string name, ushort discriminator) => null; + public User GetUser(string mention) => null; + public Task> DownloadBans() => null; + public Task> DownloadInvites() => null; + + public Task Leave() => null; + public Task Delete() => null; + public Task Save() => null; + + public Task CreateChannel(string name, ChannelType type) => null; + public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; + public Task CreateRole(string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) => null; + + public Task Ban(User user, int pruneDays = 0) => null; + public Task Unban(User user) => null; + public Task Unban(ulong userId) => null; + + public Task ReorderChannels(IEnumerable channels) => null; + public Task ReorderRoles(IEnumerable roles, Role after = null) => null; + + public Task PruneUsers(int days = 30, bool simulate = false) => null; + } +} diff --git a/ref/Entities/User.cs b/ref/Entities/User.cs new file mode 100644 index 000000000..4bef43605 --- /dev/null +++ b/ref/Entities/User.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Discord +{ + public class User : IModel + { + public DiscordClient Client { get; } + + public ulong Id { get; } + public Server Server { get; } + + public string Name { get; } + public ushort Discriminator { get; } + public string AvatarId { get; } + public string CurrentGame { get; } + public UserStatus Status { get; } + public DateTime JoinedAt { get; } + public DateTime? LastActivityAt { get; } + + public Channel PrivateChannel => null; + public string Mention => null; + public bool IsSelfMuted => false; + public bool IsSelfDeafened => false; + public bool IsServerMuted => false; + public bool IsServerDeafened => false; + public bool IsServerSuppressed => false; + public DateTime? LastOnlineAt => null; + public Channel VoiceChannel => null; + public string AvatarUrl => null; + public IEnumerable Roles => null; + + public IEnumerable Channels => null; + + public Task Kick() => null; + + public ServerPermissions ServerPermissions => default(ServerPermissions); + public ChannelPermissions GetPermissions(Channel channel) => default(ChannelPermissions); + + public Task CreatePMChannel() => null; + + public Task SendMessage(string text) => null; + public Task SendFile(string filePath) => null; + public Task SendFile(string filename, Stream stream) => null; + + public bool HasRole(Role role) => false; + + public Task AddRoles(params Role[] roles) => null; + public Task RemoveRoles(params Role[] roles) => null; + + public Task Save() => null; + } +} \ No newline at end of file diff --git a/ref/Enums/ChannelType.cs b/ref/Enums/ChannelType.cs new file mode 100644 index 000000000..84491070b --- /dev/null +++ b/ref/Enums/ChannelType.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public enum ChannelType + { + Text, + Voice + } +} diff --git a/ref/Enums/ConnectionState.cs b/ref/Enums/ConnectionState.cs new file mode 100644 index 000000000..dfd4ac9eb --- /dev/null +++ b/ref/Enums/ConnectionState.cs @@ -0,0 +1,10 @@ +namespace Discord +{ + public enum ConnectionState + { + Disconnected, + Connecting, + Connected, + Disconnecting + } +} diff --git a/ref/Enums/ImageType.cs b/ref/Enums/ImageType.cs new file mode 100644 index 000000000..738c67a3d --- /dev/null +++ b/ref/Enums/ImageType.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public enum ImageType + { + None, + Jpeg, + Png + } +} diff --git a/ref/Enums/LogSeverity.cs b/ref/Enums/LogSeverity.cs new file mode 100644 index 000000000..879aecd09 --- /dev/null +++ b/ref/Enums/LogSeverity.cs @@ -0,0 +1,11 @@ +namespace Discord +{ + public enum LogSeverity + { + Error = 1, + Warning = 2, + Info = 3, + Verbose = 4, + Debug = 5 + } +} diff --git a/ref/Enums/MessageState.cs b/ref/Enums/MessageState.cs new file mode 100644 index 000000000..59f65614d --- /dev/null +++ b/ref/Enums/MessageState.cs @@ -0,0 +1,18 @@ +namespace Discord +{ + public enum MessageState : byte + { + /// Message did not originate from this session, or was successfully sent. + Normal = 0, + /// Message is current queued. + Queued, + /// Message was deleted. + Deleted, + /// Message was deleted before it was sent. + Aborted, + /// Message failed to be sent. + Failed, + /// Message has been removed from cache and will no longer receive updates. + Detached + } +} diff --git a/ref/Enums/PermValue.cs b/ref/Enums/PermValue.cs new file mode 100644 index 000000000..fe048b016 --- /dev/null +++ b/ref/Enums/PermValue.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public enum PermValue + { + Allow, + Deny, + Inherit + } +} diff --git a/ref/Enums/PermissionBits.cs b/ref/Enums/PermissionBits.cs new file mode 100644 index 000000000..b5acab73f --- /dev/null +++ b/ref/Enums/PermissionBits.cs @@ -0,0 +1,31 @@ +namespace Discord +{ + internal enum PermissionBits + { + //General + CreateInstantInvite = 0, + KickMembers = 1, + BanMembers = 2, + ManageRolesOrPermissions = 3, + ManageChannel = 4, + ManageServer = 5, + + //Text + ReadMessages = 10, + SendMessages = 11, + SendTTSMessages = 12, + ManageMessages = 13, + EmbedLinks = 14, + AttachFiles = 15, + ReadMessageHistory = 16, + MentionEveryone = 17, + + //Voice + Connect = 20, + Speak = 21, + MuteMembers = 22, + DeafenMembers = 23, + MoveMembers = 24, + UseVoiceActivation = 25 + } +} diff --git a/ref/Enums/PermissionTarget.cs b/ref/Enums/PermissionTarget.cs new file mode 100644 index 000000000..96595fb69 --- /dev/null +++ b/ref/Enums/PermissionTarget.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public enum PermissionTarget + { + Role, + User + } +} diff --git a/ref/Enums/Relative.cs b/ref/Enums/Relative.cs new file mode 100644 index 000000000..aade047d1 --- /dev/null +++ b/ref/Enums/Relative.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public enum Relative + { + Before, + After + } +} diff --git a/ref/Enums/UserStatus.cs b/ref/Enums/UserStatus.cs new file mode 100644 index 000000000..f2fdfda7c --- /dev/null +++ b/ref/Enums/UserStatus.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public enum UserStatus + { + Online, + Idle, + Offline + } +} diff --git a/ref/Events/ChannelEventArgs.cs b/ref/Events/ChannelEventArgs.cs new file mode 100644 index 000000000..cafdf2fa7 --- /dev/null +++ b/ref/Events/ChannelEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord +{ + public class ChannelEventArgs : EventArgs + { + public Channel Channel => null; + public Server Server => null; + } +} diff --git a/ref/Events/ChannelUpdatedEventArgs.cs b/ref/Events/ChannelUpdatedEventArgs.cs new file mode 100644 index 000000000..bc93ba7ae --- /dev/null +++ b/ref/Events/ChannelUpdatedEventArgs.cs @@ -0,0 +1,11 @@ +using System; + +namespace Discord +{ + public class ChannelUpdatedEventArgs : EventArgs + { + public Channel Before => null; + public Channel After => null; + public Server Server => null; + } +} diff --git a/ref/Events/ChannelUserEventArgs.cs b/ref/Events/ChannelUserEventArgs.cs new file mode 100644 index 000000000..a17856e82 --- /dev/null +++ b/ref/Events/ChannelUserEventArgs.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public class ChannelUserEventArgs + { + public Channel Channel => null; + public User User => null; + } +} diff --git a/ref/Events/DisconnectedEventArgs.cs b/ref/Events/DisconnectedEventArgs.cs new file mode 100644 index 000000000..616f3f09d --- /dev/null +++ b/ref/Events/DisconnectedEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord +{ + public class DisconnectedEventArgs : EventArgs + { + public bool WasUnexpected => false; + public Exception Exception => null; + } +} diff --git a/ref/Events/LogMessageEventArgs.cs b/ref/Events/LogMessageEventArgs.cs new file mode 100644 index 000000000..7dec182d1 --- /dev/null +++ b/ref/Events/LogMessageEventArgs.cs @@ -0,0 +1,12 @@ +using System; + +namespace Discord +{ + public class LogMessageEventArgs : EventArgs + { + public LogSeverity Severity => default(LogSeverity); + public string Source => null; + public string Message => null; + public Exception Exception => null; + } +} diff --git a/ref/Events/MessageEventArgs.cs b/ref/Events/MessageEventArgs.cs new file mode 100644 index 000000000..98eeb4f9d --- /dev/null +++ b/ref/Events/MessageEventArgs.cs @@ -0,0 +1,12 @@ +using System; + +namespace Discord +{ + public class MessageEventArgs : EventArgs + { + public Message Message => null; + public User User => null; + public Channel Channel => null; + public Server Server => null; + } +} diff --git a/ref/Events/MessageUpdatedEventArgs.cs b/ref/Events/MessageUpdatedEventArgs.cs new file mode 100644 index 000000000..d529907a4 --- /dev/null +++ b/ref/Events/MessageUpdatedEventArgs.cs @@ -0,0 +1,13 @@ +using System; + +namespace Discord +{ + public class MessageUpdatedEventArgs : EventArgs + { + public Message Before => null; + public Message After => null; + public User User => null; + public Channel Channel => null; + public Server Server => null; + } +} diff --git a/ref/Events/ProfileUpdatedEventArgs.cs b/ref/Events/ProfileUpdatedEventArgs.cs new file mode 100644 index 000000000..dba55af3b --- /dev/null +++ b/ref/Events/ProfileUpdatedEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord +{ + public class ProfileUpdatedEventArgs : EventArgs + { + public Profile Before => null; + public Profile After => null; + } +} diff --git a/ref/Events/RoleEventArgs.cs b/ref/Events/RoleEventArgs.cs new file mode 100644 index 000000000..db1d09cbc --- /dev/null +++ b/ref/Events/RoleEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord +{ + public class RoleEventArgs : EventArgs + { + public Role Role => null; + public Server Server => null; + } +} diff --git a/ref/Events/RoleUpdatedEventArgs.cs b/ref/Events/RoleUpdatedEventArgs.cs new file mode 100644 index 000000000..1fa0f2a81 --- /dev/null +++ b/ref/Events/RoleUpdatedEventArgs.cs @@ -0,0 +1,11 @@ +using System; + +namespace Discord +{ + public class RoleUpdatedEventArgs : EventArgs + { + public Role Before => null; + public Role After => null; + public Server Server => null; + } +} diff --git a/ref/Events/ServerEventArgs.cs b/ref/Events/ServerEventArgs.cs new file mode 100644 index 000000000..b06993de9 --- /dev/null +++ b/ref/Events/ServerEventArgs.cs @@ -0,0 +1,9 @@ +using System; + +namespace Discord +{ + public class ServerEventArgs : EventArgs + { + public Server Server => null; + } +} diff --git a/ref/Events/ServerUpdatedEventArgs.cs b/ref/Events/ServerUpdatedEventArgs.cs new file mode 100644 index 000000000..1e05f1721 --- /dev/null +++ b/ref/Events/ServerUpdatedEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord +{ + public class ServerUpdatedEventArgs : EventArgs + { + public Server Before => null; + public Server After => null; + } +} diff --git a/ref/Events/UserEventArgs.cs b/ref/Events/UserEventArgs.cs new file mode 100644 index 000000000..4ff745307 --- /dev/null +++ b/ref/Events/UserEventArgs.cs @@ -0,0 +1,9 @@ +using System; +namespace Discord +{ + public class UserEventArgs : EventArgs + { + public User User => null; + public Server Server => null; + } +} diff --git a/ref/Events/UserUpdatedEventArgs.cs b/ref/Events/UserUpdatedEventArgs.cs new file mode 100644 index 000000000..c5b2b0acd --- /dev/null +++ b/ref/Events/UserUpdatedEventArgs.cs @@ -0,0 +1,10 @@ +using System; +namespace Discord +{ + public class UserUpdatedEventArgs : EventArgs + { + public User Before => null; + public User After => null; + public Server Server => null; + } +} diff --git a/ref/Format.cs b/ref/Format.cs new file mode 100644 index 000000000..e30931ae9 --- /dev/null +++ b/ref/Format.cs @@ -0,0 +1,14 @@ +namespace Discord +{ + public static class Format + { + public static string Escape(string text) => null; + + public static string Bold(string text, bool escape = true) => null; + public static string Italics(string text, bool escape = true) => null; + public static string Underline(string text, bool escape = true) => null; + public static string Strikeout(string text, bool escape = true) => null; + + public static string Code(string text, string language = null) => null; + } +} diff --git a/ref/ILogger.cs b/ref/ILogger.cs new file mode 100644 index 000000000..a3123edc9 --- /dev/null +++ b/ref/ILogger.cs @@ -0,0 +1,30 @@ +using System; + +namespace Discord.Logging +{ + public interface ILogger + { + LogSeverity Level { get; } + + void Log(LogSeverity severity, string message, Exception exception = null); + void Error(string message, Exception exception = null); + void Error(Exception exception); + void Warning(string message, Exception exception = null); + void Warning(Exception exception); + void Info(string message, Exception exception = null); + void Info(Exception exception); + void Verbose(string message, Exception exception = null); + void Verbose(Exception exception); + void Debug(string message, Exception exception = null); + void Debug(Exception exception); + +#if DOTNET5_4 + void Log(LogSeverity severity, FormattableString message, Exception exception = null); + void Error(FormattableString message, Exception exception = null); + void Warning(FormattableString message, Exception exception = null); + void Info(FormattableString message, Exception exception = null); + void Verbose(FormattableString message, Exception exception = null); + void Debug(FormattableString message, Exception exception = null); +#endif + } +} diff --git a/ref/MessageQueue.cs b/ref/MessageQueue.cs new file mode 100644 index 000000000..e909bdacf --- /dev/null +++ b/ref/MessageQueue.cs @@ -0,0 +1,9 @@ +namespace Discord.Net +{ + public class MessageQueue + { + public int Count { get; } + + public void Clear() { } + } +} diff --git a/ref/Net/HttpException.cs b/ref/Net/HttpException.cs new file mode 100644 index 000000000..3704ffb83 --- /dev/null +++ b/ref/Net/HttpException.cs @@ -0,0 +1,16 @@ +using System; +using System.Net; + +namespace Discord.Net +{ + public class HttpException : Exception + { + public HttpStatusCode StatusCode { get; } + + public HttpException(HttpStatusCode statusCode) + : base($"The server responded with error {(int)statusCode} ({statusCode})") + { + StatusCode = statusCode; + } + } +} diff --git a/ref/Net/Rest/CompletedRequestEventArgs.cs b/ref/Net/Rest/CompletedRequestEventArgs.cs new file mode 100644 index 000000000..ed9d1673f --- /dev/null +++ b/ref/Net/Rest/CompletedRequestEventArgs.cs @@ -0,0 +1,17 @@ +namespace Discord.Net.Rest +{ + public class CompletedRequestEventArgs : RequestEventArgs + { + public object Response { get; set; } + public string ResponseJson { get; set; } + public double Milliseconds { get; set; } + + public CompletedRequestEventArgs(IRestRequest request, object response, string responseJson, double milliseconds) + : base(request) + { + Response = response; + ResponseJson = responseJson; + Milliseconds = milliseconds; + } + } +} diff --git a/ref/Net/Rest/IRestRequest.cs b/ref/Net/Rest/IRestRequest.cs new file mode 100644 index 000000000..bb8083103 --- /dev/null +++ b/ref/Net/Rest/IRestRequest.cs @@ -0,0 +1,23 @@ +namespace Discord.Net.Rest +{ + public interface IRestRequest + { + string Method { get; } + string Endpoint { get; } + object Payload { get; } + } + public interface IRestRequest : IRestRequest + where ResponseT : class + { + } + + public interface IRestFileRequest : IRestRequest + { + string Filename { get; } + Stream Stream { get; } + } + public interface IRestFileRequest : IRestFileRequest, IRestRequest + where ResponseT : class + { + } +} diff --git a/ref/Net/Rest/RequestEventArgs.cs b/ref/Net/Rest/RequestEventArgs.cs new file mode 100644 index 000000000..cac734fc6 --- /dev/null +++ b/ref/Net/Rest/RequestEventArgs.cs @@ -0,0 +1,12 @@ +using System; + +namespace Discord.Net.Rest +{ + public class RequestEventArgs : EventArgs + { + public IRestRequest Request { get; set; } + public bool Cancel { get; set; } + + public RequestEventArgs(IRestRequest request) { } + } +} diff --git a/ref/Net/Rest/RestClient.cs b/ref/Net/Rest/RestClient.cs new file mode 100644 index 000000000..33de1e9a4 --- /dev/null +++ b/ref/Net/Rest/RestClient.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Rest +{ + public abstract partial class RestClient + { + public event EventHandler SendingRequest = delegate { }; + public event EventHandler SentRequest = delegate { }; + + public CancellationToken CancelToken { get; set; } + public string Token { get; set; } + + public Task Send(IRestRequest request) + where ResponseT : class + => null; + public Task Send(IRestRequest request) => null; + + public Task Send(IRestFileRequest request) + where ResponseT : class + => null; + public Task Send(IRestFileRequest request) => null; + } +} diff --git a/ref/Net/TimeoutException.cs b/ref/Net/TimeoutException.cs new file mode 100644 index 000000000..d1a644049 --- /dev/null +++ b/ref/Net/TimeoutException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Discord.Net +{ + public class TimeoutException : OperationCanceledException + { + public TimeoutException() { } + } +} diff --git a/ref/Net/WebSocketException.cs b/ref/Net/WebSocketException.cs new file mode 100644 index 000000000..df6377e13 --- /dev/null +++ b/ref/Net/WebSocketException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Discord.Net +{ + public class WebSocketException : Exception + { + public int Code { get; } + public string Reason { get; } + + public WebSocketException(int code, string reason) { } + } +} diff --git a/ref/Net/WebSockets/BinaryMessageEventArgs.cs b/ref/Net/WebSockets/BinaryMessageEventArgs.cs new file mode 100644 index 000000000..3fd4425fa --- /dev/null +++ b/ref/Net/WebSockets/BinaryMessageEventArgs.cs @@ -0,0 +1,11 @@ +using System; + +namespace Discord.Net.WebSockets +{ + public class BinaryMessageEventArgs : EventArgs + { + public byte[] Data { get; } + + public BinaryMessageEventArgs(byte[] data) { } + } +} diff --git a/ref/Net/WebSockets/GatewaySocket.cs b/ref/Net/WebSockets/GatewaySocket.cs new file mode 100644 index 000000000..65ec02fad --- /dev/null +++ b/ref/Net/WebSockets/GatewaySocket.cs @@ -0,0 +1,31 @@ +using Discord.Logging; +using Discord.Net.Rest; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.WebSockets +{ + public class GatewaySocket : WebSocket + { + public string SessionId { get; private set; } + + public event EventHandler ReceivedDispatch = delegate { }; + + public GatewaySocket(DiscordConfig config, ILogger logger) : base(config, logger) { } + + public Task Connect(RestClient rest, CancellationToken parentCancelToken) => null; + public Task Disconnect() => null; + + public void SendIdentify(string token) { } + + public void SendResume() { } + public override void SendHeartbeat() { } + public void SendUpdateStatus(long? idleSince, string gameName) { } + public void SendUpdateVoice(ulong? serverId, ulong? channelId, bool isSelfMuted, bool isSelfDeafened) { } + public void SendRequestMembers(IEnumerable serverId, string query, int limit) { } + + public override void WaitForConnection(CancellationToken cancelToken) { } + } +} diff --git a/ref/Net/WebSockets/IWebSocketEngine.cs b/ref/Net/WebSockets/IWebSocketEngine.cs new file mode 100644 index 000000000..68f31f12b --- /dev/null +++ b/ref/Net/WebSockets/IWebSocketEngine.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.WebSockets +{ + public interface IWebSocketEngine + { + event EventHandler BinaryMessage; + event EventHandler TextMessage; + + Task Connect(string host, CancellationToken cancelToken); + Task Disconnect(); + void QueueMessage(string message); + IEnumerable GetTasks(CancellationToken cancelToken); + } +} diff --git a/ref/Net/WebSockets/TextMessageEventArgs.cs b/ref/Net/WebSockets/TextMessageEventArgs.cs new file mode 100644 index 000000000..e4e186044 --- /dev/null +++ b/ref/Net/WebSockets/TextMessageEventArgs.cs @@ -0,0 +1,11 @@ +using System; + +namespace Discord.Net.WebSockets +{ + public class TextMessageEventArgs : EventArgs + { + public string Message { get; } + + public TextMessageEventArgs(string msg) { Message = msg; } + } +} diff --git a/ref/Net/WebSockets/WebSocket.cs b/ref/Net/WebSockets/WebSocket.cs new file mode 100644 index 000000000..1257f2474 --- /dev/null +++ b/ref/Net/WebSockets/WebSocket.cs @@ -0,0 +1,22 @@ +using Discord.Logging; +using System; +using System.Threading; + +namespace Discord.Net.WebSockets +{ + public abstract partial class WebSocket + { + public CancellationToken CancelToken { get; } + public ConnectionState State { get; } + public string Host { get; } + + public event EventHandler Connected = delegate { }; + public event EventHandler Disconnected = delegate { }; + + public WebSocket(DiscordConfig config, ILogger logger) { } + + public abstract void SendHeartbeat(); + + public virtual void WaitForConnection(CancellationToken cancelToken) { } + } +} diff --git a/ref/Net/WebSockets/WebSocketEventEventArgs.cs b/ref/Net/WebSockets/WebSocketEventEventArgs.cs new file mode 100644 index 000000000..a0c60edcf --- /dev/null +++ b/ref/Net/WebSockets/WebSocketEventEventArgs.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json.Linq; +using System; + +namespace Discord.Net.WebSockets +{ + public class WebSocketEventEventArgs : EventArgs + { + public string Type { get; } + public JToken Payload { get; } + + internal WebSocketEventEventArgs(string type, JToken data) + { + Type = type; + Payload = data; + } + } +} diff --git a/ref/project.json b/ref/project.json new file mode 100644 index 000000000..565bc2e86 --- /dev/null +++ b/ref/project.json @@ -0,0 +1,81 @@ +{ + "version": "0.9.0-rc3-3", + "description": "An unofficial .Net API wrapper for the Discord client.", + "authors": [ + "RogueException" + ], + "tags": [ + "discord", + "discordapp" + ], + "projectUrl": "https://github.com/RogueException/Discord.Net", + "licenseUrl": "http://opensource.org/licenses/MIT", + "repository": { + "type": "git", + "url": "git://github.com/RogueException/Discord.Net" + }, + "compile": [ "**/*.cs", "../Discord.Net.Shared/*.cs" ], + + "compilationOptions": { + "allowUnsafe": true, + "warningsAsErrors": true + }, + + "configurations": { + "TestResponses": { + "compilationOptions": { + "define": [ + "DEBUG", + "TRACE", + "TEST_RESPONSES" + ] + } + } + }, + + "dependencies": { + "Newtonsoft.Json": "8.0.1", + "Nito.AsyncEx": "3.0.1" + }, + + "frameworks": { + "dotnet5.4": { + "dependencies": { + "System.Collections": "4.0.11-beta-23516", + "System.Collections.Concurrent": "4.0.11-beta-23516", + "System.Dynamic.Runtime": "4.0.11-beta-23516", + "System.IO.FileSystem": "4.0.1-beta-23516", + "System.IO.Compression": "4.1.0-beta-23516", + "System.Linq": "4.0.1-beta-23516", + "System.Net.Http": "4.0.1-beta-23516", + "System.Net.NameResolution": "4.0.0-beta-23516", + "System.Net.Sockets": "4.1.0-beta-23409", + "System.Net.Requests": "4.0.11-beta-23516", + "System.Net.WebSockets.Client": "4.0.0-beta-23516", + "System.Reflection": "4.1.0-beta-23516", + "System.Reflection.Emit.Lightweight": "4.0.1-beta-23516", + "System.Runtime.InteropServices": "4.0.21-beta-23516", + "System.Runtime.Serialization.Primitives": "4.1.0-beta-23516", + "System.Security.Cryptography.Algorithms": "4.0.0-beta-23516", + "System.Text.RegularExpressions": "4.0.11-beta-23516", + "System.Threading": "4.0.11-beta-23516" + } + }, + "net45": { + "frameworkAssemblies": { + "System.Runtime": { + "type": "build", + "version": "" + }, + "System.Threading.Tasks": { + "type": "build", + "version": "" + } + }, + "dependencies": { + "WebSocket4Net": "0.14.1", + "RestSharp": "105.2.3" + } + } + } +} \ No newline at end of file From 646259baa5b62600dd2f8c35d2a0c503106158d4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 24 Feb 2016 19:37:08 -0400 Subject: [PATCH 40/51] Cleaned up channels and converted more models to new layout --- src/Discord.Net/DiscordClient.cs | 20 +- .../{Models => Entities}/Channel.cs | 0 src/Discord.Net/{Models => Entities}/Color.cs | 0 .../{Models => Entities}/IChannel.cs | 0 src/Discord.Net/Entities/IPrivateChannel.cs | 7 + .../{Models => Entities}/IPublicChannel.cs | 0 .../{Models => Entities}/ITextChannel.cs | 0 .../{Models => Entities}/IVoiceChannel.cs | 0 .../{Models => Entities}/Invite.cs | 38 +- .../Managers/MessageManager.cs | 28 +- .../Managers/PermissionManager.cs | 0 .../{Models => Entities}/Message.cs | 33 +- .../{Models => Entities}/Permissions.cs | 0 .../{Models => Entities}/PrivateChannel.cs | 0 .../{Models => Entities}/Profile.cs | 14 +- .../{Models => Entities}/PublicChannel.cs | 2 +- .../{Models => Entities}/Region.cs | 0 src/Discord.Net/{Models => Entities}/Role.cs | 3 +- .../{Models => Entities}/Server.cs | 27 +- .../{Models => Entities}/TextChannel.cs | 0 src/Discord.Net/{Models => Entities}/User.cs | 65 +-- .../{Models => Entities}/VoiceChannel.cs | 0 src/Discord.Net/Enums/MessageState.cs | 18 + src/Discord.Net/Legacy.cs | 377 ------------------ 24 files changed, 139 insertions(+), 493 deletions(-) rename src/Discord.Net/{Models => Entities}/Channel.cs (100%) rename src/Discord.Net/{Models => Entities}/Color.cs (100%) rename src/Discord.Net/{Models => Entities}/IChannel.cs (100%) create mode 100644 src/Discord.Net/Entities/IPrivateChannel.cs rename src/Discord.Net/{Models => Entities}/IPublicChannel.cs (100%) rename src/Discord.Net/{Models => Entities}/ITextChannel.cs (100%) rename src/Discord.Net/{Models => Entities}/IVoiceChannel.cs (100%) rename src/Discord.Net/{Models => Entities}/Invite.cs (84%) rename src/Discord.Net/{Models => Entities}/Managers/MessageManager.cs (84%) rename src/Discord.Net/{Models => Entities}/Managers/PermissionManager.cs (100%) rename src/Discord.Net/{Models => Entities}/Message.cs (94%) rename src/Discord.Net/{Models => Entities}/Permissions.cs (100%) rename src/Discord.Net/{Models => Entities}/PrivateChannel.cs (100%) rename src/Discord.Net/{Models => Entities}/Profile.cs (91%) rename src/Discord.Net/{Models => Entities}/PublicChannel.cs (98%) rename src/Discord.Net/{Models => Entities}/Region.cs (100%) rename src/Discord.Net/{Models => Entities}/Role.cs (98%) rename src/Discord.Net/{Models => Entities}/Server.cs (96%) rename src/Discord.Net/{Models => Entities}/TextChannel.cs (100%) rename src/Discord.Net/{Models => Entities}/User.cs (93%) rename src/Discord.Net/{Models => Entities}/VoiceChannel.cs (100%) create mode 100644 src/Discord.Net/Enums/MessageState.cs delete mode 100644 src/Discord.Net/Legacy.cs diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index ac29228dc..5fa94bbc2 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -365,7 +365,7 @@ namespace Discord private PrivateChannel AddPrivateChannel(APIChannel model) { IChannel channel; - if (_channels.TryGetOrAdd(model.Id, x => new PrivateChannel(x, new User(this, model.Recipient.Id, null), model), out channel)) + if (_channels.TryGetOrAdd(model.Id, x => new PrivateChannel(x, new User(model.Recipient, this, null), model), out channel)) _privateChannels[model.Recipient.Id] = channel as PrivateChannel; return channel as PrivateChannel; } @@ -407,7 +407,7 @@ namespace Discord try { var response = await ClientAPI.Send(new GetInviteRequest(inviteIdOrXkcd)).ConfigureAwait(false); - var invite = new Invite(this, response.Code, response.XkcdPass); + var invite = new Invite(response, this); invite.Update(response); return invite; } @@ -430,7 +430,7 @@ namespace Discord #endregion #region Servers - private Server AddServer(ulong id) => _servers.GetOrAdd(id, x => new Server(this, x)); + private Server AddServer(ulong id) => _servers.GetOrAdd(id, x => new Server(x, this)); private Server RemoveServer(ulong id) { Server server; @@ -494,9 +494,9 @@ namespace Discord List largeServers = new List(); SessionId = data.SessionId; - PrivateUser = new User(this, data.User.Id, null); + PrivateUser = new User(data.User, this, null); PrivateUser.Update(data.User); - CurrentUser = new Profile(this, data.User.Id); + CurrentUser = new Profile(data.User, this); CurrentUser.Update(data.User); for (int i = 0; i < data.Guilds.Length; i++) @@ -633,7 +633,7 @@ namespace Discord var server = GetServer(data.GuildId.Value); if (server != null) { - var user = server.AddUser(data.User.Id, true, true); + var user = server.AddUser(data, true, true); user.Update(data); user.UpdateActivity(); Logger.Info($"GUILD_MEMBER_ADD: {user}"); @@ -691,7 +691,7 @@ namespace Discord { foreach (var memberData in data.Members) { - var user = server.AddUser(memberData.User.Id, true, false); + var user = server.AddUser(memberData, true, false); user.Update(memberData); } Logger.Verbose($"GUILD_MEMBERS_CHUNK: {data.Members.Length} users"); @@ -796,7 +796,7 @@ namespace Discord var server = GetServer(data.GuildId.Value); if (server != null) { - var user = new User(this, data.User.Id, server); + var user = new User(data.User, this, server); user.Update(data.User); Logger.Info($"GUILD_BAN_REMOVE: {user}"); OnUserUnbanned(user); @@ -819,9 +819,7 @@ namespace Discord if (user != null) { Message msg = null; - msg = (channel as Channel).MessageManager.Add(data.Id, user, data.Timestamp.Value); - - msg.Update(data); + msg = (channel as Channel).MessageManager.Add(data, user); user.UpdateActivity(); Logger.Verbose($"MESSAGE_CREATE: {channel} ({user})"); diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Entities/Channel.cs similarity index 100% rename from src/Discord.Net/Models/Channel.cs rename to src/Discord.Net/Entities/Channel.cs diff --git a/src/Discord.Net/Models/Color.cs b/src/Discord.Net/Entities/Color.cs similarity index 100% rename from src/Discord.Net/Models/Color.cs rename to src/Discord.Net/Entities/Color.cs diff --git a/src/Discord.Net/Models/IChannel.cs b/src/Discord.Net/Entities/IChannel.cs similarity index 100% rename from src/Discord.Net/Models/IChannel.cs rename to src/Discord.Net/Entities/IChannel.cs diff --git a/src/Discord.Net/Entities/IPrivateChannel.cs b/src/Discord.Net/Entities/IPrivateChannel.cs new file mode 100644 index 000000000..44d55a67f --- /dev/null +++ b/src/Discord.Net/Entities/IPrivateChannel.cs @@ -0,0 +1,7 @@ +namespace Discord +{ + public interface IPrivateChannel : IChannel + { + User Recipient { get; } + } +} diff --git a/src/Discord.Net/Models/IPublicChannel.cs b/src/Discord.Net/Entities/IPublicChannel.cs similarity index 100% rename from src/Discord.Net/Models/IPublicChannel.cs rename to src/Discord.Net/Entities/IPublicChannel.cs diff --git a/src/Discord.Net/Models/ITextChannel.cs b/src/Discord.Net/Entities/ITextChannel.cs similarity index 100% rename from src/Discord.Net/Models/ITextChannel.cs rename to src/Discord.Net/Entities/ITextChannel.cs diff --git a/src/Discord.Net/Models/IVoiceChannel.cs b/src/Discord.Net/Entities/IVoiceChannel.cs similarity index 100% rename from src/Discord.Net/Models/IVoiceChannel.cs rename to src/Discord.Net/Entities/IVoiceChannel.cs diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Entities/Invite.cs similarity index 84% rename from src/Discord.Net/Models/Invite.cs rename to src/Discord.Net/Entities/Invite.cs index f8ee235ba..a1b27ce4a 100644 --- a/src/Discord.Net/Models/Invite.cs +++ b/src/Discord.Net/Entities/Invite.cs @@ -88,20 +88,24 @@ namespace Discord /// Returns a URL for this invite using XkcdCode if available or Id if not. public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; - internal Invite(DiscordClient client, string code, string xkcdPass) + internal Invite(APIInvite model, DiscordClient client) + : this(model.Code, model.XkcdPass) { Client = client; + Update(model); + } + internal Invite(InviteReference model, DiscordClient client) + : this(model.Code, model.XkcdPass) + { + Client = client; + Update(model); + } + private Invite(string code, string xkcdCode) + { Code = code; - XkcdCode = xkcdPass; - } - - internal void Update(InviteReference model) - { - if (model.Guild != null) - Server = new ServerInfo(model.Guild.Id, model.Guild.Name); - if (model.Channel != null) - Channel = new ChannelInfo(model.Channel.Id, model.Channel.Name); + XkcdCode = xkcdCode; } + internal void Update(APIInvite model) { Update(model as InviteReference); @@ -118,8 +122,15 @@ namespace Discord Uses = model.Uses.Value; if (model.CreatedAt != null) CreatedAt = model.CreatedAt.Value; - } - + } + internal void Update(InviteReference model) + { + if (model.Guild != null) + Server = new ServerInfo(model.Guild.Id, model.Guild.Name); + if (model.Channel != null) + Channel = new ChannelInfo(model.Channel.Id, model.Channel.Name); + } + public async Task Delete() { try { await Client.ClientAPI.Send(new DeleteInviteRequest(Code)).ConfigureAwait(false); } @@ -130,11 +141,10 @@ namespace Discord internal Invite Clone() { - var result = new Invite(); + var result = new Invite(Code, XkcdCode); _cloner(this, result); return result; } - private Invite() { } //Used for cloning public override string ToString() => $"{Server}/{XkcdCode ?? Code}"; } diff --git a/src/Discord.Net/Models/Managers/MessageManager.cs b/src/Discord.Net/Entities/Managers/MessageManager.cs similarity index 84% rename from src/Discord.Net/Models/Managers/MessageManager.cs rename to src/Discord.Net/Entities/Managers/MessageManager.cs index 88a95dc48..14b569750 100644 --- a/src/Discord.Net/Models/Managers/MessageManager.cs +++ b/src/Discord.Net/Entities/Managers/MessageManager.cs @@ -1,4 +1,5 @@ -using Discord.API.Client.Rest; +using APIMessage = Discord.API.Client.Message; +using Discord.API.Client.Rest; using Discord.Net; using System; using System.Collections; @@ -29,15 +30,16 @@ namespace Discord } } - internal Message Add(ulong id, User user, DateTime timestamp) + internal Message Add(APIMessage model, User user) => Add(new Message(model, _channel, user)); + internal Message Add(ulong id, User user) => Add(new Message(id, _channel, user)); + private Message Add(Message message) { - Message message = new Message(id, _channel, user); message.State = MessageState.Normal; if (_size > 0) { - if (_messages.TryAdd(id, message)) + if (_messages.TryAdd(message.Id, message)) { - _orderedMessages.Enqueue(id); + _orderedMessages.Enqueue(message.Id); ulong msgId; while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId)) @@ -93,15 +95,7 @@ namespace Discord Message msg = null; ulong id = x.Author.Id; var user = server?.GetUser(id) ?? (_channel as Channel).GetUser(id); - /*if (useCache) - { - msg = Add(x.Id, user, x.Timestamp.Value); - if (user != null) - user.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp); - } - else*/ - msg = new Message(x.Id, _channel, user); - msg.Update(x); + msg = new Message(x, _channel, user); return msg; }).ToArray(); } @@ -130,11 +124,9 @@ namespace Discord Filename = filename, Stream = stream }; - var model = await _channel.Client.ClientAPI.Send(request).ConfigureAwait(false); + var response = await _channel.Client.ClientAPI.Send(request).ConfigureAwait(false); - var msg = Add(model.Id, (_channel as Channel).CurrentUser, model.Timestamp.Value); - msg.Update(model); - return msg; + return Add(response, (_channel as Channel).CurrentUser); } public IEnumerator GetEnumerator() diff --git a/src/Discord.Net/Models/Managers/PermissionManager.cs b/src/Discord.Net/Entities/Managers/PermissionManager.cs similarity index 100% rename from src/Discord.Net/Models/Managers/PermissionManager.cs rename to src/Discord.Net/Entities/Managers/PermissionManager.cs diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Entities/Message.cs similarity index 94% rename from src/Discord.Net/Models/Message.cs rename to src/Discord.Net/Entities/Message.cs index 6a45b2ec1..a812cd897 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Entities/Message.cs @@ -7,22 +7,6 @@ using APIMessage = Discord.API.Client.Message; namespace Discord { - public enum MessageState : byte - { - /// Message did not originate from this session, or was successfully sent. - Normal = 0, - /// Message is current queued. - Queued, - /// Message was deleted. - Deleted, - /// Message was deleted before it was sent. - Aborted, - /// Message failed to be sent. - Failed, - /// Message has been removed from cache and will no longer receive updates. - Detached - } - public class Message { private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); @@ -198,15 +182,19 @@ namespace Discord /// Returns if this message was sent from the logged-in accounts. public bool IsAuthor => User != null && User.Id == Client.CurrentUser?.Id; - internal Message(ulong id, ITextChannel channel, User user) + internal Message(APIMessage model, ITextChannel channel, User user) + : this(model.Id, channel, user) { + Update(model); + } + internal Message(ulong id, ITextChannel channel, User user) + { Id = id; Channel = channel; User = user; - - Attachments = _initialAttachments; - Embeds = _initialEmbeds; - } + Attachments = _initialAttachments; + Embeds = _initialEmbeds; + } internal void Update(APIMessage model) { @@ -329,11 +317,10 @@ namespace Discord internal Message Clone() { - var result = new Message(); + var result = new Message(Id, Channel, User); _cloner(this, result); return result; } - private Message() { } //Used for cloning public override string ToString() => $"{User}: {RawText}"; } diff --git a/src/Discord.Net/Models/Permissions.cs b/src/Discord.Net/Entities/Permissions.cs similarity index 100% rename from src/Discord.Net/Models/Permissions.cs rename to src/Discord.Net/Entities/Permissions.cs diff --git a/src/Discord.Net/Models/PrivateChannel.cs b/src/Discord.Net/Entities/PrivateChannel.cs similarity index 100% rename from src/Discord.Net/Models/PrivateChannel.cs rename to src/Discord.Net/Entities/PrivateChannel.cs diff --git a/src/Discord.Net/Models/Profile.cs b/src/Discord.Net/Entities/Profile.cs similarity index 91% rename from src/Discord.Net/Models/Profile.cs rename to src/Discord.Net/Entities/Profile.cs index 4e499325a..7029e4c41 100644 --- a/src/Discord.Net/Models/Profile.cs +++ b/src/Discord.Net/Entities/Profile.cs @@ -1,4 +1,5 @@ -using Discord.API.Client.Rest; +using Discord.API.Client; +using Discord.API.Client.Rest; using System; using System.IO; using System.Threading.Tasks; @@ -34,10 +35,14 @@ namespace Discord /// Gets if the email for this user has been verified. public bool? IsVerified { get; private set; } - internal Profile(DiscordClient client, ulong id) + internal Profile(UserReference model, DiscordClient client) + : this(model.Id, client) + { + } + private Profile(ulong id, DiscordClient client) { - Client = client; Id = id; + Client = client; } internal void Update(APIUser model) @@ -77,11 +82,10 @@ namespace Discord internal Profile Clone() { - var result = new Profile(); + var result = new Profile(Id, Client); _cloner(this, result); return result; } - private Profile() { } //Used for cloning public override string ToString() => Name; } diff --git a/src/Discord.Net/Models/PublicChannel.cs b/src/Discord.Net/Entities/PublicChannel.cs similarity index 98% rename from src/Discord.Net/Models/PublicChannel.cs rename to src/Discord.Net/Entities/PublicChannel.cs index 112a250ce..a1d5c4f41 100644 --- a/src/Discord.Net/Models/PublicChannel.cs +++ b/src/Discord.Net/Entities/PublicChannel.cs @@ -97,7 +97,7 @@ namespace Discord }; var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); - var invite = new Invite(Client, response.Code, response.XkcdPass); + var invite = new Invite(response, Client); return invite; } diff --git a/src/Discord.Net/Models/Region.cs b/src/Discord.Net/Entities/Region.cs similarity index 100% rename from src/Discord.Net/Models/Region.cs rename to src/Discord.Net/Entities/Region.cs diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Entities/Role.cs similarity index 98% rename from src/Discord.Net/Models/Role.cs rename to src/Discord.Net/Entities/Role.cs index 6fb989fec..4a7955994 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Entities/Role.cs @@ -124,11 +124,10 @@ namespace Discord internal Role Clone() { - var result = new Role(); + var result = new Role(Id, Server); _cloner(this, result); return result; } - private Role() { } //Used for cloning public override string ToString() => $"{Server}/{Name ?? Id.ToString()}"; } diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Entities/Server.cs similarity index 96% rename from src/Discord.Net/Models/Server.cs rename to src/Discord.Net/Entities/Server.cs index 85e74b4ac..a5e586126 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Entities/Server.cs @@ -1,4 +1,5 @@ using APIChannel = Discord.API.Client.Channel; +using APIMember = Discord.API.Client.Member; using Discord.API.Client; using Discord.API.Client.Rest; using Discord.Net; @@ -112,10 +113,10 @@ namespace Discord /// Gets the number of roles in this server. public int RoleCount => _roles.Count; - internal Server(DiscordClient client, ulong id) + internal Server(ulong id, DiscordClient client) { - Client = client; Id = id; + Client = client; } internal void Update(Guild model) @@ -181,7 +182,7 @@ namespace Discord if (model.Members != null) { foreach (var subModel in model.Members) - AddUser(subModel.User.Id, true, false).Update(subModel); + AddUser(subModel, true, false).Update(subModel); } if (model.VoiceStates != null) { @@ -230,8 +231,7 @@ namespace Discord var response = await Client.ClientAPI.Send(new GetBansRequest(Id)).ConfigureAwait(false); return response.Select(x => { - var user = new User(Client, x.Id, this); - user.Update(x); + var user = new User(x, Client, this); return user; }); } @@ -305,12 +305,12 @@ namespace Discord #region Invites /// Gets all active (non-expired) invites to this server. - public async Task> GetInvites() + public async Task> DownloadInvites() { var response = await Client.ClientAPI.Send(new GetInvitesRequest(Id)).ConfigureAwait(false); return response.Select(x => { - var invite = new Invite(Client, x.Code, x.XkcdPass); + var invite = new Invite(x, Client); invite.Update(x); return invite; }); @@ -429,22 +429,22 @@ namespace Discord #endregion #region Users - internal User AddUser(ulong id, bool cachePerms, bool incrementCount) + internal User AddUser(APIMember model, bool cachePerms, bool incrementCount) { if (incrementCount) _userCount++; Member member; - if (!_users.TryGetValue(id, out member)) //Users can only be added from websocket thread, ignore threadsafety + if (!_users.TryGetValue(model.User.Id, out member)) //Users can only be added from websocket thread, ignore threadsafety { - member = new Member(new User(Client, id, this), ServerPermissions.None); - if (id == Client.CurrentUser.Id) + member = new Member(new User(model, Client, this), ServerPermissions.None); + if (model.User.Id == Client.CurrentUser.Id) { member.User.CurrentGame = Client.CurrentGame; member.User.Status = Client.Status; } - _users[id] = member; + _users[model.User.Id] = member; if (cachePerms && Client.Config.UsePermissionsCache) { foreach (var channel in _channels) @@ -501,11 +501,10 @@ namespace Discord internal Server Clone() { - var result = new Server(); + var result = new Server(Id, Client); _cloner(this, result); return result; } - private Server() { } //Used for cloning public override string ToString() => Name ?? Id.ToIdString(); } diff --git a/src/Discord.Net/Models/TextChannel.cs b/src/Discord.Net/Entities/TextChannel.cs similarity index 100% rename from src/Discord.Net/Models/TextChannel.cs rename to src/Discord.Net/Entities/TextChannel.cs diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Entities/User.cs similarity index 93% rename from src/Discord.Net/Models/User.cs rename to src/Discord.Net/Entities/User.cs index ac2bbe17b..89a0b1133 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Entities/User.cs @@ -19,12 +19,12 @@ namespace Discord [Flags] private enum VoiceState : byte { - None = 0x0, + Normal = 0x0, SelfMuted = 0x01, SelfDeafened = 0x02, - ServerMuted = 0x04, - ServerDeafened = 0x08, - ServerSuppressed = 0x10, + ServerMuted = 0x10, + ServerDeafened = 0x20, + ServerSuppressed = 0x40, } internal struct CompositeKey : IEquatable @@ -139,18 +139,42 @@ namespace Discord } } - internal User(DiscordClient client, ulong id, Server server) - { + internal User(ExtendedMember model, DiscordClient client, Server server) + : this(model as APIMember, client, server) + { + if (model.IsServerMuted == true) + _voiceState |= VoiceState.ServerMuted; + else if (model.IsServerMuted == false) + _voiceState &= ~VoiceState.ServerMuted; + + if (model.IsServerDeafened == true) + _voiceState |= VoiceState.ServerDeafened; + else if (model.IsServerDeafened == false) + _voiceState &= ~VoiceState.ServerDeafened; + } + internal User(APIMember model, DiscordClient client, Server server) + : this(model.User.Id, client, server) + { + if (server == null) + UpdateRoles(null); + Update(model); + } + internal User(UserReference model, DiscordClient client, Server server) + : this(model.Id, client, server) + { + if (server == null) + UpdateRoles(null); + Update(model); + } + private User(ulong id, DiscordClient client, Server server) + { Client = client; Id = id; Server = server; - _roles = new Dictionary(); - Status = UserStatus.Offline; - - if (server == null) - UpdateRoles(null); - } + _roles = new Dictionary(); + Status = UserStatus.Offline; + } internal void Update(UserReference model) { @@ -170,20 +194,6 @@ namespace Discord JoinedAt = model.JoinedAt.Value; if (model.Roles != null) UpdateRoles(model.Roles.Select(x => Server.GetRole(x))); - } - internal void Update(ExtendedMember model) - { - Update(model as APIMember); - - if (model.IsServerMuted == true) - _voiceState |= VoiceState.ServerMuted; - else if (model.IsServerMuted == false) - _voiceState &= ~VoiceState.ServerMuted; - - if (model.IsServerDeafened == true) - _voiceState |= VoiceState.ServerDeafened; - else if (model.IsServerDeafened == false) - _voiceState &= ~VoiceState.ServerDeafened; } internal void Update(MemberPresence model) { @@ -323,11 +333,10 @@ namespace Discord internal User Clone() { - var result = new User(); + var result = new User(Id, Client, Server); _cloner(this, result); return result; } - private User() { } //Used for cloning public override string ToString() { diff --git a/src/Discord.Net/Models/VoiceChannel.cs b/src/Discord.Net/Entities/VoiceChannel.cs similarity index 100% rename from src/Discord.Net/Models/VoiceChannel.cs rename to src/Discord.Net/Entities/VoiceChannel.cs diff --git a/src/Discord.Net/Enums/MessageState.cs b/src/Discord.Net/Enums/MessageState.cs new file mode 100644 index 000000000..59f65614d --- /dev/null +++ b/src/Discord.Net/Enums/MessageState.cs @@ -0,0 +1,18 @@ +namespace Discord +{ + public enum MessageState : byte + { + /// Message did not originate from this session, or was successfully sent. + Normal = 0, + /// Message is current queued. + Queued, + /// Message was deleted. + Deleted, + /// Message was deleted before it was sent. + Aborted, + /// Message failed to be sent. + Failed, + /// Message has been removed from cache and will no longer receive updates. + Detached + } +} diff --git a/src/Discord.Net/Legacy.cs b/src/Discord.Net/Legacy.cs deleted file mode 100644 index be4cea7c0..000000000 --- a/src/Discord.Net/Legacy.cs +++ /dev/null @@ -1,377 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Discord.Legacy -{ - public static class Mention - { - /// Returns the string used to create a user mention. - [Obsolete("Use User.Mention instead")] - public static string User(User user) - => user.Mention; - /// Returns the string used to create a channel mention. - [Obsolete("Use Channel.Mention instead")] - public static string Channel(Channel channel) - => channel.Mention; - /// Returns the string used to create a mention to everyone in a channel. - [Obsolete("Use Server.EveryoneRole.Mention instead")] - public static string Everyone() - => $"@everyone"; - } - - public static class LegacyExtensions - { - [Obsolete("Use DiscordClient.ExecuteAndWait")] - public static void Run(this DiscordClient client, Func asyncAction) - { - client.ExecuteAndWait(asyncAction); - } - [Obsolete("Use DiscordClient.Wait")] - public static void Run(this DiscordClient client) - { - client.Wait(); - } - - [Obsolete("Use Server.FindChannels")] - public static IEnumerable FindChannels(this DiscordClient client, Server server, string name, ChannelType type = null, bool exactMatch = false) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.FindChannels(name, type, exactMatch); - } - - [Obsolete("Use Server.CreateChannel")] - public static Task CreateChannel(this DiscordClient client, Server server, string name, ChannelType type) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.CreateChannel(name, type); - } - [Obsolete("Use User.CreateChannel")] - public static Task CreatePMChannel(this DiscordClient client, User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return user.CreatePMChannel(); - } - [Obsolete("Use Channel.Edit")] - public static Task EditChannel(this DiscordClient client, Channel channel, string name = null, string topic = null, int? position = null) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.Edit(name, topic, position); - } - [Obsolete("Use Channel.Delete")] - public static Task DeleteChannel(this DiscordClient client, Channel channel) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.Delete(); - } - - [Obsolete("Use Server.ReorderChannels")] - public static Task ReorderChannels(this DiscordClient client, Server server, IEnumerable channels, Channel after = null) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.ReorderChannels(channels, after); - } - - [Obsolete("Use Server.GetInvites")] - public static Task> GetInvites(this DiscordClient client, Server server) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.GetInvites(); - } - - [Obsolete("Use Server.CreateInvite")] - public static Task CreateInvite(this DiscordClient client, Server server, int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.CreateInvite(maxAge, maxUses, tempMembership, withXkcd); - } - [Obsolete("Use Channel.CreateInvite")] - public static Task CreateInvite(this DiscordClient client, Channel channel, int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.CreateInvite(maxAge, maxUses, tempMembership, withXkcd); - } - - [Obsolete("Use Invite.Delete")] - public static Task DeleteInvite(this DiscordClient client, Invite invite) - { - if (invite == null) throw new ArgumentNullException(nameof(invite)); - return invite.Delete(); - } - [Obsolete("Use Invite.Accept")] - public static Task AcceptInvite(this DiscordClient client, Invite invite) - { - if (invite == null) throw new ArgumentNullException(nameof(invite)); - return invite.Accept(); - } - - [Obsolete("Use Channel.SendMessage")] - public static Task SendMessage(this DiscordClient client, Channel channel, string text) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.SendMessage(text); - } - [Obsolete("Use Channel.SendTTSMessage")] - public static Task SendTTSMessage(this DiscordClient client, Channel channel, string text) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.SendTTSMessage(text); - } - [Obsolete("Use Channel.SendFile")] - public static Task SendFile(this DiscordClient client, Channel channel, string filePath) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.SendFile(filePath); - } - [Obsolete("Use Channel.SendFile")] - public static Task SendFile(this DiscordClient client, Channel channel, string filename, Stream stream) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.SendFile(filename, stream); - } - [Obsolete("Use User.SendMessage")] - public static Task SendMessage(this DiscordClient client, User user, string text) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return user.SendMessage(text); - } - [Obsolete("Use User.SendFile")] - public static Task SendFile(this DiscordClient client, User user, string filePath) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return user.SendFile(filePath); - } - [Obsolete("Use User.SendFile")] - public static Task SendFile(this DiscordClient client, User user, string filename, Stream stream) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return user.SendFile(filename, stream); - } - - [Obsolete("Use Message.Edit")] - public static Task EditMessage(this DiscordClient client, Message message, string text) - { - if (message == null) throw new ArgumentNullException(nameof(message)); - return message.Edit(text); - } - - [Obsolete("Use Message.Delete")] - public static Task DeleteMessage(this DiscordClient client, Message message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); - return message.Delete(); - } - [Obsolete("Use Message.Delete")] - public static async Task DeleteMessages(this DiscordClient client, IEnumerable messages) - { - if (messages == null) throw new ArgumentNullException(nameof(messages)); - - foreach (var message in messages) - await message.Delete().ConfigureAwait(false); - } - - [Obsolete("Use Channel.DownloadMessages")] - public static Task DownloadMessages(this DiscordClient client, Channel channel, int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before, bool useCache = true) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.DownloadMessages(limit, relativeMessageId, relativeDir, useCache); - } - - [Obsolete("Use Server.GetUser")] - public static User GetUser(this DiscordClient client, Server server, ulong userId) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.GetUser(userId); - } - [Obsolete("Use Server.GetUser")] - public static User GetUser(this DiscordClient client, Server server, string username, ushort discriminator) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.GetUser(username, discriminator); - } - - [Obsolete("Use Server.FindUsers")] - public static IEnumerable FindUsers(this DiscordClient client, Server server, string name, bool exactMatch = false) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.FindUsers(name, exactMatch); - } - [Obsolete("Use Channel.FindUsers")] - public static IEnumerable FindUsers(this DiscordClient client, Channel channel, string name, bool exactMatch = false) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.FindUsers(name, exactMatch); - } - - [Obsolete("Use User.Edit")] - public static Task EditUser(this DiscordClient client, User user, bool? isMuted = null, bool? isDeafened = null, Channel voiceChannel = null, IEnumerable roles = null) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return user.Edit(isMuted, isDeafened, voiceChannel, roles); - } - - [Obsolete("Use User.Kick")] - public static Task KickUser(this DiscordClient client, User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - return user.Kick(); - } - [Obsolete("Use Server.Ban")] - public static Task BanUser(this DiscordClient client, User user, int pruneDays = 0) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - var server = user.Server; - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.Ban(user, pruneDays); - } - [Obsolete("Use Server.Unban")] - public static Task UnbanUser(this DiscordClient client, User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - var server = user.Server; - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.Unban(user); - } - [Obsolete("Use Server.Unban")] - public static Task UnbanUser(this DiscordClient client, Server server, ulong userId) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.Unban(userId); - } - - [Obsolete("Use Server.PruneUsers")] - public static Task PruneUsers(this DiscordClient client, Server server, int days = 30, bool simulate = false) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.PruneUsers(days, simulate); - } - - [Obsolete("Use DiscordClient.CurrentUser.Edit")] - public static Task EditProfile(this DiscordClient client, string currentPassword = "", - string username = null, string email = null, string password = null, - Stream avatar = null, ImageType avatarType = ImageType.Png) - => client.CurrentUser.Edit(currentPassword, username, email, password, avatar, avatarType); - - [Obsolete("Use Server.GetRole")] - public static Role GetRole(this DiscordClient client, Server server, ulong id) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.GetRole(id); - } - [Obsolete("Use Server.FindRoles")] - public static IEnumerable FindRoles(this DiscordClient client, Server server, string name) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.FindRoles(name); - } - - [Obsolete("Use Server.CreateRole")] - public static Task CreateRole(this DiscordClient client, Server server, string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.CreateRole(name, permissions, color); - } - [Obsolete("Use Role.Edit")] - public static Task EditRole(this DiscordClient client, Role role, string name = null, ServerPermissions? permissions = null, Color color = null, bool? isHoisted = null, int? position = null) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - return role.Edit(name, permissions, color, isHoisted, position); - } - - [Obsolete("Use Role.Delete")] - public static Task DeleteRole(this DiscordClient client, Role role) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - return role.Delete(); - } - - [Obsolete("Use Server.ReorderRoles")] - public static Task ReorderRoles(this DiscordClient client, Server server, IEnumerable roles, Role after = null) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.ReorderRoles(roles, after); - } - - [Obsolete("Use Server.Edit")] - public static Task EditServer(this DiscordClient client, Server server, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.Edit(name, region, icon, iconType); - } - - [Obsolete("Use Server.Leave")] - public static Task LeaveServer(this DiscordClient client, Server server) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - return server.Leave(); - } - - [Obsolete("Use DiscordClient.Regions")] - public static IEnumerable GetVoiceRegions(this DiscordClient client) - => client.Regions; - - [Obsolete("Use Channel.GetPermissionRule")] - public static ChannelPermissionOverrides GetChannelPermissions(this DiscordClient client, Channel channel, User user) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.GetPermissionsRule(user); - } - [Obsolete("Use Channel.GetPermissionRule")] - public static ChannelPermissionOverrides GetChannelPermissions(this DiscordClient client, Channel channel, Role role) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.GetPermissionsRule(role); - } - [Obsolete("Use Channel.AddPermissionRule(DualChannelPermissions)", true)] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, User user, ChannelPermissions allow, ChannelPermissions deny) - { - throw new InvalidOperationException(); - } - [Obsolete("Use Channel.AddPermissionRule")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, User user, ChannelPermissionOverrides permissions) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.AddPermissionsRule(user, permissions); - } - [Obsolete("Use Channel.AddPermissionRule(DualChannelPermissions)")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, Role role, ChannelPermissions allow, ChannelPermissions deny) - { - throw new InvalidOperationException(); - } - [Obsolete("Use Channel.AddPermissionRule")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, Role role, ChannelPermissionOverrides permissions) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.AddPermissionsRule(role, permissions); - } - [Obsolete("Use Channel.RemovePermissionRule")] - public static Task RemoveChannelPermissions(this DiscordClient client, Channel channel, User user) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.RemovePermissionsRule(user); - } - [Obsolete("Use Channel.RemovePermissionRule")] - public static Task RemoveChannelPermissions(this DiscordClient client, Channel channel, Role role) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.RemovePermissionsRule(role); - } - - [Obsolete("Removed", true)] - public static Task AckMessage(this DiscordClient client, Message message) - { - throw new InvalidOperationException(); - } - [Obsolete("Use Channel.ImportMessages", true)] - public static IEnumerable ImportMessages(Channel channel, string json) - { - throw new InvalidOperationException(); - } - [Obsolete("Use Channel.ExportMessages", true)] - public static string ExportMessages(Channel channel) - { - throw new InvalidOperationException(); - } - } -} From b3438c930993f44f7d4e015c902fc58faed4ce04 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 24 Feb 2016 19:39:10 -0400 Subject: [PATCH 41/51] Added missing using --- ref/Net/Rest/IRestRequest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ref/Net/Rest/IRestRequest.cs b/ref/Net/Rest/IRestRequest.cs index bb8083103..9d46e645f 100644 --- a/ref/Net/Rest/IRestRequest.cs +++ b/ref/Net/Rest/IRestRequest.cs @@ -1,4 +1,6 @@ -namespace Discord.Net.Rest +using System.IO; + +namespace Discord.Net.Rest { public interface IRestRequest { From 47cbac95cc431c755c05bb4565d169e67f8f9271 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 25 Feb 2016 19:02:31 -0500 Subject: [PATCH 42/51] split management into server- and user-; wrote docs for server/user --- docs/features/management.rst | 4 ---- docs/index.rst | 3 ++- test/Discord.Net.Tests/Discord.Net.Tests.csproj | 2 +- test/Discord.Net.Tests/Tests.cs | 4 +++- test/Discord.Net.Tests/packages.config | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 docs/features/management.rst diff --git a/docs/features/management.rst b/docs/features/management.rst deleted file mode 100644 index accbf5d94..000000000 --- a/docs/features/management.rst +++ /dev/null @@ -1,4 +0,0 @@ -|stub| Server Management -======================== - -|stub-desc| \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index c4469cd46..d2ff662af 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,7 +29,8 @@ This Documentation is **currently undergoing a rewrite**. Some pages (marked wit getting_started features/logging - features/management + features/server-management + features/user-management features/permissions features/commands features/voice diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index 97d328c68..0c13b7075 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -37,7 +37,7 @@ - ..\..\..\DiscordBot\packages\Newtonsoft.Json.8.0.1\lib\net45\Newtonsoft.Json.dll + ..\..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll True diff --git a/test/Discord.Net.Tests/Tests.cs b/test/Discord.Net.Tests/Tests.cs index e3d3977ad..51c045d69 100644 --- a/test/Discord.Net.Tests/Tests.cs +++ b/test/Discord.Net.Tests/Tests.cs @@ -42,7 +42,7 @@ namespace Discord.Tests //Create new server and invite the other bots to it _testServer = _hostClient.CreateServer("Discord.Net Testing", _hostClient.Regions.First()).Result; _testServerChannel = _testServer.DefaultChannel; - Invite invite = _testServer.CreateInvite(60, 1, false, false).Result; + var invite = _testServer.CreateInvite(60, 2, false, false).Result; WaitAll( _targetBot.GetInvite(invite.Code).Result.Accept(), _observerBot.GetInvite(invite.Code).Result.Accept()); @@ -122,6 +122,8 @@ namespace Discord.Tests _observerBot.Disconnect()); } + // Unit Test Helpers + private static void AssertEvent(string msg, Func action, Action> addEvent, Action> removeEvent, Func test = null) { AssertEvent(msg, action, addEvent, removeEvent, test, true); diff --git a/test/Discord.Net.Tests/packages.config b/test/Discord.Net.Tests/packages.config index 0eaf37a38..2abc396bb 100644 --- a/test/Discord.Net.Tests/packages.config +++ b/test/Discord.Net.Tests/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file From 3e3ae203a77e1f3e88b3236487e7dae3ad624d2e Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 25 Feb 2016 19:06:10 -0500 Subject: [PATCH 43/51] add the new docs pages maybe? --- docs/features/server-management.rst | 53 +++++++++++++++++++++++++++++ docs/features/user-management.rst | 22 ++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 docs/features/server-management.rst create mode 100644 docs/features/user-management.rst diff --git a/docs/features/server-management.rst b/docs/features/server-management.rst new file mode 100644 index 000000000..d555875a8 --- /dev/null +++ b/docs/features/server-management.rst @@ -0,0 +1,53 @@ +Server Management +================= + +Discord.Net will allow you to manage most settings of a Discord server. + +Usage +----- + +You can create Channels, Invites, and Roles on a server using the CreateChannel, CreateInvite, and CreateRole function of a Server, respectively. + +You may also edit a server's name, icon, and region. + +.. code-block:: c# + + // Create a Channel and retrieve the Channel object + var _channel = await _server.CreateChannel("announcements", ChannelType.Text); + + // Create an Invite and retrieve the Invite object + var _invite = await _server.CreateInvite(maxAge: null, maxUses: 25, tempMembership: false, withXkcd: false); + + // Create a Role and retrieve the Role object + var _role = await _server.CreateRole(name: "Bots", permissions: null, color: Color.DarkMagenta, isHoisted: false); + + // Edit a server + var _ioStream = new System.IO.StreamReader("clock-0500-1952.png").BaseStream + _server.Edit(name: "19:52 | UTC-05:00", region: "east", icon: _ioStream, iconType: ImageType.Png); + + // Prune Users + var _pruneCount = await _server.PruneUsers(30, true); + +Invite Parameters +----------------- + +maxAge: The time (in seconds) until the invite expires. Use null for infinite. +maxUses: The maximum amount of uses the invite has before it expires. +tempMembership: Whether or not to kick a user when they disconnect. +withXkcd: Generate the invite with an XKCD 936 style URL + +Role Parameters +--------------- + +name: The name of the role +permissions: A set of ServerPermissions for the role to use by default +color: The color of the role, recommended to use Discord.Color +isHoisted: Whether a role's users should be displayed separately from other users in the user list. + +Edit Parameters +--------------- + +name: The server's name +region: The region the voice server is hosted in +icon: A System.IO.Stream that will read an image file +iconType: The type of image being sent (png/jpeg). diff --git a/docs/features/user-management.rst b/docs/features/user-management.rst new file mode 100644 index 000000000..972b3ab4b --- /dev/null +++ b/docs/features/user-management.rst @@ -0,0 +1,22 @@ +User Management +=============== + +Banning +------- + +To ban a user, invoke the Ban function on a Server object. + +.. code-block:: c# + + _server.Ban(_user, 30); + +The pruneDays parameter, which defaults to 0, will remove all messages from a user dating back to the specified amount of days. + +Kicking +------- + +To kick a user, invoke the Kick function on the User. + +.. code-block:: c# + + _user.Kick(); From d1cae981da655b22fe44b7c7f8102929e35564e6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 26 Feb 2016 05:21:39 -0400 Subject: [PATCH 44/51] Removed csprojs (temporarily?) --- .../Discord.Net.Audio.csproj | 128 ---- .../Properties/AssemblyInfo.cs | 18 - src/Discord.Net.Audio.Net45/project.json | 14 - .../Discord.Net.Commands.csproj | 148 ---- .../Properties/AssemblyInfo.cs | 18 - src/Discord.Net.Commands.Net45/project.json | 10 - .../Discord.Net.Modules.csproj | 89 --- .../Properties/AssemblyInfo.cs | 18 - src/Discord.Net.Modules.Net45/project.json | 13 - src/Discord.Net.Net45/Discord.Net.csproj | 636 ------------------ .../Properties/AssemblyInfo.cs | 17 - src/Discord.Net.Net45/project.json | 16 - 12 files changed, 1125 deletions(-) delete mode 100644 src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj delete mode 100644 src/Discord.Net.Audio.Net45/Properties/AssemblyInfo.cs delete mode 100644 src/Discord.Net.Audio.Net45/project.json delete mode 100644 src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj delete mode 100644 src/Discord.Net.Commands.Net45/Properties/AssemblyInfo.cs delete mode 100644 src/Discord.Net.Commands.Net45/project.json delete mode 100644 src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj delete mode 100644 src/Discord.Net.Modules.Net45/Properties/AssemblyInfo.cs delete mode 100644 src/Discord.Net.Modules.Net45/project.json delete mode 100644 src/Discord.Net.Net45/Discord.Net.csproj delete mode 100644 src/Discord.Net.Net45/Properties/AssemblyInfo.cs delete mode 100644 src/Discord.Net.Net45/project.json diff --git a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj deleted file mode 100644 index f7326b4a9..000000000 --- a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj +++ /dev/null @@ -1,128 +0,0 @@ - - - - - Debug - AnyCPU - {7BFEF748-B934-4621-9B11-6302E3A9F6B3} - Library - Properties - Discord.Audio - Discord.Net.Audio - 512 - v4.5 - False - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45 - prompt - 4 - 6 - true - true - - - pdbonly - true - bin\Release\ - TRACE;NET45 - prompt - 4 - true - 6 - true - - - - - - - AudioClient.cs - - - AudioExtensions.cs - - - AudioMode.cs - - - AudioService.cs - - - AudioServiceConfig.cs - - - IAudioClient.cs - - - InternalFrameEventArgs.cs - - - InternalIsSpeakingEventArgs.cs - - - Net\VoiceSocket.cs - - - Opus\OpusConverter.cs - - - Opus\OpusDecoder.cs - - - Opus\OpusEncoder.cs - - - Sodium\SecretBox.cs - - - UserIsTalkingEventArgs.cs - - - VirtualClient.cs - - - VoiceBuffer.cs - - - VoiceDisconnectedEventArgs.cs - - - - - - {8d71a857-879a-4a10-859e-5ff824ed6688} - Discord.Net - - - - - libsodium.dll - Always - - - opus.dll - Always - - - - - - project.json - - - - - - \ No newline at end of file diff --git a/src/Discord.Net.Audio.Net45/Properties/AssemblyInfo.cs b/src/Discord.Net.Audio.Net45/Properties/AssemblyInfo.cs deleted file mode 100644 index c2c8aa3a7..000000000 --- a/src/Discord.Net.Audio.Net45/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Discord.Net.Audio")] -[assembly: AssemblyDescription("A Discord.Net extension adding voice support.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("RogueException")] -[assembly: AssemblyProduct("Discord.Net.Modules")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("76ea00e6-ea24-41e1-acb2-639c0313fa80")] - -[assembly: AssemblyVersion("0.9.0.0")] -[assembly: AssemblyFileVersion("0.9.0.0")] - diff --git a/src/Discord.Net.Audio.Net45/project.json b/src/Discord.Net.Audio.Net45/project.json deleted file mode 100644 index a9a422249..000000000 --- a/src/Discord.Net.Audio.Net45/project.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "dependencies": { - "Newtonsoft.Json": "8.0.1", - "Nito.AsyncEx": "3.0.1" - }, - "frameworks": { - "net45": { } - }, - "runtimes": { - "win": { }, - "win-x86": { }, - "win-x64": { } - } -} \ No newline at end of file diff --git a/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj b/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj deleted file mode 100644 index e3ce3c79a..000000000 --- a/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Debug - AnyCPU - {1B5603B4-6F8F-4289-B945-7BAAE523D740} - Library - Properties - Discord.Commands - Discord.Net.Commands - 512 - v4.5 - False - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45 - prompt - 4 - 6 - true - - - pdbonly - true - bin\Release\ - TRACE;NET45 - prompt - 4 - true - 6 - - - - - - - Command.cs - - - CommandBuilder.cs - - - CommandErrorEventArgs.cs - - - CommandEventArgs.cs - - - CommandExtensions.cs - - - CommandMap.cs - - - CommandParameter.cs - - - CommandParser.cs - - - CommandService.cs - - - CommandServiceConfig.cs - - - HelpMode.cs - - - Permissions\GenericPermissionChecker.cs - - - Permissions\IPermissionChecker.cs - - - Permissions\Levels\PermissionLevelChecker.cs - - - Permissions\Levels\PermissionLevelExtensions.cs - - - Permissions\Levels\PermissionLevelService.cs - - - Permissions\Users\BlacklistChecker.cs - - - Permissions\Users\BlacklistExtensions.cs - - - Permissions\Users\BlacklistService.cs - - - Permissions\Users\UserlistService.cs - - - Permissions\Users\WhitelistChecker.cs - - - Permissions\Users\WhitelistExtensions.cs - - - Permissions\Users\WhitelistService.cs - - - Permissions\Visibility\PrivateChecker.cs - - - Permissions\Visibility\PrivateExtensions.cs - - - Permissions\Visibility\PublicChecker.cs - - - Permissions\Visibility\PublicExtensions.cs - - - - - - {8d71a857-879a-4a10-859e-5ff824ed6688} - Discord.Net - - - - - - - - project.json - - - - - - \ No newline at end of file diff --git a/src/Discord.Net.Commands.Net45/Properties/AssemblyInfo.cs b/src/Discord.Net.Commands.Net45/Properties/AssemblyInfo.cs deleted file mode 100644 index 1959b2cf3..000000000 --- a/src/Discord.Net.Commands.Net45/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Discord.Net.Commands")] -[assembly: AssemblyDescription("A Discord.Net extension adding basic command support.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("RogueException")] -[assembly: AssemblyProduct("Discord.Net.Commands")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("76ea00e6-ea24-41e1-acb2-639c0313fa80")] - -[assembly: AssemblyVersion("0.9.0.0")] -[assembly: AssemblyFileVersion("0.9.0.0")] - diff --git a/src/Discord.Net.Commands.Net45/project.json b/src/Discord.Net.Commands.Net45/project.json deleted file mode 100644 index 62e5e6154..000000000 --- a/src/Discord.Net.Commands.Net45/project.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "frameworks": { - "net45": { } - }, - "runtimes": { - "win": { }, - "win-x86": { }, - "win-x64": { } - } -} \ No newline at end of file diff --git a/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj b/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj deleted file mode 100644 index cab137c25..000000000 --- a/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj +++ /dev/null @@ -1,89 +0,0 @@ - - - - - Debug - AnyCPU - {3091164F-66AE-4543-A63D-167C1116241D} - Library - Properties - Discord.Modules - Discord.Net.Modules - 512 - v4.5 - False - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45 - prompt - 4 - 6 - true - true - - - pdbonly - true - bin\Release\ - TRACE;NET45 - prompt - 4 - true - 6 - true - - - - - - - IModule.cs - - - ModuleChecker.cs - - - ModuleExtensions.cs - - - ModuleFilter.cs - - - ModuleManager.cs - - - ModuleService.cs - - - - - - {1b5603b4-6f8f-4289-b945-7baae523d740} - Discord.Net.Commands - - - {8d71a857-879a-4a10-859e-5ff824ed6688} - Discord.Net - - - - - - project.json - - - - - - \ No newline at end of file diff --git a/src/Discord.Net.Modules.Net45/Properties/AssemblyInfo.cs b/src/Discord.Net.Modules.Net45/Properties/AssemblyInfo.cs deleted file mode 100644 index 04eda1587..000000000 --- a/src/Discord.Net.Modules.Net45/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Discord.Net.Modules")] -[assembly: AssemblyDescription("A Discord.Net extension adding basic plugin support.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("RogueException")] -[assembly: AssemblyProduct("Discord.Net.Modules")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("76ea00e6-ea24-41e1-acb2-639c0313fa80")] - -[assembly: AssemblyVersion("0.9.0.0")] -[assembly: AssemblyFileVersion("0.9.0.0")] - diff --git a/src/Discord.Net.Modules.Net45/project.json b/src/Discord.Net.Modules.Net45/project.json deleted file mode 100644 index a174430f0..000000000 --- a/src/Discord.Net.Modules.Net45/project.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "dependencies": { - "Nito.AsyncEx": "3.0.1" - }, - "frameworks": { - "net45": { } - }, - "runtimes": { - "win": { }, - "win-x86": { }, - "win-x64": { } - } -} \ No newline at end of file diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj deleted file mode 100644 index 93c4eb664..000000000 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ /dev/null @@ -1,636 +0,0 @@ - - - - - Debug - AnyCPU - {8D71A857-879A-4A10-859E-5FF824ED6688} - Library - Properties - Discord - Discord.Net - 512 - v4.5 - - - False - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45 - prompt - 2 - true - 6 - true - - - pdbonly - true - bin\Release\ - TRACE;NET45 - prompt - 4 - true - true - 6 - - - true - bin\FullDebug\ - TRACE;DEBUG;NET45,TEST_RESPONSES - true - 2 - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - 6 - true - - - - - - - - API\Client\Common\Channel.cs - - - API\Client\Common\ChannelReference.cs - - - API\Client\Common\ExtendedGuild.cs - - - API\Client\Common\ExtendedMember.cs - - - API\Client\Common\Guild.cs - - - API\Client\Common\GuildReference.cs - - - API\Client\Common\Invite.cs - - - API\Client\Common\InviteReference.cs - - - API\Client\Common\Member.cs - - - API\Client\Common\MemberPresence.cs - - - API\Client\Common\MemberReference.cs - - - API\Client\Common\MemberVoiceState.cs - - - API\Client\Common\Message.cs - - - API\Client\Common\MessageReference.cs - - - API\Client\Common\Role.cs - - - API\Client\Common\RoleReference.cs - - - API\Client\Common\User.cs - - - API\Client\Common\UserReference.cs - - - API\Client\GatewaySocket\Commands\Heartbeat.cs - - - API\Client\GatewaySocket\Commands\Identify.cs - - - API\Client\GatewaySocket\Commands\RequestMembers.cs - - - API\Client\GatewaySocket\Commands\Resume.cs - - - API\Client\GatewaySocket\Commands\UpdateStatus.cs - - - API\Client\GatewaySocket\Commands\UpdateVoice.cs - - - API\Client\GatewaySocket\Events\ChannelCreate.cs - - - API\Client\GatewaySocket\Events\ChannelDelete.cs - - - API\Client\GatewaySocket\Events\ChannelUpdate.cs - - - API\Client\GatewaySocket\Events\GuildBanAdd.cs - - - API\Client\GatewaySocket\Events\GuildBanRemove.cs - - - API\Client\GatewaySocket\Events\GuildCreate.cs - - - API\Client\GatewaySocket\Events\GuildDelete.cs - - - API\Client\GatewaySocket\Events\GuildEmojisUpdate.cs - - - API\Client\GatewaySocket\Events\GuildIntegrationsUpdate.cs - - - API\Client\GatewaySocket\Events\GuildMemberAdd.cs - - - API\Client\GatewaySocket\Events\GuildMemberRemove.cs - - - API\Client\GatewaySocket\Events\GuildMembersChunk.cs - - - API\Client\GatewaySocket\Events\GuildMemberUpdate.cs - - - API\Client\GatewaySocket\Events\GuildRoleCreate.cs - - - API\Client\GatewaySocket\Events\GuildRoleDelete.cs - - - API\Client\GatewaySocket\Events\GuildRoleUpdate.cs - - - API\Client\GatewaySocket\Events\GuildUpdate.cs - - - API\Client\GatewaySocket\Events\MessageAck.cs - - - API\Client\GatewaySocket\Events\MessageCreate.cs - - - API\Client\GatewaySocket\Events\MessageDelete.cs - - - API\Client\GatewaySocket\Events\MessageUpdate.cs - - - API\Client\GatewaySocket\Events\PresenceUpdate.cs - - - API\Client\GatewaySocket\Events\Ready.cs - - - API\Client\GatewaySocket\Events\Redirect.cs - - - API\Client\GatewaySocket\Events\Resumed.cs - - - API\Client\GatewaySocket\Events\TypingStart.cs - - - API\Client\GatewaySocket\Events\UserSettingsUpdate.cs - - - API\Client\GatewaySocket\Events\UserUpdate.cs - - - API\Client\GatewaySocket\Events\VoiceServerUpdate.cs - - - API\Client\GatewaySocket\Events\VoiceStateUpdate.cs - - - API\Client\GatewaySocket\OpCodes.cs - - - API\Client\IWebSocketMessage.cs - - - API\Client\Rest\AcceptInvite.cs - - - API\Client\Rest\AckMessage.cs - - - API\Client\Rest\AddChannelPermission.cs - - - API\Client\Rest\AddGuildBan.cs - - - API\Client\Rest\CreateChannel.cs - - - API\Client\Rest\CreateGuild.cs - - - API\Client\Rest\CreateInvite.cs - - - API\Client\Rest\CreatePrivateChannel.cs - - - API\Client\Rest\CreateRole.cs - - - API\Client\Rest\DeleteChannel.cs - - - API\Client\Rest\DeleteGuild.cs - - - API\Client\Rest\DeleteInvite.cs - - - API\Client\Rest\DeleteMessage.cs - - - API\Client\Rest\DeleteRole.cs - - - API\Client\Rest\Gateway.cs - - - API\Client\Rest\GetBans.cs - - - API\Client\Rest\GetInvite.cs - - - API\Client\Rest\GetInvites.cs - - - API\Client\Rest\GetMessages.cs - - - API\Client\Rest\GetVoiceRegions.cs - - - API\Client\Rest\GetWidget.cs - - - API\Client\Rest\KickMember.cs - - - API\Client\Rest\LeaveGuild.cs - - - API\Client\Rest\Login.cs - - - API\Client\Rest\Logout.cs - - - API\Client\Rest\PruneMembers.cs - - - API\Client\Rest\RemoveChannelPermission.cs - - - API\Client\Rest\RemoveGuildBan.cs - - - API\Client\Rest\ReorderChannels.cs - - - API\Client\Rest\ReorderRoles.cs - - - API\Client\Rest\SendFile.cs - - - API\Client\Rest\SendIsTyping.cs - - - API\Client\Rest\SendMessage.cs - - - API\Client\Rest\UpdateChannel.cs - - - API\Client\Rest\UpdateGuild.cs - - - API\Client\Rest\UpdateMember.cs - - - API\Client\Rest\UpdateMessage.cs - - - API\Client\Rest\UpdateProfile.cs - - - API\Client\Rest\UpdateRole.cs - - - API\Client\VoiceSocket\Commands\Heartbeat.cs - - - API\Client\VoiceSocket\Commands\Identify.cs - - - API\Client\VoiceSocket\Commands\SelectProtocol.cs - - - API\Client\VoiceSocket\Commands\SetSpeaking.cs - - - API\Client\VoiceSocket\Events\Ready.cs - - - API\Client\VoiceSocket\Events\SessionDescription.cs - - - API\Client\VoiceSocket\Events\Speaking.cs - - - API\Client\VoiceSocket\OpCodes.cs - - - API\Converters.cs - - - API\Extensions.cs - - - API\IRestRequest.cs - - - API\Status\Common\StatusResult.cs - - - API\Status\Rest\ActiveMaintenances.cs - - - API\Status\Rest\AllIncidents.cs - - - API\Status\Rest\UnresolvedIncidents.cs - - - API\Status\Rest\UpcomingMaintenances.cs - - - DiscordClient.cs - - - DiscordClient.Events.cs - - - DiscordConfig.cs - - - DynamicIL.cs - - - Enums\ChannelType.cs - - - Enums\ConnectionState.cs - - - Enums\ImageType.cs - - - Enums\LogSeverity.cs - - - Enums\PermissionBits.cs - - - Enums\PermissionTarget.cs - - - Enums\PermValue.cs - - - Enums\Relative.cs - - - Enums\StringEnum.cs - - - Enums\UserStatus.cs - - - ETF\ETFReader.cs - - - ETF\ETFType.cs - - - ETF\ETFWriter.cs - - - Events\ChannelEventArgs.cs - - - Events\ChannelUpdatedEventArgs.cs - - - Events\ChannelUserEventArgs.cs - - - Events\DisconnectedEventArgs.cs - - - Events\LogMessageEventArgs.cs - - - Events\MessageEventArgs.cs - - - Events\MessageUpdatedEventArgs.cs - - - Events\ProfileUpdatedEventArgs.cs - - - Events\RoleEventArgs.cs - - - Events\RoleUpdatedEventArgs.cs - - - Events\ServerEventArgs.cs - - - Events\ServerUpdatedEventArgs.cs - - - Events\UserEventArgs.cs - - - Events\UserUpdatedEventArgs.cs - - - Format.cs - - - IMentionable.cs - - - InternalExtensions.cs - - - IService.cs - - - Legacy.cs - - - Logging\ILogger.cs - - - Logging\Logger.cs - - - Logging\LogManager.cs - - - MessageQueue.cs - - - Models\Channel.cs - - - Models\Color.cs - - - Models\Invite.cs - - - Models\Message.cs - - - Models\Permissions.cs - - - Models\PrivateChannel.cs - - - Models\Profile.cs - - - Models\PublicChannel.cs - - - Models\Region.cs - - - Models\Role.cs - - - Models\Server.cs - - - Models\TextChannel.cs - - - Models\User.cs - - - Models\VoiceChannel.cs - - - Net\HttpException.cs - - - Net\Rest\CompletedRequestEventArgs.cs - - - Net\Rest\ETFRestClient.cs - - - Net\Rest\IRestEngine.cs - - - Net\Rest\JsonRestClient.cs - - - Net\Rest\RequestEventArgs.cs - - - Net\Rest\RestClient.cs - - - Net\Rest\SharpRestEngine.cs - - - Net\TimeoutException.cs - - - Net\WebSockets\WebSocketException.cs - - - Net\WebSockets\BinaryMessageEventArgs.cs - - - Net\WebSockets\BuiltInEngine.cs - - - Net\WebSockets\GatewaySocket.cs - - - Net\WebSockets\IWebSocketEngine.cs - - - Net\WebSockets\TextMessageEventArgs.cs - - - Net\WebSockets\WebSocket.cs - - - Net\WebSockets\WebSocketEventEventArgs.cs - - - Net\WebSockets\WS4NetEngine.cs - - - ServiceCollection.cs - - - TaskManager.cs - - - - - - project.json - - - - - - - - - - \ No newline at end of file diff --git a/src/Discord.Net.Net45/Properties/AssemblyInfo.cs b/src/Discord.Net.Net45/Properties/AssemblyInfo.cs deleted file mode 100644 index 64a8616f5..000000000 --- a/src/Discord.Net.Net45/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Discord.Net")] -[assembly: AssemblyDescription("An unofficial .Net API wrapper for the Discord client.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("RogueException")] -[assembly: AssemblyProduct("Discord.Net")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("76ea00e6-ea24-41e1-acb2-639c0313fa80")] - -[assembly: AssemblyVersion("0.9.0.0")] -[assembly: AssemblyFileVersion("0.9.0.0")] diff --git a/src/Discord.Net.Net45/project.json b/src/Discord.Net.Net45/project.json deleted file mode 100644 index 82e8e459f..000000000 --- a/src/Discord.Net.Net45/project.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "dependencies": { - "Newtonsoft.Json": "8.0.1", - "Nito.AsyncEx": "3.0.1", - "RestSharp": "105.2.3", - "WebSocket4Net": "0.14.1" - }, - "frameworks": { - "net45": { } - }, - "runtimes": { - "win": { }, - "win-x86": { }, - "win-x64": { } - } -} \ No newline at end of file From 0759f957d82ca9fb8bb2c1d2e4011644adf48b8d Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 26 Feb 2016 05:22:01 -0400 Subject: [PATCH 45/51] Updated reference channel entities --- ref/DiscordClient.Events.cs | 2 +- ref/DiscordClient.cs | 6 +- ref/Entities/Channels/Channel.cs | 22 ---- ref/Entities/Channels/IChannel.cs | 15 ++- ref/Entities/Channels/IPrivateChannel.cs | 1 + ref/Entities/Channels/IPublicChannel.cs | 46 +++++--- ref/Entities/Channels/ITextChannel.cs | 16 ++- ref/Entities/Channels/IVoiceChannel.cs | 3 +- ref/Entities/Channels/PrivateChannel.cs | 58 ++++++++--- ref/Entities/Channels/TextChannel.cs | 104 +++++++++++++------ ref/Entities/Channels/VoiceChannel.cs | 89 ++++++++++------ ref/Entities/IEntity.cs | 23 ++++ ref/Entities/IModel.cs | 13 --- ref/Entities/IModifiable.cs | 11 ++ ref/Entities/Message.cs | 2 +- ref/Entities/Profile.cs | 2 +- ref/Entities/Role.cs | 2 +- ref/Entities/Server.cs | 4 +- ref/Enums/ChannelType.cs | 13 ++- ref/Enums/{MessageState.cs => ModelState.cs} | 2 +- ref/Events/ChannelUpdatedEventArgs.cs | 4 +- ref/Events/ChannelUserEventArgs.cs | 8 -- ref/Events/TypingEventArgs.cs | 14 +++ 23 files changed, 298 insertions(+), 162 deletions(-) delete mode 100644 ref/Entities/Channels/Channel.cs create mode 100644 ref/Entities/IEntity.cs delete mode 100644 ref/Entities/IModel.cs create mode 100644 ref/Entities/IModifiable.cs rename ref/Enums/{MessageState.cs => ModelState.cs} (94%) delete mode 100644 ref/Events/ChannelUserEventArgs.cs create mode 100644 ref/Events/TypingEventArgs.cs diff --git a/ref/DiscordClient.Events.cs b/ref/DiscordClient.Events.cs index 42b2601e3..888ba5c99 100644 --- a/ref/DiscordClient.Events.cs +++ b/ref/DiscordClient.Events.cs @@ -23,7 +23,7 @@ namespace Discord public event EventHandler ServerUpdated = delegate { }; public event EventHandler ServerUnavailable = delegate { }; public event EventHandler UserBanned = delegate { }; - public event EventHandler UserIsTyping = delegate { }; + public event EventHandler UserIsTyping = delegate { }; public event EventHandler UserJoined = delegate { }; public event EventHandler UserLeft = delegate { }; public event EventHandler UserUpdated = delegate { }; diff --git a/ref/DiscordClient.cs b/ref/DiscordClient.cs index f2a1cb95d..166f17268 100644 --- a/ref/DiscordClient.cs +++ b/ref/DiscordClient.cs @@ -29,7 +29,7 @@ namespace Discord public string CurrentGame { get; } public IEnumerable Servers { get; } - public IEnumerable PrivateChannels { get; } + public IEnumerable PrivateChannels { get; } public IEnumerable Regions { get; } public DiscordClient() { } @@ -43,8 +43,8 @@ namespace Discord public void SetStatus(UserStatus status) { } public void SetGame(string game) { } - public Channel GetChannel(ulong id) => null; - public Task CreatePrivateChannel(ulong userId) => null; + public PrivateChannel GetPrivateChannel(ulong id) => null; + public Task CreatePrivateChannel(ulong userId) => null; public Task GetInvite(string inviteIdOrXkcd) => null; diff --git a/ref/Entities/Channels/Channel.cs b/ref/Entities/Channels/Channel.cs deleted file mode 100644 index 8cb4a1afb..000000000 --- a/ref/Entities/Channels/Channel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Discord -{ - public abstract class Channel : IChannel - { - public ulong Id { get; } - - public abstract DiscordClient Client { get; } - public abstract ChannelType Type { get; } - public bool IsText { get; } - public bool IsVoice { get; } - public bool IsPrivate { get; } - public bool IsPublic { get; } - - public abstract User CurrentUser { get; } - public abstract IEnumerable Users { get; } - - public abstract Task Save(); - } -} diff --git a/ref/Entities/Channels/IChannel.cs b/ref/Entities/Channels/IChannel.cs index 7fbe67208..55e723303 100644 --- a/ref/Entities/Channels/IChannel.cs +++ b/ref/Entities/Channels/IChannel.cs @@ -1,17 +1,22 @@ using System.Collections.Generic; +using System.Threading.Tasks; namespace Discord { - public interface IChannel : IModel + public interface IChannel : IEntity { - DiscordClient Client { get; } - + /// Gets the type flags for this channel. ChannelType Type { get; } + /// Gets whether this is a text channel. bool IsText { get; } + /// Gets whether this is a voice channel. bool IsVoice { get; } + /// Gets whether this is a private channel. bool IsPrivate { get; } + /// Gets whether this is a public channel. bool IsPublic { get; } - - IEnumerable Users { get; } + + /// Gets a collection of all users in this channel. + Task> GetUsers(); } } diff --git a/ref/Entities/Channels/IPrivateChannel.cs b/ref/Entities/Channels/IPrivateChannel.cs index 44d55a67f..0ac5ec3aa 100644 --- a/ref/Entities/Channels/IPrivateChannel.cs +++ b/ref/Entities/Channels/IPrivateChannel.cs @@ -2,6 +2,7 @@ { public interface IPrivateChannel : IChannel { + /// Gets the recipient of the messages in this private channel. User Recipient { get; } } } diff --git a/ref/Entities/Channels/IPublicChannel.cs b/ref/Entities/Channels/IPublicChannel.cs index 8b8f87b45..91bdf81a6 100644 --- a/ref/Entities/Channels/IPublicChannel.cs +++ b/ref/Entities/Channels/IPublicChannel.cs @@ -5,26 +5,40 @@ namespace Discord { public interface IPublicChannel : IChannel { + /// Gets the server this channel is a member of. Server Server { get; } - - string Name { get; set; } - int Position { get; set; } - + /// Gets a collection of permission overwrites for this channel. IEnumerable PermissionOverwrites { get; } + /// Gets the name of this public channel. + string Name { get; } + /// Gets the position of this public channel relative to others of the same type. + int Position { get; } - PermissionOverwrite? GetPermissionsRule(User user); - PermissionOverwrite? GetPermissionsRule(Role role); - Task> DownloadInvites(); - - Task Delete(); + /// Gets the permission overwrite for a specific user, or null if one does not exist. + Task GetPermissionOverwrite(User user); + /// Gets the permission overwrite for a specific role, or null if one does not exist. + Task GetPermissionOverwrite(Role role); + /// Downloads a collection of all invites to this server. + Task> GetInvites(); + + /// Adds or updates the permission overwrite for the given user. + Task UpdatePermissionOverwrite(User user, ChannelPermissions allow, ChannelPermissions deny); + /// Adds or updates the permission overwrite for the given user. + Task UpdatePermissionOverwrite(User user, TriStateChannelPermissions permissions); + /// Adds or updates the permission overwrite for the given role. + Task UpdatePermissionOverwrite(Role role, ChannelPermissions allow, ChannelPermissions deny); + /// Adds or updates the permission overwrite for the given role. + Task UpdatePermissionOverwrite(Role role, TriStateChannelPermissions permissions); + /// Removes the permission overwrite for the given user, if one exists. + Task RemovePermissionOverwrite(User user); + /// Removes the permission overwrite for the given role, if one exists. + Task RemovePermissionOverwrite(Role role); + /// Creates a new invite to this channel. + /// Time (in seconds) until the invite expires. Set to null to never expire. + /// The max amount of times this invite may be used. Set to null to have unlimited uses. + /// If true, a user accepting this invite will be kicked from the server after closing their client. + /// If true, creates a human-readable link. Not supported if maxAge is set to null. Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false); - - Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny); - Task AddPermissionsRule(User user, TriStateChannelPermissions permissions); - Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny); - Task AddPermissionsRule(Role role, TriStateChannelPermissions permissions); - Task RemovePermissionsRule(User user); - Task RemovePermissionsRule(Role role); } } diff --git a/ref/Entities/Channels/ITextChannel.cs b/ref/Entities/Channels/ITextChannel.cs index aa7e09435..b1449fac9 100644 --- a/ref/Entities/Channels/ITextChannel.cs +++ b/ref/Entities/Channels/ITextChannel.cs @@ -6,13 +6,25 @@ namespace Discord { public interface ITextChannel : IChannel { - Message GetMessage(ulong id); - Task> DownloadMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before); + /// Gets the message in this text channel with the given id, or null if none was found. + Task GetMessage(ulong id); + /// Gets the last N messages from this text channel. + /// The maximum number of messages to retrieve. + Task> GetMessages(int limit = 100); + /// Gets a collection of messages in this channel. + /// The maximum number of messages to retrieve. + /// The message to start downloading relative to. + /// The direction, from relativeMessageId, to download messages in. + Task> GetMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before); + /// Sends a message to this text channel. Task SendMessage(string text, bool isTTS = false); + /// Sends a file to this text channel, with an optional caption. Task SendFile(string filePath, string text = null, bool isTTS = false); + /// Sends a file to this text channel, with an optional caption. Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false); + /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. Task SendIsTyping(); } } diff --git a/ref/Entities/Channels/IVoiceChannel.cs b/ref/Entities/Channels/IVoiceChannel.cs index 85d093209..f8c3be999 100644 --- a/ref/Entities/Channels/IVoiceChannel.cs +++ b/ref/Entities/Channels/IVoiceChannel.cs @@ -2,6 +2,7 @@ { public interface IVoiceChannel : IChannel { - int Bitrate { get; set; } + /// Gets the requested bitrate, in bits per second, of this voice channel. + int Bitrate { get; } } } diff --git a/ref/Entities/Channels/PrivateChannel.cs b/ref/Entities/Channels/PrivateChannel.cs index ca25dadab..80f3284c1 100644 --- a/ref/Entities/Channels/PrivateChannel.cs +++ b/ref/Entities/Channels/PrivateChannel.cs @@ -1,31 +1,55 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace Discord { - public class PrivateChannel : Channel, ITextChannel, IPrivateChannel - { - public User Recipient { get; } - public IEnumerable Messages { get; } - - public override DiscordClient Client => null; - public override ChannelType Type => default(ChannelType); + public class PrivateChannel : ITextChannel, IPrivateChannel + { + /// + public ulong Id { get; } + /// + public DiscordClient Client { get; } + /// + public ModelState State { get; } + /// + public ChannelType Type { get; } + /// + public bool IsPrivate => true; + /// + public bool IsPublic => false; + /// + public bool IsText => true; + /// + public bool IsVoice => false; - public override User CurrentUser => null; - public override IEnumerable Users => null; + /// + public Server Server { get; } + /// + public User Recipient { get; } - public Message GetMessage(ulong id) => null; - public Task> DownloadMessages(int limit) => null; - public Task> DownloadMessages(int limit, ulong? relativeMessageId, Relative relativeDir) => null; + /// + public Task> GetUsers(); + /// + public Task GetMessage(ulong id) => null; + /// + public Task> GetMessages(int limit = 100) => null; + /// + public Task> GetMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) => null; + /// public Task SendMessage(string text, bool isTTS = false) => null; - public Task SendFile(string path, string text = null, bool isTTS = false) => null; + /// + public Task SendFile(string filePath, string text = null, bool isTTS = false) => null; + /// public Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false) => null; + /// public Task SendIsTyping() => null; - - public override Task Save() => null; + + /// + public Task Delete() => null; + /// + public Task Update() => null; } } diff --git a/ref/Entities/Channels/TextChannel.cs b/ref/Entities/Channels/TextChannel.cs index e09528e01..9b96ff02c 100644 --- a/ref/Entities/Channels/TextChannel.cs +++ b/ref/Entities/Channels/TextChannel.cs @@ -1,50 +1,94 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace Discord { - public class TextChannel : Channel, ITextChannel, IPublicChannel, IMentionable + public class TextChannel : ITextChannel, IPublicChannel, IMentionable, IModifiable { - public Server Server { get; } - public string Mention { get; } - public IEnumerable PermissionOverwrites { get; } - public IEnumerable Messages { get; } + public sealed class Properties + { + } - public string Topic { get; set; } - public bool IsTyping { get; set; } - public string Name { get; set; } - public int Position { get; set; } + /// + public ulong Id { get; } + /// + public DiscordClient Discord { get; } + /// + public ModelState State { get; } + /// + public ChannelType Type => ChannelType.Public | ChannelType.Text; + /// + public bool IsPrivate => false; + /// + public bool IsPublic => true; + /// + public bool IsText => true; + /// + public bool IsVoice => false; - public override DiscordClient Client => null; - public override ChannelType Type => default(ChannelType); - public override User CurrentUser => null; - public override IEnumerable Users => null; + /// + public string Name { get; } + /// + public string Topic { get; } + /// + public int Position { get; } + /// + public string Mention { get; } + /// + public Server Server { get; } + /// + public IEnumerable PermissionOverwrites { get; } + /// + public IEnumerable Users { get; } - public Message GetMessage(ulong id) => null; - public PermissionOverwrite? GetPermissionsRule(User user) => null; - public PermissionOverwrite? GetPermissionsRule(Role role) => null; + /// + public Task> GetUsers() => null; + /// + public Task GetMessage(ulong id) => null; + /// + public Task> GetMessages(int limit = 100) => null; + /// + public Task> GetMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) => null; + /// + public Task GetPermissionOverwrite(User user) => null; + /// + public Task GetPermissionOverwrite(Role role) => null; + /// + public Task> GetInvites() => null; - public Task> DownloadMessages(int limit) => null; - public Task> DownloadMessages(int limit, ulong? relativeMessageId, Relative relativeDir) => null; - public Task> DownloadInvites() => null; + /// + public Task UpdatePermissionOverwrite(User user, ChannelPermissions allow, ChannelPermissions deny) => null; + /// + public Task UpdatePermissionOverwrite(User user, TriStateChannelPermissions permissions) => null; + /// + public Task UpdatePermissionOverwrite(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; + /// + public Task UpdatePermissionOverwrite(Role role, TriStateChannelPermissions permissions) => null; + /// + public Task RemovePermissionOverwrite(User user) => null; + /// + public Task RemovePermissionOverwrite(Role role) => null; + /// public Task SendMessage(string text, bool isTTS = false) => null; - public Task SendFile(string path, string text = null, bool isTTS = false) => null; + /// + public Task SendFile(string filePath, string text = null, bool isTTS = false) => null; + /// public Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false) => null; + /// public Task SendIsTyping() => null; - public Task CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool tempMembership = false, bool withXkcd = false) => null; - - public Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny) => null; - public Task AddPermissionsRule(User user, TriStateChannelPermissions permissions) => null; - public Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; - public Task AddPermissionsRule(Role role, TriStateChannelPermissions permissions) => null; - public Task RemovePermissionsRule(User user) => null; - public Task RemovePermissionsRule(Role role) => null; + /// + public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; + /// public Task Delete() => null; - public override Task Save() => null; + /// + public Task Update() => null; + /// + public Task Modify(Action func) => null; } } diff --git a/ref/Entities/Channels/VoiceChannel.cs b/ref/Entities/Channels/VoiceChannel.cs index c38d6c40b..f1a93ce01 100644 --- a/ref/Entities/Channels/VoiceChannel.cs +++ b/ref/Entities/Channels/VoiceChannel.cs @@ -1,49 +1,74 @@ using System; using System.Collections.Generic; -using System.IO; using System.Threading.Tasks; namespace Discord { - public class VoiceChannel : IPublicChannel, IVoiceChannel + public class VoiceChannel : IPublicChannel, IVoiceChannel, IModifiable { + public sealed class Properties + { + public int Bitrate { get; set; } + } + + /// public ulong Id { get; } + /// public DiscordClient Client { get; } - public Server Server { get; } + /// + public ModelState State { get; } + /// public ChannelType Type { get; } - public bool IsText { get; } - public bool IsVoice { get; } - public bool IsPrivate { get; } - public bool IsPublic { get; } - public IEnumerable PermissionOverwrites { get; } - public IEnumerable Users { get; } - - public string Name { get; set; } - public int Position { get; set; } - public int Bitrate { get; set; } + /// + public bool IsPrivate => false; + /// + public bool IsPublic => true; + /// + public bool IsText => false; + /// + public bool IsVoice => true; - public Message GetMessage(ulong id) => null; - public PermissionOverwrite? GetPermissionsRule(User user) => null; - public PermissionOverwrite? GetPermissionsRule(Role role) => null; - - public Task> DownloadMessages(int limit) => null; - public Task> DownloadMessages(int limit, ulong? relativeMessageId, Relative relativeDir) => null; - public Task> DownloadInvites() => null; - - public Task SendMessage(string text, bool isTTS = false) => null; - public Task SendFile(string path, string text = null, bool isTTS = false) => null; - public Task SendFile(Stream stream, string filename, string text = null, bool isTTS = false) => null; + /// + public string Name { get; } + /// + public int Position { get; } + /// + public int Bitrate { get; } + /// + public Server Server { get; } + /// + public IEnumerable PermissionOverwrites { get; } - public Task CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool tempMembership = false, bool withXkcd = false) => null; + /// + public PermissionOverwrite? GetPermissionOverwrite(User user) => null; + /// + public PermissionOverwrite? GetPermissionOverwrite(Role role) => null; + /// + public Task> GetUsers() => null; + /// + public Task> GetInvites() => null; + + /// + public Task UpdatePermissionOverwrite(User user, ChannelPermissions allow, ChannelPermissions deny) => null; + /// + public Task UpdatePermissionOverwrite(User user, TriStateChannelPermissions permissions) => null; + /// + public Task UpdatePermissionOverwrite(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; + /// + public Task UpdatePermissionOverwrite(Role role, TriStateChannelPermissions permissions) => null; + /// + public Task RemovePermissionOverwrite(User user) => null; + /// + public Task RemovePermissionOverwrite(Role role) => null; - public Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny) => null; - public Task AddPermissionsRule(User user, TriStateChannelPermissions permissions) => null; - public Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; - public Task AddPermissionsRule(Role role, TriStateChannelPermissions permissions) => null; - public Task RemovePermissionsRule(User user) => null; - public Task RemovePermissionsRule(Role role) => null; + /// + public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; + /// public Task Delete() => null; - public Task Save() => null; + /// + public Task Update() => null; + /// + public Task Modify(Action func) => null; } } diff --git a/ref/Entities/IEntity.cs b/ref/Entities/IEntity.cs new file mode 100644 index 000000000..b95312519 --- /dev/null +++ b/ref/Entities/IEntity.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IEntity : IEntity + { + /// Gets the unique identifier for this object. + TId Id { get; } + } + public interface IEntity + { + /// Gets the DiscordClient that manages this object. + DiscordClient Client { get; } + /// Gets the state of this object. + ModelState State { get; } + + /// Deletes this object. + Task Delete(); + /// Downloads the latest values and updates this object. + Task Update(); + } +} diff --git a/ref/Entities/IModel.cs b/ref/Entities/IModel.cs deleted file mode 100644 index 58c016a27..000000000 --- a/ref/Entities/IModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; - -namespace Discord -{ - public interface IModel : IModel - { - TId Id { get; } - } - public interface IModel - { - Task Save(); - } -} diff --git a/ref/Entities/IModifiable.cs b/ref/Entities/IModifiable.cs new file mode 100644 index 000000000..f264c96f2 --- /dev/null +++ b/ref/Entities/IModifiable.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IModifiable + { + /// Modifies one or more of the properties of this object. + Task Modify(Action func); + } +} diff --git a/ref/Entities/Message.cs b/ref/Entities/Message.cs index f315315fc..ca019da3f 100644 --- a/ref/Entities/Message.cs +++ b/ref/Entities/Message.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Discord { - public class Message : IModel + public class Message : IEntity { public class Attachment : File { diff --git a/ref/Entities/Profile.cs b/ref/Entities/Profile.cs index 067a58988..73965de65 100644 --- a/ref/Entities/Profile.cs +++ b/ref/Entities/Profile.cs @@ -2,7 +2,7 @@ namespace Discord { - public class Profile : IModel + public class Profile : IEntity { public DiscordClient Client { get; } diff --git a/ref/Entities/Role.cs b/ref/Entities/Role.cs index ae2c92988..b9e35dc46 100644 --- a/ref/Entities/Role.cs +++ b/ref/Entities/Role.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace Discord { - public class Role : IModel, IMentionable + public class Role : IEntity, IMentionable { public DiscordClient Client { get; } diff --git a/ref/Entities/Server.cs b/ref/Entities/Server.cs index 75b98d979..0a81d8737 100644 --- a/ref/Entities/Server.cs +++ b/ref/Entities/Server.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Discord { - public class Server : IModel + public class Server : IEntity { public class Emoji { @@ -28,7 +28,7 @@ namespace Discord public Role EveryoneRole { get; } public IEnumerable Features { get; } public IEnumerable CustomEmojis { get; } - public IEnumerable Channels { get; } + public IEnumerable Channels { get; } public IEnumerable TextChannels { get; } public IEnumerable VoiceChannels { get; } public IEnumerable Users { get; } diff --git a/ref/Enums/ChannelType.cs b/ref/Enums/ChannelType.cs index 84491070b..5ebbf3aa6 100644 --- a/ref/Enums/ChannelType.cs +++ b/ref/Enums/ChannelType.cs @@ -1,8 +1,13 @@ -namespace Discord +using System; + +namespace Discord { - public enum ChannelType + [Flags] + public enum ChannelType : byte { - Text, - Voice + Public = 0x01, + Private = 0x02, + Text = 0x10, + Voice = 0x20 } } diff --git a/ref/Enums/MessageState.cs b/ref/Enums/ModelState.cs similarity index 94% rename from ref/Enums/MessageState.cs rename to ref/Enums/ModelState.cs index 59f65614d..a4477e94d 100644 --- a/ref/Enums/MessageState.cs +++ b/ref/Enums/ModelState.cs @@ -1,6 +1,6 @@ namespace Discord { - public enum MessageState : byte + public enum ModelState : byte { /// Message did not originate from this session, or was successfully sent. Normal = 0, diff --git a/ref/Events/ChannelUpdatedEventArgs.cs b/ref/Events/ChannelUpdatedEventArgs.cs index bc93ba7ae..eee390f37 100644 --- a/ref/Events/ChannelUpdatedEventArgs.cs +++ b/ref/Events/ChannelUpdatedEventArgs.cs @@ -4,8 +4,8 @@ namespace Discord { public class ChannelUpdatedEventArgs : EventArgs { - public Channel Before => null; - public Channel After => null; + public IChannel Before => null; + public IChannel After => null; public Server Server => null; } } diff --git a/ref/Events/ChannelUserEventArgs.cs b/ref/Events/ChannelUserEventArgs.cs deleted file mode 100644 index a17856e82..000000000 --- a/ref/Events/ChannelUserEventArgs.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Discord -{ - public class ChannelUserEventArgs - { - public Channel Channel => null; - public User User => null; - } -} diff --git a/ref/Events/TypingEventArgs.cs b/ref/Events/TypingEventArgs.cs new file mode 100644 index 000000000..73d47f688 --- /dev/null +++ b/ref/Events/TypingEventArgs.cs @@ -0,0 +1,14 @@ +namespace Discord +{ + public class TypingEventArgs + { + public ITextChannel Channel { get; } + public User User { get; } + + public TypingEventArgs(ITextChannel channel, User user) + { + Channel = channel; + User = user; + } + } +} From 6b3c1410e95780db7689a2430080ba8952bf2690 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 26 Feb 2016 05:35:12 -0400 Subject: [PATCH 46/51] Fixes to ref channel --- ref/Entities/Channels/IPublicChannel.cs | 4 ++-- ref/Entities/Channels/PrivateChannel.cs | 2 +- ref/Entities/Channels/TextChannel.cs | 11 +++++++---- ref/Entities/Channels/VoiceChannel.cs | 4 +++- ref/Entities/IEntity.cs | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ref/Entities/Channels/IPublicChannel.cs b/ref/Entities/Channels/IPublicChannel.cs index 91bdf81a6..f0e868bc7 100644 --- a/ref/Entities/Channels/IPublicChannel.cs +++ b/ref/Entities/Channels/IPublicChannel.cs @@ -15,9 +15,9 @@ namespace Discord int Position { get; } /// Gets the permission overwrite for a specific user, or null if one does not exist. - Task GetPermissionOverwrite(User user); + PermissionOverwrite? GetPermissionOverwrite(User user); /// Gets the permission overwrite for a specific role, or null if one does not exist. - Task GetPermissionOverwrite(Role role); + PermissionOverwrite? GetPermissionOverwrite(Role role); /// Downloads a collection of all invites to this server. Task> GetInvites(); diff --git a/ref/Entities/Channels/PrivateChannel.cs b/ref/Entities/Channels/PrivateChannel.cs index 80f3284c1..4d21f3902 100644 --- a/ref/Entities/Channels/PrivateChannel.cs +++ b/ref/Entities/Channels/PrivateChannel.cs @@ -9,7 +9,7 @@ namespace Discord /// public ulong Id { get; } /// - public DiscordClient Client { get; } + public DiscordClient Discord { get; } /// public ModelState State { get; } /// diff --git a/ref/Entities/Channels/TextChannel.cs b/ref/Entities/Channels/TextChannel.cs index 9b96ff02c..8596d73f8 100644 --- a/ref/Entities/Channels/TextChannel.cs +++ b/ref/Entities/Channels/TextChannel.cs @@ -9,6 +9,9 @@ namespace Discord { public sealed class Properties { + public string Name { get; } + public string Topic { get; } + public int Position { get; } } /// @@ -43,6 +46,10 @@ namespace Discord /// public IEnumerable Users { get; } + /// + public PermissionOverwrite? GetPermissionOverwrite(User user) => null; + /// + public PermissionOverwrite? GetPermissionOverwrite(Role role) => null; /// public Task> GetUsers() => null; /// @@ -52,10 +59,6 @@ namespace Discord /// public Task> GetMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) => null; /// - public Task GetPermissionOverwrite(User user) => null; - /// - public Task GetPermissionOverwrite(Role role) => null; - /// public Task> GetInvites() => null; /// diff --git a/ref/Entities/Channels/VoiceChannel.cs b/ref/Entities/Channels/VoiceChannel.cs index f1a93ce01..6a6232a9d 100644 --- a/ref/Entities/Channels/VoiceChannel.cs +++ b/ref/Entities/Channels/VoiceChannel.cs @@ -8,13 +8,15 @@ namespace Discord { public sealed class Properties { + public string Name { get; } public int Bitrate { get; set; } + public int Position { get; } } /// public ulong Id { get; } /// - public DiscordClient Client { get; } + public DiscordClient Discord { get; } /// public ModelState State { get; } /// diff --git a/ref/Entities/IEntity.cs b/ref/Entities/IEntity.cs index b95312519..eb30ddd55 100644 --- a/ref/Entities/IEntity.cs +++ b/ref/Entities/IEntity.cs @@ -11,7 +11,7 @@ namespace Discord public interface IEntity { /// Gets the DiscordClient that manages this object. - DiscordClient Client { get; } + DiscordClient Discord { get; } /// Gets the state of this object. ModelState State { get; } From 19e02e923df48a55bab0627447b7a6aa160dc521 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 28 Feb 2016 04:48:32 -0400 Subject: [PATCH 47/51] Fixed several issues in ref entities --- ref/DiscordClient.Events.cs | 32 ------------------- ref/DiscordClient.cs | 28 ++++++++++++++-- ref/Entities/Channels/IPublicChannel.cs | 22 +++++++------ ref/Entities/Channels/PrivateChannel.cs | 14 ++++---- ref/Entities/Channels/TextChannel.cs | 22 ++++++------- ref/Entities/Channels/VoiceChannel.cs | 20 +++++------- ref/Entities/IEntity.cs | 6 ++-- ref/Entities/Invite.cs | 15 +++++---- ref/Entities/Message.cs | 14 ++++---- .../Permissions/ChannelPermissions.cs | 7 ++-- ...Permissions.cs => OverwritePermissions.cs} | 12 +++---- .../Permissions/PermissionOverwrite.cs | 9 ------ ref/Entities/Permissions/ServerPermissions.cs | 2 +- ref/Entities/Profile.cs | 12 ++++--- ref/Entities/Region.cs | 9 ------ ref/Entities/Role.cs | 10 +++--- ref/Entities/Server.cs | 19 +++++------ ref/Entities/User.cs | 32 ++++++++----------- ref/Enums/EntityState.cs | 18 +++++++++++ ref/Enums/LogSeverity.cs | 1 + ref/Enums/ModelState.cs | 18 ----------- ref/Enums/PermissionBits.cs | 31 ------------------ ref/Events/ChannelEventArgs.cs | 3 +- ref/Events/ChannelUpdatedEventArgs.cs | 1 - ref/Events/MessageEventArgs.cs | 3 +- ref/Events/MessageUpdatedEventArgs.cs | 3 +- ref/Events/UserEventArgs.cs | 1 - ref/Events/UserUpdatedEventArgs.cs | 1 - ref/Net/WebSockets/WebSocketEventEventArgs.cs | 6 ---- 29 files changed, 146 insertions(+), 225 deletions(-) delete mode 100644 ref/DiscordClient.Events.cs rename ref/Entities/Permissions/{TriStateChannelPermissions.cs => OverwritePermissions.cs} (81%) delete mode 100644 ref/Entities/Permissions/PermissionOverwrite.cs create mode 100644 ref/Enums/EntityState.cs delete mode 100644 ref/Enums/ModelState.cs delete mode 100644 ref/Enums/PermissionBits.cs diff --git a/ref/DiscordClient.Events.cs b/ref/DiscordClient.Events.cs deleted file mode 100644 index 888ba5c99..000000000 --- a/ref/DiscordClient.Events.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Discord -{ - public partial class DiscordClient - { - public event EventHandler Ready = delegate { }; - public event EventHandler ChannelCreated = delegate { }; - public event EventHandler ChannelDestroyed = delegate { }; - public event EventHandler ChannelUpdated = delegate { }; - public event EventHandler MessageAcknowledged = delegate { }; - public event EventHandler MessageDeleted = delegate { }; - public event EventHandler MessageReceived = delegate { }; - public event EventHandler MessageSent = delegate { }; - public event EventHandler MessageUpdated = delegate { }; - public event EventHandler ProfileUpdated = delegate { }; - public event EventHandler RoleCreated = delegate { }; - public event EventHandler RoleUpdated = delegate { }; - public event EventHandler RoleDeleted = delegate { }; - public event EventHandler JoinedServer = delegate { }; - public event EventHandler LeftServer = delegate { }; - public event EventHandler ServerAvailable = delegate { }; - public event EventHandler ServerUpdated = delegate { }; - public event EventHandler ServerUnavailable = delegate { }; - public event EventHandler UserBanned = delegate { }; - public event EventHandler UserIsTyping = delegate { }; - public event EventHandler UserJoined = delegate { }; - public event EventHandler UserLeft = delegate { }; - public event EventHandler UserUpdated = delegate { }; - public event EventHandler UserUnbanned = delegate { }; - } -} diff --git a/ref/DiscordClient.cs b/ref/DiscordClient.cs index 166f17268..027383ae7 100644 --- a/ref/DiscordClient.cs +++ b/ref/DiscordClient.cs @@ -5,15 +5,39 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; -using System.Net; using System.Threading; using System.Threading.Tasks; namespace Discord { /// Provides a connection to the DiscordApp service. - public partial class DiscordClient : IDisposable + public class DiscordClient : IDisposable { + public event EventHandler Ready = delegate { }; + public event EventHandler ChannelCreated = delegate { }; + public event EventHandler ChannelUpdated = delegate { }; + public event EventHandler ChannelDestroyed = delegate { }; + public event EventHandler MessageAcknowledged = delegate { }; + public event EventHandler MessageDeleted = delegate { }; + public event EventHandler MessageReceived = delegate { }; + public event EventHandler MessageSent = delegate { }; + public event EventHandler MessageUpdated = delegate { }; + public event EventHandler ProfileUpdated = delegate { }; + public event EventHandler RoleCreated = delegate { }; + public event EventHandler RoleUpdated = delegate { }; + public event EventHandler RoleDeleted = delegate { }; + public event EventHandler JoinedServer = delegate { }; + public event EventHandler LeftServer = delegate { }; + public event EventHandler ServerAvailable = delegate { }; + public event EventHandler ServerUpdated = delegate { }; + public event EventHandler ServerUnavailable = delegate { }; + public event EventHandler UserBanned = delegate { }; + public event EventHandler UserIsTyping = delegate { }; + public event EventHandler UserJoined = delegate { }; + public event EventHandler UserLeft = delegate { }; + public event EventHandler UserUpdated = delegate { }; + public event EventHandler UserUnbanned = delegate { }; + public DiscordConfig Config { get; } public RestClient ClientAPI { get; } public RestClient StatusAPI { get; } diff --git a/ref/Entities/Channels/IPublicChannel.cs b/ref/Entities/Channels/IPublicChannel.cs index f0e868bc7..91a05d861 100644 --- a/ref/Entities/Channels/IPublicChannel.cs +++ b/ref/Entities/Channels/IPublicChannel.cs @@ -3,32 +3,34 @@ using System.Threading.Tasks; namespace Discord { + public struct PermissionOverwriteEntry + { + public PermissionTarget TargetType { get; } + public ulong TargetId { get; } + public OverwritePermissions Permissions { get; } + } public interface IPublicChannel : IChannel { /// Gets the server this channel is a member of. Server Server { get; } /// Gets a collection of permission overwrites for this channel. - IEnumerable PermissionOverwrites { get; } + IEnumerable PermissionOverwrites { get; } /// Gets the name of this public channel. string Name { get; } /// Gets the position of this public channel relative to others of the same type. - int Position { get; } + int Position { get; } /// Gets the permission overwrite for a specific user, or null if one does not exist. - PermissionOverwrite? GetPermissionOverwrite(User user); + OverwritePermissions? GetPermissionOverwrite(User user); /// Gets the permission overwrite for a specific role, or null if one does not exist. - PermissionOverwrite? GetPermissionOverwrite(Role role); + OverwritePermissions? GetPermissionOverwrite(Role role); /// Downloads a collection of all invites to this server. Task> GetInvites(); /// Adds or updates the permission overwrite for the given user. - Task UpdatePermissionOverwrite(User user, ChannelPermissions allow, ChannelPermissions deny); - /// Adds or updates the permission overwrite for the given user. - Task UpdatePermissionOverwrite(User user, TriStateChannelPermissions permissions); - /// Adds or updates the permission overwrite for the given role. - Task UpdatePermissionOverwrite(Role role, ChannelPermissions allow, ChannelPermissions deny); + Task UpdatePermissionOverwrite(User user, OverwritePermissions permissions); /// Adds or updates the permission overwrite for the given role. - Task UpdatePermissionOverwrite(Role role, TriStateChannelPermissions permissions); + Task UpdatePermissionOverwrite(Role role, OverwritePermissions permissions); /// Removes the permission overwrite for the given user, if one exists. Task RemovePermissionOverwrite(User user); /// Removes the permission overwrite for the given role, if one exists. diff --git a/ref/Entities/Channels/PrivateChannel.cs b/ref/Entities/Channels/PrivateChannel.cs index 4d21f3902..6364bf884 100644 --- a/ref/Entities/Channels/PrivateChannel.cs +++ b/ref/Entities/Channels/PrivateChannel.cs @@ -11,7 +11,7 @@ namespace Discord /// public DiscordClient Discord { get; } /// - public ModelState State { get; } + public EntityState State { get; } /// public ChannelType Type { get; } /// @@ -22,14 +22,12 @@ namespace Discord public bool IsText => true; /// public bool IsVoice => false; - - /// - public Server Server { get; } + /// public User Recipient { get; } /// - public Task> GetUsers(); + public Task> GetUsers() => null; /// public Task GetMessage(ulong id) => null; /// @@ -46,10 +44,10 @@ namespace Discord /// public Task SendIsTyping() => null; - - /// - public Task Delete() => null; + /// public Task Update() => null; + /// + public Task Delete() => null; } } diff --git a/ref/Entities/Channels/TextChannel.cs b/ref/Entities/Channels/TextChannel.cs index 8596d73f8..2d977111c 100644 --- a/ref/Entities/Channels/TextChannel.cs +++ b/ref/Entities/Channels/TextChannel.cs @@ -19,7 +19,7 @@ namespace Discord /// public DiscordClient Discord { get; } /// - public ModelState State { get; } + public EntityState State { get; } /// public ChannelType Type => ChannelType.Public | ChannelType.Text; /// @@ -42,14 +42,14 @@ namespace Discord /// public Server Server { get; } /// - public IEnumerable PermissionOverwrites { get; } + public IEnumerable PermissionOverwrites { get; } /// public IEnumerable Users { get; } /// - public PermissionOverwrite? GetPermissionOverwrite(User user) => null; + public OverwritePermissions? GetPermissionOverwrite(User user) => null; /// - public PermissionOverwrite? GetPermissionOverwrite(Role role) => null; + public OverwritePermissions? GetPermissionOverwrite(Role role) => null; /// public Task> GetUsers() => null; /// @@ -60,15 +60,11 @@ namespace Discord public Task> GetMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before) => null; /// public Task> GetInvites() => null; - - /// - public Task UpdatePermissionOverwrite(User user, ChannelPermissions allow, ChannelPermissions deny) => null; + /// - public Task UpdatePermissionOverwrite(User user, TriStateChannelPermissions permissions) => null; + public Task UpdatePermissionOverwrite(User user, OverwritePermissions permissions) => null; /// - public Task UpdatePermissionOverwrite(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; - /// - public Task UpdatePermissionOverwrite(Role role, TriStateChannelPermissions permissions) => null; + public Task UpdatePermissionOverwrite(Role role, OverwritePermissions permissions) => null; /// public Task RemovePermissionOverwrite(User user) => null; /// @@ -87,11 +83,11 @@ namespace Discord /// public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; - /// - public Task Delete() => null; /// public Task Update() => null; /// public Task Modify(Action func) => null; + /// + public Task Delete() => null; } } diff --git a/ref/Entities/Channels/VoiceChannel.cs b/ref/Entities/Channels/VoiceChannel.cs index 6a6232a9d..100569c71 100644 --- a/ref/Entities/Channels/VoiceChannel.cs +++ b/ref/Entities/Channels/VoiceChannel.cs @@ -18,7 +18,7 @@ namespace Discord /// public DiscordClient Discord { get; } /// - public ModelState State { get; } + public EntityState State { get; } /// public ChannelType Type { get; } /// @@ -39,25 +39,21 @@ namespace Discord /// public Server Server { get; } /// - public IEnumerable PermissionOverwrites { get; } + public IEnumerable PermissionOverwrites { get; } /// - public PermissionOverwrite? GetPermissionOverwrite(User user) => null; + public OverwritePermissions? GetPermissionOverwrite(User user) => null; /// - public PermissionOverwrite? GetPermissionOverwrite(Role role) => null; + public OverwritePermissions? GetPermissionOverwrite(Role role) => null; /// public Task> GetUsers() => null; /// public Task> GetInvites() => null; /// - public Task UpdatePermissionOverwrite(User user, ChannelPermissions allow, ChannelPermissions deny) => null; + public Task UpdatePermissionOverwrite(User user, OverwritePermissions permissions) => null; /// - public Task UpdatePermissionOverwrite(User user, TriStateChannelPermissions permissions) => null; - /// - public Task UpdatePermissionOverwrite(Role role, ChannelPermissions allow, ChannelPermissions deny) => null; - /// - public Task UpdatePermissionOverwrite(Role role, TriStateChannelPermissions permissions) => null; + public Task UpdatePermissionOverwrite(Role role, OverwritePermissions permissions) => null; /// public Task RemovePermissionOverwrite(User user) => null; /// @@ -66,11 +62,11 @@ namespace Discord /// public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; - /// - public Task Delete() => null; /// public Task Update() => null; /// public Task Modify(Action func) => null; + /// + public Task Delete() => null; } } diff --git a/ref/Entities/IEntity.cs b/ref/Entities/IEntity.cs index eb30ddd55..ddd791d74 100644 --- a/ref/Entities/IEntity.cs +++ b/ref/Entities/IEntity.cs @@ -13,10 +13,8 @@ namespace Discord /// Gets the DiscordClient that manages this object. DiscordClient Discord { get; } /// Gets the state of this object. - ModelState State { get; } - - /// Deletes this object. - Task Delete(); + EntityState State { get; } + /// Downloads the latest values and updates this object. Task Update(); } diff --git a/ref/Entities/Invite.cs b/ref/Entities/Invite.cs index 041ef2e97..1e6da77dc 100644 --- a/ref/Entities/Invite.cs +++ b/ref/Entities/Invite.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace Discord { - public class Invite : IModel + public class Invite : IEntity { public class ServerInfo { @@ -23,10 +23,11 @@ namespace Discord public string AvatarId { get; } public string AvatarUrl { get; } } - - public DiscordClient Client { get; } - string IModel.Id => Code; + string IEntity.Id => Code; + public DiscordClient Discord { get; } + public EntityState State { get; } + public string Code { get; } public string XkcdCode { get; } @@ -40,9 +41,9 @@ namespace Discord public DateTime CreatedAt { get; } public string Url { get; } - public Task Delete() => null; public Task Accept() => null; - public Task Save() => null; - } + public Task Update() => null; + public Task Delete() => null; + } } diff --git a/ref/Entities/Message.cs b/ref/Entities/Message.cs index ca019da3f..617446dad 100644 --- a/ref/Entities/Message.cs +++ b/ref/Entities/Message.cs @@ -38,13 +38,14 @@ namespace Discord public int? Width { get; } public int? Height { get; } } - - public DiscordClient Client { get; } + public ulong Id { get; } + public DiscordClient Discord { get; } + public EntityState State { get; } + public ITextChannel Channel { get; } public User User { get; } public bool IsTTS { get; } - public MessageState State { get; } public string RawText { get; } public string Text { get; } public DateTime Timestamp { get; } @@ -59,10 +60,9 @@ namespace Discord public Server Server => null; public bool IsAuthor => false; - public Task Delete() => null; - - public Task Save() => null; - public bool IsMentioningMe(bool includeRoles = false) => false; + + public Task Update() => null; + public Task Delete() => null; } } diff --git a/ref/Entities/Permissions/ChannelPermissions.cs b/ref/Entities/Permissions/ChannelPermissions.cs index 9a4248923..d01f0430e 100644 --- a/ref/Entities/Permissions/ChannelPermissions.cs +++ b/ref/Entities/Permissions/ChannelPermissions.cs @@ -6,12 +6,11 @@ public static ChannelPermissions TextOnly { get; } public static ChannelPermissions PrivateOnly { get; } public static ChannelPermissions VoiceOnly { get; } - public static ChannelPermissions All(Channel channel) => default(ChannelPermissions); - public static ChannelPermissions All(ChannelType channelType, bool isPrivate) => default(ChannelPermissions); + public static ChannelPermissions All(ChannelType channelType) => default(ChannelPermissions); public uint RawValue { get; } - public bool CreateInstantInvit { get; } + public bool CreateInstantInvite { get; } public bool ManagePermission { get; } public bool ManageChannel { get; } @@ -44,7 +43,7 @@ { } - public ChannelPermissions Modify(ChannelPermissions basePerms, bool? createInstantInvite = null, bool? managePermissions = null, + public ChannelPermissions Modify(bool? createInstantInvite = null, bool? managePermissions = null, bool? manageChannel = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, diff --git a/ref/Entities/Permissions/TriStateChannelPermissions.cs b/ref/Entities/Permissions/OverwritePermissions.cs similarity index 81% rename from ref/Entities/Permissions/TriStateChannelPermissions.cs rename to ref/Entities/Permissions/OverwritePermissions.cs index 7a26bd185..1cda173ec 100644 --- a/ref/Entities/Permissions/TriStateChannelPermissions.cs +++ b/ref/Entities/Permissions/OverwritePermissions.cs @@ -1,8 +1,8 @@ namespace Discord { - public struct TriStateChannelPermissions + public struct OverwritePermissions { - public static TriStateChannelPermissions InheritAll { get; } + public static OverwritePermissions InheritAll { get; } public uint AllowValue { get; } public uint DenyValue { get; } @@ -26,7 +26,7 @@ public PermValue MoveMembers { get; } public PermValue UseVoiceActivation { get; } - public TriStateChannelPermissions(PermValue? createInstantInvite = null, PermValue? managePermissions = null, + public OverwritePermissions(PermValue? createInstantInvite = null, PermValue? managePermissions = null, PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, @@ -35,16 +35,16 @@ { } - public TriStateChannelPermissions(uint allow = 0, uint deny = 0) + public OverwritePermissions(uint allow = 0, uint deny = 0) : this() { } - public TriStateChannelPermissions Modify(PermValue? createInstantInvite = null, PermValue? managePermissions = null, + public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? managePermissions = null, PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null) - => default(TriStateChannelPermissions); + => default(OverwritePermissions); } } diff --git a/ref/Entities/Permissions/PermissionOverwrite.cs b/ref/Entities/Permissions/PermissionOverwrite.cs deleted file mode 100644 index 94658160a..000000000 --- a/ref/Entities/Permissions/PermissionOverwrite.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Discord -{ - public struct PermissionOverwrite - { - public PermissionTarget TargetType { get; } - public ulong TargetId { get; } - public TriStateChannelPermissions Permissions { get; } - } -} diff --git a/ref/Entities/Permissions/ServerPermissions.cs b/ref/Entities/Permissions/ServerPermissions.cs index 67f208b97..fe85c07dd 100644 --- a/ref/Entities/Permissions/ServerPermissions.cs +++ b/ref/Entities/Permissions/ServerPermissions.cs @@ -44,7 +44,7 @@ { } - public ServerPermissions Modify(ServerPermissions basePerms, bool? createInstantInvite = null, bool? manageRoles = null, + public ServerPermissions Modify(bool? createInstantInvite = null, bool? manageRoles = null, bool? kickMembers = null, bool? banMembers = null, bool? manageChannel = null, bool? manageServer = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, diff --git a/ref/Entities/Profile.cs b/ref/Entities/Profile.cs index 73965de65..aa61e51b2 100644 --- a/ref/Entities/Profile.cs +++ b/ref/Entities/Profile.cs @@ -3,10 +3,11 @@ namespace Discord { public class Profile : IEntity - { - public DiscordClient Client { get; } - + { public ulong Id { get; } + public DiscordClient Discord { get; } + public EntityState State { get; } + public string AvatarId { get; } public string AvatarUrl { get; } public ushort Discriminator { get; } @@ -16,8 +17,9 @@ namespace Discord public string Email { get; } public bool? IsVerified { get; } - public string Name { get; set; } + public string Name { get; set; } - public Task Save() => null; + public Task Update() => null; + public Task Delete() => null; } } diff --git a/ref/Entities/Region.cs b/ref/Entities/Region.cs index 5b0354048..fbb801eaa 100644 --- a/ref/Entities/Region.cs +++ b/ref/Entities/Region.cs @@ -7,14 +7,5 @@ public string Hostname { get; } public int Port { get; } public bool Vip { get; } - - internal Region(string id, string name, string hostname, int port, bool vip) - { - Id = id; - Name = name; - Hostname = hostname; - Port = port; - Vip = vip; - } } } diff --git a/ref/Entities/Role.cs b/ref/Entities/Role.cs index b9e35dc46..92edb7bd2 100644 --- a/ref/Entities/Role.cs +++ b/ref/Entities/Role.cs @@ -5,9 +5,10 @@ namespace Discord { public class Role : IEntity, IMentionable { - public DiscordClient Client { get; } - public ulong Id { get; } + public DiscordClient Discord { get; } + public EntityState State { get; } + public Server Server { get; } public string Name { get; } @@ -21,9 +22,8 @@ namespace Discord public IEnumerable Members { get; } public string Mention { get; } - - public Task Delete() => null; - public Task Save() => null; + public Task Update() => null; + public Task Delete() => null; } } diff --git a/ref/Entities/Server.cs b/ref/Entities/Server.cs index 0a81d8737..04838b6d5 100644 --- a/ref/Entities/Server.cs +++ b/ref/Entities/Server.cs @@ -16,6 +16,9 @@ namespace Discord } public ulong Id { get; } + public DiscordClient Discord { get; } + public EntityState State { get; } + public User CurrentUser { get; } public string IconId { get; } public string SplashId { get; } @@ -50,11 +53,8 @@ namespace Discord public Task> DownloadBans() => null; public Task> DownloadInvites() => null; - public Task Leave() => null; - public Task Delete() => null; - public Task Save() => null; - - public Task CreateChannel(string name, ChannelType type) => null; + public Task CreateTextChannel(string name) => null; + public Task CreateVoiceChannel(string name) => null; public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; public Task CreateRole(string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) => null; @@ -62,9 +62,10 @@ namespace Discord public Task Unban(User user) => null; public Task Unban(ulong userId) => null; - public Task ReorderChannels(IEnumerable channels) => null; - public Task ReorderRoles(IEnumerable roles, Role after = null) => null; - public Task PruneUsers(int days = 30, bool simulate = false) => null; - } + + public Task Update() => null; + public Task Leave() => null; + public Task Delete() => null; + } } diff --git a/ref/Entities/User.cs b/ref/Entities/User.cs index 4bef43605..94ae357fd 100644 --- a/ref/Entities/User.cs +++ b/ref/Entities/User.cs @@ -5,13 +5,13 @@ using System.Threading.Tasks; namespace Discord { - public class User : IModel - { - public DiscordClient Client { get; } - + public class User : IEntity + { public ulong Id { get; } - public Server Server { get; } - + public DiscordClient Discord { get; } + public EntityState State { get; } + + public Server Server { get; } public string Name { get; } public ushort Discriminator { get; } public string AvatarId { get; } @@ -20,7 +20,6 @@ namespace Discord public DateTime JoinedAt { get; } public DateTime? LastActivityAt { get; } - public Channel PrivateChannel => null; public string Mention => null; public bool IsSelfMuted => false; public bool IsSelfDeafened => false; @@ -28,18 +27,14 @@ namespace Discord public bool IsServerDeafened => false; public bool IsServerSuppressed => false; public DateTime? LastOnlineAt => null; - public Channel VoiceChannel => null; + public VoiceChannel VoiceChannel => null; public string AvatarUrl => null; - public IEnumerable Roles => null; - - public IEnumerable Channels => null; - - public Task Kick() => null; - + public IEnumerable Roles => null; + public IEnumerable Channels => null; public ServerPermissions ServerPermissions => default(ServerPermissions); - public ChannelPermissions GetPermissions(Channel channel) => default(ChannelPermissions); - - public Task CreatePMChannel() => null; + + public ChannelPermissions GetPermissions(IPublicChannel channel) => default(ChannelPermissions); + public Task GetPrivateChannel() => null; public Task SendMessage(string text) => null; public Task SendFile(string filePath) => null; @@ -50,6 +45,7 @@ namespace Discord public Task AddRoles(params Role[] roles) => null; public Task RemoveRoles(params Role[] roles) => null; - public Task Save() => null; + public Task Update() => null; + public Task Kick() => null; } } \ No newline at end of file diff --git a/ref/Enums/EntityState.cs b/ref/Enums/EntityState.cs new file mode 100644 index 000000000..6ae71e4a3 --- /dev/null +++ b/ref/Enums/EntityState.cs @@ -0,0 +1,18 @@ +namespace Discord +{ + public enum EntityState : byte + { + /// Object is not attached to a cache manager nor receiving live updates. + Detached = 0, + /// Object is attached to a cache manager and receiving live updates. + Attached, + /// Object was deleted. + Deleted, + /// Object is currently waiting to be created. + Queued, + /// Object's creation was aborted. + Aborted, + /// Object's creation failed. + Failed + } +} diff --git a/ref/Enums/LogSeverity.cs b/ref/Enums/LogSeverity.cs index 879aecd09..785b0ef46 100644 --- a/ref/Enums/LogSeverity.cs +++ b/ref/Enums/LogSeverity.cs @@ -2,6 +2,7 @@ { public enum LogSeverity { + Critical = 0, Error = 1, Warning = 2, Info = 3, diff --git a/ref/Enums/ModelState.cs b/ref/Enums/ModelState.cs deleted file mode 100644 index a4477e94d..000000000 --- a/ref/Enums/ModelState.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Discord -{ - public enum ModelState : byte - { - /// Message did not originate from this session, or was successfully sent. - Normal = 0, - /// Message is current queued. - Queued, - /// Message was deleted. - Deleted, - /// Message was deleted before it was sent. - Aborted, - /// Message failed to be sent. - Failed, - /// Message has been removed from cache and will no longer receive updates. - Detached - } -} diff --git a/ref/Enums/PermissionBits.cs b/ref/Enums/PermissionBits.cs deleted file mode 100644 index b5acab73f..000000000 --- a/ref/Enums/PermissionBits.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Discord -{ - internal enum PermissionBits - { - //General - CreateInstantInvite = 0, - KickMembers = 1, - BanMembers = 2, - ManageRolesOrPermissions = 3, - ManageChannel = 4, - ManageServer = 5, - - //Text - ReadMessages = 10, - SendMessages = 11, - SendTTSMessages = 12, - ManageMessages = 13, - EmbedLinks = 14, - AttachFiles = 15, - ReadMessageHistory = 16, - MentionEveryone = 17, - - //Voice - Connect = 20, - Speak = 21, - MuteMembers = 22, - DeafenMembers = 23, - MoveMembers = 24, - UseVoiceActivation = 25 - } -} diff --git a/ref/Events/ChannelEventArgs.cs b/ref/Events/ChannelEventArgs.cs index cafdf2fa7..583075e08 100644 --- a/ref/Events/ChannelEventArgs.cs +++ b/ref/Events/ChannelEventArgs.cs @@ -4,7 +4,6 @@ namespace Discord { public class ChannelEventArgs : EventArgs { - public Channel Channel => null; - public Server Server => null; + public IChannel Channel => null; } } diff --git a/ref/Events/ChannelUpdatedEventArgs.cs b/ref/Events/ChannelUpdatedEventArgs.cs index eee390f37..bcd809521 100644 --- a/ref/Events/ChannelUpdatedEventArgs.cs +++ b/ref/Events/ChannelUpdatedEventArgs.cs @@ -6,6 +6,5 @@ namespace Discord { public IChannel Before => null; public IChannel After => null; - public Server Server => null; } } diff --git a/ref/Events/MessageEventArgs.cs b/ref/Events/MessageEventArgs.cs index 98eeb4f9d..5b251fa9d 100644 --- a/ref/Events/MessageEventArgs.cs +++ b/ref/Events/MessageEventArgs.cs @@ -6,7 +6,6 @@ namespace Discord { public Message Message => null; public User User => null; - public Channel Channel => null; - public Server Server => null; + public ITextChannel Channel => null; } } diff --git a/ref/Events/MessageUpdatedEventArgs.cs b/ref/Events/MessageUpdatedEventArgs.cs index d529907a4..3205721f3 100644 --- a/ref/Events/MessageUpdatedEventArgs.cs +++ b/ref/Events/MessageUpdatedEventArgs.cs @@ -7,7 +7,6 @@ namespace Discord public Message Before => null; public Message After => null; public User User => null; - public Channel Channel => null; - public Server Server => null; + public ITextChannel Channel => null; } } diff --git a/ref/Events/UserEventArgs.cs b/ref/Events/UserEventArgs.cs index 4ff745307..6f638c915 100644 --- a/ref/Events/UserEventArgs.cs +++ b/ref/Events/UserEventArgs.cs @@ -4,6 +4,5 @@ namespace Discord public class UserEventArgs : EventArgs { public User User => null; - public Server Server => null; } } diff --git a/ref/Events/UserUpdatedEventArgs.cs b/ref/Events/UserUpdatedEventArgs.cs index c5b2b0acd..6fb5fa2c3 100644 --- a/ref/Events/UserUpdatedEventArgs.cs +++ b/ref/Events/UserUpdatedEventArgs.cs @@ -5,6 +5,5 @@ namespace Discord { public User Before => null; public User After => null; - public Server Server => null; } } diff --git a/ref/Net/WebSockets/WebSocketEventEventArgs.cs b/ref/Net/WebSockets/WebSocketEventEventArgs.cs index a0c60edcf..676c0ba6e 100644 --- a/ref/Net/WebSockets/WebSocketEventEventArgs.cs +++ b/ref/Net/WebSockets/WebSocketEventEventArgs.cs @@ -7,11 +7,5 @@ namespace Discord.Net.WebSockets { public string Type { get; } public JToken Payload { get; } - - internal WebSocketEventEventArgs(string type, JToken data) - { - Type = type; - Payload = data; - } } } From 0d9d8fc712c1ad67653672e706befc8ef95beb98 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 4 Mar 2016 05:49:47 -0400 Subject: [PATCH 48/51] Added Login/Logout events, Logout method and skip token validate to ref --- ref/DiscordClient.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ref/DiscordClient.cs b/ref/DiscordClient.cs index 027383ae7..a1708309f 100644 --- a/ref/DiscordClient.cs +++ b/ref/DiscordClient.cs @@ -1,7 +1,5 @@ -using Discord.Net; -using Discord.Net.Rest; +using Discord.Net.Rest; using Discord.Net.WebSockets; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; @@ -13,7 +11,9 @@ namespace Discord /// Provides a connection to the DiscordApp service. public class DiscordClient : IDisposable { - public event EventHandler Ready = delegate { }; + public event EventHandler LoggedIn = delegate { }; + public event EventHandler LoggedOut = delegate { }; + public event EventHandler Log = delegate { }; public event EventHandler ChannelCreated = delegate { }; public event EventHandler ChannelUpdated = delegate { }; public event EventHandler ChannelDestroyed = delegate { }; @@ -39,11 +39,10 @@ namespace Discord public event EventHandler UserUnbanned = delegate { }; public DiscordConfig Config { get; } - public RestClient ClientAPI { get; } - public RestClient StatusAPI { get; } + public IRestClient ClientAPI { get; } + public IRestClient StatusAPI { get; } public GatewaySocket GatewaySocket { get; } public MessageQueue MessageQueue { get; } - public JsonSerializer Serializer { get; } public ConnectionState State { get; } public CancellationToken CancelToken { get; } @@ -59,10 +58,14 @@ namespace Discord public DiscordClient() { } public DiscordClient(DiscordConfig config) { } public DiscordClient(Action configFunc) { } + + public Task Login(string email, string password, string token = null) => null; + public Task Login(string token, bool validate = true) => null; + public Task Logout() => null; - public Task Connect(string email, string password, string token = null) => null; + /*public Task Connect(string email, string password, string token = null) => null; public Task Connect(string token) => null; - public Task Disconnect() => null; + public Task Disconnect() => null;*/ public void SetStatus(UserStatus status) { } public void SetGame(string game) { } From 0a07442b6ed3c2fb7d971bdefcc43d01352d6d29 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 4 Mar 2016 05:50:24 -0400 Subject: [PATCH 49/51] Moved ban/unban from Server to User in ref --- ref/Entities/Server.cs | 4 ---- ref/Entities/User.cs | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ref/Entities/Server.cs b/ref/Entities/Server.cs index 04838b6d5..3b3b2b283 100644 --- a/ref/Entities/Server.cs +++ b/ref/Entities/Server.cs @@ -57,10 +57,6 @@ namespace Discord public Task CreateVoiceChannel(string name) => null; public Task CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) => null; public Task CreateRole(string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) => null; - - public Task Ban(User user, int pruneDays = 0) => null; - public Task Unban(User user) => null; - public Task Unban(ulong userId) => null; public Task PruneUsers(int days = 30, bool simulate = false) => null; diff --git a/ref/Entities/User.cs b/ref/Entities/User.cs index 94ae357fd..d4eacaef4 100644 --- a/ref/Entities/User.cs +++ b/ref/Entities/User.cs @@ -11,7 +11,8 @@ namespace Discord public DiscordClient Discord { get; } public EntityState State { get; } - public Server Server { get; } + public Server Server { get; } + public string Name { get; } public ushort Discriminator { get; } public string AvatarId { get; } @@ -47,5 +48,8 @@ namespace Discord public Task Update() => null; public Task Kick() => null; + public Task Ban(User user, int pruneDays = 0) => null; + public Task Unban(User user) => null; + public Task Unban(ulong userId) => null; } } \ No newline at end of file From 5a38129c183af66591ebc348d5e01abed5776355 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 4 Mar 2016 05:50:51 -0400 Subject: [PATCH 50/51] Minor ref changes --- ref/MessageQueue.cs | 2 +- ref/Net/Rest/IRestClient.cs | 23 +++++++++++++++++++++++ ref/Net/Rest/RestClient.cs | 25 ------------------------- ref/Net/WebSockets/GatewaySocket.cs | 15 ++++++--------- 4 files changed, 30 insertions(+), 35 deletions(-) create mode 100644 ref/Net/Rest/IRestClient.cs delete mode 100644 ref/Net/Rest/RestClient.cs diff --git a/ref/MessageQueue.cs b/ref/MessageQueue.cs index e909bdacf..5f56abd1e 100644 --- a/ref/MessageQueue.cs +++ b/ref/MessageQueue.cs @@ -1,4 +1,4 @@ -namespace Discord.Net +namespace Discord { public class MessageQueue { diff --git a/ref/Net/Rest/IRestClient.cs b/ref/Net/Rest/IRestClient.cs new file mode 100644 index 000000000..83c0405c7 --- /dev/null +++ b/ref/Net/Rest/IRestClient.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Rest +{ + public interface IRestClient + { + event EventHandler SendingRequest; + event EventHandler SentRequest; + + CancellationToken CancelToken { get; } + string Token { get; } + + Task Send(IRestRequest request) + where ResponseT : class; + Task Send(IRestRequest request); + + Task Send(IRestFileRequest request) + where ResponseT : class; + Task Send(IRestFileRequest request); + } +} diff --git a/ref/Net/Rest/RestClient.cs b/ref/Net/Rest/RestClient.cs deleted file mode 100644 index 33de1e9a4..000000000 --- a/ref/Net/Rest/RestClient.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Discord.Net.Rest -{ - public abstract partial class RestClient - { - public event EventHandler SendingRequest = delegate { }; - public event EventHandler SentRequest = delegate { }; - - public CancellationToken CancelToken { get; set; } - public string Token { get; set; } - - public Task Send(IRestRequest request) - where ResponseT : class - => null; - public Task Send(IRestRequest request) => null; - - public Task Send(IRestFileRequest request) - where ResponseT : class - => null; - public Task Send(IRestFileRequest request) => null; - } -} diff --git a/ref/Net/WebSockets/GatewaySocket.cs b/ref/Net/WebSockets/GatewaySocket.cs index 65ec02fad..e8f2ddd3d 100644 --- a/ref/Net/WebSockets/GatewaySocket.cs +++ b/ref/Net/WebSockets/GatewaySocket.cs @@ -1,5 +1,4 @@ -using Discord.Logging; -using Discord.Net.Rest; +using Discord.Net.Rest; using System; using System.Collections.Generic; using System.Threading; @@ -7,25 +6,23 @@ using System.Threading.Tasks; namespace Discord.Net.WebSockets { - public class GatewaySocket : WebSocket + public class GatewaySocket { - public string SessionId { get; private set; } + public string SessionId { get; } public event EventHandler ReceivedDispatch = delegate { }; - public GatewaySocket(DiscordConfig config, ILogger logger) : base(config, logger) { } - - public Task Connect(RestClient rest, CancellationToken parentCancelToken) => null; + public Task Connect(IRestClient rest, CancellationToken parentCancelToken) => null; public Task Disconnect() => null; public void SendIdentify(string token) { } public void SendResume() { } - public override void SendHeartbeat() { } + public void SendHeartbeat() { } public void SendUpdateStatus(long? idleSince, string gameName) { } public void SendUpdateVoice(ulong? serverId, ulong? channelId, bool isSelfMuted, bool isSelfDeafened) { } public void SendRequestMembers(IEnumerable serverId, string query, int limit) { } - public override void WaitForConnection(CancellationToken cancelToken) { } + public void WaitForConnection(CancellationToken cancelToken) { } } } From 30ac95280e367253bcae6772cd04e706cfce6eff Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 12 Mar 2016 20:20:30 -0400 Subject: [PATCH 51/51] Ref project update --- ref/DiscordClient.cs | 53 ++++++-------- ref/DiscordConfig.cs | 57 +++++++++++---- ref/Entities/Channels/IChannel.cs | 14 ++-- ref/Entities/Channels/IPrivateChannel.cs | 8 -- ref/Entities/Channels/IPublicChannel.cs | 21 +++--- ref/Entities/Channels/ITextChannel.cs | 4 +- ref/Entities/Channels/IVoiceChannel.cs | 8 -- ref/Entities/Channels/PrivateChannel.cs | 26 ++++--- ref/Entities/Channels/TextChannel.cs | 36 +++++---- ref/Entities/Channels/VoiceChannel.cs | 32 ++++---- ref/Entities/IEntity.cs | 3 +- ref/Entities/Invite.cs | 49 ------------- ref/Entities/Invite/BasicInvite.cs | 37 ++++++++++ ref/Entities/Invite/Invite.cs | 18 +++++ ref/Entities/Message.cs | 8 +- .../Permissions/PermissionOverwriteEntry.cs | 9 +++ ref/Entities/Role.cs | 2 +- ref/Entities/Server.cs | 22 +++--- ref/Entities/User.cs | 55 -------------- ref/Entities/Users/IUser.cs | 19 +++++ ref/Entities/Users/PrivateUser.cs | 43 +++++++++++ ref/Entities/Users/ServerUser.cs | 73 +++++++++++++++++++ ref/Events/MessageEventArgs.cs | 2 +- ref/Events/MessageUpdatedEventArgs.cs | 2 +- ref/Events/TypingEventArgs.cs | 4 +- ref/Events/UserEventArgs.cs | 2 +- ref/Events/UserUpdatedEventArgs.cs | 4 +- ref/Net/Rest/IRestClientProvider.cs | 10 +++ ref/Net/WebSockets/IWebSocket.cs | 15 ++++ ref/Net/WebSockets/IWebSocketProvider.cs | 9 +++ ref/Net/WebSockets/WebSocket.cs | 22 ------ 31 files changed, 385 insertions(+), 282 deletions(-) delete mode 100644 ref/Entities/Channels/IPrivateChannel.cs delete mode 100644 ref/Entities/Channels/IVoiceChannel.cs delete mode 100644 ref/Entities/Invite.cs create mode 100644 ref/Entities/Invite/BasicInvite.cs create mode 100644 ref/Entities/Invite/Invite.cs create mode 100644 ref/Entities/Permissions/PermissionOverwriteEntry.cs delete mode 100644 ref/Entities/User.cs create mode 100644 ref/Entities/Users/IUser.cs create mode 100644 ref/Entities/Users/PrivateUser.cs create mode 100644 ref/Entities/Users/ServerUser.cs create mode 100644 ref/Net/Rest/IRestClientProvider.cs create mode 100644 ref/Net/WebSockets/IWebSocket.cs create mode 100644 ref/Net/WebSockets/IWebSocketProvider.cs delete mode 100644 ref/Net/WebSockets/WebSocket.cs diff --git a/ref/DiscordClient.cs b/ref/DiscordClient.cs index a1708309f..aa777e04f 100644 --- a/ref/DiscordClient.cs +++ b/ref/DiscordClient.cs @@ -11,9 +11,15 @@ namespace Discord /// Provides a connection to the DiscordApp service. public class DiscordClient : IDisposable { + public event EventHandler Log = delegate { }; + public event EventHandler LoggedIn = delegate { }; public event EventHandler LoggedOut = delegate { }; - public event EventHandler Log = delegate { }; + public event EventHandler Connected = delegate { }; + public event EventHandler Disconnected = delegate { }; + public event EventHandler VoiceConnected = delegate { }; + public event EventHandler VoiceDisconnected = delegate { }; + public event EventHandler ChannelCreated = delegate { }; public event EventHandler ChannelUpdated = delegate { }; public event EventHandler ChannelDestroyed = delegate { }; @@ -38,47 +44,30 @@ namespace Discord public event EventHandler UserUpdated = delegate { }; public event EventHandler UserUnbanned = delegate { }; - public DiscordConfig Config { get; } - public IRestClient ClientAPI { get; } - public IRestClient StatusAPI { get; } - public GatewaySocket GatewaySocket { get; } public MessageQueue MessageQueue { get; } - - public ConnectionState State { get; } - public CancellationToken CancelToken { get; } - public Profile CurrentUser { get; } - public string SessionId { get; } - public UserStatus Status { get; } - public string CurrentGame { get; } - - public IEnumerable Servers { get; } - public IEnumerable PrivateChannels { get; } - public IEnumerable Regions { get; } + public IRestClient RestClient { get; } + public GatewaySocket GatewaySocket { get; } + public Profile CurrentUser { get; } public DiscordClient() { } public DiscordClient(DiscordConfig config) { } - public DiscordClient(Action configFunc) { } - public Task Login(string email, string password, string token = null) => null; - public Task Login(string token, bool validate = true) => null; + public Task Login(string token) => null; public Task Logout() => null; - /*public Task Connect(string email, string password, string token = null) => null; - public Task Connect(string token) => null; - public Task Disconnect() => null;*/ - - public void SetStatus(UserStatus status) { } - public void SetGame(string game) { } - - public PrivateChannel GetPrivateChannel(ulong id) => null; - public Task CreatePrivateChannel(ulong userId) => null; + public Task Connect() => null; + public Task Connect(int connectionId, int totalConnections) => null; + public Task Disconnect() => null; + public Task> GetPrivateChannels() => null; + public Task GetPrivateChannel(ulong userId) => null; public Task GetInvite(string inviteIdOrXkcd) => null; + public Task> GetRegions() => null; + public Task GetRegion(string id) => null; + public Task> GetServers() => null; + public Task GetServer(ulong id) => null; - public Region GetRegion(string id) => null; - - public Server GetServer(ulong id) => null; - public IEnumerable FindServers(string name) => null; + public Task CreatePrivateChannel(ulong userId) => null; public Task CreateServer(string name, Region region, ImageType iconType = ImageType.None, Stream icon = null) => null; public void Dispose() { } diff --git a/ref/DiscordConfig.cs b/ref/DiscordConfig.cs index 657a58437..e6b1a5568 100644 --- a/ref/DiscordConfig.cs +++ b/ref/DiscordConfig.cs @@ -1,36 +1,65 @@ -using System; +using Discord.Net.Rest; +using Discord.Net.WebSockets; +using System.Reflection; namespace Discord -{ - public class DiscordConfig +{ + public class DiscordConfig { public const int MaxMessageSize = 2000; + public const int MaxMessagesPerBatch = 100; + public const string LibName = "Discord.Net"; - public static readonly string LibVersion = null; + public static string LibVersion => typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown"; public const string LibUrl = "https://github.com/RogueException/Discord.Net"; public const string ClientAPIUrl = "https://discordapp.com/api/"; - public const string StatusAPIUrl = "https://srhpyqt94yxb.statuspage.io/api/v2/"; //"https://status.discordapp.com/api/v2/"; public const string CDNUrl = "https://cdn.discordapp.com/"; public const string InviteUrl = "https://discord.gg/"; + /// Gets or sets name of your application, used in the user agent. public string AppName { get; set; } = null; + /// Gets or sets url to your application, used in the user agent. public string AppUrl { get; set; } = null; + /// Gets or sets the version of your application, used in the user agent. public string AppVersion { get; set; } = null; + + /// Gets or sets the minimum log level severity that will be sent to the LogMessage event. public LogSeverity LogLevel { get; set; } = LogSeverity.Info; - + + /// Gets or sets the time (in milliseconds) to wait for the websocket to connect and initialize. public int ConnectionTimeout { get; set; } = 30000; - public int ReconnectDelay { get; set; } = 1000; - public int FailedReconnectDelay { get; set; } = 15000; - - public bool CacheToken { get; set; } = true; - public int MessageCacheSize { get; set; } = 100; + /// Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting. + public int ReconnectDelay { get; set; } = 1000; + /// Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. + public int FailedReconnectDelay { get; set; } = 15000; + + //Performance + /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. + public int MessageCacheSize { get; set; } = 100; + /// + /// Gets or sets whether the permissions cache should be used. + /// This makes operations such as User.GetPermissions(Channel), User.ServerPermissions, Channel.GetUser, and Channel.Members much faster while increasing memory usage. + /// public bool UsePermissionsCache { get; set; } = true; + /// Gets or sets whether the a copy of a model is generated on an update event to allow you to check which properties changed. public bool EnablePreUpdateEvents { get; set; } = true; + /// + /// Gets or sets the max number of users a server may have for offline users to be included in the READY packet. Max is 250. + /// Decreasing this may reduce CPU usage while increasing login time and network usage. + /// public int LargeThreshold { get; set; } = 250; - - public EventHandler LogHandler { get; set; } - public string UserAgent { get; } + + //Engines + + /// Gets or sets the REST engine to use.. Defaults to DefaultRestClientProvider, which uses .Net's HttpClient class. + public IRestClientProvider RestClientProvider { get; set; } = null; + /// + /// Gets or sets the WebSocket engine to use. Defaults to DefaultWebSocketProvider, which uses .Net's WebSocketClient class. + /// WebSockets are only used if DiscordClient.Connect() is called. + /// + public IWebSocketProvider WebSocketProvider { get; set; } = null; } } + diff --git a/ref/Entities/Channels/IChannel.cs b/ref/Entities/Channels/IChannel.cs index 55e723303..fb82fb30d 100644 --- a/ref/Entities/Channels/IChannel.cs +++ b/ref/Entities/Channels/IChannel.cs @@ -7,16 +7,12 @@ namespace Discord { /// Gets the type flags for this channel. ChannelType Type { get; } - /// Gets whether this is a text channel. - bool IsText { get; } - /// Gets whether this is a voice channel. - bool IsVoice { get; } - /// Gets whether this is a private channel. - bool IsPrivate { get; } - /// Gets whether this is a public channel. - bool IsPublic { get; } + /// Gets the name of this channel. + string Name { get; } + /// Gets a user in this channel with the given id. + Task GetUser(ulong id); /// Gets a collection of all users in this channel. - Task> GetUsers(); + Task> GetUsers(); } } diff --git a/ref/Entities/Channels/IPrivateChannel.cs b/ref/Entities/Channels/IPrivateChannel.cs deleted file mode 100644 index 0ac5ec3aa..000000000 --- a/ref/Entities/Channels/IPrivateChannel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Discord -{ - public interface IPrivateChannel : IChannel - { - /// Gets the recipient of the messages in this private channel. - User Recipient { get; } - } -} diff --git a/ref/Entities/Channels/IPublicChannel.cs b/ref/Entities/Channels/IPublicChannel.cs index 91a05d861..bd005a288 100644 --- a/ref/Entities/Channels/IPublicChannel.cs +++ b/ref/Entities/Channels/IPublicChannel.cs @@ -3,36 +3,33 @@ using System.Threading.Tasks; namespace Discord { - public struct PermissionOverwriteEntry - { - public PermissionTarget TargetType { get; } - public ulong TargetId { get; } - public OverwritePermissions Permissions { get; } - } public interface IPublicChannel : IChannel { /// Gets the server this channel is a member of. Server Server { get; } /// Gets a collection of permission overwrites for this channel. IEnumerable PermissionOverwrites { get; } - /// Gets the name of this public channel. - string Name { get; } /// Gets the position of this public channel relative to others of the same type. int Position { get; } + /// Gets a user in this channel with the given id. + new Task GetUser(ulong id); + /// Gets a collection of all users in this channel. + new Task> GetUsers(); + /// Gets the permission overwrite for a specific user, or null if one does not exist. - OverwritePermissions? GetPermissionOverwrite(User user); + OverwritePermissions? GetPermissionOverwrite(ServerUser user); /// Gets the permission overwrite for a specific role, or null if one does not exist. OverwritePermissions? GetPermissionOverwrite(Role role); /// Downloads a collection of all invites to this server. Task> GetInvites(); - + /// Adds or updates the permission overwrite for the given user. - Task UpdatePermissionOverwrite(User user, OverwritePermissions permissions); + Task UpdatePermissionOverwrite(ServerUser user, OverwritePermissions permissions); /// Adds or updates the permission overwrite for the given role. Task UpdatePermissionOverwrite(Role role, OverwritePermissions permissions); /// Removes the permission overwrite for the given user, if one exists. - Task RemovePermissionOverwrite(User user); + Task RemovePermissionOverwrite(ServerUser user); /// Removes the permission overwrite for the given role, if one exists. Task RemovePermissionOverwrite(Role role); diff --git a/ref/Entities/Channels/ITextChannel.cs b/ref/Entities/Channels/ITextChannel.cs index b1449fac9..f3701abbf 100644 --- a/ref/Entities/Channels/ITextChannel.cs +++ b/ref/Entities/Channels/ITextChannel.cs @@ -10,12 +10,12 @@ namespace Discord Task GetMessage(ulong id); /// Gets the last N messages from this text channel. /// The maximum number of messages to retrieve. - Task> GetMessages(int limit = 100); + Task> GetMessages(int limit = DiscordConfig.MaxMessagesPerBatch); /// Gets a collection of messages in this channel. /// The maximum number of messages to retrieve. /// The message to start downloading relative to. /// The direction, from relativeMessageId, to download messages in. - Task> GetMessages(int limit = 100, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before); + Task> GetMessages(int limit = DiscordConfig.MaxMessagesPerBatch, ulong? relativeMessageId = null, Relative relativeDir = Relative.Before); /// Sends a message to this text channel. Task SendMessage(string text, bool isTTS = false); diff --git a/ref/Entities/Channels/IVoiceChannel.cs b/ref/Entities/Channels/IVoiceChannel.cs deleted file mode 100644 index f8c3be999..000000000 --- a/ref/Entities/Channels/IVoiceChannel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Discord -{ - public interface IVoiceChannel : IChannel - { - /// Gets the requested bitrate, in bits per second, of this voice channel. - int Bitrate { get; } - } -} diff --git a/ref/Entities/Channels/PrivateChannel.cs b/ref/Entities/Channels/PrivateChannel.cs index 6364bf884..ee72c0828 100644 --- a/ref/Entities/Channels/PrivateChannel.cs +++ b/ref/Entities/Channels/PrivateChannel.cs @@ -4,30 +4,32 @@ using System.Threading.Tasks; namespace Discord { - public class PrivateChannel : ITextChannel, IPrivateChannel + public class PrivateChannel : ITextChannel, IChannel { - /// - public ulong Id { get; } /// public DiscordClient Discord { get; } /// public EntityState State { get; } /// - public ChannelType Type { get; } - /// - public bool IsPrivate => true; + public ulong Id { get; } /// - public bool IsPublic => false; + public PrivateUser Recipient { get; } /// - public bool IsText => true; + public PrivateUser CurrentUser { get; } + /// - public bool IsVoice => false; - + ChannelType IChannel.Type => ChannelType.Private | ChannelType.Text; /// - public User Recipient { get; } + public string Name { get; } /// - public Task> GetUsers() => null; + public Task GetUser(ulong id) => null; + /// + Task IChannel.GetUser(ulong id) => null; + /// + public Task> GetUsers() => null; + /// + Task> IChannel.GetUsers() => null; /// public Task GetMessage(ulong id) => null; /// diff --git a/ref/Entities/Channels/TextChannel.cs b/ref/Entities/Channels/TextChannel.cs index 2d977111c..0b1b81c77 100644 --- a/ref/Entities/Channels/TextChannel.cs +++ b/ref/Entities/Channels/TextChannel.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Discord { - public class TextChannel : ITextChannel, IPublicChannel, IMentionable, IModifiable + public class TextChannel : ITextChannel, IMentionable, IModifiable { public sealed class Properties { @@ -14,22 +14,17 @@ namespace Discord public int Position { get; } } - /// - public ulong Id { get; } - /// - public DiscordClient Discord { get; } /// public EntityState State { get; } /// - public ChannelType Type => ChannelType.Public | ChannelType.Text; - /// - public bool IsPrivate => false; + public ulong Id { get; } /// - public bool IsPublic => true; + public Server Server { get; } + /// - public bool IsText => true; + public DiscordClient Discord { get; } /// - public bool IsVoice => false; + public ChannelType Type => ChannelType.Public | ChannelType.Text; /// public string Name { get; } @@ -37,21 +32,24 @@ namespace Discord public string Topic { get; } /// public int Position { get; } + /// public string Mention { get; } /// - public Server Server { get; } - /// public IEnumerable PermissionOverwrites { get; } - /// - public IEnumerable Users { get; } /// - public OverwritePermissions? GetPermissionOverwrite(User user) => null; + public OverwritePermissions? GetPermissionOverwrite(ServerUser user) => null; /// public OverwritePermissions? GetPermissionOverwrite(Role role) => null; /// - public Task> GetUsers() => null; + public Task GetUser(ulong id) => null; + /// + Task IChannel.GetUser(ulong id) => null; + /// + public Task> GetUsers() => null; + /// + Task> IChannel.GetUsers() => null; /// public Task GetMessage(ulong id) => null; /// @@ -62,11 +60,11 @@ namespace Discord public Task> GetInvites() => null; /// - public Task UpdatePermissionOverwrite(User user, OverwritePermissions permissions) => null; + public Task UpdatePermissionOverwrite(ServerUser user, OverwritePermissions permissions) => null; /// public Task UpdatePermissionOverwrite(Role role, OverwritePermissions permissions) => null; /// - public Task RemovePermissionOverwrite(User user) => null; + public Task RemovePermissionOverwrite(ServerUser user) => null; /// public Task RemovePermissionOverwrite(Role role) => null; diff --git a/ref/Entities/Channels/VoiceChannel.cs b/ref/Entities/Channels/VoiceChannel.cs index 100569c71..6552fadd7 100644 --- a/ref/Entities/Channels/VoiceChannel.cs +++ b/ref/Entities/Channels/VoiceChannel.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Discord { - public class VoiceChannel : IPublicChannel, IVoiceChannel, IModifiable + public class VoiceChannel : IPublicChannel, IModifiable { public sealed class Properties { @@ -16,19 +16,14 @@ namespace Discord /// public ulong Id { get; } /// - public DiscordClient Discord { get; } - /// public EntityState State { get; } /// - public ChannelType Type { get; } - /// - public bool IsPrivate => false; - /// - public bool IsPublic => true; + public Server Server { get; } + /// - public bool IsText => false; + public DiscordClient Discord { get; } /// - public bool IsVoice => true; + ChannelType IChannel.Type => ChannelType.Public | ChannelType.Voice; /// public string Name { get; } @@ -36,26 +31,33 @@ namespace Discord public int Position { get; } /// public int Bitrate { get; } + /// - public Server Server { get; } + public string Mention { get; } /// public IEnumerable PermissionOverwrites { get; } /// - public OverwritePermissions? GetPermissionOverwrite(User user) => null; + public OverwritePermissions? GetPermissionOverwrite(ServerUser user) => null; /// public OverwritePermissions? GetPermissionOverwrite(Role role) => null; /// - public Task> GetUsers() => null; + public Task GetUser(ulong id) => null; + /// + Task IChannel.GetUser(ulong id) => null; + /// + public Task> GetUsers() => null; + /// + Task> IChannel.GetUsers() => null; /// public Task> GetInvites() => null; /// - public Task UpdatePermissionOverwrite(User user, OverwritePermissions permissions) => null; + public Task UpdatePermissionOverwrite(ServerUser user, OverwritePermissions permissions) => null; /// public Task UpdatePermissionOverwrite(Role role, OverwritePermissions permissions) => null; /// - public Task RemovePermissionOverwrite(User user) => null; + public Task RemovePermissionOverwrite(ServerUser user) => null; /// public Task RemovePermissionOverwrite(Role role) => null; diff --git a/ref/Entities/IEntity.cs b/ref/Entities/IEntity.cs index ddd791d74..ac707a69e 100644 --- a/ref/Entities/IEntity.cs +++ b/ref/Entities/IEntity.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace Discord { diff --git a/ref/Entities/Invite.cs b/ref/Entities/Invite.cs deleted file mode 100644 index 1e6da77dc..000000000 --- a/ref/Entities/Invite.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Discord -{ - public class Invite : IEntity - { - public class ServerInfo - { - public ulong Id { get; } - public string Name { get; } - } - public class ChannelInfo - { - public ulong Id { get; } - public string Name { get; } - } - public class InviterInfo - { - public ulong Id { get; } - public string Name { get; } - public ushort Discriminator { get; } - public string AvatarId { get; } - public string AvatarUrl { get; } - } - - string IEntity.Id => Code; - public DiscordClient Discord { get; } - public EntityState State { get; } - - public string Code { get; } - public string XkcdCode { get; } - - public ServerInfo Server { get; } - public ChannelInfo Channel { get; } - public int? MaxAge { get; } - public int Uses { get; } - public int? MaxUses { get; } - public bool IsRevoked { get; } - public bool IsTemporary { get; } - public DateTime CreatedAt { get; } - public string Url { get; } - - public Task Accept() => null; - - public Task Update() => null; - public Task Delete() => null; - } -} diff --git a/ref/Entities/Invite/BasicInvite.cs b/ref/Entities/Invite/BasicInvite.cs new file mode 100644 index 000000000..37cd1704d --- /dev/null +++ b/ref/Entities/Invite/BasicInvite.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public class BasicInvite : IEntity + { + public class TargetInfo + { + public ulong Id { get; } + public string Name { get; } + } + public class InviterInfo + { + public ulong Id { get; } + public string Name { get; } + public ushort Discriminator { get; } + public string AvatarId { get; } + public string AvatarUrl { get; } + } + + string IEntity.Id => Code; + public DiscordClient Discord { get; } + public EntityState State { get; } + + public string Code { get; } + public string XkcdCode { get; } + + public TargetInfo Server { get; } + public TargetInfo Channel { get; } + + public string Url { get; } + + public Task Accept() => null; + + public virtual Task Update() => null; + } +} diff --git a/ref/Entities/Invite/Invite.cs b/ref/Entities/Invite/Invite.cs new file mode 100644 index 000000000..11fead2af --- /dev/null +++ b/ref/Entities/Invite/Invite.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace Discord +{ + public class Invite : BasicInvite + { + public int? MaxAge { get; } + public int Uses { get; } + public int? MaxUses { get; } + public bool IsRevoked { get; } + public bool IsTemporary { get; } + public DateTime CreatedAt { get; } + + public override Task Update() => null; + public Task Delete() => null; + } +} diff --git a/ref/Entities/Message.cs b/ref/Entities/Message.cs index 617446dad..78c4e41bd 100644 --- a/ref/Entities/Message.cs +++ b/ref/Entities/Message.cs @@ -44,7 +44,7 @@ namespace Discord public EntityState State { get; } public ITextChannel Channel { get; } - public User User { get; } + public IUser User { get; } public bool IsTTS { get; } public string RawText { get; } public string Text { get; } @@ -53,9 +53,9 @@ namespace Discord public Attachment[] Attachments { get; } public Embed[] Embeds { get; } - public IEnumerable MentionedUsers { get; } - public IEnumerable MentionedChannels { get; } - public IEnumerable MentionedRoles { get; } + public IReadOnlyList MentionedUsers { get; } + public IReadOnlyList MentionedChannels { get; } + public IReadOnlyList MentionedRoles { get; } public Server Server => null; public bool IsAuthor => false; diff --git a/ref/Entities/Permissions/PermissionOverwriteEntry.cs b/ref/Entities/Permissions/PermissionOverwriteEntry.cs new file mode 100644 index 000000000..bbc11fba8 --- /dev/null +++ b/ref/Entities/Permissions/PermissionOverwriteEntry.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public struct PermissionOverwriteEntry + { + public PermissionTarget TargetType { get; } + public ulong TargetId { get; } + public OverwritePermissions Permissions { get; } + } +} diff --git a/ref/Entities/Role.cs b/ref/Entities/Role.cs index 92edb7bd2..f5155db2e 100644 --- a/ref/Entities/Role.cs +++ b/ref/Entities/Role.cs @@ -19,7 +19,7 @@ namespace Discord public Color Color { get; } public bool IsEveryone { get; } - public IEnumerable Members { get; } + public IEnumerable Members { get; } public string Mention { get; } diff --git a/ref/Entities/Server.cs b/ref/Entities/Server.cs index 3b3b2b283..a9078cb4b 100644 --- a/ref/Entities/Server.cs +++ b/ref/Entities/Server.cs @@ -19,7 +19,7 @@ namespace Discord public DiscordClient Discord { get; } public EntityState State { get; } - public User CurrentUser { get; } + public ServerUser CurrentUser { get; } public string IconId { get; } public string SplashId { get; } public string IconUrl { get; } @@ -34,24 +34,24 @@ namespace Discord public IEnumerable Channels { get; } public IEnumerable TextChannels { get; } public IEnumerable VoiceChannels { get; } - public IEnumerable Users { get; } + public IEnumerable Users { get; } public IEnumerable Roles { get; } public string Name { get; set; } public Region Region { get; set; } public int AFKTimeout { get; set; } public DateTime JoinedAt { get; set; } - public User Owner { get; set; } + public ServerUser Owner { get; set; } public VoiceChannel AFKChannel { get; set; } - public IPublicChannel GetChannel(ulong id) => null; - public IPublicChannel GetChannel(string mention) => null; - public Role GetRole(ulong id) => null; - public User GetUser(ulong id) => null; - public User GetUser(string name, ushort discriminator) => null; - public User GetUser(string mention) => null; - public Task> DownloadBans() => null; - public Task> DownloadInvites() => null; + public Task GetChannel(ulong id) => null; + public Task GetChannel(string mention) => null; + public Task GetRole(ulong id) => null; + public Task GetUser(ulong id) => null; + public Task GetUser(string name, ushort discriminator) => null; + public Task GetUser(string mention) => null; + public Task> GetBans() => null; + public Task> GetInvites() => null; public Task CreateTextChannel(string name) => null; public Task CreateVoiceChannel(string name) => null; diff --git a/ref/Entities/User.cs b/ref/Entities/User.cs deleted file mode 100644 index d4eacaef4..000000000 --- a/ref/Entities/User.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Discord -{ - public class User : IEntity - { - public ulong Id { get; } - public DiscordClient Discord { get; } - public EntityState State { get; } - - public Server Server { get; } - - public string Name { get; } - public ushort Discriminator { get; } - public string AvatarId { get; } - public string CurrentGame { get; } - public UserStatus Status { get; } - public DateTime JoinedAt { get; } - public DateTime? LastActivityAt { get; } - - public string Mention => null; - public bool IsSelfMuted => false; - public bool IsSelfDeafened => false; - public bool IsServerMuted => false; - public bool IsServerDeafened => false; - public bool IsServerSuppressed => false; - public DateTime? LastOnlineAt => null; - public VoiceChannel VoiceChannel => null; - public string AvatarUrl => null; - public IEnumerable Roles => null; - public IEnumerable Channels => null; - public ServerPermissions ServerPermissions => default(ServerPermissions); - - public ChannelPermissions GetPermissions(IPublicChannel channel) => default(ChannelPermissions); - public Task GetPrivateChannel() => null; - - public Task SendMessage(string text) => null; - public Task SendFile(string filePath) => null; - public Task SendFile(string filename, Stream stream) => null; - - public bool HasRole(Role role) => false; - - public Task AddRoles(params Role[] roles) => null; - public Task RemoveRoles(params Role[] roles) => null; - - public Task Update() => null; - public Task Kick() => null; - public Task Ban(User user, int pruneDays = 0) => null; - public Task Unban(User user) => null; - public Task Unban(ulong userId) => null; - } -} \ No newline at end of file diff --git a/ref/Entities/Users/IUser.cs b/ref/Entities/Users/IUser.cs new file mode 100644 index 000000000..02dd2d85b --- /dev/null +++ b/ref/Entities/Users/IUser.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public interface IUser : IEntity, IMentionable + { + bool IsPrivate { get; } + + string Name { get; } + ushort Discriminator { get; } + bool IsBot { get; } + string AvatarId { get; } + string AvatarUrl { get; } + string CurrentGame { get; } + UserStatus Status { get; } + + Task GetPrivateChannel(); + } +} diff --git a/ref/Entities/Users/PrivateUser.cs b/ref/Entities/Users/PrivateUser.cs new file mode 100644 index 000000000..a6cc9d6e7 --- /dev/null +++ b/ref/Entities/Users/PrivateUser.cs @@ -0,0 +1,43 @@ +using System.Threading.Tasks; + +namespace Discord +{ + //TODO: Should this be linked directly to the Profile when it represents us, instead of maintaining a cache of values? + public class PrivateUser : IUser + { + /// + public EntityState State { get; internal set; } + /// + public ulong Id { get; } + /// Returns the private channel for this user. + public PrivateChannel Channel { get; } + + /// + bool IUser.IsPrivate => true; + + /// + public string Name { get; } + /// + public ushort Discriminator { get; } + /// + public bool IsBot { get; } + /// + public string AvatarId { get; } + /// + public string CurrentGame { get; } + /// + public UserStatus Status { get; } + + /// + public DiscordClient Discord => Channel.Discord; + /// + public string AvatarUrl { get; } + /// + public string Mention { get; } + + /// + Task IUser.GetPrivateChannel() => Task.FromResult(Channel); + + public Task Update() => null; + } +} diff --git a/ref/Entities/Users/ServerUser.cs b/ref/Entities/Users/ServerUser.cs new file mode 100644 index 000000000..4ff86f67a --- /dev/null +++ b/ref/Entities/Users/ServerUser.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public class ServerUser : IUser + { + /// + public EntityState State { get; } + /// + public ulong Id { get; } + /// Returns the private channel for this user. + public Server Server { get; } + + /// + bool IUser.IsPrivate => false; + + /// + public string Name { get; } + /// + public ushort Discriminator { get; } + /// + public bool IsBot { get; } + /// + public string AvatarId { get; } + /// + public string CurrentGame { get; } + /// + public UserStatus Status { get; } + /// + public DateTime JoinedAt { get; } + /// + public IReadOnlyList Roles { get; } + + /// Returns true if this user has marked themselves as muted. + public bool IsSelfMuted { get; } + /// Returns true if this user has marked themselves as deafened. + public bool IsSelfDeafened { get; } + /// Returns true if the server is blocking audio from this user. + public bool IsServerMuted { get; } + /// Returns true if the server is blocking audio to this user. + public bool IsServerDeafened { get; } + /// Returns true if the server is temporarily blocking audio to/from this user. + public bool IsServerSuppressed { get; } + /// Gets this user's current voice channel. + public VoiceChannel VoiceChannel { get; } + + /// + public DiscordClient Discord { get; } + /// + public string AvatarUrl { get; } + /// + public string Mention { get; } + + public ServerPermissions ServerPermissions { get; } + + public ChannelPermissions GetPermissions(IPublicChannel channel) => default(ChannelPermissions); + /// + public Task GetPrivateChannel() => null; + public Task> GetChannels() => null; + + public bool HasRole(Role role) => false; + + public Task AddRoles(params Role[] roles) => null; + public Task RemoveRoles(params Role[] roles) => null; + + public Task Update() => null; + public Task Kick() => null; + public Task Ban(int pruneDays = 0) => null; + public Task Unban() => null; + } +} \ No newline at end of file diff --git a/ref/Events/MessageEventArgs.cs b/ref/Events/MessageEventArgs.cs index 5b251fa9d..f75c7f1a8 100644 --- a/ref/Events/MessageEventArgs.cs +++ b/ref/Events/MessageEventArgs.cs @@ -5,7 +5,7 @@ namespace Discord public class MessageEventArgs : EventArgs { public Message Message => null; - public User User => null; + public IUser User => null; public ITextChannel Channel => null; } } diff --git a/ref/Events/MessageUpdatedEventArgs.cs b/ref/Events/MessageUpdatedEventArgs.cs index 3205721f3..d323bf809 100644 --- a/ref/Events/MessageUpdatedEventArgs.cs +++ b/ref/Events/MessageUpdatedEventArgs.cs @@ -6,7 +6,7 @@ namespace Discord { public Message Before => null; public Message After => null; - public User User => null; + public IUser User => null; public ITextChannel Channel => null; } } diff --git a/ref/Events/TypingEventArgs.cs b/ref/Events/TypingEventArgs.cs index 73d47f688..f45313687 100644 --- a/ref/Events/TypingEventArgs.cs +++ b/ref/Events/TypingEventArgs.cs @@ -3,9 +3,9 @@ public class TypingEventArgs { public ITextChannel Channel { get; } - public User User { get; } + public IUser User { get; } - public TypingEventArgs(ITextChannel channel, User user) + public TypingEventArgs(ITextChannel channel, IUser user) { Channel = channel; User = user; diff --git a/ref/Events/UserEventArgs.cs b/ref/Events/UserEventArgs.cs index 6f638c915..f1cce29fc 100644 --- a/ref/Events/UserEventArgs.cs +++ b/ref/Events/UserEventArgs.cs @@ -3,6 +3,6 @@ namespace Discord { public class UserEventArgs : EventArgs { - public User User => null; + public IUser User => null; } } diff --git a/ref/Events/UserUpdatedEventArgs.cs b/ref/Events/UserUpdatedEventArgs.cs index 6fb5fa2c3..c45c60701 100644 --- a/ref/Events/UserUpdatedEventArgs.cs +++ b/ref/Events/UserUpdatedEventArgs.cs @@ -3,7 +3,7 @@ namespace Discord { public class UserUpdatedEventArgs : EventArgs { - public User Before => null; - public User After => null; + public IUser Before => null; + public IUser After => null; } } diff --git a/ref/Net/Rest/IRestClientProvider.cs b/ref/Net/Rest/IRestClientProvider.cs new file mode 100644 index 000000000..cb22a7474 --- /dev/null +++ b/ref/Net/Rest/IRestClientProvider.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Threading; + +namespace Discord.Net.Rest +{ + public interface IRestClientProvider + { + IRestClient Create(string baseUrl, CancellationToken cancelToken); + } +} diff --git a/ref/Net/WebSockets/IWebSocket.cs b/ref/Net/WebSockets/IWebSocket.cs new file mode 100644 index 000000000..06a274305 --- /dev/null +++ b/ref/Net/WebSockets/IWebSocket.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading; + +namespace Discord.Net.WebSockets +{ + public interface IWebSocket + { + CancellationToken CancelToken { get; } + ConnectionState State { get; } + string Host { get; set; } + + event EventHandler Connected; + event EventHandler Disconnected; + } +} diff --git a/ref/Net/WebSockets/IWebSocketProvider.cs b/ref/Net/WebSockets/IWebSocketProvider.cs new file mode 100644 index 000000000..20f7559be --- /dev/null +++ b/ref/Net/WebSockets/IWebSocketProvider.cs @@ -0,0 +1,9 @@ +using System.Threading; + +namespace Discord.Net.WebSockets +{ + public interface IWebSocketProvider + { + IWebSocket Create(CancellationToken cancelToken); + } +} diff --git a/ref/Net/WebSockets/WebSocket.cs b/ref/Net/WebSockets/WebSocket.cs deleted file mode 100644 index 1257f2474..000000000 --- a/ref/Net/WebSockets/WebSocket.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Discord.Logging; -using System; -using System.Threading; - -namespace Discord.Net.WebSockets -{ - public abstract partial class WebSocket - { - public CancellationToken CancelToken { get; } - public ConnectionState State { get; } - public string Host { get; } - - public event EventHandler Connected = delegate { }; - public event EventHandler Disconnected = delegate { }; - - public WebSocket(DiscordConfig config, ILogger logger) { } - - public abstract void SendHeartbeat(); - - public virtual void WaitForConnection(CancellationToken cancelToken) { } - } -}