Update sample projects & samples in docs (#2823)

* update them all

* more docs

* moar docs
This commit is contained in:
Mihail Gribkov
2024-01-11 18:25:56 +03:00
committed by GitHub
parent 8227d70b86
commit e2e8c0fd6a
31 changed files with 732 additions and 806 deletions

View File

@@ -3,10 +3,8 @@ using Discord.WebSocket;
public class Program public class Program
{ {
private DiscordSocketClient _client; private static DiscordSocketClient _client;
static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); public static async Task MainAsync()
public async Task MainAsync()
{ {
// When working with events that have Cacheable<IMessage, ulong> parameters, // When working with events that have Cacheable<IMessage, ulong> parameters,
// you must enable the message cache in your config settings if you plan to // you must enable the message cache in your config settings if you plan to
@@ -27,7 +25,7 @@ public class Program
await Task.Delay(-1); await Task.Delay(-1);
} }
private async Task MessageUpdated(Cacheable<IMessage, ulong> before, SocketMessage after, ISocketMessageChannel channel) private static async Task MessageUpdated(Cacheable<IMessage, ulong> before, SocketMessage after, ISocketMessageChannel channel)
{ {
// If the message was not in the cache, downloading it will result in getting a copy of `after`. // If the message was not in the cache, downloading it will result in getting a copy of `after`.
var message = await before.GetOrDownloadAsync(); var message = await before.GetOrDownloadAsync();

View File

@@ -1,14 +1,6 @@
public class Program public class Program
{ {
private readonly IServiceProvider _serviceProvider; private static IServiceProvider _serviceProvider;
public Program()
{
_serviceProvider = CreateProvider();
}
static void Main(string[] args)
=> new Program().RunAsync(args).GetAwaiter().GetResult();
static IServiceProvider CreateProvider() static IServiceProvider CreateProvider()
{ {
@@ -17,8 +9,8 @@ public class Program
return collection.BuildServiceProvider(); return collection.BuildServiceProvider();
} }
async Task RunAsync(string[] args) static async Task Main(string[] args)
{ {
//... _serviceProvider = CreateProvider();
} }
} }

View File

@@ -1,8 +1,6 @@
public class Program public class Program
{ {
public static Task Main(string[] args) => new Program().MainAsync(); public static async Task Main()
public async Task MainAsync()
{ {
} }
} }

View File

@@ -1,6 +1,6 @@
private DiscordSocketClient _client; private static DiscordSocketClient _client;
public async Task MainAsync() public static async Task Main()
{ {
_client = new DiscordSocketClient(); _client = new DiscordSocketClient();

View File

@@ -1,10 +1,8 @@
public class Program public class Program
{ {
private DiscordSocketClient _client; private static DiscordSocketClient _client;
public static Task Main(string[] args) => new Program().MainAsync(); public async Task Main()
public async Task MainAsync()
{ {
_client = new DiscordSocketClient(); _client = new DiscordSocketClient();
_client.Log += Log; _client.Log += Log;

View File

@@ -1,4 +1,4 @@
private Task Log(LogMessage msg) private static Task Log(LogMessage msg)
{ {
Console.WriteLine(msg.ToString()); Console.WriteLine(msg.ToString());
return Task.CompletedTask; return Task.CompletedTask;

View File

@@ -1,11 +1,11 @@
public async Task MainAsync() public static async Task Main()
{ {
// ... // ...
_client.MessageReceived += MessageReceived; _client.MessageReceived += MessageReceived;
// ... // ...
} }
private async Task MessageReceived(SocketMessage message) private static async Task MessageReceived(SocketMessage message)
{ {
if (message.Content == "!ping") if (message.Content == "!ping")
{ {

View File

@@ -10,21 +10,7 @@ using Discord.WebSocket;
class Program class Program
{ {
// Program entry point // Program entry point
static Task Main(string[] args) static async Task Main(string[] args)
{
// Call the Program constructor, followed by the
// MainAsync method and wait until it finishes (which should be never).
return new Program().MainAsync();
}
private readonly DiscordSocketClient _client;
// Keep the CommandService and DI container around for use with commands.
// These two types require you install the Discord.Net.Commands package.
private readonly CommandService _commands;
private readonly IServiceProvider _services;
private Program()
{ {
_client = new DiscordSocketClient(new DiscordSocketConfig _client = new DiscordSocketClient(new DiscordSocketConfig
{ {
@@ -58,9 +44,15 @@ class Program
// Setup your DI container. // Setup your DI container.
_services = ConfigureServices(); _services = ConfigureServices();
} }
private static DiscordSocketClient _client;
// Keep the CommandService and DI container around for use with commands.
// These two types require you install the Discord.Net.Commands package.
private static CommandService _commands;
private static IServiceProvider _services;
// If any services require the client, or the CommandService, or something else you keep on hand, // If any services require the client, or the CommandService, or something else you keep on hand,
// pass them as parameters into this method as needed. // pass them as parameters into this method as needed.
// If this method is getting pretty long, you can seperate it out into another file using partials. // If this method is getting pretty long, you can seperate it out into another file using partials.
@@ -110,7 +102,7 @@ class Program
return Task.CompletedTask; return Task.CompletedTask;
} }
private async Task MainAsync() private static async Task MainAsync()
{ {
// Centralize the logic for commands into a separate method. // Centralize the logic for commands into a separate method.
await InitCommands(); await InitCommands();
@@ -125,7 +117,7 @@ class Program
await Task.Delay(Timeout.Infinite); await Task.Delay(Timeout.Infinite);
} }
private async Task InitCommands() private static async Task InitCommands()
{ {
// Either search the program and add all Module classes that can be found. // Either search the program and add all Module classes that can be found.
// Module classes MUST be marked 'public' or they will be ignored. // Module classes MUST be marked 'public' or they will be ignored.
@@ -140,7 +132,7 @@ class Program
_client.MessageReceived += HandleCommandAsync; _client.MessageReceived += HandleCommandAsync;
} }
private async Task HandleCommandAsync(SocketMessage arg) private static async Task HandleCommandAsync(SocketMessage arg)
{ {
// Bail out if it's a System Message. // Bail out if it's a System Message.
var msg = arg as SocketUserMessage; var msg = arg as SocketUserMessage;

View File

@@ -7,11 +7,11 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="2.0.0" /> <PackageReference Include="Discord.Net" Version="3.13.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -4,8 +4,8 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace BasicBot namespace BasicBot;
{
// This is a minimal, bare-bones example of using Discord.Net. // This is a minimal, bare-bones example of using Discord.Net.
// //
// If writing a bot with commands/interactions, we recommend using the Discord.Net.Commands/Discord.Net.Interactions // If writing a bot with commands/interactions, we recommend using the Discord.Net.Commands/Discord.Net.Interactions
@@ -22,20 +22,15 @@ namespace BasicBot
{ {
// Non-static readonly fields can only be assigned in a constructor. // Non-static readonly fields can only be assigned in a constructor.
// If you want to assign it elsewhere, consider removing the readonly keyword. // If you want to assign it elsewhere, consider removing the readonly keyword.
private readonly DiscordSocketClient _client; private static DiscordSocketClient _client;
// Discord.Net heavily utilizes TAP for async, so we create // Discord.Net heavily utilizes TAP for async, so we create
// an asynchronous context from the beginning. // an asynchronous context from the beginning.
static void Main(string[] args) public static async Task Main(string[] args)
=> new Program()
.MainAsync()
.GetAwaiter()
.GetResult();
public Program()
{ {
// Config used by DiscordSocketClient // Config used by DiscordSocketClient
// Define intents for the client // Define intents for the client
// Note that GatewayIntents.MessageContent is a privileged intent, and requires extra setup in the developer portal.
var config = new DiscordSocketConfig var config = new DiscordSocketConfig
{ {
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
@@ -50,10 +45,8 @@ namespace BasicBot
_client.Ready += ReadyAsync; _client.Ready += ReadyAsync;
_client.MessageReceived += MessageReceivedAsync; _client.MessageReceived += MessageReceivedAsync;
_client.InteractionCreated += InteractionCreatedAsync; _client.InteractionCreated += InteractionCreatedAsync;
}
public async Task MainAsync()
{
// Tokens should be considered secret data, and never hard-coded. // Tokens should be considered secret data, and never hard-coded.
await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token")); await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
// Different approaches to making your token a secret is by putting them in local .json, .yaml, .xml or .txt files, then reading them on startup. // Different approaches to making your token a secret is by putting them in local .json, .yaml, .xml or .txt files, then reading them on startup.
@@ -64,7 +57,7 @@ namespace BasicBot
await Task.Delay(Timeout.Infinite); await Task.Delay(Timeout.Infinite);
} }
private Task LogAsync(LogMessage log) private static Task LogAsync(LogMessage log)
{ {
Console.WriteLine(log.ToString()); Console.WriteLine(log.ToString());
return Task.CompletedTask; return Task.CompletedTask;
@@ -72,7 +65,7 @@ namespace BasicBot
// The Ready event indicates that the client has opened a // The Ready event indicates that the client has opened a
// connection and it is now safe to access the cache. // connection and it is now safe to access the cache.
private Task ReadyAsync() private static Task ReadyAsync()
{ {
Console.WriteLine($"{_client.CurrentUser} is connected!"); Console.WriteLine($"{_client.CurrentUser} is connected!");
@@ -81,7 +74,7 @@ namespace BasicBot
// This is not the recommended way to write a bot - consider // This is not the recommended way to write a bot - consider
// reading over the Commands Framework sample. // reading over the Commands Framework sample.
private async Task MessageReceivedAsync(SocketMessage message) private static async Task MessageReceivedAsync(SocketMessage message)
{ {
// The bot should never respond to itself. // The bot should never respond to itself.
if (message.Author.Id == _client.CurrentUser.Id) if (message.Author.Id == _client.CurrentUser.Id)
@@ -102,7 +95,7 @@ namespace BasicBot
// For better functionality & a more developer-friendly approach to handling any kind of interaction, refer to: // For better functionality & a more developer-friendly approach to handling any kind of interaction, refer to:
// https://discordnet.dev/guides/int_framework/intro.html // https://discordnet.dev/guides/int_framework/intro.html
private async Task InteractionCreatedAsync(SocketInteraction interaction) private static async Task InteractionCreatedAsync(SocketInteraction interaction)
{ {
// safety-casting is the best way to prevent something being cast from being null. // safety-casting is the best way to prevent something being cast from being null.
// If this check does not pass, it could not be cast to said type. // If this check does not pass, it could not be cast to said type.
@@ -117,4 +110,3 @@ namespace BasicBot
} }
} }
} }
}

View File

@@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net.WebSocket" Version="3.10.0"/> <PackageReference Include="Discord.Net.WebSocket" Version="3.13.0"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -4,8 +4,8 @@ using Discord.WebSocket;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace InteractionFramework.Attributes namespace InteractionFramework.Attributes;
{
internal class DoUserCheck : PreconditionAttribute internal class DoUserCheck : PreconditionAttribute
{ {
public override Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services) public override Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services)
@@ -14,8 +14,6 @@ namespace InteractionFramework.Attributes
if (context.Interaction is not SocketMessageComponent componentContext) if (context.Interaction is not SocketMessageComponent componentContext)
return Task.FromResult(PreconditionResult.FromError("Context unrecognized as component context.")); return Task.FromResult(PreconditionResult.FromError("Context unrecognized as component context."));
else
{
// The approach here entirely depends on how you construct your custom ID. In this case, the format is: // The approach here entirely depends on how you construct your custom ID. In this case, the format is:
// unique-name:*,* // unique-name:*,*
@@ -30,9 +28,6 @@ namespace InteractionFramework.Attributes
? Task.FromResult(PreconditionResult.FromSuccess()) ? Task.FromResult(PreconditionResult.FromSuccess())
: Task.FromResult(PreconditionResult.FromError("User ID does not match component ID!")); : Task.FromResult(PreconditionResult.FromError("User ID does not match component ID!"));
else
return Task.FromResult(PreconditionResult.FromError("Parse cannot be done if no userID exists.")); return Task.FromResult(PreconditionResult.FromError("Parse cannot be done if no userID exists."));
} }
} }
}
}

View File

@@ -1,13 +1,10 @@
using Discord; using Discord;
using Discord.Interactions; using Discord.Interactions;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace InteractionFramework.Attributes namespace InteractionFramework.Attributes;
{
public class RequireOwnerAttribute : PreconditionAttribute public class RequireOwnerAttribute : PreconditionAttribute
{ {
public override async Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services) public override async Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services)
@@ -16,12 +13,11 @@ namespace InteractionFramework.Attributes
{ {
case TokenType.Bot: case TokenType.Bot:
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false); var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false);
if (context.User.Id != application.Owner.Id) return context.User.Id != application.Owner.Id
return PreconditionResult.FromError(ErrorMessage ?? "Command can only be run by the owner of the bot."); ? PreconditionResult.FromError(ErrorMessage ?? "Command can only be run by the owner of the bot.")
return PreconditionResult.FromSuccess(); : PreconditionResult.FromSuccess();
default: default:
return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}."); return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}.");
} }
} }
} }
}

View File

@@ -1,13 +1,7 @@
using Discord.Interactions; using Discord.Interactions;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InteractionFramework namespace InteractionFramework;
{
public enum ExampleEnum public enum ExampleEnum
{ {
First, First,
@@ -17,4 +11,3 @@ namespace InteractionFramework
[ChoiceDisplay("Twenty First")] [ChoiceDisplay("Twenty First")]
TwentyFirst TwentyFirst
} }
}

View File

@@ -6,8 +6,8 @@ using System;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace InteractionFramework namespace InteractionFramework;
{
public class InteractionHandler public class InteractionHandler
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
@@ -34,6 +34,9 @@ namespace InteractionFramework
// Process the InteractionCreated payloads to execute Interactions commands // Process the InteractionCreated payloads to execute Interactions commands
_client.InteractionCreated += HandleInteraction; _client.InteractionCreated += HandleInteraction;
// Also process the result of the command execution.
_handler.InteractionExecuted += HandleInteractionExecute;
} }
private async Task LogAsync(LogMessage log) private async Task LogAsync(LogMessage log)
@@ -41,12 +44,9 @@ namespace InteractionFramework
private async Task ReadyAsync() private async Task ReadyAsync()
{ {
// Context & Slash commands can be automatically registered, but this process needs to happen after the client enters the READY state. // Register the commands globally.
// Since Global Commands take around 1 hour to register, we should use a test guild to instantly update and test our commands. // alternatively you can use _handler.RegisterCommandsGloballyAsync() to register commands to a specific guild.
if (Program.IsDebug()) await _handler.RegisterCommandsGloballyAsync();
await _handler.RegisterCommandsToGuildAsync(_configuration.GetValue<ulong>("testGuild"), true);
else
await _handler.RegisterCommandsGloballyAsync(true);
} }
private async Task HandleInteraction(SocketInteraction interaction) private async Task HandleInteraction(SocketInteraction interaction)
@@ -59,6 +59,8 @@ namespace InteractionFramework
// Execute the incoming command. // Execute the incoming command.
var result = await _handler.ExecuteCommandAsync(context, _services); var result = await _handler.ExecuteCommandAsync(context, _services);
// Due to async nature of InteractionFramework, the result here may always be success.
// That's why we also need to handle the InteractionExecuted event.
if (!result.IsSuccess) if (!result.IsSuccess)
switch (result.Error) switch (result.Error)
{ {
@@ -77,5 +79,17 @@ namespace InteractionFramework
await interaction.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync()); await interaction.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync());
} }
} }
private async Task HandleInteractionExecute(ICommandInfo commandInfo, IInteractionContext context, IResult result)
{
if (!result.IsSuccess)
switch (result.Error)
{
case InteractionCommandError.UnmetPrecondition:
// implement
break;
default:
break;
}
} }
} }

View File

@@ -4,8 +4,8 @@ using InteractionFramework.Attributes;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace InteractionFramework.Modules namespace InteractionFramework.Modules;
{
// Interaction modules must be public and inherit from an IInteractionModuleBase // Interaction modules must be public and inherit from an IInteractionModuleBase
public class ExampleModule : InteractionModuleBase<SocketInteractionContext> public class ExampleModule : InteractionModuleBase<SocketInteractionContext>
{ {
@@ -97,4 +97,3 @@ namespace InteractionFramework.Modules
} }
} }
} }
}

View File

@@ -7,20 +7,20 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace InteractionFramework namespace InteractionFramework;
{
public class Program public class Program
{ {
private readonly IConfiguration _configuration; private static IConfiguration _configuration;
private readonly IServiceProvider _services; private static IServiceProvider _services;
private readonly DiscordSocketConfig _socketConfig = new() private static readonly DiscordSocketConfig _socketConfig = new()
{ {
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers, GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers,
AlwaysDownloadUsers = true, AlwaysDownloadUsers = true,
}; };
public Program() public static async Task Main(string[] args)
{ {
_configuration = new ConfigurationBuilder() _configuration = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "DC_") .AddEnvironmentVariables(prefix: "DC_")
@@ -34,15 +34,7 @@ namespace InteractionFramework
.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordSocketClient>())) .AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordSocketClient>()))
.AddSingleton<InteractionHandler>() .AddSingleton<InteractionHandler>()
.BuildServiceProvider(); .BuildServiceProvider();
}
static void Main(string[] args)
=> new Program().RunAsync()
.GetAwaiter()
.GetResult();
public async Task RunAsync()
{
var client = _services.GetRequiredService<DiscordSocketClient>(); var client = _services.GetRequiredService<DiscordSocketClient>();
client.Log += LogAsync; client.Log += LogAsync;
@@ -59,16 +51,6 @@ namespace InteractionFramework
await Task.Delay(Timeout.Infinite); await Task.Delay(Timeout.Infinite);
} }
private async Task LogAsync(LogMessage message) private static async Task LogAsync(LogMessage message)
=> Console.WriteLine(message.ToString()); => Console.WriteLine(message.ToString());
public static bool IsDebug()
{
#if DEBUG
return true;
#else
return false;
#endif
}
}
} }

View File

@@ -8,12 +8,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Discord.Net.Interactions" Version="3.10.0" /> <PackageReference Include="Discord.Net.Interactions" Version="3.13.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,9 +1,8 @@
using Discord.Interactions; using Discord.Interactions;
using Discord.WebSocket;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ShardedClient.Modules namespace ShardedClient.Modules;
{
// A display of portability, which shows how minimal the difference between the 2 frameworks is. // A display of portability, which shows how minimal the difference between the 2 frameworks is.
public class InteractionModule : InteractionModuleBase<ShardedInteractionContext> public class InteractionModule : InteractionModuleBase<ShardedInteractionContext>
{ {
@@ -15,4 +14,3 @@ namespace ShardedClient.Modules
await RespondAsync(msg); await RespondAsync(msg);
} }
} }
}

View File

@@ -1,8 +1,8 @@
using Discord.Commands; using Discord.Commands;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ShardedClient.Modules namespace ShardedClient.Modules;
{
// Remember to make your module reference the ShardedCommandContext // Remember to make your module reference the ShardedCommandContext
public class PublicModule : ModuleBase<ShardedCommandContext> public class PublicModule : ModuleBase<ShardedCommandContext>
{ {
@@ -14,4 +14,3 @@ namespace ShardedClient.Modules
await ReplyAsync(msg); await ReplyAsync(msg);
} }
} }
}

View File

@@ -8,20 +8,14 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ShardedClient namespace ShardedClient;
{
// This is a minimal example of using Discord.Net's Sharded Client // This is a minimal example of using Discord.Net's Sharded Client
// The provided DiscordShardedClient class simplifies having multiple // The provided DiscordShardedClient class simplifies having multiple
// DiscordSocketClient instances (or shards) to serve a large number of guilds. // DiscordSocketClient instances (or shards) to serve a large number of guilds.
class Program class Program
{ {
static void Main(string[] args) public static async Task Main(string[] args)
=> new Program()
.MainAsync()
.GetAwaiter()
.GetResult();
public async Task MainAsync()
{ {
// You specify the amount of shards you'd like to have with the // You specify the amount of shards you'd like to have with the
// DiscordSocketConfig. Generally, it's recommended to // DiscordSocketConfig. Generally, it's recommended to
@@ -36,8 +30,8 @@ namespace ShardedClient
// when you are finished using it, at the end of your app's lifetime. // when you are finished using it, at the end of your app's lifetime.
// If you use another dependency injection framework, you should inspect // If you use another dependency injection framework, you should inspect
// its documentation for the best way to do this. // its documentation for the best way to do this.
using (var services = ConfigureServices(config)) await using var services = ConfigureServices(config);
{
var client = services.GetRequiredService<DiscordShardedClient>(); var client = services.GetRequiredService<DiscordShardedClient>();
// The Sharded Client does not have a Ready event. // The Sharded Client does not have a Ready event.
@@ -58,9 +52,8 @@ namespace ShardedClient
await Task.Delay(Timeout.Infinite); await Task.Delay(Timeout.Infinite);
} }
}
private ServiceProvider ConfigureServices(DiscordSocketConfig config) private static ServiceProvider ConfigureServices(DiscordSocketConfig config)
=> new ServiceCollection() => new ServiceCollection()
.AddSingleton(new DiscordShardedClient(config)) .AddSingleton(new DiscordShardedClient(config))
.AddSingleton<CommandService>() .AddSingleton<CommandService>()
@@ -70,16 +63,15 @@ namespace ShardedClient
.BuildServiceProvider(); .BuildServiceProvider();
private Task ReadyAsync(DiscordSocketClient shard) private static Task ReadyAsync(DiscordSocketClient shard)
{ {
Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!"); Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!");
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task LogAsync(LogMessage log) private static Task LogAsync(LogMessage log)
{ {
Console.WriteLine(log.ToString()); Console.WriteLine(log.ToString());
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
}

View File

@@ -6,8 +6,8 @@ using System;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ShardedClient.Services namespace ShardedClient.Services;
{
public class CommandHandlingService public class CommandHandlingService
{ {
private readonly CommandService _commands; private readonly CommandService _commands;
@@ -69,4 +69,3 @@ namespace ShardedClient.Services
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
}

View File

@@ -1,13 +1,14 @@
using Discord; using Discord;
using Discord.Interactions; using Discord.Interactions;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ShardedClient.Services namespace ShardedClient.Services;
{
public class InteractionHandlingService public class InteractionHandlingService
{ {
private readonly InteractionService _service; private readonly InteractionService _service;
@@ -50,13 +51,15 @@ namespace ShardedClient.Services
return Task.CompletedTask; return Task.CompletedTask;
} }
private bool _hasRegistered = false;
private async Task ReadyAsync(DiscordSocketClient _) private async Task ReadyAsync(DiscordSocketClient _)
{ {
#if DEBUG // ShardReady is called for each shard; to avoid getting ratelimited we only want to register commands once.
await _service.RegisterCommandsToGuildAsync(1 /* implement */); if (!_hasRegistered)
#else {
await _service.RegisterCommandsGloballyAsync(); await _service.RegisterCommandsGloballyAsync();
#endif _hasRegistered = true;
} }
} }
} }

View File

@@ -7,8 +7,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Discord.Net" Version="3.10.0" /> <PackageReference Include="Discord.Net" Version="3.13.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -4,8 +4,8 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using TextCommandFramework.Services; using TextCommandFramework.Services;
namespace TextCommandFramework.Modules namespace TextCommandFramework.Modules;
{
// Modules must be public and inherit from an IModuleBase // Modules must be public and inherit from an IModuleBase
public class PublicModule : ModuleBase<SocketCommandContext> public class PublicModule : ModuleBase<SocketCommandContext>
{ {
@@ -66,4 +66,3 @@ namespace TextCommandFramework.Modules
public Task GuildOnlyCommand() public Task GuildOnlyCommand()
=> ReplyAsync("Nothing to see here!"); => ReplyAsync("Nothing to see here!");
} }
}

View File

@@ -8,8 +8,8 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using TextCommandFramework.Services; using TextCommandFramework.Services;
namespace TextCommandFramework namespace TextCommandFramework;
{
// This is a minimal example of using Discord.Net's command // This is a minimal example of using Discord.Net's command
// framework - by no means does it show everything the framework // framework - by no means does it show everything the framework
// is capable of. // is capable of.
@@ -22,17 +22,13 @@ namespace TextCommandFramework
{ {
// There is no need to implement IDisposable like before as we are // There is no need to implement IDisposable like before as we are
// using dependency injection, which handles calling Dispose for us. // using dependency injection, which handles calling Dispose for us.
static void Main(string[] args) public static async Task Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();
public async Task MainAsync()
{ {
// You should dispose a service provider created using ASP.NET // You should dispose a service provider created using ASP.NET
// when you are finished using it, at the end of your app's lifetime. // when you are finished using it, at the end of your app's lifetime.
// If you use another dependency injection framework, you should inspect // If you use another dependency injection framework, you should inspect
// its documentation for the best way to do this. // its documentation for the best way to do this.
using (var services = ConfigureServices()) await using var services = ConfigureServices();
{
var client = services.GetRequiredService<DiscordSocketClient>(); var client = services.GetRequiredService<DiscordSocketClient>();
client.Log += LogAsync; client.Log += LogAsync;
@@ -48,16 +44,15 @@ namespace TextCommandFramework
await Task.Delay(Timeout.Infinite); await Task.Delay(Timeout.Infinite);
} }
}
private Task LogAsync(LogMessage log) private static Task LogAsync(LogMessage log)
{ {
Console.WriteLine(log.ToString()); Console.WriteLine(log.ToString());
return Task.CompletedTask; return Task.CompletedTask;
} }
private ServiceProvider ConfigureServices() private static ServiceProvider ConfigureServices()
{ {
return new ServiceCollection() return new ServiceCollection()
.AddSingleton(new DiscordSocketConfig .AddSingleton(new DiscordSocketConfig
@@ -72,4 +67,3 @@ namespace TextCommandFramework
.BuildServiceProvider(); .BuildServiceProvider();
} }
} }
}

View File

@@ -6,8 +6,8 @@ using System;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TextCommandFramework.Services namespace TextCommandFramework.Services;
{
public class CommandHandlingService public class CommandHandlingService
{ {
private readonly CommandService _commands; private readonly CommandService _commands;
@@ -72,4 +72,3 @@ namespace TextCommandFramework.Services
await context.Channel.SendMessageAsync($"error: {result}"); await context.Channel.SendMessageAsync($"error: {result}");
} }
} }
}

View File

@@ -2,8 +2,8 @@ using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TextCommandFramework.Services namespace TextCommandFramework.Services;
{
public class PictureService public class PictureService
{ {
private readonly HttpClient _http; private readonly HttpClient _http;
@@ -17,4 +17,3 @@ namespace TextCommandFramework.Services
return await resp.Content.ReadAsStreamAsync(); return await resp.Content.ReadAsStreamAsync();
} }
} }
}

View File

@@ -7,9 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Discord.Net.Commands" Version="3.10.0" /> <PackageReference Include="Discord.Net.Commands" Version="3.13.0" />
<PackageReference Include="Discord.Net.Websocket" Version="3.10.0" /> <PackageReference Include="Discord.Net.Websocket" Version="3.13.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -2,23 +2,20 @@ using Discord;
using Discord.Webhook; using Discord.Webhook;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace WebHookClient namespace WebHookClient;
{
// This is a minimal example of using Discord.Net's Webhook Client // This is a minimal example of using Discord.Net's Webhook Client
// Webhooks are send-only components of Discord that allow you to make a POST request // Webhooks are send-only components of Discord that allow you to make a POST request
// To a channel specific URL to send a message to that channel. // To a channel specific URL to send a message to that channel.
class Program class Program
{ {
static void Main(string[] args) public static async Task Main()
=> new Program().MainAsync().GetAwaiter().GetResult();
public async Task MainAsync()
{ {
// The webhook url follows the format https://discord.com/api/webhooks/{id}/{token} // The webhook url follows the format https://discord.com/api/webhooks/{id}/{token}
// Because anyone with the webhook URL can use your webhook // Because anyone with the webhook URL can use your webhook
// you should NOT hard code the URL or ID + token into your application. // you should NOT hard code the URL or ID + token into your application.
using (var client = new DiscordWebhookClient("https://discord.com/api/webhooks/123/abc123")) using var client = new DiscordWebhookClient("https://discord.com/api/webhooks/123/abc123");
{
var embed = new EmbedBuilder var embed = new EmbedBuilder
{ {
Title = "Test Embed", Title = "Test Embed",
@@ -30,5 +27,3 @@ namespace WebHookClient
await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() }); await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() });
} }
} }
}
}

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net.Webhook" Version="3.10.0" /> <PackageReference Include="Discord.Net.Webhook" Version="3.13.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>