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";
+ }*/
+}