From e8bdc980fe630e24e9feffd2812864b4fdf58aff Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 16 Dec 2015 18:01:41 -0400 Subject: [PATCH] Reworked Rest system --- .../API/Messages/GatewaySocket.cs | 20 -- .../API/Messages/VoiceSocket.cs | 111 ----------- src/Discord.Net.Audio/Discord.Net.Audio.xproj | 2 +- src/Discord.Net.Audio/DiscordAudioClient.cs | 22 +-- .../Net/WebSockets/VoiceWebSocket.cs | 59 ++---- src/Discord.Net/API/Client/Common/Channel.cs | 34 ++++ .../API/Client/Common/ChannelReference.cs | 17 ++ .../API/Client/Common/ExtendedGuild.cs | 26 +++ src/Discord.Net/API/Client/Common/Guild.cs | 28 +++ .../API/Client/Common/GuildReference.cs | 13 ++ src/Discord.Net/API/Client/Common/Invite.cs | 23 +++ .../API/Client/Common/InviteReference.cs | 16 ++ src/Discord.Net/API/Client/Common/Member.cs | 14 ++ .../API/Client/Common/MemberPresence.cs | 15 ++ .../API/Client/Common/MemberReference.cs | 13 ++ .../API/Client/Common/MemberVoiceState.cs | 26 +++ src/Discord.Net/API/Client/Common/Message.cs | 85 +++++++++ .../API/Client/Common/MessageReference.cs | 15 ++ .../API/Client/Common/RoleReference.cs | 13 ++ .../API/Client/Common/UserReference.cs | 17 ++ .../GatewaySocket/Commands/Heartbeat.cs | 12 ++ .../Client/GatewaySocket/Commands/Identify.cs | 23 +++ .../GatewaySocket/Commands/RequestMembers.cs | 20 ++ .../Client/GatewaySocket/Commands/Resume.cs | 17 ++ .../GatewaySocket/Commands/UpdateStatus.cs | 17 ++ .../GatewaySocket/Commands/UpdateVoice.cs | 22 +++ .../GatewaySocket/Events/ChannelCreate.cs | 4 + .../GatewaySocket/Events/ChannelDelete.cs | 4 + .../GatewaySocket/Events/ChannelUpdate.cs | 4 + .../GatewaySocket/Events/GuildBanAdd.cs | 13 ++ .../GatewaySocket/Events/GuildBanRemove.cs | 13 ++ .../GatewaySocket/Events/GuildCreate.cs | 4 + .../GatewaySocket/Events/GuildDelete.cs | 4 + .../Events/GuildIntegrationsUpdate.cs | 4 + .../GatewaySocket/Events/GuildMemberAdd.cs | 4 + .../GatewaySocket/Events/GuildMemberRemove.cs | 4 + .../GatewaySocket/Events/GuildMemberUpdate.cs | 4 + .../GatewaySocket/Events/GuildMembersChunk.cs | 10 + .../GatewaySocket/Events/GuildRoleCreate.cs | 13 ++ .../GatewaySocket/Events/GuildRoleDelete.cs | 4 + .../GatewaySocket/Events/GuildRoleUpdate.cs | 13 ++ .../GatewaySocket/Events/GuildUpdate.cs | 4 + .../Client/GatewaySocket/Events/MessageAck.cs | 4 + .../GatewaySocket/Events/MessageCreate.cs | 4 + .../GatewaySocket/Events/MessageDelete.cs | 4 + .../GatewaySocket/Events/MessageUpdate.cs | 4 + .../GatewaySocket/Events/PresenceUpdate.cs | 4 + .../API/Client/GatewaySocket/Events/Ready.cs | 32 ++++ .../Client/GatewaySocket/Events/Redirect.cs | 10 + .../Client/GatewaySocket/Events/Resumed.cs | 10 + .../GatewaySocket/Events/TypingStart.cs | 15 ++ .../Events/UserSettingsUpdate.cs | 4 + .../Client/GatewaySocket/Events/UserUpdate.cs | 4 + .../GatewaySocket/Events/VoiceServerUpdate.cs | 15 ++ .../GatewaySocket/Events/VoiceStateUpdate.cs | 4 + .../API/Client/GatewaySocket/OpCodes.cs | 24 +++ .../API/Client/IWebSocketMessage.cs | 29 +++ .../API/Client/Rest/AcceptInvite.cs | 20 ++ src/Discord.Net/API/Client/Rest/AckMessage.cs | 25 +++ .../API/Client/Rest/AddGuildBan.cs | 24 +++ .../API/Client/Rest/BroadcastTyping.cs | 20 ++ .../API/Client/Rest/CreateChannel.cs | 25 +++ .../API/Client/Rest/CreateGuild.cs | 20 ++ .../API/Client/Rest/CreateInvite.cs | 31 ++++ .../API/Client/Rest/CreatePrivateChannel.cs | 17 ++ src/Discord.Net/API/Client/Rest/CreateRole.cs | 20 ++ .../API/Client/Rest/DeleteChannel.cs | 20 ++ .../API/Client/Rest/DeleteInvite.cs | 20 ++ .../API/Client/Rest/DeleteMessage.cs | 22 +++ src/Discord.Net/API/Client/Rest/DeleteRole.cs | 22 +++ src/Discord.Net/API/Client/Rest/Gateway.cs | 19 ++ src/Discord.Net/API/Client/Rest/GetInvite.cs | 20 ++ src/Discord.Net/API/Client/Rest/GetInvites.cs | 20 ++ .../API/Client/Rest/GetMessages.cs | 35 ++++ .../API/Client/Rest/GetVoiceRegions.cs | 25 +++ src/Discord.Net/API/Client/Rest/GetWidget.cs | 61 ++++++ src/Discord.Net/API/Client/Rest/KickMember.cs | 22 +++ src/Discord.Net/API/Client/Rest/LeaveGuild.cs | 20 ++ src/Discord.Net/API/Client/Rest/Logout.cs | 13 ++ .../API/Client/Rest/PruneMembers.cs | 29 +++ .../Client/Rest/RemoveChannelPermission.cs | 22 +++ .../API/Client/Rest/RemoveGuildBan.cs | 22 +++ .../API/Client/Rest/ReorderChannels.cs | 46 +++++ .../API/Client/Rest/ReorderRoles.cs | 46 +++++ src/Discord.Net/API/Client/Rest/SendFile.cs | 26 +++ .../API/Client/Rest/SendMessage.cs | 30 +++ .../API/Client/Rest/UpdateChannel.cs | 27 +++ .../API/Client/Rest/UpdateGuild.cs | 32 ++++ .../API/Client/Rest/UpdateMember.cs | 33 ++++ .../API/Client/Rest/UpdateMessage.cs | 28 +++ .../API/Client/Rest/UpdateProfile.cs | 24 +++ src/Discord.Net/API/Client/Rest/UpdateRole.cs | 31 ++++ .../Client/VoiceSocket/Commands/Heartbeat.cs | 9 + .../Client/VoiceSocket/Commands/Identify.cs | 21 +++ .../VoiceSocket/Commands/SelectProtocol.cs | 29 +++ .../VoiceSocket/Commands/SetSpeaking.cs | 16 ++ .../API/Client/VoiceSocket/Events/Ready.cs | 16 ++ .../VoiceSocket/Events/SessionDescription.cs | 12 ++ .../API/Client/VoiceSocket/Events/Speaking.cs | 15 ++ .../API/Client/VoiceSocket/OpCodes.cs | 18 ++ src/Discord.Net/API/Enums/StringEnum.cs | 14 -- src/Discord.Net/API/Extensions.cs | 19 ++ src/Discord.Net/API/IRestRequest.cs | 26 +++ src/Discord.Net/API/Messages/Auth.cs | 29 --- src/Discord.Net/API/Messages/Channels.cs | 107 ----------- src/Discord.Net/API/Messages/GatewaySocket.cs | 173 ------------------ src/Discord.Net/API/Messages/Invites.cs | 61 ------ src/Discord.Net/API/Messages/Maintenance.cs | 35 ---- src/Discord.Net/API/Messages/Members.cs | 112 ------------ src/Discord.Net/API/Messages/Messages.cs | 155 ---------------- src/Discord.Net/API/Messages/Permissions.cs | 23 --- src/Discord.Net/API/Messages/Roles.cs | 94 ---------- src/Discord.Net/API/Messages/Servers.cs | 108 ----------- src/Discord.Net/API/Messages/Users.cs | 63 ------- src/Discord.Net/API/Status/Common/Incident.cs | 31 ++++ src/Discord.Net/API/Status/Rest/Active.cs | 13 ++ src/Discord.Net/API/Status/Rest/Upcoming.cs | 13 ++ src/Discord.Net/DiscordClient.Channels.cs | 34 +++- src/Discord.Net/DiscordClient.Invites.cs | 22 ++- src/Discord.Net/DiscordClient.Messages.cs | 121 +++++++----- src/Discord.Net/DiscordClient.Permissions.cs | 16 +- src/Discord.Net/DiscordClient.Roles.cs | 116 +++++++----- src/Discord.Net/DiscordClient.Servers.cs | 57 +++--- src/Discord.Net/DiscordClient.Users.cs | 95 ++++++---- .../{API => }/Enums/ChannelType.cs | 16 +- .../{API => }/Enums/PermissionTarget.cs | 16 +- src/Discord.Net/Enums/StringEnum.cs | 14 ++ src/Discord.Net/{API => }/Enums/UserStatus.cs | 20 +- src/Discord.Net/Models/Channel.cs | 7 +- src/Discord.Net/Models/GlobalUser.cs | 6 +- src/Discord.Net/Models/Invite.cs | 27 ++- src/Discord.Net/Models/Message.cs | 7 +- src/Discord.Net/Models/Region.cs | 18 +- src/Discord.Net/Models/Role.cs | 6 +- src/Discord.Net/Models/Server.cs | 13 +- src/Discord.Net/Models/User.cs | 32 ++-- src/Discord.Net/Net/Rest/RestClient.Events.cs | 2 +- src/Discord.Net/Net/Rest/RestClient.cs | 147 ++++++--------- src/Discord.Net/Net/Rest/SharpRestEngine.cs | 4 +- .../Net/WebSockets/GatewaySocket.cs | 84 +++------ src/Discord.Net/Net/WebSockets/WebSocket.cs | 7 +- src/Discord.Net/Optional.cs | 28 +++ 142 files changed, 2435 insertions(+), 1562 deletions(-) delete mode 100644 src/Discord.Net.Audio/API/Messages/GatewaySocket.cs delete mode 100644 src/Discord.Net.Audio/API/Messages/VoiceSocket.cs create mode 100644 src/Discord.Net/API/Client/Common/Channel.cs create mode 100644 src/Discord.Net/API/Client/Common/ChannelReference.cs create mode 100644 src/Discord.Net/API/Client/Common/ExtendedGuild.cs create mode 100644 src/Discord.Net/API/Client/Common/Guild.cs create mode 100644 src/Discord.Net/API/Client/Common/GuildReference.cs create mode 100644 src/Discord.Net/API/Client/Common/Invite.cs create mode 100644 src/Discord.Net/API/Client/Common/InviteReference.cs create mode 100644 src/Discord.Net/API/Client/Common/Member.cs create mode 100644 src/Discord.Net/API/Client/Common/MemberPresence.cs create mode 100644 src/Discord.Net/API/Client/Common/MemberReference.cs create mode 100644 src/Discord.Net/API/Client/Common/MemberVoiceState.cs create mode 100644 src/Discord.Net/API/Client/Common/Message.cs create mode 100644 src/Discord.Net/API/Client/Common/MessageReference.cs create mode 100644 src/Discord.Net/API/Client/Common/RoleReference.cs create mode 100644 src/Discord.Net/API/Client/Common/UserReference.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs create mode 100644 src/Discord.Net/API/Client/GatewaySocket/OpCodes.cs create mode 100644 src/Discord.Net/API/Client/IWebSocketMessage.cs create mode 100644 src/Discord.Net/API/Client/Rest/AcceptInvite.cs create mode 100644 src/Discord.Net/API/Client/Rest/AckMessage.cs create mode 100644 src/Discord.Net/API/Client/Rest/AddGuildBan.cs create mode 100644 src/Discord.Net/API/Client/Rest/BroadcastTyping.cs create mode 100644 src/Discord.Net/API/Client/Rest/CreateChannel.cs create mode 100644 src/Discord.Net/API/Client/Rest/CreateGuild.cs create mode 100644 src/Discord.Net/API/Client/Rest/CreateInvite.cs create mode 100644 src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs create mode 100644 src/Discord.Net/API/Client/Rest/CreateRole.cs create mode 100644 src/Discord.Net/API/Client/Rest/DeleteChannel.cs create mode 100644 src/Discord.Net/API/Client/Rest/DeleteInvite.cs create mode 100644 src/Discord.Net/API/Client/Rest/DeleteMessage.cs create mode 100644 src/Discord.Net/API/Client/Rest/DeleteRole.cs create mode 100644 src/Discord.Net/API/Client/Rest/Gateway.cs create mode 100644 src/Discord.Net/API/Client/Rest/GetInvite.cs create mode 100644 src/Discord.Net/API/Client/Rest/GetInvites.cs create mode 100644 src/Discord.Net/API/Client/Rest/GetMessages.cs create mode 100644 src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs create mode 100644 src/Discord.Net/API/Client/Rest/GetWidget.cs create mode 100644 src/Discord.Net/API/Client/Rest/KickMember.cs create mode 100644 src/Discord.Net/API/Client/Rest/LeaveGuild.cs create mode 100644 src/Discord.Net/API/Client/Rest/Logout.cs create mode 100644 src/Discord.Net/API/Client/Rest/PruneMembers.cs create mode 100644 src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs create mode 100644 src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs create mode 100644 src/Discord.Net/API/Client/Rest/ReorderChannels.cs create mode 100644 src/Discord.Net/API/Client/Rest/ReorderRoles.cs create mode 100644 src/Discord.Net/API/Client/Rest/SendFile.cs create mode 100644 src/Discord.Net/API/Client/Rest/SendMessage.cs create mode 100644 src/Discord.Net/API/Client/Rest/UpdateChannel.cs create mode 100644 src/Discord.Net/API/Client/Rest/UpdateGuild.cs create mode 100644 src/Discord.Net/API/Client/Rest/UpdateMember.cs create mode 100644 src/Discord.Net/API/Client/Rest/UpdateMessage.cs create mode 100644 src/Discord.Net/API/Client/Rest/UpdateProfile.cs create mode 100644 src/Discord.Net/API/Client/Rest/UpdateRole.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs create mode 100644 src/Discord.Net/API/Client/VoiceSocket/OpCodes.cs delete mode 100644 src/Discord.Net/API/Enums/StringEnum.cs create mode 100644 src/Discord.Net/API/Extensions.cs create mode 100644 src/Discord.Net/API/IRestRequest.cs delete mode 100644 src/Discord.Net/API/Messages/Auth.cs delete mode 100644 src/Discord.Net/API/Messages/Channels.cs delete mode 100644 src/Discord.Net/API/Messages/GatewaySocket.cs delete mode 100644 src/Discord.Net/API/Messages/Invites.cs delete mode 100644 src/Discord.Net/API/Messages/Maintenance.cs delete mode 100644 src/Discord.Net/API/Messages/Members.cs delete mode 100644 src/Discord.Net/API/Messages/Messages.cs delete mode 100644 src/Discord.Net/API/Messages/Permissions.cs delete mode 100644 src/Discord.Net/API/Messages/Roles.cs delete mode 100644 src/Discord.Net/API/Messages/Servers.cs delete mode 100644 src/Discord.Net/API/Messages/Users.cs create mode 100644 src/Discord.Net/API/Status/Common/Incident.cs create mode 100644 src/Discord.Net/API/Status/Rest/Active.cs create mode 100644 src/Discord.Net/API/Status/Rest/Upcoming.cs rename src/Discord.Net/{API => }/Enums/ChannelType.cs (69%) rename src/Discord.Net/{API => }/Enums/PermissionTarget.cs (68%) create mode 100644 src/Discord.Net/Enums/StringEnum.cs rename src/Discord.Net/{API => }/Enums/UserStatus.cs (66%) create mode 100644 src/Discord.Net/Optional.cs diff --git a/src/Discord.Net.Audio/API/Messages/GatewaySocket.cs b/src/Discord.Net.Audio/API/Messages/GatewaySocket.cs deleted file mode 100644 index fadb94f0c..000000000 --- a/src/Discord.Net.Audio/API/Messages/GatewaySocket.cs +++ /dev/null @@ -1,20 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; - -namespace Discord.API -{ - internal sealed class VoiceServerUpdateEvent - { - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ServerId; - [JsonProperty("endpoint")] - public string Endpoint; - [JsonProperty("token")] - public string Token; - } -} diff --git a/src/Discord.Net.Audio/API/Messages/VoiceSocket.cs b/src/Discord.Net.Audio/API/Messages/VoiceSocket.cs deleted file mode 100644 index 01f4d50b2..000000000 --- a/src/Discord.Net.Audio/API/Messages/VoiceSocket.cs +++ /dev/null @@ -1,111 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; - -namespace Discord.API -{ - public enum VoiceOpCodes : byte - { - /// Client --> Server - Used to associate a connection with a token. - Identify = 0, - /// Client --> Server - Used to specify configuration. - SelectProtocol = 1, - /// Client <-- Server - Used to notify that the voice connection was successful and informs the client of available protocols. - Ready = 2, - /// Client <-> Server - Used to keep the connection alive and measure latency. - Heartbeat = 3, - /// Client <-- Server - Used to provide an encryption key to the client. - SessionDescription = 4, - /// Client <-> Server - Used to inform that a certain user is speaking. - Speaking = 5 - } - - //Commands - internal sealed class IdentifyCommand : WebSocketMessage - { - public IdentifyCommand() : base((int)VoiceOpCodes.Identify) { } - public class Data - { - [JsonProperty("server_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ServerId; - [JsonProperty("user_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong UserId; - [JsonProperty("session_id")] - public string SessionId; - [JsonProperty("token")] - public string Token; - } - } - internal sealed class SelectProtocolCommand : WebSocketMessage - { - public SelectProtocolCommand() : base((int)VoiceOpCodes.SelectProtocol) { } - public class Data - { - public class SocketInfo - { - [JsonProperty("address")] - public string Address; - [JsonProperty("port")] - public int Port; - [JsonProperty("mode")] - public string Mode = "xsalsa20_poly1305"; - } - [JsonProperty("protocol")] - public string Protocol = "udp"; - [JsonProperty("data")] - public SocketInfo SocketData = new SocketInfo(); - } - } - internal sealed class HeartbeatCommand : WebSocketMessage - { - public HeartbeatCommand() : base((int)VoiceOpCodes.Heartbeat, EpochTime.GetMilliseconds()) { } - } - internal sealed class SpeakingCommand : WebSocketMessage - { - public SpeakingCommand() : base((int)VoiceOpCodes.Speaking) { } - public class Data - { - [JsonProperty("delay")] - public int Delay; - [JsonProperty("speaking")] - public bool IsSpeaking; - } - } - - //Events - public class VoiceReadyEvent - { - [JsonProperty("ssrc")] - public uint SSRC; - [JsonProperty("port")] - public ushort Port; - [JsonProperty("modes")] - public string[] Modes; - [JsonProperty("heartbeat_interval")] - public int HeartbeatInterval; - } - - public class JoinServerEvent - { - [JsonProperty("secret_key")] - public byte[] SecretKey; - [JsonProperty("mode")] - public string Mode; - } - - public class IsTalkingEvent - { - [JsonProperty("user_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong UserId; - [JsonProperty("ssrc")] - public uint SSRC; - [JsonProperty("speaking")] - public bool IsSpeaking; - } -} diff --git a/src/Discord.Net.Audio/Discord.Net.Audio.xproj b/src/Discord.Net.Audio/Discord.Net.Audio.xproj index 4eb480f88..053555480 100644 --- a/src/Discord.Net.Audio/Discord.Net.Audio.xproj +++ b/src/Discord.Net.Audio/Discord.Net.Audio.xproj @@ -7,7 +7,7 @@ dff7afe3-ca77-4109-bade-b4b49a4f6648 - Discord.Audio + Discord ..\..\artifacts\obj\$(MSBuildProjectName) ..\..\artifacts\bin\$(MSBuildProjectName)\ diff --git a/src/Discord.Net.Audio/DiscordAudioClient.cs b/src/Discord.Net.Audio/DiscordAudioClient.cs index 6ec8f9d1e..535ce4078 100644 --- a/src/Discord.Net.Audio/DiscordAudioClient.cs +++ b/src/Discord.Net.Audio/DiscordAudioClient.cs @@ -1,4 +1,5 @@ using Discord.API; +using Discord.API.Client.GatewaySocket; using Discord.Net.WebSockets; using System; using System.Threading.Tasks; @@ -76,7 +77,7 @@ namespace Discord.Audio case "VOICE_SERVER_UPDATE": { var data = e.Payload.ToObject(_gatewaySocket.Serializer); - var serverId = data.ServerId; + var serverId = data.GuildId; if (serverId == ServerId) { @@ -96,10 +97,6 @@ namespace Discord.Audio }; } - public Task Disconnect() - { - return _voiceSocket.Disconnect(); - } internal void SetServerId(ulong serverId) { @@ -115,14 +112,17 @@ namespace Discord.Audio await _voiceSocket.Disconnect().ConfigureAwait(false); _voiceSocket.ChannelId = channel.Id; - _gatewaySocket.SendJoinVoice(channel.Server.Id, channel.Id); + _gatewaySocket.SendUpdateVoice(channel.Server.Id, channel.Id, + (_service.Config.Mode | AudioMode.Outgoing) == 0, + (_service.Config.Mode | AudioMode.Incoming) == 0); await _voiceSocket.WaitForConnection(_service.Config.ConnectionTimeout).ConfigureAwait(false); - } + } + public Task Disconnect() => _voiceSocket.Disconnect(); - /// Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. - /// PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. - /// Number of bytes in this frame. - public void Send(byte[] data, int count) + /// Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. + /// PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. + /// Number of bytes in this frame. + public void Send(byte[] data, int count) { if (data == null) throw new ArgumentException(nameof(data)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); diff --git a/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs b/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs index 717822e73..476f1b131 100644 --- a/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs +++ b/src/Discord.Net.Audio/Net/WebSockets/VoiceWebSocket.cs @@ -1,4 +1,6 @@ using Discord.API; +using Discord.API.Client; +using Discord.API.Client.VoiceSocket; using Discord.Audio; using Discord.Audio.Opus; using Discord.Audio.Sodium; @@ -394,14 +396,14 @@ namespace Discord.Net.WebSockets { await base.ProcessMessage(json).ConfigureAwait(false); var msg = JsonConvert.DeserializeObject(json); - var opCode = (VoiceOpCodes)msg.Operation; + var opCode = (OpCodes)msg.Operation; switch (opCode) { - case VoiceOpCodes.Ready: + case OpCodes.Ready: { if (_state != ConnectionState.Connected) { - var payload = (msg.Payload as JToken).ToObject(_serializer); + var payload = (msg.Payload as JToken).ToObject(_serializer); _heartbeatInterval = payload.HeartbeatInterval; _ssrc = payload.SSRC; var address = (await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(); @@ -435,7 +437,7 @@ namespace Discord.Net.WebSockets } } break; - case VoiceOpCodes.Heartbeat: + case OpCodes.Heartbeat: { long time = EpochTime.GetMilliseconds(); var payload = (long)msg.Payload; @@ -443,17 +445,17 @@ namespace Discord.Net.WebSockets //TODO: Use this to estimate latency } break; - case VoiceOpCodes.SessionDescription: + case OpCodes.SessionDescription: { - var payload = (msg.Payload as JToken).ToObject(_serializer); + var payload = (msg.Payload as JToken).ToObject(_serializer); _secretKey = payload.SecretKey; - SendIsTalking(true); + SendSetSpeaking(true); EndConnect(); } break; - case VoiceOpCodes.Speaking: + case OpCodes.Speaking: { - var payload = (msg.Payload as JToken).ToObject(_serializer); + var payload = (msg.Payload as JToken).ToObject(_serializer); RaiseIsSpeaking(payload.UserId, payload.IsSpeaking); } break; @@ -493,37 +495,14 @@ namespace Discord.Net.WebSockets }); } - public void SendIdentify() - { - var msg = new IdentifyCommand(); - msg.Payload.ServerId = _serverId.Value; - msg.Payload.SessionId = _client.SessionId; - msg.Payload.Token = _audioClient.Token; - msg.Payload.UserId = _client.UserId.Value; - QueueMessage(msg); - } - - public void SendSelectProtocol(string externalIp, int externalPort) - { - var msg = new SelectProtocolCommand(); - msg.Payload.Protocol = "udp"; - msg.Payload.SocketData.Address = externalIp; - msg.Payload.SocketData.Mode = _encryptionMode; - msg.Payload.SocketData.Port = externalPort; - QueueMessage(msg); - } + public override void SendHeartbeat() + => QueueMessage(new HeartbeatCommand()); + public void SendIdentify() + => QueueMessage(new IdentifyCommand { GuildId = _serverId.Value, UserId = _client.UserId.Value, SessionId = _client.SessionId, Token = _audioClient.Token }); + public void SendSelectProtocol(string externalAddress, int externalPort) + => QueueMessage(new SelectProtocolCommand { Protocol = "udp", ExternalAddress = externalAddress, ExternalPort = externalPort, EncryptionMode = _encryptionMode }); + public void SendSetSpeaking(bool value) + => QueueMessage(new SetSpeakingCommand { IsSpeaking = value, Delay = 0 }); - public void SendIsTalking(bool value) - { - var isTalking = new SpeakingCommand(); - isTalking.Payload.IsSpeaking = value; - isTalking.Payload.Delay = 0; - QueueMessage(isTalking); - } - - public override void SendHeartbeat() - { - QueueMessage(new HeartbeatCommand()); - } } } \ No newline at end of file diff --git a/src/Discord.Net/API/Client/Common/Channel.cs b/src/Discord.Net/API/Client/Common/Channel.cs new file mode 100644 index 000000000..7a06a441c --- /dev/null +++ b/src/Discord.Net/API/Client/Common/Channel.cs @@ -0,0 +1,34 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class Channel : ChannelReference + { + public sealed class PermissionOverwrite + { + [JsonProperty("type")] + public string Type { get; set; } + [JsonProperty("id")] + [JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("deny")] + public uint Deny { get; set; } + [JsonProperty("allow")] + public uint Allow { get; set; } + } + + [JsonProperty("last_message_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong LastMessageId { get; set; } + [JsonProperty("is_private")] + public bool IsPrivate { get; set; } + [JsonProperty("position")] + public int Position { get; set; } + [JsonProperty("topic")] + public string Topic { get; set; } + [JsonProperty("permission_overwrites")] + public PermissionOverwrite[] PermissionOverwrites { get; set; } + [JsonProperty("recipient")] + public UserReference Recipient { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/ChannelReference.cs b/src/Discord.Net/API/Client/Common/ChannelReference.cs new file mode 100644 index 000000000..5ac70d202 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/ChannelReference.cs @@ -0,0 +1,17 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class ChannelReference + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/ExtendedGuild.cs b/src/Discord.Net/API/Client/Common/ExtendedGuild.cs new file mode 100644 index 000000000..80e9bec64 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/ExtendedGuild.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class ExtendedGuild : Guild + { + public sealed class ExtendedMemberInfo : Member + { + [JsonProperty("mute")] + public bool? IsServerMuted { get; set; } + [JsonProperty("deaf")] + public bool? IsServerDeafened { get; set; } + } + + [JsonProperty("channels")] + public Channel[] Channels { get; set; } + [JsonProperty("members")] + public ExtendedMemberInfo[] Members { get; set; } + [JsonProperty("presences")] + public MemberPresence[] Presences { get; set; } + [JsonProperty("voice_states")] + public MemberVoiceState[] VoiceStates { get; set; } + [JsonProperty("unavailable")] + public bool? Unavailable { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/Guild.cs b/src/Discord.Net/API/Client/Common/Guild.cs new file mode 100644 index 000000000..17429b458 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/Guild.cs @@ -0,0 +1,28 @@ +using Discord.API.Converters; +using Newtonsoft.Json; +using System; + +namespace Discord.API.Client +{ + public class Guild : GuildReference + { + [JsonProperty("afk_channel_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong? AFKChannelId { get; set; } + [JsonProperty("afk_timeout")] + public int? AFKTimeout { get; set; } + [JsonProperty("embed_channel_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong? EmbedChannelId { get; set; } + [JsonProperty("embed_enabled")] + public bool EmbedEnabled { get; set; } + [JsonProperty("icon")] + public string Icon { get; set; } + [JsonProperty("joined_at")] + public DateTime? JoinedAt { get; set; } + [JsonProperty("owner_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong? OwnerId { get; set; } + [JsonProperty("region")] + public string Region { get; set; } + [JsonProperty("roles")] + public Role[] Roles { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/GuildReference.cs b/src/Discord.Net/API/Client/Common/GuildReference.cs new file mode 100644 index 000000000..5b87c3cc2 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/GuildReference.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class GuildReference + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/Invite.cs b/src/Discord.Net/API/Client/Common/Invite.cs new file mode 100644 index 000000000..571f551ee --- /dev/null +++ b/src/Discord.Net/API/Client/Common/Invite.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using System; + +namespace Discord.API.Client +{ + public class Invite : InviteReference + { + [JsonProperty("max_age")] + public int? MaxAge { get; set; } + [JsonProperty("max_uses")] + public int? MaxUses { get; set; } + [JsonProperty("revoked")] + public bool? IsRevoked { get; set; } + [JsonProperty("temporary")] + public bool? IsTemporary { get; set; } + [JsonProperty("uses")] + public int? Uses { get; set; } + [JsonProperty("created_at")] + public DateTime? CreatedAt { get; set; } + [JsonProperty("inviter")] + public UserReference Inviter { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/InviteReference.cs b/src/Discord.Net/API/Client/Common/InviteReference.cs new file mode 100644 index 000000000..4b7226808 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/InviteReference.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class InviteReference + { + [JsonProperty("guild")] + public GuildReference Guild { get; set; } + [JsonProperty("channel")] + public ChannelReference Channel { get; set; } + [JsonProperty("code")] + public string Code { get; set; } + [JsonProperty("xkcdpass")] + public string XkcdPass { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/Member.cs b/src/Discord.Net/API/Client/Common/Member.cs new file mode 100644 index 000000000..1af23c207 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/Member.cs @@ -0,0 +1,14 @@ +using Discord.API.Converters; +using Newtonsoft.Json; +using System; + +namespace Discord.API.Client +{ + public class Member : MemberReference + { + [JsonProperty("joined_at")] + public DateTime? JoinedAt { get; set; } + [JsonProperty("roles"), JsonConverter(typeof(LongStringArrayConverter))] + public ulong[] Roles { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/MemberPresence.cs b/src/Discord.Net/API/Client/Common/MemberPresence.cs new file mode 100644 index 000000000..52a445190 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/MemberPresence.cs @@ -0,0 +1,15 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class MemberPresence : MemberReference + { + [JsonProperty("game_id")] + public int? GameId { get; set; } + [JsonProperty("status")] + public string Status { get; set; } + [JsonProperty("roles"), JsonConverter(typeof(LongStringArrayConverter))] + public ulong[] Roles { get; set; } //TODO: Might be temporary + } +} diff --git a/src/Discord.Net/API/Client/Common/MemberReference.cs b/src/Discord.Net/API/Client/Common/MemberReference.cs new file mode 100644 index 000000000..e064921ee --- /dev/null +++ b/src/Discord.Net/API/Client/Common/MemberReference.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class MemberReference + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; set; } + [JsonProperty("user")] + public UserReference User { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/MemberVoiceState.cs b/src/Discord.Net/API/Client/Common/MemberVoiceState.cs new file mode 100644 index 000000000..1f16b667b --- /dev/null +++ b/src/Discord.Net/API/Client/Common/MemberVoiceState.cs @@ -0,0 +1,26 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class MemberVoiceState : MemberReference + { + [JsonProperty("channel_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong? ChannelId { get; set; } + [JsonProperty("session_id")] + public string SessionId { get; set; } + [JsonProperty("token")] + public string Token { get; set; } + + [JsonProperty("self_mute")] + public bool? IsSelfMuted { get; set; } + [JsonProperty("self_deaf")] + public bool? IsSelfDeafened { get; set; } + [JsonProperty("mute")] + public bool? IsServerMuted { get; set; } + [JsonProperty("deaf")] + public bool? IsServerDeafened { get; set; } + [JsonProperty("suppress")] + public bool? IsServerSuppressed { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/Message.cs b/src/Discord.Net/API/Client/Common/Message.cs new file mode 100644 index 000000000..8a17e7cef --- /dev/null +++ b/src/Discord.Net/API/Client/Common/Message.cs @@ -0,0 +1,85 @@ +using Newtonsoft.Json; +using System; + +namespace Discord.API.Client +{ + public class Message : MessageReference + { + public sealed class Attachment + { + [JsonProperty("id")] + public string Id { get; set; } + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("proxy_url")] + public string ProxyUrl { get; set; } + [JsonProperty("size")] + public int Size { get; set; } + [JsonProperty("filename")] + public string Filename { get; set; } + [JsonProperty("width")] + public int Width { get; set; } + [JsonProperty("height")] + public int Height { get; set; } + } + + public sealed class Embed + { + public sealed class Reference + { + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + } + + public sealed class ThumbnailInfo + { + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("proxy_url")] + public string ProxyUrl { get; set; } + [JsonProperty("width")] + public int Width { get; set; } + [JsonProperty("height")] + public int Height { get; set; } + } + + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + [JsonProperty("title")] + public string Title { get; set; } + [JsonProperty("description")] + public string Description { get; set; } + [JsonProperty("author")] + public Reference Author { get; set; } + [JsonProperty("provider")] + public Reference Provider { get; set; } + [JsonProperty("thumbnail")] + public ThumbnailInfo Thumbnail { get; set; } + } + + [JsonProperty("tts")] + public bool? IsTextToSpeech { get; set; } + [JsonProperty("mention_everyone")] + public bool? IsMentioningEveryone { get; set; } + [JsonProperty("timestamp")] + public DateTime? Timestamp { get; set; } + [JsonProperty("edited_timestamp")] + public DateTime? EditedTimestamp { get; set; } + [JsonProperty("mentions")] + public UserReference[] Mentions { get; set; } + [JsonProperty("embeds")] + public Embed[] Embeds { get; set; } //TODO: Parse this + [JsonProperty("attachments")] + public Attachment[] Attachments { get; set; } + [JsonProperty("content")] + public string Content { get; set; } + [JsonProperty("author")] + public UserReference Author { get; set; } + [JsonProperty("nonce")] + public string Nonce { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/MessageReference.cs b/src/Discord.Net/API/Client/Common/MessageReference.cs new file mode 100644 index 000000000..c2afa6cd5 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/MessageReference.cs @@ -0,0 +1,15 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class MessageReference + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("message_id"), JsonConverter(typeof(LongStringConverter))] //Only used in MESSAGE_ACK + public ulong MessageId { get { return Id; } set { Id = value; } } + [JsonProperty("channel_id"), JsonConverter(typeof(LongStringConverter))] + public ulong ChannelId { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/RoleReference.cs b/src/Discord.Net/API/Client/Common/RoleReference.cs new file mode 100644 index 000000000..ce7d25fcc --- /dev/null +++ b/src/Discord.Net/API/Client/Common/RoleReference.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class RoleReference + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; set; } + [JsonProperty("role_id"), JsonConverter(typeof(LongStringConverter))] + public ulong RoleId { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Common/UserReference.cs b/src/Discord.Net/API/Client/Common/UserReference.cs new file mode 100644 index 000000000..f2223df87 --- /dev/null +++ b/src/Discord.Net/API/Client/Common/UserReference.cs @@ -0,0 +1,17 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public class UserReference + { + [JsonProperty("username")] + public string Username { get; set; } + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("discriminator")] + public ushort? Discriminator { get; set; } + [JsonProperty("avatar")] + public string Avatar { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs new file mode 100644 index 000000000..68a0e98e6 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class HeartbeatCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.Heartbeat; + object IWebSocketMessage.Payload => EpochTime.GetMilliseconds(); + bool IWebSocketMessage.IsPrivate => false; + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs new file mode 100644 index 000000000..8f3892fe0 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Discord.API.Client.GatewaySocket +{ + internal sealed class IdentifyCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.Identify; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + [JsonProperty("v")] + public int Version { get; set; } + [JsonProperty("token")] + public string Token { get; set; } + [JsonProperty("properties")] + public Dictionary Properties { get; set; } + [JsonProperty("large_threshold", NullValueHandling = NullValueHandling.Ignore)] + public int? LargeThreshold { get; set; } + [JsonProperty("compress")] + public bool UseCompression { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs new file mode 100644 index 000000000..de230afe2 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs @@ -0,0 +1,20 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + [JsonObject(MemberSerialization.OptIn)] + internal sealed class RequestMembersCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.RequestGuildMembers; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; set; } + [JsonProperty("query")] + public string Query { get; set; } + [JsonProperty("limit")] + public int Limit { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs new file mode 100644 index 000000000..1b95b8076 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class ResumeCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.Resume; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + [JsonProperty("session_id")] + public string SessionId { get; set; } + [JsonProperty("seq")] + public int Sequence { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs new file mode 100644 index 000000000..be90e2efc --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateStatusCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.StatusUpdate; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + [JsonProperty("idle_since")] + public long? IdleSince { get; set; } + [JsonProperty("game_id")] + public int? GameId { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs new file mode 100644 index 000000000..b86609d0f --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs @@ -0,0 +1,22 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateVoiceCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.StatusUpdate; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; set; } + [JsonProperty("channel_id"), JsonConverter(typeof(LongStringConverter))] + public ulong ChannelId { get; set; } + [JsonProperty("self_mute")] + public bool IsSelfMuted { get; set; } + [JsonProperty("self_deaf")] + public bool IsSelfDeafened { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs new file mode 100644 index 000000000..da4e47aa9 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class ChannelCreateEvent : Channel { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs new file mode 100644 index 000000000..64452dbe2 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class ChannelDeleteEvent : Channel { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs new file mode 100644 index 000000000..908c65267 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class ChannelUpdateEvent : Channel { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs new file mode 100644 index 000000000..f58db2bf2 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildBanAddEvent + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; } + [JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))] + public ulong UserId { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs new file mode 100644 index 000000000..7b0dbed0c --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildBanRemoveEvent + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; } + [JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))] + public ulong UserId { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs new file mode 100644 index 000000000..13e3393fc --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildCreateEvent : ExtendedGuild { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs new file mode 100644 index 000000000..6850cc2b9 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildDeleteEvent : ExtendedGuild { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs new file mode 100644 index 000000000..d5ac59521 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + //public sealed class GuildIntegrationsUpdateEvent { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs new file mode 100644 index 000000000..ab543a41b --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildMemberAddEvent : Member { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs new file mode 100644 index 000000000..cd76cd81a --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildMemberRemoveEvent : Member { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs new file mode 100644 index 000000000..8e67d5e12 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildMemberUpdateEvent : Member { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs new file mode 100644 index 000000000..8c2564ed0 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildMembersChunkEvent + { + [JsonProperty("members")] + public Member[] Members; + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs new file mode 100644 index 000000000..b181305fb --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildRoleCreateEvent + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; } + [JsonProperty("role")] + public Role Data { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs new file mode 100644 index 000000000..470420157 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildRoleDeleteEvent : RoleReference { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs new file mode 100644 index 000000000..614ffc2ab --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs @@ -0,0 +1,13 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildRoleUpdateEvent + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; } + [JsonProperty("role")] + public Role Data { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs new file mode 100644 index 000000000..ba1665f09 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class GuildUpdateEvent : Guild { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs new file mode 100644 index 000000000..ed4048a62 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class MessageAckEvent : MessageReference { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs new file mode 100644 index 000000000..9eb514b61 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class MessageCreateEvent : Message { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs new file mode 100644 index 000000000..9d160eb58 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class MessageDeleteEvent : MessageReference { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs new file mode 100644 index 000000000..6fa852749 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class MessageUpdateEvent : Message { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs new file mode 100644 index 000000000..4ecbccf05 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class PresenceUpdateEvent : MemberPresence { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs new file mode 100644 index 000000000..7a276f4d3 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class ReadyEvent + { + public sealed class ReadState + { + [JsonProperty("id")] + public string ChannelId { get; } + [JsonProperty("mention_count")] + public int MentionCount { get; } + [JsonProperty("last_message_id")] + public string LastMessageId { get; } + } + + [JsonProperty("v")] + public int Version { get; } + [JsonProperty("user")] + public User User { get; } + [JsonProperty("session_id")] + public string SessionId { get; } + [JsonProperty("read_state")] + public ReadState[] ReadStates { get; } + [JsonProperty("guilds")] + public ExtendedGuild[] Guilds { get; } + [JsonProperty("private_channels")] + public Channel[] PrivateChannels { get; } + [JsonProperty("heartbeat_interval")] + public int HeartbeatInterval { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs new file mode 100644 index 000000000..64d7bb379 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class RedirectEvent + { + [JsonProperty("url")] + public string Url { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs new file mode 100644 index 000000000..2f1980cc8 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class ResumedEvent + { + [JsonProperty("heartbeat_interval")] + public int HeartbeatInterval { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs new file mode 100644 index 000000000..735cd61ed --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs @@ -0,0 +1,15 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class TypingStartEvent + { + [JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))] + public ulong UserId { get; } + [JsonProperty("channel_id"), JsonConverter(typeof(LongStringConverter))] + public ulong ChannelId { get; } + [JsonProperty("timestamp")] + public int Timestamp { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs new file mode 100644 index 000000000..04effff5e --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + //public sealed class UserSettingsUpdateEvent { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs new file mode 100644 index 000000000..6b43b34b6 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class UserUpdateEvent : User { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs new file mode 100644 index 000000000..c97c9e50d --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs @@ -0,0 +1,15 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.GatewaySocket +{ + public sealed class VoiceServerUpdateEvent + { + [JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; } + [JsonProperty("endpoint")] + public string Endpoint { get; } + [JsonProperty("token")] + public string Token { get; } + } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs new file mode 100644 index 000000000..448920b42 --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs @@ -0,0 +1,4 @@ +namespace Discord.API.Client.GatewaySocket +{ + public sealed class VoiceStateUpdateEvent : MemberVoiceState { } +} diff --git a/src/Discord.Net/API/Client/GatewaySocket/OpCodes.cs b/src/Discord.Net/API/Client/GatewaySocket/OpCodes.cs new file mode 100644 index 000000000..9942c670e --- /dev/null +++ b/src/Discord.Net/API/Client/GatewaySocket/OpCodes.cs @@ -0,0 +1,24 @@ +namespace Discord.API.Client.GatewaySocket +{ + public enum OpCodes : byte + { + /// C←S - Used to send most events. + Dispatch = 0, + /// C↔S - Used to keep the connection alive and measure latency. + Heartbeat = 1, + /// C→S - Used to associate a connection with a token and specify configuration. + Identify = 2, + /// C→S - Used to update client's status and current game id. + StatusUpdate = 3, + /// C→S - Used to join a particular voice channel. + VoiceStateUpdate = 4, + /// C→S - Used to ensure the server's voice server is alive. Only send this if voice connection fails or suddenly drops. + VoiceServerPing = 5, + /// C→S - Used to resume a connection after a redirect occurs. + Resume = 6, + /// C←S - Used to notify a client that they must reconnect to another gateway. + Redirect = 7, + /// C→S - Used to request all members that were withheld by large_threshold + RequestGuildMembers = 8 + } +} diff --git a/src/Discord.Net/API/Client/IWebSocketMessage.cs b/src/Discord.Net/API/Client/IWebSocketMessage.cs new file mode 100644 index 000000000..42b5460fd --- /dev/null +++ b/src/Discord.Net/API/Client/IWebSocketMessage.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client +{ + public interface IWebSocketMessage + { + int OpCode { get; } + object Payload { get; } + bool IsPrivate { get; } + } + public class WebSocketMessage + { + [JsonProperty("op")] + public int Operation { get; } + [JsonProperty("d")] + public object Payload { get; } + [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)] + public string Type { get; } + [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)] + public int? Sequence { get; } + + public WebSocketMessage() { } + public WebSocketMessage(IWebSocketMessage msg) + { + Operation = msg.OpCode; + Payload = msg.Payload; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/AcceptInvite.cs b/src/Discord.Net/API/Client/Rest/AcceptInvite.cs new file mode 100644 index 000000000..5e69b7d71 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/AcceptInvite.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class AcceptInviteRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/invite/{InviteId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public string InviteId { get; } + + public AcceptInviteRequest(string inviteId) + { + InviteId = inviteId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/AckMessage.cs b/src/Discord.Net/API/Client/Rest/AckMessage.cs new file mode 100644 index 000000000..a21abe9c4 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/AckMessage.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class AckMessageRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/messages/{MessageId}/ack"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + public ulong MessageId { get; } + + /*[JsonProperty("manual")] + public bool Manual { get; set; }*/ + + public AckMessageRequest(ulong channelId, ulong messageId) + { + ChannelId = channelId; + MessageId = messageId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/AddGuildBan.cs b/src/Discord.Net/API/Client/Rest/AddGuildBan.cs new file mode 100644 index 000000000..e9e1a0596 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/AddGuildBan.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class AddGuildBanRequest : IRestRequest + { + string IRestRequest.Method => "PUT"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/bans/{UserId}?delete-message-days={PruneDays}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + public ulong UserId { get; } + + public int PruneDays { get; set; } = 0; + + public AddGuildBanRequest(ulong guildId, ulong userId) + { + GuildId = guildId; + UserId = UserId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/BroadcastTyping.cs b/src/Discord.Net/API/Client/Rest/BroadcastTyping.cs new file mode 100644 index 000000000..8f7128aee --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/BroadcastTyping.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class BroadcastTypingRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/typing"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + + public BroadcastTypingRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/CreateChannel.cs b/src/Discord.Net/API/Client/Rest/CreateChannel.cs new file mode 100644 index 000000000..cbb9c9e50 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/CreateChannel.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class CreateChannelRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/channels"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + + public CreateChannelRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/CreateGuild.cs b/src/Discord.Net/API/Client/Rest/CreateGuild.cs new file mode 100644 index 000000000..222788e39 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/CreateGuild.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class CreateGuildRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("region")] + public string Region { get; set; } + [JsonProperty("icon")] + public string IconBase64 { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Rest/CreateInvite.cs b/src/Discord.Net/API/Client/Rest/CreateInvite.cs new file mode 100644 index 000000000..61421826d --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/CreateInvite.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class CreateInviteRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/invites"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + + [JsonProperty("max_age")] + public int MaxAge { get; set; } = 1800; + [JsonProperty("max_uses")] + public int MaxUses { get; set; } = 0; + [JsonProperty("temporary")] + public bool IsTemporary { get; set; } = false; + [JsonProperty("xkcdpass")] + public bool WithXkcdPass { get; set; } = false; + /*[JsonProperty("validate")] + public bool Validate { get; set; }*/ + + public CreateInviteRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs b/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs new file mode 100644 index 000000000..c0c7c2578 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs @@ -0,0 +1,17 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class CreatePrivateChannelRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/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 new file mode 100644 index 000000000..6330484e4 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/CreateRole.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class CreateRoleRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/roles"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + public CreateRoleRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/DeleteChannel.cs b/src/Discord.Net/API/Client/Rest/DeleteChannel.cs new file mode 100644 index 000000000..1366335be --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/DeleteChannel.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class DeleteChannelRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + + public DeleteChannelRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/DeleteInvite.cs b/src/Discord.Net/API/Client/Rest/DeleteInvite.cs new file mode 100644 index 000000000..d965f19e8 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/DeleteInvite.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class DeleteInviteRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/invite/{InviteCode}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public string InviteCode { get; } + + public DeleteInviteRequest(string inviteCode) + { + InviteCode = inviteCode; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/DeleteMessage.cs b/src/Discord.Net/API/Client/Rest/DeleteMessage.cs new file mode 100644 index 000000000..476a516e1 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/DeleteMessage.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class DeleteMessageRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/messages/{MessageId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + public ulong MessageId { get; } + + public DeleteMessageRequest(ulong channelId, ulong messageId) + { + ChannelId = channelId; + MessageId = messageId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/DeleteRole.cs b/src/Discord.Net/API/Client/Rest/DeleteRole.cs new file mode 100644 index 000000000..c547c7824 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/DeleteRole.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class DeleteRoleRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/roles/{RoleId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + public ulong RoleId { get; } + + public DeleteRoleRequest(ulong guildId, ulong roleId) + { + GuildId = guildId; + RoleId = roleId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/Gateway.cs b/src/Discord.Net/API/Client/Rest/Gateway.cs new file mode 100644 index 000000000..baa24bf74 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/Gateway.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GatewayRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/gateway"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + } + + public sealed class GatewayResponse + { + [JsonProperty("url")] + public string Url { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Rest/GetInvite.cs b/src/Discord.Net/API/Client/Rest/GetInvite.cs new file mode 100644 index 000000000..5683adad8 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/GetInvite.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetInviteRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/invite/{InviteCode}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public string InviteCode { get; } + + public GetInviteRequest(string inviteCode) + { + InviteCode = inviteCode; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/GetInvites.cs b/src/Discord.Net/API/Client/Rest/GetInvites.cs new file mode 100644 index 000000000..546e09015 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/GetInvites.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetInvitesRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/invites"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + public GetInvitesRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/GetMessages.cs b/src/Discord.Net/API/Client/Rest/GetMessages.cs new file mode 100644 index 000000000..51c7bf2e3 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/GetMessages.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using System.Text; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetMessagesRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint + { + get + { + StringBuilder query = new StringBuilder(); + this.AddQueryParam(query, "limit", Limit.ToString()); + if (RelativeDir != null) + this.AddQueryParam(query, RelativeDir, RelativeId.Value.ToString()); + return $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/messages{query}"; + } + } + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + + public int Limit { get; set; } = 100; + public string RelativeDir { get; set; } = null; + public ulong? RelativeId { get; set; } = 0; + + public GetMessagesRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs b/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs new file mode 100644 index 000000000..60882d8c6 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetVoiceRegionsRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/voice/regions"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + } + + public sealed class GetVoiceRegionsResponse + { + [JsonProperty("sample_hostname")] + public string Hostname { get; set; } + [JsonProperty("sample_port")] + public int Port { get; set; } + [JsonProperty("id")] + public string Id { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Rest/GetWidget.cs b/src/Discord.Net/API/Client/Rest/GetWidget.cs new file mode 100644 index 000000000..bb4e46f23 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/GetWidget.cs @@ -0,0 +1,61 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetWidgetRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/servers/{GuildId}/widget.json"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + public GetWidgetRequest(ulong guildId) + { + GuildId = guildId; + } + } + + public sealed class GetWidgetResponse + { + public sealed class Channel + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("position")] + public int Position { get; set; } + } + public sealed class User : UserReference + { + [JsonProperty("avatar_url")] + public string AvatarUrl { get; set; } + [JsonProperty("status")] + public string Status { get; set; } + [JsonProperty("game")] + public UserGame Game { get; set; } + } + public sealed class UserGame + { + [JsonProperty("id")] + public int Id { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + } + + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; set; } + [JsonProperty("channels")] + public Channel[] Channels { get; set; } + [JsonProperty("members")] + public MemberReference[] Members { get; set; } + [JsonProperty("instant_invite")] + public string InstantInviteUrl { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Rest/KickMember.cs b/src/Discord.Net/API/Client/Rest/KickMember.cs new file mode 100644 index 000000000..482967a1a --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/KickMember.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class KickMemberRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/members/{UserId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + public ulong UserId { get; } + + public KickMemberRequest(ulong guildId, ulong userId) + { + GuildId = guildId; + UserId = userId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs new file mode 100644 index 000000000..881cdb3be --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class LeaveGuildRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + public LeaveGuildRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/Logout.cs b/src/Discord.Net/API/Client/Rest/Logout.cs new file mode 100644 index 000000000..45a752174 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/Logout.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class LogoutRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/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 new file mode 100644 index 000000000..2e1cdc552 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/PruneMembers.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class PruneMembersRequest : IRestRequest + { + string IRestRequest.Method => IsSimulation ? "GET" : "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/prune?days={Days}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + public int Days { get; set; } = 30; + public bool IsSimulation { get; set; } = false; + + public PruneMembersRequest(ulong guildId) + { + GuildId = guildId; + } + } + + public sealed class PruneMembersResponse + { + [JsonProperty("pruned")] + public int Pruned { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs b/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs new file mode 100644 index 000000000..0579606d6 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class RemoveChannelPermissionsRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/permissions/{TargetId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + public ulong TargetId { get; } + + public RemoveChannelPermissionsRequest(ulong channelId, ulong targetId) + { + ChannelId = channelId; + TargetId = targetId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs b/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs new file mode 100644 index 000000000..b6ed30973 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class RemoveGuildBanRequest : IRestRequest + { + string IRestRequest.Method => "DELETE"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/bans/{UserId}"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + public ulong UserId { get; } + + public RemoveGuildBanRequest(ulong guildId, ulong userId) + { + GuildId = guildId; + UserId = userId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/ReorderChannels.cs b/src/Discord.Net/API/Client/Rest/ReorderChannels.cs new file mode 100644 index 000000000..6999578e9 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/ReorderChannels.cs @@ -0,0 +1,46 @@ +using Discord.API.Converters; +using Newtonsoft.Json; +using System.Linq; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class ReorderChannelsRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/channels"; + object IRestRequest.Payload + { + get + { + int pos = StartPos; + return ChannelIds.Select(x => new Channel(x, pos++)); + } + } + bool IRestRequest.IsPrivate => false; + + public sealed class Channel + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id; + [JsonProperty("position")] + public int Position; + + public Channel(ulong id, int position) + { + Id = id; + Position = position; + } + } + + public ulong GuildId { get; } + + public ulong[] ChannelIds { get; set; } = new ulong[0]; + public int StartPos { get; set; } = 0; + + public ReorderChannelsRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/ReorderRoles.cs b/src/Discord.Net/API/Client/Rest/ReorderRoles.cs new file mode 100644 index 000000000..cb083af86 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/ReorderRoles.cs @@ -0,0 +1,46 @@ +using Discord.API.Converters; +using Newtonsoft.Json; +using System.Linq; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class ReorderRolesRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/roles"; + object IRestRequest.Payload + { + get + { + int pos = StartPos; + return RoleIds.Select(x => new Role(x, pos++)); + } + } + bool IRestRequest.IsPrivate => false; + + public sealed class Role + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; } + [JsonProperty("position")] + public int Position { get; } + + public Role(ulong id, int pos) + { + Id = id; + Position = pos; + } + } + + public ulong GuildId { get; } + + public ulong[] RoleIds { get; set; } = new ulong[0]; + public int StartPos { get; set; } = 0; + + public ReorderRolesRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/SendFile.cs b/src/Discord.Net/API/Client/Rest/SendFile.cs new file mode 100644 index 000000000..16fdd1d17 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/SendFile.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using System.IO; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class SendFileRequest : IRestFileRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/messages"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + string IRestFileRequest.Filename => Filename; + Stream IRestFileRequest.Stream => Stream; + + public ulong ChannelId { get; } + + public string Filename { get; set; } + public Stream Stream { get; set; } + + public SendFileRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/SendMessage.cs b/src/Discord.Net/API/Client/Rest/SendMessage.cs new file mode 100644 index 000000000..195998ea4 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/SendMessage.cs @@ -0,0 +1,30 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class SendMessageRequest : IRestRequest + { + string IRestRequest.Method => "POST"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/messages"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + + [JsonProperty("content")] + public string Content { get; set; } + [JsonProperty("mentions"), JsonConverter(typeof(LongStringArrayConverter))] + public ulong[] MentionedUserIds { get; set; } + [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] + public string Nonce { get; set; } + [JsonProperty("tts")] + public bool IsTTS { get; set; } + + public SendMessageRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs new file mode 100644 index 000000000..91294c461 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateChannelRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("topic")] + public string Topic { get; set; } + [JsonProperty("position")] + public int Position { get; set; } + + public UpdateChannelRequest(ulong channelId) + { + ChannelId = channelId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/UpdateGuild.cs b/src/Discord.Net/API/Client/Rest/UpdateGuild.cs new file mode 100644 index 000000000..7a5433f03 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/UpdateGuild.cs @@ -0,0 +1,32 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateGuildRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("region")] + public string Region { get; set; } + [JsonProperty("icon")] + public string IconBase64 { get; set; } + [JsonProperty("afk_channel_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong? AFKChannelId { get; set; } + [JsonProperty("afk_timeout")] + public int AFKTimeout { get; set; } + + public UpdateGuildRequest(ulong guildId) + { + GuildId = guildId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/UpdateMember.cs b/src/Discord.Net/API/Client/Rest/UpdateMember.cs new file mode 100644 index 000000000..4dcf34b4e --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/UpdateMember.cs @@ -0,0 +1,33 @@ +using Discord.API.Converters; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateMemberRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/members/{UserId}"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + public ulong UserId { get; } + + [JsonProperty("mute")] + public bool IsMuted { get; set; } + [JsonProperty("deaf")] + public bool IsDeafened { get; set; } + [JsonProperty("channel_id"), JsonConverter(typeof(NullableLongStringConverter))] + public ulong? VoiceChannelId { get; set; } + [JsonProperty("roles"), JsonConverter(typeof(LongStringArrayConverter))] + public ulong[] RoleIds { get; set; } + + public UpdateMemberRequest(ulong guildId, ulong userId) + { + GuildId = guildId; + UserId = userId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/UpdateMessage.cs b/src/Discord.Net/API/Client/Rest/UpdateMessage.cs new file mode 100644 index 000000000..564714374 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/UpdateMessage.cs @@ -0,0 +1,28 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateMessageRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/channels/{ChannelId}/messages/{MessageId}"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong ChannelId { get; } + public ulong MessageId { get; } + + [JsonProperty("content")] + public string Content { get; set; } = ""; + [JsonProperty("mentions"), JsonConverter(typeof(LongStringArrayConverter))] + public ulong[] MentionedUserIds { get; set; } = new ulong[0]; + + public UpdateMessageRequest(ulong channelId, ulong messageId) + { + ChannelId = channelId; + MessageId = messageId; + } + } +} diff --git a/src/Discord.Net/API/Client/Rest/UpdateProfile.cs b/src/Discord.Net/API/Client/Rest/UpdateProfile.cs new file mode 100644 index 000000000..686a8cd20 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/UpdateProfile.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateProfileRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/users/@me"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + [JsonProperty("password")] + public string CurrentPassword { get; set; } + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("new_password")] + public string Password { get; set; } + [JsonProperty("username")] + public string Username { get; set; } + [JsonProperty("avatar")] + public string AvatarBase64 { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/Rest/UpdateRole.cs b/src/Discord.Net/API/Client/Rest/UpdateRole.cs new file mode 100644 index 000000000..adb765e26 --- /dev/null +++ b/src/Discord.Net/API/Client/Rest/UpdateRole.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class UpdateRoleRequest : IRestRequest + { + string IRestRequest.Method => "PATCH"; + string IRestRequest.Endpoint => $"{DiscordConfig.ClientAPIUrl}/guilds/{GuildId}/roles/{RoleId}"; + object IRestRequest.Payload => this; + bool IRestRequest.IsPrivate => false; + + public ulong GuildId { get; } + public ulong RoleId { get; } + + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("permissions")] + public uint Permissions { get; set; } + [JsonProperty("hoist")] + public bool IsHoisted { get; set; } + [JsonProperty("color")] + public uint Color { get; set; } + + public UpdateRoleRequest(ulong guildId, ulong roleId) + { + GuildId = guildId; + RoleId = roleId; + } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs new file mode 100644 index 000000000..a9727dd91 --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs @@ -0,0 +1,9 @@ +namespace Discord.API.Client.VoiceSocket +{ + public sealed class HeartbeatCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.Heartbeat; + object IWebSocketMessage.Payload => EpochTime.GetMilliseconds(); + bool IWebSocketMessage.IsPrivate => false; + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs new file mode 100644 index 000000000..1836b234c --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs @@ -0,0 +1,21 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.VoiceSocket +{ + public sealed class IdentifyCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.Identify; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => true; + + [JsonProperty("server_id"), JsonConverter(typeof(LongStringConverter))] + public ulong GuildId { get; set; } + [JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))] + public ulong UserId { get; set; } + [JsonProperty("session_id")] + public string SessionId { get; set; } + [JsonProperty("token")] + public string Token { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs new file mode 100644 index 000000000..aa8e8127b --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.VoiceSocket +{ + public sealed class SelectProtocolCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.SelectProtocol; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + public sealed class Data + { + [JsonProperty("address")] + public string Address { get; set; } + [JsonProperty("port")] + public int Port { get; set; } + [JsonProperty("mode")] + public string Mode { get; set; } + } + [JsonProperty("protocol")] + public string Protocol { get; set; } = "udp"; + [JsonProperty("data")] + private Data ProtocolData { get; } = new Data(); + + public string ExternalAddress { get { return ProtocolData.Address; } set { ProtocolData.Address = value; } } + public int ExternalPort { get { return ProtocolData.Port; } set { ProtocolData.Port = value; } } + public string EncryptionMode { get { return ProtocolData.Mode; } set { ProtocolData.Mode = value; } } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs new file mode 100644 index 000000000..13ab00524 --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.VoiceSocket +{ + public sealed class SetSpeakingCommand : IWebSocketMessage + { + int IWebSocketMessage.OpCode => (int)OpCodes.Speaking; + object IWebSocketMessage.Payload => this; + bool IWebSocketMessage.IsPrivate => false; + + [JsonProperty("speaking")] + public bool IsSpeaking { get; set; } + [JsonProperty("delay")] + public int Delay { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs b/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs new file mode 100644 index 000000000..b0fa34c1d --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.VoiceSocket +{ + public sealed class ReadyEvent + { + [JsonProperty("ssrc")] + public uint SSRC { get; set; } + [JsonProperty("port")] + public ushort Port { get; set; } + [JsonProperty("modes")] + public string[] Modes { get; set; } + [JsonProperty("heartbeat_interval")] + public int HeartbeatInterval { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs b/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs new file mode 100644 index 000000000..13f190cfc --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Client.VoiceSocket +{ + public sealed class SessionDescriptionEvent + { + [JsonProperty("secret_key")] + public byte[] SecretKey { get; set; } + [JsonProperty("mode")] + public string Mode { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs b/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs new file mode 100644 index 000000000..b3de0f800 --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs @@ -0,0 +1,15 @@ +using Discord.API.Converters; +using Newtonsoft.Json; + +namespace Discord.API.Client.VoiceSocket +{ + public sealed class SpeakingEvent + { + [JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))] + public ulong UserId { get; set; } + [JsonProperty("ssrc")] + public uint SSRC { get; set; } + [JsonProperty("speaking")] + public bool IsSpeaking { get; set; } + } +} diff --git a/src/Discord.Net/API/Client/VoiceSocket/OpCodes.cs b/src/Discord.Net/API/Client/VoiceSocket/OpCodes.cs new file mode 100644 index 000000000..e82ab5286 --- /dev/null +++ b/src/Discord.Net/API/Client/VoiceSocket/OpCodes.cs @@ -0,0 +1,18 @@ +namespace Discord.API.Client.VoiceSocket +{ + public enum OpCodes : byte + { + /// C→S - Used to associate a connection with a token. + Identify = 0, + /// C→S - Used to specify configuration. + SelectProtocol = 1, + /// C←S - Used to notify that the voice connection was successful and informs the client of available protocols. + Ready = 2, + /// C↔S - Used to keep the connection alive and measure latency. + Heartbeat = 3, + /// C←S - Used to provide an encryption key to the client. + SessionDescription = 4, + /// C↔S - Used to inform that a certain user is speaking. + Speaking = 5 + } +} diff --git a/src/Discord.Net/API/Enums/StringEnum.cs b/src/Discord.Net/API/Enums/StringEnum.cs deleted file mode 100644 index 98cf70e0e..000000000 --- a/src/Discord.Net/API/Enums/StringEnum.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Discord -{ - public abstract class StringEnum - { - protected string _value; - protected StringEnum(string value) - { - _value = value; - } - - public string Value => _value; - public override string ToString() => _value; - } -} diff --git a/src/Discord.Net/API/Extensions.cs b/src/Discord.Net/API/Extensions.cs new file mode 100644 index 000000000..77d25fe4e --- /dev/null +++ b/src/Discord.Net/API/Extensions.cs @@ -0,0 +1,19 @@ +using System; +using System.Text; + +namespace Discord.API +{ + internal static class RestRequestExtensions + { + public static void AddQueryParam(this IRestRequest request, StringBuilder builder, string name, string value) + { + if (builder.Length == 0) + builder.Append('?'); + else + builder.Append('&'); + builder.Append(Uri.EscapeDataString(name)); + builder.Append('='); + builder.Append(Uri.EscapeDataString(value)); + } + } +} diff --git a/src/Discord.Net/API/IRestRequest.cs b/src/Discord.Net/API/IRestRequest.cs new file mode 100644 index 000000000..b8c7b818c --- /dev/null +++ b/src/Discord.Net/API/IRestRequest.cs @@ -0,0 +1,26 @@ +using System.IO; + +namespace Discord.API +{ + public interface IRestRequest + { + string Method { get; } + string Endpoint { get; } + object Payload { get; } + bool IsPrivate { 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/src/Discord.Net/API/Messages/Auth.cs b/src/Discord.Net/API/Messages/Auth.cs deleted file mode 100644 index 8265d750d..000000000 --- a/src/Discord.Net/API/Messages/Auth.cs +++ /dev/null @@ -1,29 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Newtonsoft.Json; - -namespace Discord.API -{ - //Gateway - public class GatewayResponse - { - [JsonProperty("url")] - public string Url; - } - - //Login - public sealed class LoginRequest - { - [JsonProperty("email")] - public string Email; - [JsonProperty("password")] - public string Password; - } - public sealed class LoginResponse - { - [JsonProperty("token")] - public string Token; - } -} diff --git a/src/Discord.Net/API/Messages/Channels.cs b/src/Discord.Net/API/Messages/Channels.cs deleted file mode 100644 index 31c9a8398..000000000 --- a/src/Discord.Net/API/Messages/Channels.cs +++ /dev/null @@ -1,107 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using System.Collections; -using System.Collections.Generic; - -namespace Discord.API -{ - //Common - public class ChannelReference - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong GuildId; - [JsonProperty("name")] - public string Name; - [JsonProperty("type")] - public string Type; - } - public class ChannelInfo : ChannelReference - { - public sealed class PermissionOverwrite - { - [JsonProperty("type")] - public string Type; - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("deny")] - public uint Deny; - [JsonProperty("allow")] - public uint Allow; - } - - [JsonProperty("last_message_id")] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? LastMessageId; - [JsonProperty("is_private")] - public bool IsPrivate; - [JsonProperty("position")] - public int? Position; - [JsonProperty("topic")] - public string Topic; - [JsonProperty("permission_overwrites")] - public PermissionOverwrite[] PermissionOverwrites; - [JsonProperty("recipient")] - public UserReference Recipient; - } - - //Create - public class CreateChannelRequest - { - [JsonProperty("name")] - public string Name; - [JsonProperty("type")] - public string Type; - } - public class CreatePMChannelRequest - { - [JsonProperty("recipient_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong RecipientId; - } - public class CreateChannelResponse : ChannelInfo { } - - //Edit - public class EditChannelRequest - { - [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] - public string Name; - [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)] - public string Topic; - } - public class EditChannelResponse : ChannelInfo { } - - //Destroy - public class DestroyChannelResponse : ChannelInfo { } - - //Reorder - public class ReorderChannelsRequest : IEnumerable - { - public sealed class Channel - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("position")] - public uint Position; - } - private IEnumerable _channels; - public ReorderChannelsRequest(IEnumerable channels) { _channels = channels; } - - public IEnumerator GetEnumerator() => _channels.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _channels.GetEnumerator(); - } - - //Events - internal sealed class ChannelCreateEvent : ChannelInfo { } - internal sealed class ChannelDeleteEvent : ChannelInfo { } - internal sealed class ChannelUpdateEvent : ChannelInfo { } -} diff --git a/src/Discord.Net/API/Messages/GatewaySocket.cs b/src/Discord.Net/API/Messages/GatewaySocket.cs deleted file mode 100644 index 45d3704e4..000000000 --- a/src/Discord.Net/API/Messages/GatewaySocket.cs +++ /dev/null @@ -1,173 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace Discord.API -{ - public enum GatewayOpCodes : byte - { - /// Client <-- Server - Used to send most events. - Dispatch = 0, - /// Client <-> Server - Used to keep the connection alive and measure latency. - Heartbeat = 1, - /// Client --> Server - Used to associate a connection with a token and specify configuration. - Identify = 2, - /// Client --> Server - Used to update client's status and current game id. - StatusUpdate = 3, - /// Client --> Server - Used to join a particular voice channel. - VoiceStateUpdate = 4, - /// Client --> Server - Used to ensure the server's voice server is alive. Only send this if voice connection fails or suddenly drops. - VoiceServerPing = 5, - /// Client --> Server - Used to resume a connection after a redirect occurs. - Resume = 6, - /// Client <-- Server - Used to notify a client that they must reconnect to another gateway. - Redirect = 7, - /// Client --> Server - Used to request all members that were withheld by large_threshold - RequestGuildMembers = 8 - } - - //Common - public class WebSocketMessage - { - public WebSocketMessage() { } - public WebSocketMessage(int op) { Operation = op; } - - [JsonProperty("op")] - public int Operation; - [JsonProperty("d")] - public object Payload; - [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)] - public string Type; - [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)] - public int? Sequence; - } - public abstract class WebSocketMessage : WebSocketMessage - where T : new() - { - public WebSocketMessage() { Payload = new T(); } - public WebSocketMessage(int op) : base(op) { Payload = new T(); } - public WebSocketMessage(int op, T payload) : base(op) { Payload = payload; } - - [JsonIgnore] - public new T Payload - { - get - { - if (base.Payload is JToken) - base.Payload = (base.Payload as JToken).ToObject(); - return (T)base.Payload; - } - set { base.Payload = value; } - } - } - - //Commands - internal sealed class HeartbeatCommand : WebSocketMessage - { - public HeartbeatCommand() : base((int)GatewayOpCodes.Heartbeat, EpochTime.GetMilliseconds()) { } - } - internal sealed class IdentifyCommand : WebSocketMessage - { - public IdentifyCommand() : base((int)GatewayOpCodes.Identify) { } - public class Data - { - [JsonProperty("token")] - public string Token; - [JsonProperty("v")] - public int Version = 3; - [JsonProperty("properties")] - public Dictionary Properties = new Dictionary(); - [JsonProperty("large_threshold", NullValueHandling = NullValueHandling.Ignore)] - public int? LargeThreshold; - [JsonProperty("compress", NullValueHandling = NullValueHandling.Ignore)] - public bool? Compress; - } - } - - internal sealed class StatusUpdateCommand : WebSocketMessage - { - public StatusUpdateCommand() : base((int)GatewayOpCodes.StatusUpdate) { } - public class Data - { - [JsonProperty("idle_since")] - public long? IdleSince; - [JsonProperty("game_id")] - public int? GameId; - } - } - - internal sealed class JoinVoiceCommand : WebSocketMessage - { - public JoinVoiceCommand() : base((int)GatewayOpCodes.VoiceStateUpdate) { } - public class Data - { - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ServerId; - [JsonProperty("channel_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ChannelId; - [JsonProperty("self_mute")] - public string SelfMute; - [JsonProperty("self_deaf")] - public string SelfDeaf; - } - } - - internal sealed class ResumeCommand : WebSocketMessage - { - public ResumeCommand() : base((int)GatewayOpCodes.Resume) { } - public class Data - { - [JsonProperty("session_id")] - public string SessionId; - [JsonProperty("seq")] - public int Sequence; - } - } - - //Events - internal sealed class ReadyEvent - { - public sealed class ReadStateInfo - { - [JsonProperty("id")] - public string ChannelId; - [JsonProperty("mention_count")] - public int MentionCount; - [JsonProperty("last_message_id")] - public string LastMessageId; - } - - [JsonProperty("v")] - public int Version; - [JsonProperty("user")] - public UserInfo User; - [JsonProperty("session_id")] - public string SessionId; - [JsonProperty("read_state")] - public ReadStateInfo[] ReadState; - [JsonProperty("guilds")] - public ExtendedGuildInfo[] Guilds; - [JsonProperty("private_channels")] - public ChannelInfo[] PrivateChannels; - [JsonProperty("heartbeat_interval")] - public int HeartbeatInterval; - } - - internal sealed class RedirectEvent - { - [JsonProperty("url")] - public string Url; - } - internal sealed class ResumeEvent - { - [JsonProperty("heartbeat_interval")] - public int HeartbeatInterval; - } -} diff --git a/src/Discord.Net/API/Messages/Invites.cs b/src/Discord.Net/API/Messages/Invites.cs deleted file mode 100644 index d2344dae2..000000000 --- a/src/Discord.Net/API/Messages/Invites.cs +++ /dev/null @@ -1,61 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Discord.API -{ - //Common - public class InviteReference - { - [JsonProperty("inviter")] - public UserReference Inviter; - [JsonProperty("guild")] - public GuildReference Guild; - [JsonProperty("channel")] - public ChannelReference Channel; - [JsonProperty("code")] - public string Code; - [JsonProperty("xkcdpass")] - public string XkcdPass; - } - public class InviteInfo : InviteReference - { - [JsonProperty("max_age")] - public int? MaxAge; - [JsonProperty("max_uses")] - public int? MaxUses; - [JsonProperty("revoked")] - public bool? IsRevoked; - [JsonProperty("temporary")] - public bool? IsTemporary; - [JsonProperty("uses")] - public int? Uses; - [JsonProperty("created_at")] - public DateTime? CreatedAt; - } - - //Create - public class CreateInviteRequest - { - [JsonProperty("max_age")] - public int MaxAge; - [JsonProperty("max_uses")] - public int MaxUses; - [JsonProperty("temporary")] - public bool IsTemporary; - [JsonProperty("xkcdpass")] - public bool WithXkcdPass; - } - public class CreateInviteResponse : InviteInfo { } - - //Get - public class GetInviteResponse : InviteReference { } - public class GetInvitesResponse : List { } - - //Accept - public class AcceptInviteResponse : InviteReference { } -} diff --git a/src/Discord.Net/API/Messages/Maintenance.cs b/src/Discord.Net/API/Messages/Maintenance.cs deleted file mode 100644 index 78c0928cc..000000000 --- a/src/Discord.Net/API/Messages/Maintenance.cs +++ /dev/null @@ -1,35 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using System; - -namespace Discord.API -{ - public class GetIncidentsResponse - { - [JsonProperty("page")] - public PageData Page; - [JsonProperty("scheduled_maintenances")] - public MaintenanceData[] ScheduledMaintenances; - - public sealed class PageData - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("name")] - public string Name; - [JsonProperty("url")] - public string Url; - [JsonProperty("updated-at")] - public DateTime? UpdatedAt; - } - - public sealed class MaintenanceData - { - } - } -} diff --git a/src/Discord.Net/API/Messages/Members.cs b/src/Discord.Net/API/Messages/Members.cs deleted file mode 100644 index df7fa4501..000000000 --- a/src/Discord.Net/API/Messages/Members.cs +++ /dev/null @@ -1,112 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Discord.API -{ - //Common - public class MemberReference - { - [JsonProperty("user_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong UserId; //Used in bans - - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong GuildId; - - private UserReference _user; - [JsonProperty("user")] - public UserReference User - { - get { return _user; } - set - { - _user = value; - UserId = User.Id; - } - } - } - public class MemberInfo : MemberReference - { - [JsonProperty("joined_at")] - public DateTime? JoinedAt; - [JsonProperty("roles")] - [JsonConverter(typeof(LongStringArrayConverter))] - public ulong[] Roles; - } - public class ExtendedMemberInfo : MemberInfo - { - [JsonProperty("mute")] - public bool? IsServerMuted; - [JsonProperty("deaf")] - public bool? IsServerDeafened; - } - public class PresenceInfo : MemberReference - { - [JsonProperty("game_id")] - public int? GameId; - [JsonProperty("status")] - public string Status; - [JsonProperty("roles")] //TODO: Might be temporary - [JsonConverter(typeof(LongStringArrayConverter))] - public ulong[] Roles; - } - public class VoiceMemberInfo : MemberReference - { - [JsonProperty("channel_id")] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? ChannelId; - [JsonProperty("session_id")] - public string SessionId; - [JsonProperty("token")] - public string Token; - - [JsonProperty("self_mute")] - public bool? IsSelfMuted; - [JsonProperty("self_deaf")] - public bool? IsSelfDeafened; - [JsonProperty("mute")] - public bool? IsServerMuted; - [JsonProperty("deaf")] - public bool? IsServerDeafened; - [JsonProperty("suppress")] - public bool? IsServerSuppressed; - } - - public class EditMemberRequest - { - [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)] - public bool? Mute; - [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)] - public bool? Deaf; - [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? ChannelId; - [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(LongStringEnumerableConverter))] - public IEnumerable Roles; - } - - public class PruneUsersResponse - { - [JsonProperty("pruned")] - public int? Pruned; - } - - //Events - internal sealed class MemberAddEvent : MemberInfo { } - internal sealed class MemberUpdateEvent : MemberInfo { } - internal sealed class MemberRemoveEvent : MemberInfo { } - internal sealed class MemberVoiceStateUpdateEvent : VoiceMemberInfo { } - internal sealed class MembersChunkEvent - { - [JsonProperty("members")] - public MemberInfo[] Members; - } -} diff --git a/src/Discord.Net/API/Messages/Messages.cs b/src/Discord.Net/API/Messages/Messages.cs deleted file mode 100644 index 88e50ed74..000000000 --- a/src/Discord.Net/API/Messages/Messages.cs +++ /dev/null @@ -1,155 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Discord.API -{ - //Common - public class MessageReference - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("channel_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ChannelId; - [JsonProperty("message_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong MessageId { get { return Id; } set { Id = value; } } - } - public class MessageInfo : MessageReference - { - public sealed class Attachment - { - [JsonProperty("id")] - public string Id; - [JsonProperty("url")] - public string Url; - [JsonProperty("proxy_url")] - public string ProxyUrl; - [JsonProperty("size")] - public int Size; - [JsonProperty("filename")] - public string Filename; - [JsonProperty("width")] - public int Width; - [JsonProperty("height")] - public int Height; - } - - public sealed class Embed - { - public sealed class Reference - { - [JsonProperty("url")] - public string Url; - [JsonProperty("name")] - public string Name; - } - - public sealed class ThumbnailInfo - { - [JsonProperty("url")] - public string Url; - [JsonProperty("proxy_url")] - public string ProxyUrl; - [JsonProperty("width")] - public int Width; - [JsonProperty("height")] - public int Height; - } - - [JsonProperty("url")] - public string Url; - [JsonProperty("type")] - public string Type; - [JsonProperty("title")] - public string Title; - [JsonProperty("description")] - public string Description; - [JsonProperty("author")] - public Reference Author; - [JsonProperty("provider")] - public Reference Provider; - [JsonProperty("thumbnail")] - public ThumbnailInfo Thumbnail; - } - - [JsonProperty("tts")] - public bool? IsTextToSpeech; - [JsonProperty("mention_everyone")] - public bool? IsMentioningEveryone; - [JsonProperty("timestamp")] - public DateTime? Timestamp; - [JsonProperty("edited_timestamp")] - public DateTime? EditedTimestamp; - [JsonProperty("mentions")] - public UserReference[] Mentions; - [JsonProperty("embeds")] - public Embed[] Embeds; //TODO: Parse this - [JsonProperty("attachments")] - public Attachment[] Attachments; - [JsonProperty("content")] - public string Content; - [JsonProperty("author")] - public UserReference Author; - [JsonProperty("nonce")] - public string Nonce; - } - - //Create - internal sealed class SendMessageRequest - { - [JsonProperty("content")] - public string Content; - [JsonProperty("mentions")] - [JsonConverter(typeof(LongStringEnumerableConverter))] - public IEnumerable Mentions; - [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] - public string Nonce; - [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)] - public bool IsTTS; - } - public sealed class SendMessageResponse : MessageInfo { } - - //Edit - internal sealed class EditMessageRequest - { - [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] - public string Content; - [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(LongStringEnumerableConverter))] - public IEnumerable Mentions; - } - public sealed class EditMessageResponse : MessageInfo { } - - //Get - public sealed class GetMessagesResponse : List { } - - //Commands - internal sealed class GetUsersCommand : WebSocketMessage - { - public GetUsersCommand() : base(8) { } - public class Data - { - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ServerId; - [JsonProperty("query")] - public string Query; - [JsonProperty("limit")] - public int Limit; - } - } - - //Events - internal sealed class MessageCreateEvent : MessageInfo { } - internal sealed class MessageUpdateEvent : MessageInfo { } - internal sealed class MessageDeleteEvent : MessageReference { } - internal sealed class MessageAckEvent : MessageReference { } -} diff --git a/src/Discord.Net/API/Messages/Permissions.cs b/src/Discord.Net/API/Messages/Permissions.cs deleted file mode 100644 index 60ad362f9..000000000 --- a/src/Discord.Net/API/Messages/Permissions.cs +++ /dev/null @@ -1,23 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; - -namespace Discord.API -{ - //Create/Edit - internal sealed class SetChannelPermissionsRequest - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("type")] - public string Type; - [JsonProperty("allow")] - public uint Allow; - [JsonProperty("deny")] - public uint Deny; - } -} diff --git a/src/Discord.Net/API/Messages/Roles.cs b/src/Discord.Net/API/Messages/Roles.cs deleted file mode 100644 index 82a3d615f..000000000 --- a/src/Discord.Net/API/Messages/Roles.cs +++ /dev/null @@ -1,94 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using System.Collections; -using System.Collections.Generic; - -namespace Discord.API -{ - //Common - public class RoleReference - { - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong GuildId; - [JsonProperty("role_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong RoleId; - } - public class RoleInfo - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("permissions")] - public uint? Permissions; - [JsonProperty("name")] - public string Name; - [JsonProperty("position")] - public int? Position; - [JsonProperty("hoist")] - public bool? Hoist; - [JsonProperty("color")] - public uint? Color; - [JsonProperty("managed")] - public bool? Managed; - } - - //Create - public sealed class CreateRoleResponse : RoleInfo { } - - //Edit - public sealed class EditRoleRequest - { - [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] - public string Name; - [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)] - public uint? Permissions; - [JsonProperty("hoist", NullValueHandling = NullValueHandling.Ignore)] - public bool? Hoist; - [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)] - public uint? Color; - } - public sealed class EditRoleResponse : RoleInfo { } - - //Reorder - public sealed class ReorderRolesRequest : IEnumerable - { - public sealed class Role - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("position")] - public uint Position; - } - private IEnumerable _roles; - public ReorderRolesRequest(IEnumerable roles) { _roles = roles; } - - public IEnumerator GetEnumerator() => _roles.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _roles.GetEnumerator(); - } - - //Events - internal sealed class RoleCreateEvent - { - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong GuildId; - [JsonProperty("role")] - public RoleInfo Data; - } - internal sealed class RoleUpdateEvent - { - [JsonProperty("guild_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong GuildId; - [JsonProperty("role")] - public RoleInfo Data; - } - internal sealed class RoleDeleteEvent : RoleReference { } -} diff --git a/src/Discord.Net/API/Messages/Servers.cs b/src/Discord.Net/API/Messages/Servers.cs deleted file mode 100644 index a8ef377b6..000000000 --- a/src/Discord.Net/API/Messages/Servers.cs +++ /dev/null @@ -1,108 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace Discord.API -{ - //Common - public class GuildReference - { - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("name")] - public string Name; - } - public class GuildInfo : GuildReference - { - [JsonProperty("afk_channel_id")] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? AFKChannelId; - [JsonProperty("afk_timeout")] - public int? AFKTimeout; - [JsonProperty("embed_channel_id")] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? EmbedChannelId; - [JsonProperty("embed_enabled")] - public bool EmbedEnabled; - [JsonProperty("icon")] - public string Icon; - [JsonProperty("joined_at")] - public DateTime? JoinedAt; - [JsonProperty("owner_id")] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? OwnerId; - [JsonProperty("region")] - public string Region; - [JsonProperty("roles")] - public RoleInfo[] Roles; - } - public class ExtendedGuildInfo : GuildInfo - { - [JsonProperty("channels")] - public ChannelInfo[] Channels; - [JsonProperty("members")] - public ExtendedMemberInfo[] Members; - [JsonProperty("presences")] - public PresenceInfo[] Presences; - [JsonProperty("voice_states")] - public VoiceMemberInfo[] VoiceStates; - [JsonProperty("unavailable")] - public bool? Unavailable; - } - - //Create - internal sealed class CreateServerRequest - { - [JsonProperty("name")] - public string Name; - [JsonProperty("region")] - public string Region; - } - public sealed class CreateServerResponse : GuildInfo { } - - //Edit - internal sealed class EditServerRequest - { - [JsonProperty("name")] - public string Name; - [JsonProperty("region")] - public string Region; - [JsonProperty("icon")] - public string Icon; - [JsonProperty("afk_channel_id")] - [JsonConverter(typeof(NullableLongStringConverter))] - public ulong? AFKChannelId; - [JsonProperty("afk_timeout")] - public int AFKTimeout; - } - public sealed class EditServerResponse : GuildInfo { } - - //Delete - public sealed class DeleteServerResponse : GuildInfo { } - - //GetRegions - public class GetRegionsResponse : List - { - public sealed class RegionData - { - [JsonProperty("sample_hostname")] - public string Hostname; - [JsonProperty("sample_port")] - public int Port; - [JsonProperty("id")] - public string Id; - [JsonProperty("name")] - public string Name; - } - } - //Events - internal sealed class GuildCreateEvent : ExtendedGuildInfo { } - internal sealed class GuildUpdateEvent : GuildInfo { } - internal sealed class GuildDeleteEvent : ExtendedGuildInfo { } -} diff --git a/src/Discord.Net/API/Messages/Users.cs b/src/Discord.Net/API/Messages/Users.cs deleted file mode 100644 index 76ef48fc8..000000000 --- a/src/Discord.Net/API/Messages/Users.cs +++ /dev/null @@ -1,63 +0,0 @@ -//Ignore unused/unassigned variable warnings -#pragma warning disable CS0649 -#pragma warning disable CS0169 - -using Discord.API.Converters; -using Newtonsoft.Json; - -namespace Discord.API -{ - //Common - public class UserReference - { - [JsonProperty("username")] - public string Username; - [JsonProperty("id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong Id; - [JsonProperty("discriminator")] - public ushort? Discriminator; - [JsonProperty("avatar")] - public string Avatar; - } - public class UserInfo : UserReference - { - [JsonProperty("email")] - public string Email; - [JsonProperty("verified")] - public bool? IsVerified; - } - - //Edit - internal sealed class EditUserRequest - { - [JsonProperty("password")] - public string CurrentPassword; - [JsonProperty("email")] - public string Email; - [JsonProperty("new_password")] - public string Password; - [JsonProperty("username")] - public string Username; - [JsonProperty("avatar")] - public string Avatar; - } - public sealed class EditUserResponse : UserInfo { } - - //Events - internal sealed class UserUpdateEvent : UserInfo { } - internal sealed class PresenceUpdateEvent : PresenceInfo { } - internal sealed class TypingStartEvent - { - [JsonProperty("user_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong UserId; - [JsonProperty("channel_id")] - [JsonConverter(typeof(LongStringConverter))] - public ulong ChannelId; - [JsonProperty("timestamp")] - public int Timestamp; - } - internal sealed class BanAddEvent : MemberReference { } - internal sealed class BanRemoveEvent : MemberReference { } -} diff --git a/src/Discord.Net/API/Status/Common/Incident.cs b/src/Discord.Net/API/Status/Common/Incident.cs new file mode 100644 index 000000000..84e4be02b --- /dev/null +++ b/src/Discord.Net/API/Status/Common/Incident.cs @@ -0,0 +1,31 @@ +using Discord.API.Converters; +using Newtonsoft.Json; +using System; + +namespace Discord.API.Status +{ + public class Incident + { + [JsonProperty("page")] + public PageData Page { get; } + [JsonProperty("scheduled_maintenances")] + public MaintenanceData[] ScheduledMaintenances { get; } + + public sealed class PageData + { + [JsonProperty("id"), JsonConverter(typeof(LongStringConverter))] + public ulong Id { get; } + [JsonProperty("name")] + public string Name { get; } + [JsonProperty("url")] + public string Url { get; } + [JsonProperty("updated-at")] + public DateTime? UpdatedAt { get; } + } + + public sealed class MaintenanceData + { + //TODO: Complete + } + } +} diff --git a/src/Discord.Net/API/Status/Rest/Active.cs b/src/Discord.Net/API/Status/Rest/Active.cs new file mode 100644 index 000000000..8f78cab82 --- /dev/null +++ b/src/Discord.Net/API/Status/Rest/Active.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Discord.API.Status.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetActiveIncidentsRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.StatusAPIUrl}/scheduled-maintenances/active.json"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + } +} diff --git a/src/Discord.Net/API/Status/Rest/Upcoming.cs b/src/Discord.Net/API/Status/Rest/Upcoming.cs new file mode 100644 index 000000000..676362714 --- /dev/null +++ b/src/Discord.Net/API/Status/Rest/Upcoming.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Discord.API.Status.Rest +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class GetUpcomingIncidentsRequest : IRestRequest + { + string IRestRequest.Method => "GET"; + string IRestRequest.Endpoint => $"{DiscordConfig.StatusAPIUrl}/scheduled-maintenances/upcoming.json"; + object IRestRequest.Payload => null; + bool IRestRequest.IsPrivate => false; + } +} diff --git a/src/Discord.Net/DiscordClient.Channels.cs b/src/Discord.Net/DiscordClient.Channels.cs index 30b5f6a0d..6d95cf9c9 100644 --- a/src/Discord.Net/DiscordClient.Channels.cs +++ b/src/Discord.Net/DiscordClient.Channels.cs @@ -1,3 +1,4 @@ +using Discord.API.Client.Rest; using Discord.Net; using System; using System.Collections.Concurrent; @@ -118,7 +119,9 @@ namespace Discord if (type == null) throw new ArgumentNullException(nameof(type)); CheckReady(); - var response = await _api.CreateChannel(server.Id, name, type.Value).ConfigureAwait(false); + var request = new CreateChannelRequest(server.Id) { Name = name, Type = type.Value }; + var response = await _rest.Send(request).ConfigureAwait(false); + var channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient?.Id); channel.Update(response); return channel; @@ -133,9 +136,11 @@ namespace Discord Channel channel = null; if (user != null) channel = user.Global.PrivateChannel; - if (channel == null) - { - var response = await _api.CreatePMChannel(_currentUser.Id, user.Id).ConfigureAwait(false); + if (channel == null) + { + var request = new CreatePrivateChannelRequest() { RecipientId = user.Id }; + var response = await _rest.Send(request).ConfigureAwait(false); + var recipient = _users.GetOrAdd(response.Recipient.Id, null); recipient.Update(response.Recipient); channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient.Id); @@ -150,8 +155,16 @@ namespace Discord if (channel == null) throw new ArgumentNullException(nameof(channel)); CheckReady(); - if (name != null || topic != null) - await _api.EditChannel(channel.Id, name: name, topic: topic).ConfigureAwait(false); + if (name != null || topic != null) + { + var request = new UpdateChannelRequest(channel.Id) + { + Name = name ?? channel.Name, + Topic = topic ?? channel.Topic, + Position = channel.Position + }; + await _rest.Send(request).ConfigureAwait(false); + } if (position != null) { @@ -189,7 +202,12 @@ namespace Discord if (channels == null) throw new ArgumentNullException(nameof(channels)); CheckReady(); - return _api.ReorderChannels(server.Id, channels.Select(x => x.Id), after?.Position ?? 0); + var request = new ReorderChannelsRequest(server.Id) + { + ChannelIds = channels.Select(x => x.Id).ToArray(), + StartPos = after != null ? after.Position + 1 : channels.Min(x => x.Position) + }; + return _rest.Send(request); } /// Destroys the provided channel. @@ -198,7 +216,7 @@ namespace Discord if (channel == null) throw new ArgumentNullException(nameof(channel)); CheckReady(); - try { await _api.DestroyChannel(channel.Id).ConfigureAwait(false); } + try { await _rest.Send(new DeleteChannelRequest(channel.Id)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } } diff --git a/src/Discord.Net/DiscordClient.Invites.cs b/src/Discord.Net/DiscordClient.Invites.cs index 9b3ca65a8..48f8c8151 100644 --- a/src/Discord.Net/DiscordClient.Invites.cs +++ b/src/Discord.Net/DiscordClient.Invites.cs @@ -1,3 +1,4 @@ +using Discord.API.Client.Rest; using Discord.Net; using System; using System.Linq; @@ -23,7 +24,7 @@ namespace Discord if (index >= 0) inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); - var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); + var response = await _rest.Send(new GetInviteRequest(inviteIdOrXkcd)).ConfigureAwait(false); var invite = new Invite(response.Code, response.XkcdPass); invite.Update(response); return invite; @@ -35,7 +36,7 @@ namespace Discord if (server == null) throw new ArgumentNullException(nameof(server)); CheckReady(); - var response = await _api.GetInvites(server.Id).ConfigureAwait(false); + var response = await _rest.Send(new GetInvitesRequest(server.Id)).ConfigureAwait(false); return response.Select(x => { var invite = new Invite(x.Code, x.XkcdPass); @@ -61,15 +62,22 @@ namespace Discord /// 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 0. /// The max amount of times this invite may be used. Set to 0 to have unlimited uses. - public async Task CreateInvite(Channel channel, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false) + public async Task CreateInvite(Channel channel, int maxAge = 1800, int maxUses = 0, bool isTemporary = false, bool withXkcd = false) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge)); if (maxUses < 0) throw new ArgumentOutOfRangeException(nameof(maxUses)); CheckReady(); - var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, - tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false); + var request = new CreateInviteRequest(channel.Id) + { + MaxAge = maxAge, + MaxUses = maxUses, + IsTemporary = isTemporary, + WithXkcdPass = withXkcd + }; + + var response = await _rest.Send(request).ConfigureAwait(false); var invite = new Invite(response.Code, response.XkcdPass); return invite; } @@ -80,7 +88,7 @@ namespace Discord if (invite == null) throw new ArgumentNullException(nameof(invite)); CheckReady(); - try { await _api.DeleteInvite(invite.Id).ConfigureAwait(false); } + try { await _rest.Send(new DeleteInviteRequest(invite.Code)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } @@ -90,7 +98,7 @@ namespace Discord if (invite == null) throw new ArgumentNullException(nameof(invite)); CheckReady(); - return _api.AcceptInvite(invite.Id); + return _rest.Send(new AcceptInviteRequest(invite.Code)); } } } \ No newline at end of file diff --git a/src/Discord.Net/DiscordClient.Messages.cs b/src/Discord.Net/DiscordClient.Messages.cs index c67f6e980..49625c36b 100644 --- a/src/Discord.Net/DiscordClient.Messages.cs +++ b/src/Discord.Net/DiscordClient.Messages.cs @@ -1,4 +1,5 @@ using Discord.API; +using Discord.API.Client.Rest; using Discord.Net; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -9,9 +10,12 @@ using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; +using APIMessage = Discord.API.Client.Message; +using APIUser = Discord.API.Client.User; namespace Discord { + public enum RelativeDirection { Before, After} internal sealed class Messages : AsyncCollection { private bool _isEnabled; @@ -155,7 +159,13 @@ namespace Discord if (stream == null) throw new ArgumentNullException(nameof(stream)); CheckReady(); - var model = await _api.SendFile(channel.Id, filename, stream).ConfigureAwait(false); + var request = new SendFileRequest(channel.Id) + { + Filename = filename, + Stream = stream + }; + var model = await _rest.Send(request).ConfigureAwait(false); + var msg = _messages.GetOrAdd(model.Id, channel.Id, model.Author.Id); msg.Update(model); RaiseMessageSent(msg); @@ -197,11 +207,11 @@ namespace Discord var nonce = GenerateNonce(); msg = new Message(this, 0, channel.Id, _currentUser.Id); //_messages.GetOrAdd(nonce, channel.Id, _privateUser.Id); var currentUser = msg.User; - msg.Update(new MessageInfo - { + msg.Update(new APIMessage + { Content = text, Timestamp = DateTime.UtcNow, - Author = new UserReference { Avatar = currentUser.AvatarId, Discriminator = currentUser.Discriminator, Id = _currentUser.Id, Username = currentUser.Name }, + Author = new APIUser { Avatar = currentUser.AvatarId, Discriminator = currentUser.Discriminator, Id = _currentUser.Id, Username = currentUser.Name }, ChannelId = channel.Id, Nonce = IdConvert.ToString(nonce), IsTextToSpeech = isTextToSpeech @@ -212,7 +222,14 @@ namespace Discord } else { - var model = await _api.SendMessage(channel.Id, text, mentionedUsers.Select(x => x.Id), null, isTextToSpeech).ConfigureAwait(false); + var request = new SendMessageRequest(channel.Id) + { + Content = text, + MentionedUserIds = mentionedUsers.Select(x => x.Id).ToArray(), + Nonce = null, + IsTTS = isTextToSpeech + }; + var model = await _rest.Send(request).ConfigureAwait(false); msg = _messages.GetOrAdd(model.Id, channel.Id, model.Author.Id); msg.Update(model); RaiseMessageSent(msg); @@ -235,11 +252,18 @@ namespace Discord if (text.Length > MaxMessageSize) throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {MaxMessageSize} characters or less."); - + if (Config.UseMessageQueue) _pendingMessages.Enqueue(new MessageQueueItem(message, text, mentionedUsers.Select(x => x.Id).ToArray())); else - await _api.EditMessage(message.Id, message.Channel.Id, text, mentionedUsers.Select(x => x.Id)).ConfigureAwait(false); + { + var request = new UpdateMessageRequest(message.Channel.Id, message.Id) + { + Content = text, + MentionedUserIds = mentionedUsers.Select(x => x.Id).ToArray() + }; + await _rest.Send(request).ConfigureAwait(false); + } } /// Deletes the provided message. @@ -248,10 +272,8 @@ namespace Discord if (message == null) throw new ArgumentNullException(nameof(message)); CheckReady(); - try - { - await _api.DeleteMessage(message.Id, message.Channel.Id).ConfigureAwait(false); - } + var request = new DeleteMessageRequest(message.Id, message.Channel.Id); + try { await _rest.Send(request).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } public async Task DeleteMessages(IEnumerable messages) @@ -261,27 +283,31 @@ namespace Discord foreach (var message in messages) { - try - { - await _api.DeleteMessage(message.Id, message.Channel.Id).ConfigureAwait(false); - } + var request = new DeleteMessageRequest(message.Id, message.Channel.Id); + try { await _rest.Send(request).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } } - /// Downloads last count messages from the server, returning all messages before or after relativeMessageId, if it's provided. - public async Task DownloadMessages(Channel channel, int count, ulong? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) + /// Downloads messages from the server, returning all messages before or after relativeMessageId, if it's provided. + public async Task DownloadMessages(Channel channel, int limit = 100, ulong? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) { if (channel == null) throw new ArgumentNullException(nameof(channel)); - if (count < 0) throw new ArgumentNullException(nameof(count)); + if (limit < 0) throw new ArgumentNullException(nameof(limit)); CheckReady(); - if (count == 0) return new Message[0]; + if (limit == 0) return new Message[0]; if (channel != null && channel.Type == ChannelType.Text) { - try - { - var msgs = await _api.GetMessages(channel.Id, count, relativeMessageId, relativeDir).ConfigureAwait(false); + try + { + var request = new GetMessagesRequest(channel.Id) + { + Limit = limit, + RelativeDir = relativeDir == RelativeDirection.Before ? "before" : "after", + RelativeId = relativeMessageId + }; + var msgs = await _rest.Send(request).ConfigureAwait(false); return msgs.Select(x => { Message msg = null; @@ -310,7 +336,7 @@ namespace Discord if (message == null) throw new ArgumentNullException(nameof(message)); if (!message.IsAuthor) - _api.AckMessage(message.Id, message.Channel.Id); + _rest.Send(new AckMessageRequest(message.Id, message.Channel.Id)); } /// Deserializes messages from JSON format and imports them into the message cache. @@ -350,40 +376,41 @@ namespace Discord return JsonConvert.SerializeObject(channel.Messages); } - private Task MessageQueueAsync() - { - var cancelToken = _cancelToken; - int interval = Config.MessageQueueInterval; + private Task MessageQueueAsync() + { + var cancelToken = _cancelToken; + int interval = Config.MessageQueueInterval; - return Task.Run(async () => - { - MessageQueueItem queuedMessage; + return Task.Run(async () => + { + MessageQueueItem queuedMessage; - while (!cancelToken.IsCancellationRequested) - { - while (_pendingMessages.TryDequeue(out queuedMessage)) - { + while (!cancelToken.IsCancellationRequested) + { + while (_pendingMessages.TryDequeue(out queuedMessage)) + { var msg = queuedMessage.Message; try { if (msg.Id == 0) { - await _api.SendMessage( - msg.Channel.Id, - queuedMessage.Text, - queuedMessage.MentionedUsers, - IdConvert.ToString(msg.Id), //Nonce - msg.IsTTS) - .ConfigureAwait(false); + var request = new SendMessageRequest(msg.Channel.Id) + { + Content = queuedMessage.Text, + MentionedUserIds = queuedMessage.MentionedUsers, + Nonce = IdConvert.ToString(msg.Id), //Nonce + IsTTS = msg.IsTTS + }; + await _rest.Send(request).ConfigureAwait(false); } else { - await _api.EditMessage( - msg.Id, - msg.Channel.Id, - queuedMessage.Text, - queuedMessage.MentionedUsers) - .ConfigureAwait(false); + var request = new UpdateMessageRequest(msg.Channel.Id, msg.Id) + { + Content = queuedMessage.Text, + MentionedUserIds = queuedMessage.MentionedUsers + }; + await _rest.Send(request).ConfigureAwait(false); } } catch (WebException) { break; } diff --git a/src/Discord.Net/DiscordClient.Permissions.cs b/src/Discord.Net/DiscordClient.Permissions.cs index 132f6b47e..f55c585c4 100644 --- a/src/Discord.Net/DiscordClient.Permissions.cs +++ b/src/Discord.Net/DiscordClient.Permissions.cs @@ -1,3 +1,4 @@ +using Discord.API.Client.Rest; using Discord.Net; using System; using System.Linq; @@ -63,8 +64,17 @@ namespace Discord return SetChannelPermissions(channel, role.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny); } - private Task SetChannelPermissions(Channel channel, ulong targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) - => _api.SetChannelPermissions(channel.Id, targetId, targetType.Value, allow?.RawValue ?? 0, deny?.RawValue ?? 0); + private Task SetChannelPermissions(Channel channel, ulong targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) + { + var request = new AddChannelPermissionsRequest(channel.Id) + { + TargetId = targetId, + TargetType = targetType.Value, + Allow = allow?.RawValue ?? 0, + Deny = deny?.RawValue ?? 0 + }; + return _rest.Send(request); + } public Task RemoveChannelPermissions(Channel channel, User user) { @@ -87,7 +97,7 @@ namespace Discord try { var perms = channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).FirstOrDefault(); - await _api.DeleteChannelPermissions(channel.Id, userOrRoleId).ConfigureAwait(false); + await _rest.Send(new RemoveChannelPermissionsRequest(channel.Id, userOrRoleId)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } diff --git a/src/Discord.Net/DiscordClient.Roles.cs b/src/Discord.Net/DiscordClient.Roles.cs index 03d06ce6e..bef739755 100644 --- a/src/Discord.Net/DiscordClient.Roles.cs +++ b/src/Discord.Net/DiscordClient.Roles.cs @@ -1,3 +1,5 @@ +using Discord.API; +using Discord.API.Client.Rest; using Discord.Net; using System; using System.Collections.Generic; @@ -76,61 +78,75 @@ namespace Discord string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); //} } - - /// Note: due to current API limitations, the created role cannot be returned. - public async Task CreateRole(Server server, string name) + + /// Note: due to current API limitations, the created role cannot be returned. + public async Task CreateRole(Server server, string name, ServerPermissions permissions = null, Color color = null, bool isHoisted = false) { if (server == null) throw new ArgumentNullException(nameof(server)); if (name == null) throw new ArgumentNullException(nameof(name)); CheckReady(); - var response1 = await _api.CreateRole(server.Id).ConfigureAwait(false); + var request1 = new CreateRoleRequest(server.Id); + var response1 = await _rest.Send(request1).ConfigureAwait(false); var role = _roles.GetOrAdd(response1.Id, server.Id); role.Update(response1); - - //TODO: We shouldnt have to send permissions here, should be fixed on Discord's end soon - var response2 = await _api.EditRole(role.Server.Id, role.Id, - name: name, - permissions: role.Permissions.RawValue).ConfigureAwait(false); - role.Update(response2); - - return role; + + var request2 = new UpdateRoleRequest(role.Server.Id, role.Id) + { + Name = name, + Permissions = (permissions ?? role.Permissions).RawValue, + Color = (color ?? Color.Default).RawValue, + IsHoisted = isHoisted + }; + var response2 = await _rest.Send(request2).ConfigureAwait(false); + role.Update(response2); + + return role; } - - public async Task EditRole(Role role, string name = null, ServerPermissions permissions = null, Color color = null, bool? hoist = null, int? position = null) - { - if (role == null) throw new ArgumentNullException(nameof(role)); - CheckReady(); - //TODO: check this null workaround later, should be fixed on Discord's end soon - var response = await _api.EditRole(role.Server.Id, role.Id, - name: name ?? role.Name, - permissions: (permissions ?? role.Permissions).RawValue, - color: (color ?? role.Color).RawValue, - hoist: hoist ?? role.IsHoisted).ConfigureAwait(false); - - if (position != null) - { - int oldPos = role.Position; - int newPos = position.Value; - int minPos; - Role[] roles = role.Server.Roles.OrderBy(x => x.Position).ToArray(); - - if (oldPos < newPos) //Moving Down - { - minPos = oldPos; - for (int i = oldPos; i < newPos; i++) - roles[i] = roles[i + 1]; - roles[newPos] = role; - } - else //(oldPos > newPos) Moving Up - { - minPos = newPos; - for (int i = oldPos; i > newPos; i--) - roles[i] = roles[i - 1]; - roles[newPos] = role; - } - await _api.ReorderRoles(role.Server.Id, roles.Skip(minPos).Select(x => x.Id), minPos).ConfigureAwait(false); + public async Task EditRole(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)); + CheckReady(); + + var request1 = new UpdateRoleRequest(role.Server.Id, role.Id) + { + Name = name ?? role.Name, + Permissions = (permissions ?? role.Permissions).RawValue, + Color = (color ?? role.Color).RawValue, + IsHoisted = isHoisted ?? role.IsHoisted + }; + + var response = await _rest.Send(request1).ConfigureAwait(false); + + if (position != null) + { + int oldPos = role.Position; + int newPos = position.Value; + int minPos; + Role[] roles = role.Server.Roles.OrderBy(x => x.Position).ToArray(); + + if (oldPos < newPos) //Moving Down + { + minPos = oldPos; + for (int i = oldPos; i < newPos; i++) + roles[i] = roles[i + 1]; + roles[newPos] = role; + } + else //(oldPos > newPos) Moving Up + { + minPos = newPos; + for (int i = oldPos; i > newPos; i--) + roles[i] = roles[i - 1]; + roles[newPos] = role; + } + + var request2 = new ReorderRolesRequest(role.Server.Id) + { + RoleIds = roles.Skip(minPos).Select(x => x.Id).ToArray(), + StartPos = minPos + }; + await _rest.Send(request2).ConfigureAwait(false); } } @@ -139,7 +155,7 @@ namespace Discord if (role == null) throw new ArgumentNullException(nameof(role)); CheckReady(); - try { await _api.DeleteRole(role.Server.Id, role.Id).ConfigureAwait(false); } + try { await _rest.Send(new DeleteRoleRequest(role.Server.Id, role.Id)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } @@ -150,7 +166,11 @@ namespace Discord if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer."); CheckReady(); - return _api.ReorderRoles(server.Id, roles.Select(x => x.Id), startPos); + return _rest.Send(new ReorderRolesRequest(server.Id) + { + RoleIds = roles.Select(x => x.Id).ToArray(), + StartPos = startPos + }); } } } \ No newline at end of file diff --git a/src/Discord.Net/DiscordClient.Servers.cs b/src/Discord.Net/DiscordClient.Servers.cs index 77b7f735e..395195173 100644 --- a/src/Discord.Net/DiscordClient.Servers.cs +++ b/src/Discord.Net/DiscordClient.Servers.cs @@ -1,3 +1,4 @@ +using Discord.API.Client.Rest; using Discord.Net; using System; using System.Collections.Generic; @@ -80,33 +81,41 @@ namespace Discord return _servers.Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); } - /// Creates a new server with the provided name and region (see Regions). - public async Task CreateServer(string name, Region region) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - if (region == null) throw new ArgumentNullException(nameof(region)); - CheckReady(); + /// Creates a new server with the provided name and region (see Regions). + public async Task CreateServer(string name, Region region, ImageType iconType = ImageType.None, Stream icon = null) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + if (region == null) throw new ArgumentNullException(nameof(region)); + CheckReady(); + + var request = new CreateGuildRequest() + { + Name = name, + Region = region.Id, + IconBase64 = Base64Image(iconType, icon, null) + }; + var response = await _rest.Send(request).ConfigureAwait(false); - var response = await _api.CreateServer(name, region.Id).ConfigureAwait(false); var server = _servers.GetOrAdd(response.Id); server.Update(response); return server; } - - /// Edits the provided server, changing only non-null attributes. - public async Task EditServer(Server server, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) - { - if (server == null) throw new ArgumentNullException(nameof(server)); - CheckReady(); - var response = await _api.EditServer( - server.Id, name: name ?? server.Name, - region: region ?? server.Region, - icon: icon, - iconType: iconType, - existingIcon: server.IconId, - afkChannelId: server.AFKChannel?.Id, - afkTimeout: server.AFKTimeout).ConfigureAwait(false); + /// Edits the provided server, changing only non-null attributes. + public async Task EditServer(Server server, string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png) + { + if (server == null) throw new ArgumentNullException(nameof(server)); + CheckReady(); + + var request = new UpdateGuildRequest(server.Id) + { + Name = name ?? server.Name, + Region = region ?? server.Region, + IconBase64 = Base64Image(iconType, icon, server.IconId), + AFKChannelId = server.AFKChannel?.Id, + AFKTimeout = server.AFKTimeout + }; + var response = await _rest.Send(request).ConfigureAwait(false); server.Update(response); } @@ -116,7 +125,7 @@ namespace Discord if (server == null) throw new ArgumentNullException(nameof(server)); CheckReady(); - try { await _api.LeaveServer(server.Id).ConfigureAwait(false); } + try { await _rest.Send(new LeaveGuildRequest(server.Id)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } @@ -124,8 +133,8 @@ namespace Discord { CheckReady(); - var regions = await _api.GetVoiceRegions().ConfigureAwait(false); - return regions.Select(x => new Region { Id = x.Id, Name = x.Name, Hostname = x.Hostname, Port = x.Port }); + var regions = await _rest.Send(new GetVoiceRegionsRequest()).ConfigureAwait(false); + return regions.Select(x => new Region(x.Id, x.Name, x.Hostname, x.Port)); } } } \ No newline at end of file diff --git a/src/Discord.Net/DiscordClient.Users.cs b/src/Discord.Net/DiscordClient.Users.cs index 91f8f5a7c..70bac9e4b 100644 --- a/src/Discord.Net/DiscordClient.Users.cs +++ b/src/Discord.Net/DiscordClient.Users.cs @@ -1,4 +1,5 @@ -using Discord.Net; +using Discord.API.Client.Rest; +using Discord.Net; using System; using System.Collections.Generic; using System.IO; @@ -201,23 +202,23 @@ namespace Discord return query; } - public Task EditUser(User user, bool? mute = null, bool? deaf = null, Channel voiceChannel = null, IEnumerable roles = null, EditMode rolesMode = EditMode.Set) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - if (user.IsPrivate) throw new InvalidOperationException("Unable to edit users in a private channel"); - CheckReady(); - - //Modify the roles collection and filter out the everyone role - IEnumerable roleIds = roles == null ? null : user.Roles - .Modify(roles, rolesMode) - .Where(x => !x.IsEveryone) - .Select(x => x.Id); - - var serverId = user.Server.Id; - return _api.EditUser(serverId, user.Id, - mute: mute, deaf: deaf, - voiceChannelId: voiceChannel?.Id, - roleIds: roleIds); + public Task EditUser(User user, bool? isMuted = null, bool? isDeafened = null, Channel voiceChannel = null, IEnumerable roles = null) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + if (user.IsPrivate) throw new InvalidOperationException("Unable to edit users in a private channel"); + CheckReady(); + + //Modify the roles collection and filter out the everyone role + var roleIds = roles == null ? null : user.Roles.Where(x => !x.IsEveryone) .Select(x => x.Id); + + var request = new UpdateMemberRequest(user.Server.Id, user.Id) + { + IsMuted = isMuted ?? user.IsServerMuted, + IsDeafened = isDeafened ?? user.IsServerDeafened, + VoiceChannelId = voiceChannel?.Id, + RoleIds = roleIds.ToArray() + }; + return _rest.Send(request); } public Task KickUser(User user) @@ -226,15 +227,18 @@ namespace Discord if (user.IsPrivate) throw new InvalidOperationException("Unable to kick users from a private channel"); CheckReady(); - return _api.KickUser(user.Server.Id, user.Id); + var request = new KickMemberRequest(user.Server.Id, user.Id); + return _rest.Send(request); } - public Task BanUser(User user) + public Task BanUser(User user, int pruneDays = 0) { if (user == null) throw new ArgumentNullException(nameof(user)); if (user.IsPrivate) throw new InvalidOperationException("Unable to ban users from a private channel"); CheckReady(); - return _api.BanUser(user.Server.Id, user.Id); + var request = new AddGuildBanRequest(user.Server.Id, user.Id); + request.PruneDays = pruneDays; + return _rest.Send(request); } public async Task UnbanUser(Server server, ulong userId) { @@ -242,7 +246,7 @@ namespace Discord if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); CheckReady(); - try { await _api.UnbanUser(server.Id, userId).ConfigureAwait(false); } + try { await _rest.Send(new RemoveGuildBanRequest(server.Id, userId)).ConfigureAwait(false); } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } } @@ -252,8 +256,13 @@ namespace Discord if (days <= 0) throw new ArgumentOutOfRangeException(nameof(days)); CheckReady(); - var response = await _api.PruneUsers(server.Id, days, simulate).ConfigureAwait(false); - return response.Pruned ?? 0; + var request = new PruneMembersRequest(server.Id) + { + Days = days, + IsSimulation = simulate + }; + var response = await _rest.Send(request).ConfigureAwait(false); + return response.Pruned; } /// When Config.UseLargeThreshold is enabled, running this command will request the Discord server to provide you with all offline users for a particular server. @@ -261,24 +270,36 @@ namespace Discord { if (server == null) throw new ArgumentNullException(nameof(server)); - _webSocket.SendRequestUsers(server.Id); + _webSocket.SendRequestMembers(server.Id, "", 0); } - public async Task EditProfile(string currentPassword = "", - string username = null, string email = null, string password = null, - Stream avatar = null, ImageType avatarType = ImageType.Png) - { - if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); - CheckReady(); + public async Task EditProfile(string currentPassword = "", + string username = null, string email = null, string password = null, + Stream avatar = null, ImageType avatarType = ImageType.Png) + { + if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); + CheckReady(); + + var request = new UpdateProfileRequest() + { + CurrentPassword = currentPassword, + Email = email ?? _currentUser?.Email, + Password = password, + Username = username ?? _privateUser?.Name, + AvatarBase64 = Base64Image(avatarType, avatar, _privateUser?.AvatarId) + }; - await _api.EditProfile(currentPassword: currentPassword, - username: username ?? _privateUser?.Name, email: email ?? _currentUser?.Email, password: password, - avatar: avatar, avatarType: avatarType, existingAvatar: _privateUser?.AvatarId).ConfigureAwait(false); + await _rest.Send(request).ConfigureAwait(false); if (password != null) { - var loginResponse = await _api.Login(_currentUser.Email, password).ConfigureAwait(false); - _api.Token = loginResponse.Token; + var loginRequest = new LoginRequest() + { + Email = _currentUser.Email, + Password = password + }; + var loginResponse = await _rest.Send(loginRequest).ConfigureAwait(false); + _rest.SetToken(loginResponse.Token); } } @@ -301,7 +322,7 @@ namespace Discord } private Task SendStatus() { - _webSocket.SendStatusUpdate(_status == UserStatus.Idle ? EpochTime.GetMilliseconds() - (10 * 60 * 1000) : (long?)null, _gameId); + _webSocket.SendUpdateStatus(_status == UserStatus.Idle ? EpochTime.GetMilliseconds() - (10 * 60 * 1000) : (long?)null, _gameId); return TaskHelper.CompletedTask; } } diff --git a/src/Discord.Net/API/Enums/ChannelType.cs b/src/Discord.Net/Enums/ChannelType.cs similarity index 69% rename from src/Discord.Net/API/Enums/ChannelType.cs rename to src/Discord.Net/Enums/ChannelType.cs index 7d787ae67..59c439a8e 100644 --- a/src/Discord.Net/API/Enums/ChannelType.cs +++ b/src/Discord.Net/Enums/ChannelType.cs @@ -3,9 +3,9 @@ public class ChannelType : StringEnum { /// A text-only channel. - public static readonly ChannelType Text = new ChannelType("text"); + public static ChannelType Text { get; } = new ChannelType("text"); /// A voice-only channel. - public static readonly ChannelType Voice = new ChannelType("voice"); + public static ChannelType Voice { get; } = new ChannelType("voice"); private ChannelType(string value) : base(value) { } @@ -17,18 +17,18 @@ case null: return null; case "text": - return ChannelType.Text; + return Text; case "voice": - return ChannelType.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) => a?._value == b?._value; - public static bool operator !=(ChannelType a, ChannelType b) => a?._value != b?._value; - public override bool Equals(object obj) => (obj as ChannelType)?._value == _value; - public override int GetHashCode() => _value.GetHashCode(); + public static bool operator ==(ChannelType a, ChannelType b) => a?.Value == b?.Value; + public static bool operator !=(ChannelType a, ChannelType b) => a?.Value != b?.Value; + public override bool Equals(object obj) => (obj as ChannelType)?.Value == Value; + public override int GetHashCode() => Value.GetHashCode(); } } diff --git a/src/Discord.Net/API/Enums/PermissionTarget.cs b/src/Discord.Net/Enums/PermissionTarget.cs similarity index 68% rename from src/Discord.Net/API/Enums/PermissionTarget.cs rename to src/Discord.Net/Enums/PermissionTarget.cs index d501dc72b..65c81bbc4 100644 --- a/src/Discord.Net/API/Enums/PermissionTarget.cs +++ b/src/Discord.Net/Enums/PermissionTarget.cs @@ -3,9 +3,9 @@ public class PermissionTarget : StringEnum { /// A text-only channel. - public static readonly PermissionTarget Role = new PermissionTarget("role"); + public static PermissionTarget Role { get; } = new PermissionTarget("role"); /// A voice-only channel. - public static readonly PermissionTarget User = new PermissionTarget("member"); + public static PermissionTarget User { get; } = new PermissionTarget("member"); private PermissionTarget(string value) : base(value) { } @@ -17,18 +17,18 @@ case null: return null; case "role": - return PermissionTarget.Role; + return Role; case "member": - return PermissionTarget.User; + return User; default: return new PermissionTarget(value); } } public static implicit operator PermissionTarget(string value) => FromString(value); - public static bool operator ==(PermissionTarget a, PermissionTarget b) => a?._value == b?._value; - public static bool operator !=(PermissionTarget a, PermissionTarget b) => a?._value != b?._value; - public override bool Equals(object obj) => (obj as PermissionTarget)?._value == _value; - public override int GetHashCode() => _value.GetHashCode(); + public static bool operator ==(PermissionTarget a, PermissionTarget b) => a?.Value == b?.Value; + public static bool operator !=(PermissionTarget a, PermissionTarget b) => a?.Value != b?.Value; + public override bool Equals(object obj) => (obj as PermissionTarget)?.Value == Value; + public override int GetHashCode() => Value.GetHashCode(); } } diff --git a/src/Discord.Net/Enums/StringEnum.cs b/src/Discord.Net/Enums/StringEnum.cs new file mode 100644 index 000000000..903bdfdba --- /dev/null +++ b/src/Discord.Net/Enums/StringEnum.cs @@ -0,0 +1,14 @@ +namespace Discord +{ + public abstract class StringEnum + { + public string Value { get; } + + protected StringEnum(string value) + { + Value = value; + } + + public override string ToString() => Value; + } +} diff --git a/src/Discord.Net/API/Enums/UserStatus.cs b/src/Discord.Net/Enums/UserStatus.cs similarity index 66% rename from src/Discord.Net/API/Enums/UserStatus.cs rename to src/Discord.Net/Enums/UserStatus.cs index 367a796a0..b2da3c8f5 100644 --- a/src/Discord.Net/API/Enums/UserStatus.cs +++ b/src/Discord.Net/Enums/UserStatus.cs @@ -3,11 +3,11 @@ public class UserStatus : StringEnum { /// User is currently online and active. - public static readonly UserStatus Online = new UserStatus("online"); + public static UserStatus Online { get; } = new UserStatus("online"); /// User is currently online but inactive. - public static readonly UserStatus Idle = new UserStatus("idle"); + public static UserStatus Idle { get; } = new UserStatus("idle"); /// User is offline. - public static readonly UserStatus Offline = new UserStatus("offline"); + public static UserStatus Offline { get; } = new UserStatus("offline"); private UserStatus(string value) : base(value) { } @@ -19,20 +19,20 @@ case null: return null; case "online": - return UserStatus.Online; + return Online; case "idle": - return UserStatus.Idle; + return Idle; case "offline": - return UserStatus.Offline; + return Offline; default: return new UserStatus(value); } } public static implicit operator UserStatus(string value) => FromString(value); - public static bool operator ==(UserStatus a, UserStatus b) => a?._value == b?._value; - public static bool operator !=(UserStatus a, UserStatus b) => a?._value != b?._value; - public override bool Equals(object obj) => (obj as UserStatus)?._value == _value; - public override int GetHashCode() => _value.GetHashCode(); + public static bool operator ==(UserStatus a, UserStatus b) => a?.Value == b?.Value; + public static bool operator !=(UserStatus a, UserStatus b) => a?.Value != b?.Value; + public override bool Equals(object obj) => (obj as UserStatus)?.Value == Value; + public override int GetHashCode() => Value.GetHashCode(); } } diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index b53231344..f72612d9a 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -1,8 +1,9 @@ -using Discord.API; +using Discord.API.Client; using Newtonsoft.Json; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using APIChannel = Discord.API.Client.Channel; namespace Discord { @@ -174,12 +175,12 @@ namespace Discord if (model.Type != null) Type = model.Type; } - internal void Update(ChannelInfo model) + internal void Update(APIChannel model) { Update(model as ChannelReference); if (model.Position != null) - Position = model.Position.Value; + Position = model.Position; if (model.Topic != null) Topic = model.Topic; diff --git a/src/Discord.Net/Models/GlobalUser.cs b/src/Discord.Net/Models/GlobalUser.cs index 11e870b32..e8b847d96 100644 --- a/src/Discord.Net/Models/GlobalUser.cs +++ b/src/Discord.Net/Models/GlobalUser.cs @@ -1,8 +1,8 @@ -using Discord.API; -using Newtonsoft.Json; +using Newtonsoft.Json; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using APIUser = Discord.API.Client.User; namespace Discord { @@ -52,7 +52,7 @@ namespace Discord //Don't need to clean _users - they're considered owned by server } - internal void Update(UserInfo model) + internal void Update(APIUser model) { if (model.Email != null) Email = model.Email; diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs index 2265c742b..5c5073d63 100644 --- a/src/Discord.Net/Models/Invite.cs +++ b/src/Discord.Net/Models/Invite.cs @@ -1,5 +1,6 @@ -using System; -using Discord.API; +using Discord.API.Client; +using System; +using APIInvite = Discord.API.Client.Invite; namespace Discord { @@ -42,7 +43,7 @@ namespace Discord /// Returns the unique identifier for this user's avatar. public string AvatarId { get; } /// Returns the full path to this user's avatar. - public string AvatarUrl => AvatarId != null ? Endpoints.UserAvatar(Id, AvatarId) : null; + public string AvatarUrl => User.GetAvatarUrl(Id, AvatarId); internal InviterInfo(ulong id, string name, ushort discriminator, string avatarId) { @@ -57,10 +58,8 @@ namespace Discord public ServerInfo Server { get; private set; } /// Returns information about the channel this invite is attached to. public ChannelInfo Channel { get; private set; } - /// Returns information about the user that created this invite. - public InviterInfo Inviter { get; private set; } - public string Id { get; } + public string Code { get; } /// Returns, if enabled, an alternative human-readable code for URLs. public string XkcdCode { get; } /// Time (in seconds) until the invite expires. Set to 0 to never expire. @@ -76,11 +75,11 @@ namespace Discord public DateTime CreatedAt { get; private set; } /// Returns a URL for this invite using XkcdCode if available or Id if not. - public string Url => API.Endpoints.InviteUrl(XkcdCode ?? Id.ToString()); + public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; - internal Invite(string code, string xkcdPass) + internal Invite(string code, string xkcdPass) { - Id = code; + Code = code; XkcdCode = xkcdPass; } @@ -90,10 +89,8 @@ namespace Discord Server = new ServerInfo(model.Guild.Id, model.Guild.Name); if (model.Channel != null) Channel = new ChannelInfo(model.Channel.Id, model.Channel.Name); - if (model.Inviter != null) - Inviter = new InviterInfo(model.Inviter.Id, model.Inviter.Username, model.Inviter.Discriminator.Value, model.Inviter.Avatar); } - internal void Update(InviteInfo model) + internal void Update(APIInvite model) { Update(model as InviteReference); @@ -111,8 +108,8 @@ namespace Discord CreatedAt = model.CreatedAt.Value; } - public override bool Equals(object obj) => obj is Invite && (obj as Invite).Id == Id; - public override int GetHashCode() => unchecked(Id.GetHashCode() + 9980); - public override string ToString() => XkcdCode ?? Id; + public override bool Equals(object obj) => obj is Invite && (obj as Invite).Code == Code; + public override int GetHashCode() => unchecked(Code.GetHashCode() + 9980); + public override string ToString() => XkcdCode ?? Code; } } diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index 62b56d073..625628d85 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -1,11 +1,10 @@ -using Discord.API; -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using MemberInfo = System.Reflection.MemberInfo; +using APIMessage = Discord.API.Client.Message; namespace Discord { @@ -193,7 +192,7 @@ namespace Discord _user.Unload(); } - internal void Update(MessageInfo model) + internal void Update(APIMessage model) { var channel = Channel; var server = channel.Server; diff --git a/src/Discord.Net/Models/Region.cs b/src/Discord.Net/Models/Region.cs index f37d6949e..903113899 100644 --- a/src/Discord.Net/Models/Region.cs +++ b/src/Discord.Net/Models/Region.cs @@ -1,10 +1,18 @@ namespace Discord { public sealed class Region - { - public string Hostname; - public int Port; - public string Id; - public string Name; + { + public string Id { get; } + public string Name { get; } + public string Hostname { get; } + public int Port { get; } + + internal Region(string id, string name, string hostname, int port) + { + Id = id; + Name = name; + Hostname = hostname; + Port = port; + } } } diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index b9cf8fb19..33028a083 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -1,8 +1,8 @@ -using Discord.API; -using Newtonsoft.Json; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; +using APIRole = Discord.API.Client.Role; namespace Discord { @@ -60,7 +60,7 @@ namespace Discord _server.Unload(); } - internal void Update(RoleInfo model) + internal void Update(APIRole model) { if (model.Name != null) Name = model.Name; diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index b326f0f06..f42b77cba 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -1,9 +1,10 @@ -using Discord.API; +using Discord.API.Client; using Newtonsoft.Json; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using APIGuild = Discord.API.Client.Guild; namespace Discord { @@ -36,7 +37,7 @@ namespace Discord /// Returns the unique identifier for this user's current avatar. public string IconId { get; private set; } /// Returns the URL to this user's current avatar. - public string IconUrl => IconId != null ? Endpoints.ServerIcon(Id, IconId) : null; + public string IconUrl => IconId != null ? $"{DiscordConfig.CDNUrl}/icons/{Id}/{IconId}.jpg" : null; /// Returns the user that first created this server. [JsonIgnore] @@ -144,7 +145,7 @@ namespace Discord Name = model.Name; } - internal void Update(GuildInfo model) + internal void Update(Guild model) { Update(model as GuildReference); @@ -172,9 +173,9 @@ namespace Discord _afkChannel.Id = model.AFKChannelId; //Can be null } - internal void Update(ExtendedGuildInfo model) + internal void Update(ExtendedGuild model) { - Update(model as GuildInfo); + Update(model as APIGuild); var channels = _client.Channels; foreach (var subModel in model.Channels) @@ -191,7 +192,7 @@ namespace Discord } foreach (var subModel in model.VoiceStates) { - var user = usersCache[subModel.UserId, Id]; + var user = usersCache[subModel.User.Id, Id]; if (user != null) user.Update(subModel); } diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index b98520013..c324ead94 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -1,8 +1,9 @@ -using Discord.API; +using Discord.API.Client; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; +using APIMember = Discord.API.Client.Member; namespace Discord { @@ -21,22 +22,25 @@ namespace Discord => UserId == other.UserId && ServerId == other.ServerId; public override int GetHashCode() => unchecked(ServerId.GetHashCode() + UserId.GetHashCode() + 23); - } + } - /// Returns a unique identifier combining this user's id with its server's. - internal CompositeKey UniqueId => new CompositeKey(_server.Id ?? 0, Id); + public static string GetAvatarUrl(ulong userId, string avatarId) => avatarId != null ? $"{DiscordConfig.CDNUrl}/avatars/{userId}/{avatarId}.jpg" : null; + + /// Returns a unique identifier combining this user's id with its server's. + internal CompositeKey UniqueId => new CompositeKey(_server.Id ?? 0, Id); /// Returns the name of this user on this server. public string Name { get; private set; } /// Returns a by-name unique identifier separating this user from others with the same name. public ushort Discriminator { get; private set; } /// Returns the unique identifier for this user's current avatar. public string AvatarId { get; private set; } - /// Returns the URL to this user's current avatar. - public string AvatarUrl => AvatarId != null ? Endpoints.UserAvatar(Id, AvatarId) : null; - /// Returns the datetime that this user joined this server. - public DateTime JoinedAt { get; private set; } + /// Returns the URL to this user's current avatar. + public string AvatarUrl => GetAvatarUrl(Id, AvatarId); + /// Returns the datetime that this user joined this server. + public DateTime JoinedAt { get; private set; } + - public bool IsSelfMuted { get; private set; } + public bool IsSelfMuted { get; private set; } public bool IsSelfDeafened { get; private set; } public bool IsServerMuted { get; private set; } public bool IsServerDeafened { get; private set; } @@ -183,7 +187,7 @@ namespace Discord if (model.Avatar != null) AvatarId = model.Avatar; } - internal void Update(MemberInfo model) + internal void Update(APIMember model) { if (model.User != null) Update(model.User); @@ -193,16 +197,16 @@ namespace Discord if (model.Roles != null) UpdateRoles(model.Roles.Select(x => _client.Roles[x])); } - internal void Update(ExtendedMemberInfo model) + internal void Update(ExtendedGuild.ExtendedMemberInfo model) { - Update(model as API.MemberInfo); + Update(model as APIMember); if (model.IsServerDeafened != null) IsServerDeafened = model.IsServerDeafened.Value; if (model.IsServerMuted != null) IsServerMuted = model.IsServerMuted.Value; } - internal void Update(PresenceInfo model) + internal void Update(MemberPresence model) { if (model.User != null) Update(model.User as UserReference); @@ -218,7 +222,7 @@ namespace Discord GameId = model.GameId; //Allows null } - internal void Update(VoiceMemberInfo model) + internal void Update(MemberVoiceState model) { if (model.IsServerDeafened != null) IsServerDeafened = model.IsServerDeafened.Value; diff --git a/src/Discord.Net/Net/Rest/RestClient.Events.cs b/src/Discord.Net/Net/Rest/RestClient.Events.cs index b569fd655..77c0d9969 100644 --- a/src/Discord.Net/Net/Rest/RestClient.Events.cs +++ b/src/Discord.Net/Net/Rest/RestClient.Events.cs @@ -2,7 +2,7 @@ namespace Discord.Net.Rest { - internal sealed partial class RestClient + public sealed partial class RestClient { public class RequestEventArgs : EventArgs { diff --git a/src/Discord.Net/Net/Rest/RestClient.cs b/src/Discord.Net/Net/Rest/RestClient.cs index 5b91f0804..c5466a1bf 100644 --- a/src/Discord.Net/Net/Rest/RestClient.cs +++ b/src/Discord.Net/Net/Rest/RestClient.cs @@ -2,13 +2,12 @@ using Newtonsoft.Json; using System; using System.Diagnostics; -using System.IO; using System.Threading; using System.Threading.Tasks; namespace Discord.Net.Rest { - internal sealed partial class RestClient + public sealed partial class RestClient { private readonly DiscordConfig _config; private readonly IRestEngine _engine; @@ -18,95 +17,69 @@ namespace Discord.Net.Rest { _config = config; #if !DOTNET5_4 - _engine = new RestSharpEngine(config, logger); + _engine = new RestSharpEngine(config, logger, DiscordConfig.ClientAPIUrl); #else //_engine = new BuiltInRestEngine(config, logger); #endif } public void SetToken(string token) => _engine.SetToken(token); + public void SetCancelToken(CancellationToken token) => _cancelToken = token; - //DELETE - internal Task Delete(string path, object data) where ResponseT : class - => Send("DELETE", path, data); - internal Task Delete(string path) where ResponseT : class - => Send("DELETE", path); - internal Task Delete(string path, object data) - => Send("DELETE", path, data); - internal Task Delete(string path) - => Send("DELETE", path); - - //GET - internal Task Get(string path) where ResponseT : class - => Send("GET", path); - internal Task Get(string path) - => Send("GET", path); - - //PATCH - internal Task Patch(string path, object data) where ResponseT : class - => Send("PATCH", path, data); - internal Task Patch(string path, object data) - => Send("PATCH", path, data); - - internal Task Post(string path, object data) where ResponseT : class - => Send("POST", path, data); - internal Task Post(string path) where ResponseT : class - => Send("POST", path); - internal Task Post(string path, object data) - => Send("POST", path, data); - internal Task Post(string path) - => Send("POST", path); - - internal Task Put(string path, object data) where ResponseT : class - => Send("PUT", path, data); - internal Task Put(string path) where ResponseT : class - => Send("PUT", path); - internal Task Put(string path, object data) - => Send("PUT", path, data); - internal Task Put(string path) - => Send("PUT", path); - - internal Task PostFile(string path, string filename, Stream stream) where ResponseT : class - => SendFile("POST", path, filename, stream); - internal Task PostFile(string path, string filename, Stream stream) - => SendFile("POST", path, filename, stream); - - internal Task PutFile(string path, string filename, Stream stream) where ResponseT : class - => SendFile("PUT", path, filename, stream); - internal Task PutFile(string path, string filename, Stream stream) - => SendFile("PUT", path, filename, stream); - - private async Task Send(string method, string path, object content = null) + public async Task Send(IRestRequest request) where ResponseT : class { - string responseJson = await Send(method, path, content, true).ConfigureAwait(false); - return DeserializeResponse(responseJson); + if (request == null) throw new ArgumentNullException(nameof(request)); + + string responseJson = await Send(request, true).ConfigureAwait(false); + return DeserializeResponse(responseJson); } - private Task Send(string method, string path, object content = null) - => Send(method, path, content, false); - private async Task Send(string method, string path, object content, bool hasResponse) - { - Stopwatch stopwatch = null; - string requestJson = null; - if (content != null) - requestJson = JsonConvert.SerializeObject(content); + public Task Send(IRestRequest request) + { + if (request == null) throw new ArgumentNullException(nameof(request)); + + return Send(request, false); + } - if (_config.LogLevel >= LogSeverity.Verbose) + public async Task Send(IRestFileRequest request) + where ResponseT : class + { + if (request == null) throw new ArgumentNullException(nameof(request)); + + string requestJson = JsonConvert.SerializeObject(request.Payload); + string responseJson = await SendFile(request, true).ConfigureAwait(false); + return DeserializeResponse(responseJson); + } + public Task Send(IRestFileRequest request) + { + if (request == null) throw new ArgumentNullException(nameof(request)); + + return SendFile(request, false); + } + + private async Task Send(IRestRequest request, bool hasResponse) + { + var method = request.Method; + var path = request.Endpoint; + object payload = request.Payload; + var isPrivate = request.IsPrivate; + + string requestJson = null; + if (payload != null) + requestJson = JsonConvert.SerializeObject(payload); + + Stopwatch stopwatch = null; + if (_config.LogLevel >= LogSeverity.Verbose) stopwatch = Stopwatch.StartNew(); - - string responseJson = await _engine.Send(method, path, requestJson, _cancelToken).ConfigureAwait(false); -#if TEST_RESPONSES - if (!hasResponse && !string.IsNullOrEmpty(responseJson)) - throw new Exception("API check failed: Response is not empty."); -#endif + string responseJson = await _engine.Send(method, path, requestJson, _cancelToken).ConfigureAwait(false); if (_config.LogLevel >= LogSeverity.Verbose) { stopwatch.Stop(); - if (content != null && _config.LogLevel >= LogSeverity.Debug) + if (payload != null && _config.LogLevel >= LogSeverity.Debug) { - if (path.StartsWith(Endpoints.Auth)) + if (isPrivate) RaiseOnRequest(method, path, "[Hidden]", stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); else RaiseOnRequest(method, path, requestJson, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); @@ -117,33 +90,25 @@ namespace Discord.Net.Rest return responseJson; } - - private async Task SendFile(string method, string path, string filename, Stream stream) - where ResponseT : class - { - string responseJson = await SendFile(method, path, filename, stream, true).ConfigureAwait(false); - return DeserializeResponse(responseJson); - } - private Task SendFile(string method, string path, string filename, Stream stream) - => SendFile(method, path, filename, stream, false); - private async Task SendFile(string method, string path, string filename, Stream stream, bool hasResponse) + + private async Task SendFile(IRestFileRequest request, bool hasResponse) { - Stopwatch stopwatch = null; + var method = request.Method; + var path = request.Endpoint; + var filename = request.Filename; + var stream = request.Stream; + var isPrivate = request.IsPrivate; + Stopwatch stopwatch = null; if (_config.LogLevel >= LogSeverity.Verbose) stopwatch = Stopwatch.StartNew(); string responseJson = await _engine.SendFile(method, path, filename, stream, _cancelToken).ConfigureAwait(false); -#if TEST_RESPONSES - if (!hasResponse && !string.IsNullOrEmpty(responseJson)) - throw new Exception("API check failed: Response is not empty."); -#endif - if (_config.LogLevel >= LogSeverity.Verbose) { stopwatch.Stop(); - if (_config.LogLevel >= LogSeverity.Debug) + if (_config.LogLevel >= LogSeverity.Debug && !isPrivate) RaiseOnRequest(method, path, filename, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); else RaiseOnRequest(method, path, null, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); @@ -169,7 +134,5 @@ namespace Discord.Net.Rest return JsonConvert.DeserializeObject(json); #endif } - - internal void SetCancelToken(CancellationToken token) => _cancelToken = token; } } diff --git a/src/Discord.Net/Net/Rest/SharpRestEngine.cs b/src/Discord.Net/Net/Rest/SharpRestEngine.cs index 1a92527dc..70c3d143d 100644 --- a/src/Discord.Net/Net/Rest/SharpRestEngine.cs +++ b/src/Discord.Net/Net/Rest/SharpRestEngine.cs @@ -18,12 +18,12 @@ namespace Discord.Net.Rest private readonly object _rateLimitLock; private DateTime _rateLimitTime; - public RestSharpEngine(DiscordConfig config, Logger logger) + public RestSharpEngine(DiscordConfig config, Logger logger, string baseUrl) { _config = config; _logger = logger; _rateLimitLock = new object(); - _client = new RestSharp.RestClient(Endpoints.BaseApi) + _client = new RestSharp.RestClient(baseUrl) { PreAuthenticate = false, ReadWriteTimeout = _config.RestTimeout, diff --git a/src/Discord.Net/Net/WebSockets/GatewaySocket.cs b/src/Discord.Net/Net/WebSockets/GatewaySocket.cs index e2b44a5fd..46b80ebe7 100644 --- a/src/Discord.Net/Net/WebSockets/GatewaySocket.cs +++ b/src/Discord.Net/Net/WebSockets/GatewaySocket.cs @@ -1,4 +1,5 @@ -using Discord.API; +using Discord.API.Client; +using Discord.API.Client.GatewaySocket; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -76,10 +77,10 @@ namespace Discord.Net.WebSockets if (msg.Sequence.HasValue) _lastSeq = msg.Sequence.Value; - var opCode = (GatewayOpCodes)msg.Operation; + var opCode = (OpCodes)msg.Operation; switch (opCode) { - case GatewayOpCodes.Dispatch: + case OpCodes.Dispatch: { JToken token = msg.Payload as JToken; if (msg.Type == "READY") @@ -90,7 +91,7 @@ namespace Discord.Net.WebSockets } else if (msg.Type == "RESUMED") { - var payload = token.ToObject(_serializer); + var payload = token.ToObject(_serializer); _heartbeatInterval = payload.HeartbeatInterval; } RaiseReceivedDispatch(msg.Type, token); @@ -98,7 +99,7 @@ namespace Discord.Net.WebSockets EndConnect(); //Complete the connect } break; - case GatewayOpCodes.Redirect: + case OpCodes.Redirect: { var payload = (msg.Payload as JToken).ToObject(_serializer); if (payload.Url != null) @@ -117,57 +118,32 @@ namespace Discord.Net.WebSockets } } - public void SendIdentify() - { - var msg = new IdentifyCommand(); - msg.Payload.Token = _client.Token; - msg.Payload.Properties["$device"] = "Discord.Net"; - if (_client.Config.UseLargeThreshold) - msg.Payload.LargeThreshold = 100; - msg.Payload.Compress = true; - QueueMessage(msg); - } - - public void SendResume() - { - var msg = new ResumeCommand(); - msg.Payload.SessionId = _sessionId; - msg.Payload.Sequence = _lastSeq; - QueueMessage(msg); - } - - public override void SendHeartbeat() - { - QueueMessage(new HeartbeatCommand()); - } - - public void SendStatusUpdate(long? idleSince, int? gameId) - { - var msg = new StatusUpdateCommand(); - msg.Payload.IdleSince = idleSince; - msg.Payload.GameId = gameId; - QueueMessage(msg); - } - - public void SendJoinVoice(ulong serverId, ulong channelId) - { - var msg = new JoinVoiceCommand(); - msg.Payload.ServerId = serverId; - msg.Payload.ChannelId = channelId; - QueueMessage(msg); - } - public void SendLeaveVoice(ulong serverId) - { - var msg = new JoinVoiceCommand(); - msg.Payload.ServerId = serverId; + public void SendIdentify() + { + var props = new Dictionary + { + ["$device"] = "Discord.Net" + }; + var msg = new IdentifyCommand() + { + Version = 3, + Token = _client.Token, + Properties = props, + LargeThreshold = _client.Config.UseLargeThreshold ? 100 : (int?)null, + UseCompression = true + }; QueueMessage(msg); } - public void SendRequestUsers(ulong serverId, string query = "", int limit = 0) - { - var msg = new GetUsersCommand(); - msg.Payload.ServerId = serverId; - QueueMessage(msg); - } + public void SendResume() + => QueueMessage(new ResumeCommand { SessionId = _sessionId, Sequence = _lastSeq }); + public override void SendHeartbeat() + => QueueMessage(new HeartbeatCommand()); + public void SendUpdateStatus(long? idleSince, int? gameId) + => QueueMessage(new UpdateStatusCommand { IdleSince = idleSince, GameId = gameId }); + public void SendUpdateVoice(ulong serverId, ulong channelId, bool isSelfMuted, bool isSelfDeafened) + => QueueMessage(new UpdateVoiceCommand { GuildId = serverId, ChannelId = channelId, IsSelfMuted = isSelfMuted, IsSelfDeafened = isSelfDeafened }); + public void SendRequestMembers(ulong serverId, string query, int limit) + => QueueMessage(new RequestMembersCommand { GuildId = serverId, Query = query, Limit = limit }); } } diff --git a/src/Discord.Net/Net/WebSockets/WebSocket.cs b/src/Discord.Net/Net/WebSockets/WebSocket.cs index e4708dbb7..49db00e11 100644 --- a/src/Discord.Net/Net/WebSockets/WebSocket.cs +++ b/src/Discord.Net/Net/WebSockets/WebSocket.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using Discord.API.Client; +using Newtonsoft.Json; using System; using System.IO; using System.IO.Compression; @@ -162,9 +163,9 @@ namespace Discord.Net.WebSockets _logger.Debug( $"In: {json}"); return TaskHelper.CompletedTask; } - protected void QueueMessage(object message) + protected void QueueMessage(IWebSocketMessage message) { - string json = JsonConvert.SerializeObject(message); + string json = JsonConvert.SerializeObject(new WebSocketMessage(message)); if (_logger.Level >= LogSeverity.Debug) _logger.Debug( $"Out: " + json); _engine.QueueMessage(json); diff --git a/src/Discord.Net/Optional.cs b/src/Discord.Net/Optional.cs new file mode 100644 index 000000000..8088691a5 --- /dev/null +++ b/src/Discord.Net/Optional.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Discord +{ + /*public struct Optional + { + public bool HasValue { get; } + public T Value { get; } + + public Optional(T value) + { + HasValue = true; + Value = value; + } + + public static implicit operator Optional(T value) => new Optional(value); + public static bool operator ==(Optional a, Optional b) => + a.HasValue == b.HasValue && EqualityComparer.Default.Equals(a.Value, b.Value); + public static bool operator !=(Optional a, Optional b) => + a.HasValue != b.HasValue || EqualityComparer.Default.Equals(a.Value, b.Value); + public override bool Equals(object obj) => + this == ((Optional)obj); + public override int GetHashCode() => + unchecked(HasValue.GetHashCode() + Value?.GetHashCode() ?? 0); + + public override string ToString() => Value?.ToString() ?? "null"; + }*/ +}