diff --git a/TODO b/TODO
new file mode 100644
index 000000000..43a38094d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,32 @@
+- REST
+ - Models
+ - Preconditions
+ - Endpoints
+ - Channel
+ - Emoji
+ - Guild
+ - Invite
+ - User
+ - Voice
+ - Webhook
+- Gateway
+ - Models
+ - Client
+ - Socket
+ * Receive
+ * Compression
+ - Voice (long)
+- Core
+ - CDN
+ - Datastore
+ - Entities
+ - Channel
+ - Emoji
+ - Guild
+ - User
+- Tests
+ - Unit test Gateway stability / deadlockability?
+- Extensions
+ - Commands
+ ? design - use finite's or quahu's
+ - Interactivity
diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj
index 260238f05..806d2361b 100644
--- a/src/Discord.Net/Discord.Net.csproj
+++ b/src/Discord.Net/Discord.Net.csproj
@@ -4,6 +4,7 @@
netstandard2.1
8.0
enable
+ Discord
diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs
new file mode 100644
index 000000000..7fdd37045
--- /dev/null
+++ b/src/Discord.Net/DiscordClient.cs
@@ -0,0 +1,25 @@
+using Discord.Rest;
+using Discord.Socket;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Discord
+{
+ internal class DiscordClient : IDiscordClient
+ {
+ public DiscordRestApi Rest => _restApi;
+ public DiscordGatewayApi Gateway => _gatewayApi;
+
+ private readonly DiscordConfig _config;
+ private readonly DiscordRestApi _restApi;
+ private readonly DiscordGatewayApi _gatewayApi;
+
+ public DiscordClient(DiscordConfig config, DiscordRestApi restApi, DiscordGatewayApi gatewayApi)
+ {
+ _config = config;
+ _restApi = restApi;
+ _gatewayApi = gatewayApi;
+ }
+ }
+}
diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net/DiscordConfig.cs
new file mode 100644
index 000000000..2fb4de090
--- /dev/null
+++ b/src/Discord.Net/DiscordConfig.cs
@@ -0,0 +1,33 @@
+using Discord.Socket;
+using Discord.Socket.Providers;
+using System;
+
+namespace Discord
+{
+ public class DiscordConfig
+ {
+ ///
+ /// Discord.Net version
+ ///
+ public const string Version = "3.0.0a0";
+ ///
+ /// Discord.Net User-Agent
+ ///
+ public const string UserAgent = "DiscordBot (https://github.com/discord-net/Discord.Net, " + Version + ")";
+
+ ///
+ /// The default, fallback Gateway URI. This will generally be replaced by .
+ ///
+ public static readonly Uri DefaultGatewayUri = new Uri("wss://gateway.discord.gg");
+ ///
+ /// The base URL for the Rest API.
+ ///
+ public string RestApiUrl { get; set; } = "https://discordapp.com/api/v6/";
+ ///
+ /// The URI to use when connecting to the gateway. If specified, this will override the URI Discord instructs us to use.
+ ///
+ public Uri? GatewayUri = null;
+
+ public SocketFactory SocketFactory { get; set; } = DefaultSocketFactory.Create;
+ }
+}
diff --git a/src/Discord.Net/IDiscordClient.cs b/src/Discord.Net/IDiscordClient.cs
new file mode 100644
index 000000000..913726676
--- /dev/null
+++ b/src/Discord.Net/IDiscordClient.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Discord.Rest;
+using Discord.Socket;
+
+namespace Discord
+{
+ internal interface IDiscordClient
+ {
+ static IDiscordClient Create(DiscordConfig config)
+ {
+ var rest = new DiscordRestApi(config);
+ var gateway = new DiscordGatewayApi(config);
+
+ return new DiscordClient(config, rest, gateway);
+ }
+
+ DiscordRestApi Rest { get; }
+ DiscordGatewayApi Gateway { get; }
+ }
+}
diff --git a/src/Discord.Net/Rest/DiscordRestApi.cs b/src/Discord.Net/Rest/DiscordRestApi.cs
new file mode 100644
index 000000000..052d4adc4
--- /dev/null
+++ b/src/Discord.Net/Rest/DiscordRestApi.cs
@@ -0,0 +1,28 @@
+using System.Text.Json;
+using System.Threading.Tasks;
+using Refit;
+using Discord.Rest.Models;
+
+// This is essentially a reimplementation of Wumpus.Net.Rest
+namespace Discord.Rest
+{
+ public class DiscordRestApi : IDiscordRestApi
+ {
+ private readonly IDiscordRestApi _api;
+
+ public DiscordRestApi(DiscordConfig config)
+ {
+ var jsonOptions = new JsonSerializerOptions();
+ var refitSettings = new RefitSettings
+ {
+ ContentSerializer = new JsonContentSerializer(jsonOptions),
+ };
+ _api = RestService.For(config.RestApiUrl, refitSettings);
+ }
+
+ public Task GetGatewayInfoAsync()
+ {
+ return _api.GetGatewayInfoAsync();
+ }
+ }
+}
diff --git a/src/Discord.Net/Rest/IDiscordRestApi.cs b/src/Discord.Net/Rest/IDiscordRestApi.cs
new file mode 100644
index 000000000..3e58c1ce3
--- /dev/null
+++ b/src/Discord.Net/Rest/IDiscordRestApi.cs
@@ -0,0 +1,12 @@
+using System.Threading.Tasks;
+using Refit;
+using Discord.Rest.Models;
+
+namespace Discord.Rest
+{
+ public interface IDiscordRestApi
+ {
+ [Get("/gateway/bot")]
+ Task GetGatewayInfoAsync();
+ }
+}
diff --git a/src/Discord.Net/Rest/JsonContentSerializer.cs b/src/Discord.Net/Rest/JsonContentSerializer.cs
new file mode 100644
index 000000000..dbbf0f0f4
--- /dev/null
+++ b/src/Discord.Net/Rest/JsonContentSerializer.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+using Refit;
+
+// https://blog.martincostello.com/refit-and-system-text-json/
+
+namespace Discord.Rest
+{
+ public class JsonContentSerializer : IContentSerializer
+ {
+ private static readonly MediaTypeHeaderValue _jsonMediaType = new MediaTypeHeaderValue("application/json") { CharSet = Encoding.UTF8.WebName };
+ private readonly JsonSerializerOptions _serializerOptions;
+
+ public JsonContentSerializer(JsonSerializerOptions serializerOptions)
+ {
+ _serializerOptions = serializerOptions;
+ }
+
+ public async Task DeserializeAsync(HttpContent content)
+ {
+ using var json = await content.ReadAsStreamAsync().ConfigureAwait(false);
+ return await JsonSerializer.DeserializeAsync(json, _serializerOptions).ConfigureAwait(false);
+ }
+
+ public async Task SerializeAsync(T data)
+ {
+ var stream = new MemoryStream();
+ try
+ {
+ await JsonSerializer.SerializeAsync(stream, data, _serializerOptions).ConfigureAwait(false);
+ await stream.FlushAsync();
+
+ var content = new StreamContent(stream);
+ content.Headers.ContentType = _jsonMediaType;
+
+ return content;
+ }
+ catch
+ {
+ await stream.DisposeAsync().ConfigureAwait(false);
+ throw;
+ }
+ }
+ }
+}
diff --git a/src/Discord.Net/Rest/Models/GatewayInfo.cs b/src/Discord.Net/Rest/Models/GatewayInfo.cs
new file mode 100644
index 000000000..e1497be06
--- /dev/null
+++ b/src/Discord.Net/Rest/Models/GatewayInfo.cs
@@ -0,0 +1,25 @@
+#pragma warning disable CS8618 // Uninitialized NRT expected in models
+using System.Text.Json.Serialization;
+
+namespace Discord.Rest.Models
+{
+ public class GatewayInfo
+ {
+ [JsonPropertyName("url")]
+ public string Url { get; set; }
+ [JsonPropertyName("shards")]
+ public int Shards { get; set; }
+ [JsonPropertyName("session_start_limit")]
+ public GatewaySessionStartInfo SessionStartInfo { get; set; }
+ }
+
+ public class GatewaySessionStartInfo
+ {
+ [JsonPropertyName("total")]
+ public int Total { get; set; }
+ [JsonPropertyName("remaining")]
+ public int Remaining { get; set; }
+ [JsonPropertyName("reset_after")]
+ public int ResetAfter { get; set; }
+ }
+}
diff --git a/src/Discord.Net/Rest/Requests/Test.cs b/src/Discord.Net/Rest/Requests/Test.cs
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/Discord.Net/Socket/Gateway.cs b/src/Discord.Net/Socket/DiscordGatewayApi.cs
similarity index 77%
rename from src/Discord.Net/Socket/Gateway.cs
rename to src/Discord.Net/Socket/DiscordGatewayApi.cs
index df4e1ab6d..ad2305d29 100644
--- a/src/Discord.Net/Socket/Gateway.cs
+++ b/src/Discord.Net/Socket/DiscordGatewayApi.cs
@@ -2,17 +2,17 @@ using System;
using System.Threading;
using System.Threading.Tasks;
-namespace Discord.Net.Socket
+namespace Discord.Socket
{
- public class Gateway
+ public class DiscordGatewayApi
{
static readonly Uri DefaultGatewayUri = new Uri("wss://gateway.discord.gg");
ISocket Socket { get; set; }
- public Gateway(SocketFactory socketFactory)
+ public DiscordGatewayApi(DiscordConfig config)
{
- Socket = socketFactory(OnAborted, OnPacket);
+ Socket = config.SocketFactory(OnAborted, OnPacket);
}
public async Task ConnectAsync(Uri? gatewayUri)
diff --git a/src/Discord.Net/Socket/ISocket.cs b/src/Discord.Net/Socket/ISocket.cs
index 0c65e0828..98a0eacca 100644
--- a/src/Discord.Net/Socket/ISocket.cs
+++ b/src/Discord.Net/Socket/ISocket.cs
@@ -2,7 +2,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
-namespace Discord.Net.Socket
+namespace Discord.Socket
{
public delegate ISocket SocketFactory(OnAbortionHandler abortionHandler, OnPacketHandler packetHandler);
diff --git a/src/Discord.Net/Socket/Providers/DefaultSocket.cs b/src/Discord.Net/Socket/Providers/DefaultSocket.cs
index c4501d2a5..66fab72ce 100644
--- a/src/Discord.Net/Socket/Providers/DefaultSocket.cs
+++ b/src/Discord.Net/Socket/Providers/DefaultSocket.cs
@@ -3,7 +3,7 @@ using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
-namespace Discord.Net.Socket.Providers
+namespace Discord.Socket.Providers
{
public static class DefaultSocketFactory
{