Skip to content

Commit 0cf14da

Browse files
committed
v3 api: first pass
1 parent 3ca31cc commit 0cf14da

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1356
-698
lines changed

Discord.Net.sln

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26124.0
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29021.104
55
MinimumVisualStudioVersion = 15.0.26124.0
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{424AA4CA-E283-4B83-B288-B0181516D1F9}"
77
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{279F3738-D35E-402B-BC99-CBE44821C468}"
9+
EndProject
810
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Tests.Unit", "test\Discord.Tests.Unit\Discord.Tests.Unit.csproj", "{97B3208E-FBB3-43D8-8944-EE06F6FE4032}"
911
EndProject
1012
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Tests.Integration", "test\Discord.Tests.Integration\Discord.Tests.Integration.csproj", "{D2A17BA6-C6A5-42DB-A4BC-F97C07904BA2}"
1113
EndProject
12-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{279F3738-D35E-402B-BC99-CBE44821C468}"
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net", "src\Discord.Net\Discord.Net.csproj", "{65F1C45D-6D82-4E24-9F56-2840D2E6EFB9}"
15+
EndProject
16+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{942F56B3-26D5-428A-AE1A-37480D26B516}"
1317
EndProject
14-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net", "src\Discord.Net\Discord.Net.csproj", "{65F1C45D-6D82-4E24-9F56-2840D2E6EFB9}"
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "examples\0_Using_the_Client\Sample.csproj", "{E8E39B45-F718-443B-B6BD-2ED092047B57}"
1519
EndProject
1620
Global
1721
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -59,6 +63,18 @@ Global
5963
{65F1C45D-6D82-4E24-9F56-2840D2E6EFB9}.Release|x64.Build.0 = Release|Any CPU
6064
{65F1C45D-6D82-4E24-9F56-2840D2E6EFB9}.Release|x86.ActiveCfg = Release|Any CPU
6165
{65F1C45D-6D82-4E24-9F56-2840D2E6EFB9}.Release|x86.Build.0 = Release|Any CPU
66+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Debug|x64.ActiveCfg = Debug|Any CPU
69+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Debug|x64.Build.0 = Debug|Any CPU
70+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Debug|x86.ActiveCfg = Debug|Any CPU
71+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Debug|x86.Build.0 = Debug|Any CPU
72+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Release|Any CPU.ActiveCfg = Release|Any CPU
73+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Release|x64.ActiveCfg = Release|Any CPU
75+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Release|x64.Build.0 = Release|Any CPU
76+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Release|x86.ActiveCfg = Release|Any CPU
77+
{E8E39B45-F718-443B-B6BD-2ED092047B57}.Release|x86.Build.0 = Release|Any CPU
6278
EndGlobalSection
6379
GlobalSection(SolutionProperties) = preSolution
6480
HideSolutionNode = FALSE
@@ -67,6 +83,7 @@ Global
6783
{97B3208E-FBB3-43D8-8944-EE06F6FE4032} = {424AA4CA-E283-4B83-B288-B0181516D1F9}
6884
{D2A17BA6-C6A5-42DB-A4BC-F97C07904BA2} = {424AA4CA-E283-4B83-B288-B0181516D1F9}
6985
{65F1C45D-6D82-4E24-9F56-2840D2E6EFB9} = {279F3738-D35E-402B-BC99-CBE44821C468}
86+
{E8E39B45-F718-443B-B6BD-2ED092047B57} = {942F56B3-26D5-428A-AE1A-37480D26B516}
7087
EndGlobalSection
7188
GlobalSection(ExtensibilityGlobals) = postSolution
7289
SolutionGuid = {1F07E0CE-26FE-4660-A0C3-4F9CC2BD7B72}

README.md

+17-30
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,27 @@
11
# Discord.Net
2-
[![NuGet](https://img.shields.io/nuget/vpre/Discord.Net.svg?maxAge=2592000?style=plastic)](https://www.nuget.org/packages/Discord.Net)
3-
[![MyGet](https://img.shields.io/myget/discord-net/vpre/Discord.Net.svg)](https://www.myget.org/feed/Packages/discord-net)
4-
[![Build status](https://ci.appveyor.com/api/projects/status/5sb7n8a09w9clute/branch/dev?svg=true)](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev)
5-
[![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/jkrBmQR)
62

7-
An unofficial .NET API Wrapper for the Discord client (http://discordapp.com).
3+
An unofficial .NET API Wrapper for the Discord client (https://discordapp.com).
84

9-
Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/jkrBmQR).
5+
This branch is an unstable reference design of the next major release, Discord.Net v3. Currently, this branch
6+
does not offer any functionality, and is only being used for API design.
107

11-
## Installation
12-
### Stable (NuGet)
13-
Our stable builds available from NuGet through the Discord.Net metapackage:
14-
- [Discord.Net](https://www.nuget.org/packages/Discord.Net/)
8+
## Contributing
159

16-
The individual components may also be installed from NuGet:
17-
- [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/)
18-
- [Discord.Net.Rest](https://www.nuget.org/packages/Discord.Net.Rest/)
19-
- [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/)
20-
- [Discord.Net.Webhook](https://www.nuget.org/packages/Discord.Net.Webhook/)
10+
This branch is being developed on preview releases of .NET Core and Visual Studio; the following features
11+
are necessary to contribute:
12+
- C# 8 with Nullable Reference Types and IAsyncEnumerable
13+
- .NET Core 3 with ValueTask support
2114

22-
### Unstable (MyGet)
23-
Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`).
15+
The following configurations are known to work:
16+
- Visual Studio 2019 16.2 Preview 3
17+
- .NET Core SDK 3.0.100-preview6-012264
2418

25-
## Compiling
26-
In order to compile Discord.Net, you require the following:
19+
### Documentation
2720

28-
### Using Visual Studio
29-
- [Visual Studio 2017](https://www.microsoft.com/net/core#windowsvs2017)
30-
- [.NET Core SDK](https://www.microsoft.com/net/download/core)
21+
Documentation Strings are currently being left out, primarily because I find them extremely distracting when
22+
trying to read over long interfaces, but also because this design will likely be iterated on before being finalized,
23+
and it would be annoying to have to move docstrings around.
3124

32-
The .NET Core workload must be selected during Visual Studio installation.
25+
Please leave docstrings out of contributions until the implementation round is finished.
3326

34-
### Using Command Line
35-
- [.NET Core SDK](https://www.microsoft.com/net/download/core)
36-
37-
## Known Issues
38-
39-
### WebSockets (Win7 and earlier)
40-
.NET Core 1.1 does not support WebSockets on Win7 and earlier. This issue has been fixed since the release of .NET Core 2.1. It is recommended to target .NET Core 2.1 or above for your project if you wish to run your bot on legacy platforms; alternatively, you may choose to install the [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) package.
27+
Usability documentation is being provided in sample solutions.
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Discord;
4+
5+
namespace Sample
6+
{
7+
public class Program
8+
{
9+
static void Main()
10+
=> new Program().MainAsync().GetAwaiter().GetResult();
11+
public async Task MainAsync()
12+
{
13+
string token = "abc";
14+
var client = DiscordClientBuilder.FromConfig(new DiscordClientConfig
15+
{
16+
Token = token,
17+
DefaultStateBehavior = StateBehavior.SyncOnly
18+
});
19+
await client.StartAndWaitAsync();
20+
21+
client.MessageCreated += msg => Task.Run(() => OnMessageCreated(msg)).Observe();
22+
23+
await Task.Delay(-1);
24+
}
25+
public async Task OnMessageCreated(IMessage msg)
26+
{
27+
if (!(msg is IUserMessage message))
28+
return;
29+
var guild = await msg.GetGuildAsync();
30+
if (guild == null)
31+
return;
32+
var owner = await guild.GetOwnerAsync();
33+
var dm = await (await owner.GetUserAsync()).GetOrCreateDMChannelAsync();
34+
await dm.SendMessageAsync($"{msg.Author} said: {msg.Content}");
35+
}
36+
}
37+
38+
public static class TaskExtensions
39+
{
40+
public static void Observe(this Task task)
41+
{
42+
task.ContinueWith(t =>
43+
{
44+
if (t.IsFaulted)
45+
{
46+
var flattened = t.Exception.Flatten();
47+
foreach (var ex in flattened.InnerExceptions)
48+
Console.WriteLine(ex.ToString());
49+
}
50+
});
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
</Project>

src/Discord.Net/Discord.Net.csproj

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
5-
<LangVersion>7.3</LangVersion>
4+
<TargetFramework>netcoreapp3.0</TargetFramework> <!-- TODO: netstandard3/.NET 5/whatever is mainstream when we release -->
5+
<LangVersion>8.0</LangVersion>
6+
<RootNamespace>Discord</RootNamespace>
7+
<Nullable>Enable</Nullable>
68
</PropertyGroup>
79

810
<ItemGroup>
11+
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.2" />
912
<PackageReference Include="Wumpus.Net.Gateway" Version="0.2.2-build-00031" />
1013
<PackageReference Include="Wumpus.Net.Rest" Version="0.2.2-build-00031" />
1114
</ItemGroup>
1215

16+
<ItemGroup>
17+
<Folder Include="Models\Emotes\" />
18+
<Folder Include="Models\Guilds\" />
19+
<Folder Include="Models\Roles\" />
20+
</ItemGroup>
21+
1322
</Project>

src/Discord.Net/DiscordClient.cs

+38-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ namespace Discord
99
internal class DiscordClient : IDiscordClient, IDisposable
1010
{
1111
public event Action Ready;
12+
public event Action Connected;
13+
public event Action Disconnected;
14+
public event Action<IChannel> ChannelCreated;
15+
public event Action<IChannel> ChannelDeleted;
16+
public event Action<IMessage> MessageCreated;
1217

13-
private int _shard, _totalShards;
18+
private readonly int _shard, _totalShards;
19+
private TaskCompletionSource<bool>? _readyPromise;
1420

1521
public WumpusGatewayClient Gateway { get; }
1622
public WumpusRestClient Rest { get; }
@@ -23,28 +29,59 @@ public DiscordClient(DiscordClientConfig config)
2329
Gateway = new WumpusGatewayClient();
2430
Rest = new WumpusRestClient();
2531

32+
if (config.Token == null)
33+
throw new ArgumentNullException(nameof(config.Token), "The client token must be set.");
2634
var auth = new AuthenticationHeaderValue("", config.Token);
35+
// todo: port ChrisJ's token validator
2736
Gateway.Authorization = auth;
2837
Rest.Authorization = auth;
38+
39+
RegisterEvents();
2940
}
3041

3142
public async Task StartAsync()
3243
{
44+
if (Gateway.State != ConnectionState.Disconnected)
45+
await StopAsync().ConfigureAwait(false);
46+
3347
var gateway = await Rest.GetBotGatewayAsync().ConfigureAwait(false);
3448
await Gateway.RunAsync(gateway.Url.ToString(), _shard, _totalShards).ConfigureAwait(false);
3549
}
3650

51+
public async Task StartAndWaitAsync()
52+
{
53+
if (Gateway.State != ConnectionState.Disconnected)
54+
await StopAsync().ConfigureAwait(false);
55+
56+
_readyPromise = new TaskCompletionSource<bool>();
57+
await StartAsync().ConfigureAwait(false);
58+
await _readyPromise.Task; // todo: need a timeout or cancellation token
59+
_readyPromise = null;
60+
}
61+
3762
public async Task StopAsync()
3863
{
3964
await Gateway.StopAsync().ConfigureAwait(false);
4065
}
4166

4267
private void RegisterEvents()
4368
{
69+
Gateway.Connected += OnConnected;
70+
Gateway.Disconnected += OnDisconnected;
4471
Gateway.Ready += OnReady;
4572
}
73+
private void OnConnected()
74+
{
75+
Connected?.Invoke();
76+
}
77+
private void OnDisconnected(Exception e)
78+
{
79+
Disconnected?.Invoke();
80+
}
4681
private void OnReady(ReadyEvent args)
4782
{
83+
if (_readyPromise != null)
84+
_readyPromise.TrySetResult(true);
4885
// TODO: Cache
4986
Ready?.Invoke();
5087
}

src/Discord.Net/DiscordClientBuilder.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ namespace Discord
22
{
33
public sealed class DiscordClientBuilder
44
{
5-
public IDiscordClient FromConfig(DiscordClientConfig config)
5+
private DiscordClientBuilder() { }
6+
public static IDiscordClient FromConfig(DiscordClientConfig config)
67
{
78
return new DiscordClient(config);
89
}

src/Discord.Net/DiscordClientConfig.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ namespace Discord
22
{
33
public class DiscordClientConfig
44
{
5-
public string Token { get; set; }
5+
public string? Token { get; set; }
66

77
public int Shard { get; set; } = 0;
88
public int TotalShards { get; set; } = 1;
9+
10+
IStateProvider StateProvider { get; set; } = new ConcurrentMemoryStateProvider(); // todo: use a factory so the state is never exposed
11+
public StateBehavior DefaultStateBehavior { get; set; } = StateBehavior.Default;
912
}
1013
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// todo: meth
2+
// todo: props
3+
// todo: docs
4+
namespace Discord
5+
{
6+
public interface IActivity
7+
{
8+
string Name { get; }
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// todo: docs
2+
using Voltaic;
3+
using Wumpus;
4+
using Model = Wumpus.Requests.ModifyChannelParams;
5+
6+
namespace Discord
7+
{
8+
public class GuildChannelProperties
9+
{
10+
public Optional<string> Name { get; set; }
11+
public Optional<int> Position { get; set; }
12+
public Optional<SnowflakeOrEntity<ICategoryChannel>?> Category { get; set; }
13+
14+
internal virtual Model ToWumpus()
15+
{
16+
var model = new Model();
17+
if (Name.IsSpecified)
18+
model.Name = new Utf8String(Name.Value);
19+
model.Position = Position;
20+
if (Category.IsSpecified)
21+
{
22+
model.ParentId = Category.Value.HasValue ? Category.Value.Value.Id : (Snowflake?)null;
23+
}
24+
25+
return model;
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// todo: docs
2+
namespace Discord
3+
{
4+
// review: another marker interface 😕
5+
public interface ICategoryChannel : IGuildChannel
6+
{
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// TODO: docs
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
5+
namespace Discord
6+
{
7+
public interface IChannel : ISnowflakeEntity
8+
{
9+
string Name { get; }
10+
11+
ValueTask<IUser> GetUserAsync(ulong id, StateBehavior stateBehavior = default, RequestOptions? options = default);
12+
IAsyncEnumerable<IUser> GetUsersAsync(StateBehavior stateBehavior = default, RequestOptions? options = default);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// todo: docs
2+
using System.Threading.Tasks;
3+
4+
namespace Discord
5+
{
6+
public interface IDMChannel : IMessageChannel
7+
{
8+
ValueTask<IUser> GetRecipientAsync(StateBehavior stateBehavior, RequestOptions? options = default);
9+
ulong RecipientId { get; }
10+
11+
// not applicable: CloseAsync
12+
}
13+
}

0 commit comments

Comments
 (0)