Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4d85806
centralize the service provider
akiraveliara May 17, 2024
4f42a50
prepare the rest client for IOC
akiraveliara May 17, 2024
eaf6a5e
obsolete all the old events...
akiraveliara May 17, 2024
1b0e386
obsolete SocketErrored even more
akiraveliara May 18, 2024
6c22cd2
enables registering event handlers to the service collection
akiraveliara May 21, 2024
6fc8ed7
fix event-related build errors
akiraveliara May 21, 2024
1e62cd4
make DiscordApiClient IOC-constructible
akiraveliara May 21, 2024
5c8f2c4
death (migrate events (not dispatch yet) to the interim model)
akiraveliara May 24, 2024
b2ebdae
update DiscordClient.Dipshit.cs
akiraveliara May 26, 2024
905ce60
fix most trivial build errors
akiraveliara May 26, 2024
68675df
more progress towards usability
akiraveliara May 29, 2024
32035b6
DSharpPlus.dll should now work:tm:
akiraveliara May 29, 2024
5a3e002
build?
akiraveliara May 29, 2024
af572a6
miser, miser
akiraveliara May 29, 2024
06a7058
add public construction code
akiraveliara May 29, 2024
0fc1bbd
Merge branch 'master' into aki/discordclient-ioc
akiraveliara May 30, 2024
7284840
update docs
akiraveliara May 30, 2024
edd2984
convenience
akiraveliara May 30, 2024
ec97bdc
details
akiraveliara May 31, 2024
bfef5fc
migration guide (partial, only as far as this PR is concerned)
akiraveliara May 31, 2024
8948ab0
build
akiraveliara May 31, 2024
a6de76c
merge conflicts
akiraveliara Jun 2, 2024
72efde2
switch to CreateDefault
akiraveliara Jun 2, 2024
989eab0
nit
akiraveliara Jun 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions DSharpPlus.Commands/CommandsConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
using System;

namespace DSharpPlus.Commands;

/// <summary>
/// The configuration copied to an instance of <see cref="CommandsExtension"/>.
/// </summary>
public sealed record CommandsConfiguration
{
/// <summary>
/// The service provider to use for dependency injection.
/// </summary>
public required IServiceProvider ServiceProvider { get; set; }

/// <summary>
/// The guild id to use for debugging. Leave as 0 to disable.
/// </summary>
Expand Down
10 changes: 7 additions & 3 deletions DSharpPlus.Commands/CommandsExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

using DSharpPlus.AsyncEvents;
using DSharpPlus.Commands.ContextChecks;
using DSharpPlus.Commands.ContextChecks.ParameterChecks;
Expand All @@ -23,16 +24,19 @@
using DSharpPlus.Commands.Trees.Metadata;
using DSharpPlus.Entities;
using DSharpPlus.Exceptions;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

using CheckFunc = System.Func
<
object,
DSharpPlus.Commands.ContextChecks.ContextCheckAttribute,
DSharpPlus.Commands.CommandContext,
System.Threading.Tasks.ValueTask<string?>
>;

using ParameterCheckFunc = System.Func
<
object,
Expand All @@ -49,8 +53,8 @@ namespace DSharpPlus.Commands;
/// </summary>
public sealed class CommandsExtension : BaseExtension
{
/// <inheritdoc cref="CommandsConfiguration.ServiceProvider"/>
public IServiceProvider ServiceProvider { get; init; }
/// <inheritdoc cref="DiscordClient.ServiceProvider"/>
public IServiceProvider ServiceProvider { get; private set; }

/// <inheritdoc cref="CommandsConfiguration.DebugGuildId"/>
public ulong DebugGuildId { get; init; }
Expand Down Expand Up @@ -106,7 +110,6 @@ internal CommandsExtension(CommandsConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(configuration);

this.ServiceProvider = configuration.ServiceProvider;
this.DebugGuildId = configuration.DebugGuildId;
this.UseDefaultCommandErrorHandler = configuration.UseDefaultCommandErrorHandler;
this.RegisterDefaultCommandProcessors = configuration.RegisterDefaultCommandProcessors;
Expand Down Expand Up @@ -136,6 +139,7 @@ protected override void Setup(DiscordClient client)
}

this.Client = client;
this.ServiceProvider = client.ServiceProvider;
this.Client.SessionCreated += async (_, _) => await RefreshAsync();

AddCheck<DirectMessageUsageCheck>();
Expand Down
7 changes: 0 additions & 7 deletions DSharpPlus.CommandsNext/CommandsNextConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,6 @@ public sealed class CommandsNextConfiguration
/// </summary>
public bool EnableDms { internal get; set; } = true;

/// <summary>
/// <para>Sets the service provider for this CommandsNext instance.</para>
/// <para>Objects in this provider are used when instantiating command modules. This allows passing data around without resorting to static members.</para>
/// <para>Defaults to null.</para>
/// </summary>
public IServiceProvider Services { internal get; set; } = new ServiceCollection().BuildServiceProvider(true);

/// <summary>
/// <para>Gets whether any extra arguments passed to commands should be ignored or not. If this is set to false, extra arguments will throw, otherwise they will be ignored.</para>
/// <para>Defaults to false.</para>
Expand Down
4 changes: 3 additions & 1 deletion DSharpPlus.CommandsNext/CommandsNextExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

using DSharpPlus.AsyncEvents;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.CommandsNext.Builders;
Expand All @@ -15,6 +16,7 @@
using DSharpPlus.CommandsNext.Executors;
using DSharpPlus.Entities;
using DSharpPlus.EventArgs;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

Expand All @@ -38,7 +40,7 @@ internal CultureInfo DefaultParserCulture
/// Gets the service provider this CommandsNext module was configured with.
/// </summary>
public IServiceProvider Services
=> this.Config.Services;
=> this.Client.ServiceProvider;

internal CommandsNextExtension(CommandsNextConfiguration cfg)
{
Expand Down
8 changes: 8 additions & 0 deletions DSharpPlus/Clients/DiscordClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

using DSharpPlus.AsyncEvents;
using DSharpPlus.Entities;
using DSharpPlus.EventArgs;
Expand All @@ -15,7 +16,9 @@
using DSharpPlus.Net.Abstractions;
using DSharpPlus.Net.Models;
using DSharpPlus.Net.Serialization;

using Microsoft.Extensions.Logging;

using Newtonsoft.Json.Linq;

namespace DSharpPlus;
Expand All @@ -38,6 +41,11 @@ public sealed partial class DiscordClient : BaseDiscordClient
#endregion

#region Public Fields/Properties
/// <summary>
/// Gets the service provider used within this Discord application.
/// </summary>
public IServiceProvider ServiceProvider { get; internal set; }

/// <summary>
/// Gets the gateway protocol version.
/// </summary>
Expand Down
66 changes: 66 additions & 0 deletions DSharpPlus/Clients/DiscordClientBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;

using DSharpPlus.Extensions;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace DSharpPlus;

/// <summary>
/// Enables building a DiscordClient from complex configuration, registering extensions and fine-tuning behavioural aspects.
/// </summary>
public sealed class DiscordClientBuilder
{
private readonly IServiceCollection serviceCollection;
private bool addDefaultLogging = true;

/// <summary>
/// Creates a new DiscordClientBuilder from the provided service collection. This is private in favor of static
/// methods that control creation based on certain presets. IServiceCollection-based configuration occurs separate
/// from this type.
/// </summary>
private DiscordClientBuilder(IServiceCollection serviceCollection)
=> this.serviceCollection = serviceCollection;

/// <summary>
/// Creates a new DiscordClientBuilder without sharding, using the specified token.
/// </summary>
/// <param name="token">The token to use for this application.</param>
/// <param name="serviceCollection">The service collection to base this builder on.</param>
/// <returns>A new DiscordClientBuilder.</returns>
public static DiscordClientBuilder Default(string token, IServiceCollection? serviceCollection = null)
{
serviceCollection ??= new ServiceCollection();

DiscordClientBuilder builder = new(serviceCollection);
builder.serviceCollection.Configure<TokenContainer>(x => x.GetToken = () => token);
builder.serviceCollection.AddDSharpPlusDefaultsSingleShard();

return builder;
}

/// <summary>
/// Disables the DSharpPlus default logger for this DiscordClientBuilder.
/// </summary>
/// <returns>The current instance for chaining.</returns>
public DiscordClientBuilder DisableDefaultLogging()
{
this.addDefaultLogging = false;
return this;
}

/// <summary>
/// Builds a new client from the present builder.
/// </summary>
public DiscordClient Build()
{
if (this.addDefaultLogging)
{
this.serviceCollection.AddLogging(builder => builder.AddProvider(new DefaultLoggerProvider()));
}

IServiceProvider provider = this.serviceCollection.BuildServiceProvider();
return provider.GetRequiredService<DiscordClient>();
}
}
1 change: 1 addition & 0 deletions DSharpPlus/DSharpPlus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Polly.Core" />
Expand Down
10 changes: 4 additions & 6 deletions DSharpPlus/DiscordConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Net;

using DSharpPlus.Net;
using DSharpPlus.Net.Udp;
using DSharpPlus.Net.WebSocket;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -99,12 +101,8 @@ public string Token
/// </summary>
public IWebProxy Proxy { internal get; set; } = null;

/// <summary>
/// <para>Sets the timeout for HTTP requests.</para>
/// <para>Set to <see cref="System.Threading.Timeout.InfiniteTimeSpan"/> to disable timeouts.</para>
/// <para>Defaults to 10 seconds.</para>
/// </summary>
public TimeSpan HttpTimeout { internal get; set; } = TimeSpan.FromSeconds(100);
/// <inheritdoc cref="RestClientOptions.Timeout"/>
public TimeSpan HttpTimeout { internal get; set; } = TimeSpan.FromSeconds(10);

/// <summary>
/// <para>Defines that the client should attempt to reconnect indefinitely.</para>
Expand Down
35 changes: 35 additions & 0 deletions DSharpPlus/Extensions/ServiceCollectionExtensions.InternalSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Net.Http;

using DSharpPlus.Net;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace DSharpPlus.Extensions;

public static class ServiceCollectionExtensions
{
internal static IServiceCollection AddDSharpPlusDefaultsSingleShard(this IServiceCollection serviceCollection)
{
// peripheral setup
serviceCollection.AddSingleton<IMessageCacheProvider, MessageCache>();

// rest setup
serviceCollection.AddKeyedSingleton<HttpClient>("DSharpPlus.Rest.HttpClient")
Comment thread
akiraveliara marked this conversation as resolved.
.AddSingleton<RestClient>
(
serviceProvider =>
{
HttpClient client = serviceProvider.GetRequiredKeyedService<HttpClient>("DSharpPlus.Rest.HttpClient");
ILogger<RestClient> logger = serviceProvider.GetRequiredService<ILogger<RestClient>>();
IOptions<RestClientOptions> options = serviceProvider.GetRequiredService<IOptions<RestClientOptions>>();
IOptions<TokenContainer> token = serviceProvider.GetRequiredService<IOptions<TokenContainer>>();

return new(logger, client, options, token);
}
);

return serviceCollection;
}
}
46 changes: 23 additions & 23 deletions DSharpPlus/Net/Rest/RestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
using System.Threading.Tasks;
using DSharpPlus.Exceptions;
using DSharpPlus.Metrics;

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

using Polly;

namespace DSharpPlus.Net;
Expand All @@ -29,26 +32,34 @@ internal sealed partial class RestClient : IDisposable

private volatile bool disposed;

internal RestClient(DiscordConfiguration config, ILogger logger)
internal RestClient
(
ILogger<RestClient> logger,
HttpClient client,
IOptions<RestClientOptions> options,
IOptions<TokenContainer> tokenContainer
)
: this
(
config.Proxy,
config.HttpTimeout,
client,
options.Value.Timeout,
logger,
config.MaximumRatelimitRetries,
config.RatelimitRetryDelayFallback,
config.TimeoutForInitialApiRequest,
config.MaximumRestRequestsPerSecond
options.Value.MaximumRatelimitRetries,
options.Value.RatelimitRetryDelayFallback,
options.Value.InitialRequestTimeout,
options.Value.MaximumConcurrentRestRequests
)
{
this.httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", Utilities.GetFormattedToken(config));
string token = tokenContainer.Value.GetToken();

this.httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bot {token}");
this.httpClient.BaseAddress = new(Endpoints.BASE_URI);
}

// This is for meta-clients, such as the webhook client
internal RestClient
(
IWebProxy proxy,
HttpClient client,
TimeSpan timeout,
ILogger logger,
int maxRetries = int.MaxValue,
Expand All @@ -58,21 +69,10 @@ internal RestClient
)
{
this.logger = logger;
this.httpClient = client;

HttpClientHandler httphandler = new()
{
UseCookies = false,
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
UseProxy = proxy != null,
Proxy = proxy
};

this.httpClient = new HttpClient(httphandler)
{
BaseAddress = new Uri(Utilities.GetApiBaseUri()),
Timeout = timeout
};

this.httpClient.BaseAddress = new Uri(Utilities.GetApiBaseUri());
this.httpClient.Timeout = timeout;
Comment on lines +74 to +75

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it'd be worthwhile to go the Remora route and expose a [rest] client builder; there's occasional reason to change what the client does (e.g., adding additional polly handlers for metrics, or using the canary api for one reason or another)

@VelvetToroyashi VelvetToroyashi May 17, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is something we should look into in a separate pull request to customize specifically rest further

this.httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
this.httpClient.BaseAddress = new(Endpoints.BASE_URI);

Expand Down
Loading