Update sample projects & samples in docs (#2823)
* update them all * more docs * moar docs
This commit is contained in:
@@ -4,117 +4,109 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BasicBot
|
||||
namespace BasicBot;
|
||||
|
||||
// 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
|
||||
// framework, rather than handling them yourself, like we do in this sample.
|
||||
//
|
||||
// You can find samples of using the command framework:
|
||||
// - Here, under the TextCommandFramework sample
|
||||
// - At the guides: https://discordnet.dev/guides/text_commands/intro.html
|
||||
//
|
||||
// You can find samples of using the interaction framework:
|
||||
// - Here, under the InteractionFramework sample
|
||||
// - At the guides: https://discordnet.dev/guides/int_framework/intro.html
|
||||
class Program
|
||||
{
|
||||
// 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
|
||||
// framework, rather than handling them yourself, like we do in this sample.
|
||||
//
|
||||
// You can find samples of using the command framework:
|
||||
// - Here, under the TextCommandFramework sample
|
||||
// - At the guides: https://discordnet.dev/guides/text_commands/intro.html
|
||||
//
|
||||
// You can find samples of using the interaction framework:
|
||||
// - Here, under the InteractionFramework sample
|
||||
// - At the guides: https://discordnet.dev/guides/int_framework/intro.html
|
||||
class Program
|
||||
// Non-static readonly fields can only be assigned in a constructor.
|
||||
// If you want to assign it elsewhere, consider removing the readonly keyword.
|
||||
private static DiscordSocketClient _client;
|
||||
|
||||
// Discord.Net heavily utilizes TAP for async, so we create
|
||||
// an asynchronous context from the beginning.
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
// Non-static readonly fields can only be assigned in a constructor.
|
||||
// If you want to assign it elsewhere, consider removing the readonly keyword.
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
// Discord.Net heavily utilizes TAP for async, so we create
|
||||
// an asynchronous context from the beginning.
|
||||
static void Main(string[] args)
|
||||
=> new Program()
|
||||
.MainAsync()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
public Program()
|
||||
// Config used by DiscordSocketClient
|
||||
// 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
|
||||
{
|
||||
// Config used by DiscordSocketClient
|
||||
// Define intents for the client
|
||||
var config = new DiscordSocketConfig
|
||||
{
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||
};
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||
};
|
||||
|
||||
// It is recommended to Dispose of a client when you are finished
|
||||
// using it, at the end of your app's lifetime.
|
||||
_client = new DiscordSocketClient(config);
|
||||
// It is recommended to Dispose of a client when you are finished
|
||||
// using it, at the end of your app's lifetime.
|
||||
_client = new DiscordSocketClient(config);
|
||||
|
||||
// Subscribing to client events, so that we may receive them whenever they're invoked.
|
||||
_client.Log += LogAsync;
|
||||
_client.Ready += ReadyAsync;
|
||||
_client.MessageReceived += MessageReceivedAsync;
|
||||
_client.InteractionCreated += InteractionCreatedAsync;
|
||||
// Subscribing to client events, so that we may receive them whenever they're invoked.
|
||||
_client.Log += LogAsync;
|
||||
_client.Ready += ReadyAsync;
|
||||
_client.MessageReceived += MessageReceivedAsync;
|
||||
_client.InteractionCreated += InteractionCreatedAsync;
|
||||
|
||||
|
||||
// Tokens should be considered secret data, and never hard-coded.
|
||||
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.
|
||||
|
||||
await _client.StartAsync();
|
||||
|
||||
// Block the program until it is closed.
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
|
||||
private static Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// The Ready event indicates that the client has opened a
|
||||
// connection and it is now safe to access the cache.
|
||||
private static Task ReadyAsync()
|
||||
{
|
||||
Console.WriteLine($"{_client.CurrentUser} is connected!");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// This is not the recommended way to write a bot - consider
|
||||
// reading over the Commands Framework sample.
|
||||
private static async Task MessageReceivedAsync(SocketMessage message)
|
||||
{
|
||||
// The bot should never respond to itself.
|
||||
if (message.Author.Id == _client.CurrentUser.Id)
|
||||
return;
|
||||
|
||||
|
||||
if (message.Content == "!ping")
|
||||
{
|
||||
// Create a new ComponentBuilder, in which dropdowns & buttons can be created.
|
||||
var cb = new ComponentBuilder()
|
||||
.WithButton("Click me!", "unique-id", ButtonStyle.Primary);
|
||||
|
||||
// Send a message with content 'pong', including a button.
|
||||
// This button needs to be build by calling .Build() before being passed into the call.
|
||||
await message.Channel.SendMessageAsync("pong!", components: cb.Build());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MainAsync()
|
||||
// For better functionality & a more developer-friendly approach to handling any kind of interaction, refer to:
|
||||
// https://discordnet.dev/guides/int_framework/intro.html
|
||||
private static async Task InteractionCreatedAsync(SocketInteraction interaction)
|
||||
{
|
||||
// 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 (interaction is SocketMessageComponent component)
|
||||
{
|
||||
// Tokens should be considered secret data, and never hard-coded.
|
||||
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.
|
||||
// Check for the ID created in the button mentioned above.
|
||||
if (component.Data.CustomId == "unique-id")
|
||||
await interaction.RespondAsync("Thank you for clicking my button!");
|
||||
|
||||
await _client.StartAsync();
|
||||
|
||||
// Block the program until it is closed.
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// The Ready event indicates that the client has opened a
|
||||
// connection and it is now safe to access the cache.
|
||||
private Task ReadyAsync()
|
||||
{
|
||||
Console.WriteLine($"{_client.CurrentUser} is connected!");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// This is not the recommended way to write a bot - consider
|
||||
// reading over the Commands Framework sample.
|
||||
private async Task MessageReceivedAsync(SocketMessage message)
|
||||
{
|
||||
// The bot should never respond to itself.
|
||||
if (message.Author.Id == _client.CurrentUser.Id)
|
||||
return;
|
||||
|
||||
|
||||
if (message.Content == "!ping")
|
||||
{
|
||||
// Create a new ComponentBuilder, in which dropdowns & buttons can be created.
|
||||
var cb = new ComponentBuilder()
|
||||
.WithButton("Click me!", "unique-id", ButtonStyle.Primary);
|
||||
|
||||
// Send a message with content 'pong', including a button.
|
||||
// This button needs to be build by calling .Build() before being passed into the call.
|
||||
await message.Channel.SendMessageAsync("pong!", components: cb.Build());
|
||||
}
|
||||
}
|
||||
|
||||
// For better functionality & a more developer-friendly approach to handling any kind of interaction, refer to:
|
||||
// https://discordnet.dev/guides/int_framework/intro.html
|
||||
private async Task InteractionCreatedAsync(SocketInteraction interaction)
|
||||
{
|
||||
// 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 (interaction is SocketMessageComponent component)
|
||||
{
|
||||
// Check for the ID created in the button mentioned above.
|
||||
if (component.Data.CustomId == "unique-id")
|
||||
await interaction.RespondAsync("Thank you for clicking my button!");
|
||||
|
||||
else
|
||||
Console.WriteLine("An ID has been received that has no handler!");
|
||||
}
|
||||
else
|
||||
Console.WriteLine("An ID has been received that has no handler!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="3.10.0"/>
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="3.13.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -4,35 +4,30 @@ using Discord.WebSocket;
|
||||
using System;
|
||||
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)
|
||||
{
|
||||
// Check if the component matches the target properly.
|
||||
if (context.Interaction is not SocketMessageComponent componentContext)
|
||||
return Task.FromResult(PreconditionResult.FromError("Context unrecognized as component context."));
|
||||
// Check if the component matches the target properly.
|
||||
if (context.Interaction is not SocketMessageComponent componentContext)
|
||||
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:
|
||||
// unique-name:*,*
|
||||
// The approach here entirely depends on how you construct your custom ID. In this case, the format is:
|
||||
// unique-name:*,*
|
||||
|
||||
// here the name and wildcards are split by ':'
|
||||
var param = componentContext.Data.CustomId.Split(':');
|
||||
// here the name and wildcards are split by ':'
|
||||
var param = componentContext.Data.CustomId.Split(':');
|
||||
|
||||
// here we determine that we should always check for the first ',' present.
|
||||
// This will deal with additional wildcards by always selecting the first wildcard present.
|
||||
if (param.Length > 1 && ulong.TryParse(param[1].Split(',')[0], out ulong id))
|
||||
return (context.User.Id == id)
|
||||
// If the user ID
|
||||
? Task.FromResult(PreconditionResult.FromSuccess())
|
||||
: Task.FromResult(PreconditionResult.FromError("User ID does not match component ID!"));
|
||||
// here we determine that we should always check for the first ',' present.
|
||||
// This will deal with additional wildcards by always selecting the first wildcard present.
|
||||
if (param.Length > 1 && ulong.TryParse(param[1].Split(',')[0], out ulong id))
|
||||
return (context.User.Id == id)
|
||||
// If the user ID
|
||||
? Task.FromResult(PreconditionResult.FromSuccess())
|
||||
: 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."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
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)
|
||||
switch (context.Client.TokenType)
|
||||
{
|
||||
switch (context.Client.TokenType)
|
||||
{
|
||||
case TokenType.Bot:
|
||||
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false);
|
||||
if (context.User.Id != application.Owner.Id)
|
||||
return PreconditionResult.FromError(ErrorMessage ?? "Command can only be run by the owner of the bot.");
|
||||
return PreconditionResult.FromSuccess();
|
||||
default:
|
||||
return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}.");
|
||||
}
|
||||
case TokenType.Bot:
|
||||
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false);
|
||||
return context.User.Id != application.Owner.Id
|
||||
? PreconditionResult.FromError(ErrorMessage ?? "Command can only be run by the owner of the bot.")
|
||||
: PreconditionResult.FromSuccess();
|
||||
default:
|
||||
return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
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,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
[ChoiceDisplay("Twenty First")]
|
||||
TwentyFirst
|
||||
}
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
[ChoiceDisplay("Twenty First")]
|
||||
TwentyFirst
|
||||
}
|
||||
|
||||
@@ -6,76 +6,90 @@ using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace InteractionFramework
|
||||
namespace InteractionFramework;
|
||||
|
||||
public class InteractionHandler
|
||||
{
|
||||
public class InteractionHandler
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly InteractionService _handler;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public InteractionHandler(DiscordSocketClient client, InteractionService handler, IServiceProvider services, IConfiguration config)
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly InteractionService _handler;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IConfiguration _configuration;
|
||||
_client = client;
|
||||
_handler = handler;
|
||||
_services = services;
|
||||
_configuration = config;
|
||||
}
|
||||
|
||||
public InteractionHandler(DiscordSocketClient client, InteractionService handler, IServiceProvider services, IConfiguration config)
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Process when the client is ready, so we can register our commands.
|
||||
_client.Ready += ReadyAsync;
|
||||
_handler.Log += LogAsync;
|
||||
|
||||
// Add the public modules that inherit InteractionModuleBase<T> to the InteractionService
|
||||
await _handler.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
|
||||
// Process the InteractionCreated payloads to execute Interactions commands
|
||||
_client.InteractionCreated += HandleInteraction;
|
||||
|
||||
// Also process the result of the command execution.
|
||||
_handler.InteractionExecuted += HandleInteractionExecute;
|
||||
}
|
||||
|
||||
private async Task LogAsync(LogMessage log)
|
||||
=> Console.WriteLine(log);
|
||||
|
||||
private async Task ReadyAsync()
|
||||
{
|
||||
// Register the commands globally.
|
||||
// alternatively you can use _handler.RegisterCommandsGloballyAsync() to register commands to a specific guild.
|
||||
await _handler.RegisterCommandsGloballyAsync();
|
||||
}
|
||||
|
||||
private async Task HandleInteraction(SocketInteraction interaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
_client = client;
|
||||
_handler = handler;
|
||||
_services = services;
|
||||
_configuration = config;
|
||||
// Create an execution context that matches the generic type parameter of your InteractionModuleBase<T> modules.
|
||||
var context = new SocketInteractionContext(_client, interaction);
|
||||
|
||||
// Execute the incoming command.
|
||||
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)
|
||||
switch (result.Error)
|
||||
{
|
||||
case InteractionCommandError.UnmetPrecondition:
|
||||
// implement
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
catch
|
||||
{
|
||||
// Process when the client is ready, so we can register our commands.
|
||||
_client.Ready += ReadyAsync;
|
||||
_handler.Log += LogAsync;
|
||||
|
||||
// Add the public modules that inherit InteractionModuleBase<T> to the InteractionService
|
||||
await _handler.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
|
||||
// Process the InteractionCreated payloads to execute Interactions commands
|
||||
_client.InteractionCreated += HandleInteraction;
|
||||
}
|
||||
|
||||
private async Task LogAsync(LogMessage log)
|
||||
=> Console.WriteLine(log);
|
||||
|
||||
private async Task ReadyAsync()
|
||||
{
|
||||
// Context & Slash commands can be automatically registered, but this process needs to happen after the client enters the READY state.
|
||||
// Since Global Commands take around 1 hour to register, we should use a test guild to instantly update and test our commands.
|
||||
if (Program.IsDebug())
|
||||
await _handler.RegisterCommandsToGuildAsync(_configuration.GetValue<ulong>("testGuild"), true);
|
||||
else
|
||||
await _handler.RegisterCommandsGloballyAsync(true);
|
||||
}
|
||||
|
||||
private async Task HandleInteraction(SocketInteraction interaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create an execution context that matches the generic type parameter of your InteractionModuleBase<T> modules.
|
||||
var context = new SocketInteractionContext(_client, interaction);
|
||||
|
||||
// Execute the incoming command.
|
||||
var result = await _handler.ExecuteCommandAsync(context, _services);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
switch (result.Error)
|
||||
{
|
||||
case InteractionCommandError.UnmetPrecondition:
|
||||
// implement
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If Slash Command execution fails it is most likely that the original interaction acknowledgement will persist. It is a good idea to delete the original
|
||||
// response, or at least let the user know that something went wrong during the command execution.
|
||||
if (interaction.Type is InteractionType.ApplicationCommand)
|
||||
await interaction.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync());
|
||||
}
|
||||
// If Slash Command execution fails it is most likely that the original interaction acknowledgement will persist. It is a good idea to delete the original
|
||||
// response, or at least let the user know that something went wrong during the command execution.
|
||||
if (interaction.Type is InteractionType.ApplicationCommand)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,97 +4,96 @@ using InteractionFramework.Attributes;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace InteractionFramework.Modules
|
||||
namespace InteractionFramework.Modules;
|
||||
|
||||
// Interaction modules must be public and inherit from an IInteractionModuleBase
|
||||
public class ExampleModule : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
// Interaction modules must be public and inherit from an IInteractionModuleBase
|
||||
public class ExampleModule : InteractionModuleBase<SocketInteractionContext>
|
||||
// Dependencies can be accessed through Property injection, public properties with public setters will be set by the service provider
|
||||
public InteractionService Commands { get; set; }
|
||||
|
||||
private InteractionHandler _handler;
|
||||
|
||||
// Constructor injection is also a valid way to access the dependencies
|
||||
public ExampleModule(InteractionHandler handler)
|
||||
{
|
||||
// Dependencies can be accessed through Property injection, public properties with public setters will be set by the service provider
|
||||
public InteractionService Commands { get; set; }
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
private InteractionHandler _handler;
|
||||
// You can use a number of parameter types in you Slash Command handlers (string, int, double, bool, IUser, IChannel, IMentionable, IRole, Enums) by default. Optionally,
|
||||
// you can implement your own TypeConverters to support a wider range of parameter types. For more information, refer to the library documentation.
|
||||
// Optional method parameters(parameters with a default value) also will be displayed as optional on Discord.
|
||||
|
||||
// Constructor injection is also a valid way to access the dependencies
|
||||
public ExampleModule(InteractionHandler handler)
|
||||
// [Summary] lets you customize the name and the description of a parameter
|
||||
[SlashCommand("echo", "Repeat the input")]
|
||||
public async Task Echo(string echo, [Summary(description: "mention the user")] bool mention = false)
|
||||
=> await RespondAsync(echo + (mention ? Context.User.Mention : string.Empty));
|
||||
|
||||
[SlashCommand("ping", "Pings the bot and returns its latency.")]
|
||||
public async Task GreetUserAsync()
|
||||
=> await RespondAsync(text: $":ping_pong: It took me {Context.Client.Latency}ms to respond to you!", ephemeral: true);
|
||||
|
||||
[SlashCommand("bitrate", "Gets the bitrate of a specific voice channel.")]
|
||||
public async Task GetBitrateAsync([ChannelTypes(ChannelType.Voice, ChannelType.Stage)] IChannel channel)
|
||||
=> await RespondAsync(text: $"This voice channel has a bitrate of {(channel as IVoiceChannel).Bitrate}");
|
||||
|
||||
// [Group] will create a command group. [SlashCommand]s and [ComponentInteraction]s will be registered with the group prefix
|
||||
[Group("test_group", "This is a command group")]
|
||||
public class GroupExample : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
// You can create command choices either by using the [Choice] attribute or by creating an enum. Every enum with 25 or less values will be registered as a multiple
|
||||
// choice option
|
||||
[SlashCommand("choice_example", "Enums create choices")]
|
||||
public async Task ChoiceExample(ExampleEnum input)
|
||||
=> await RespondAsync(input.ToString());
|
||||
}
|
||||
|
||||
// Use [ComponentInteraction] to handle message component interactions. Message component interaction with the matching customId will be executed.
|
||||
// Alternatively, you can create a wild card pattern using the '*' character. Interaction Service will perform a lazy regex search and capture the matching strings.
|
||||
// You can then access these capture groups from the method parameters, in the order they were captured. Using the wild card pattern, you can cherry pick component interactions.
|
||||
[ComponentInteraction("musicSelect:*,*")]
|
||||
public async Task ButtonPress(string id, string name)
|
||||
{
|
||||
// ...
|
||||
await RespondAsync($"Playing song: {name}/{id}");
|
||||
}
|
||||
|
||||
// Select Menu interactions, contain ids of the menu options that were selected by the user. You can access the option ids from the method parameters.
|
||||
// You can also use the wild card pattern with Select Menus, in that case, the wild card captures will be passed on to the method first, followed by the option ids.
|
||||
[ComponentInteraction("roleSelect")]
|
||||
public async Task RoleSelect(string[] selections)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// With the Attribute DoUserCheck you can make sure that only the user this button targets can click it. This is defined by the first wildcard: *.
|
||||
// See Attributes/DoUserCheckAttribute.cs for elaboration.
|
||||
[DoUserCheck]
|
||||
[ComponentInteraction("myButton:*")]
|
||||
public async Task ClickButtonAsync(string userId)
|
||||
=> await RespondAsync(text: ":thumbsup: Clicked!");
|
||||
|
||||
// This command will greet target user in the channel this was executed in.
|
||||
[UserCommand("greet")]
|
||||
public async Task GreetUserAsync(IUser user)
|
||||
=> await RespondAsync(text: $":wave: {Context.User} said hi to you, <@{user.Id}>!");
|
||||
|
||||
// Pins a message in the channel it is in.
|
||||
[MessageCommand("pin")]
|
||||
public async Task PinMessageAsync(IMessage message)
|
||||
{
|
||||
// make a safety cast to check if the message is ISystem- or IUserMessage
|
||||
if (message is not IUserMessage userMessage)
|
||||
await RespondAsync(text: ":x: You cant pin system messages!");
|
||||
|
||||
// if the pins in this channel are equal to or above 50, no more messages can be pinned.
|
||||
else if ((await Context.Channel.GetPinnedMessagesAsync()).Count >= 50)
|
||||
await RespondAsync(text: ":x: You cant pin any more messages, the max has already been reached in this channel!");
|
||||
|
||||
else
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
// You can use a number of parameter types in you Slash Command handlers (string, int, double, bool, IUser, IChannel, IMentionable, IRole, Enums) by default. Optionally,
|
||||
// you can implement your own TypeConverters to support a wider range of parameter types. For more information, refer to the library documentation.
|
||||
// Optional method parameters(parameters with a default value) also will be displayed as optional on Discord.
|
||||
|
||||
// [Summary] lets you customize the name and the description of a parameter
|
||||
[SlashCommand("echo", "Repeat the input")]
|
||||
public async Task Echo(string echo, [Summary(description: "mention the user")] bool mention = false)
|
||||
=> await RespondAsync(echo + (mention ? Context.User.Mention : string.Empty));
|
||||
|
||||
[SlashCommand("ping", "Pings the bot and returns its latency.")]
|
||||
public async Task GreetUserAsync()
|
||||
=> await RespondAsync(text: $":ping_pong: It took me {Context.Client.Latency}ms to respond to you!", ephemeral: true);
|
||||
|
||||
[SlashCommand("bitrate", "Gets the bitrate of a specific voice channel.")]
|
||||
public async Task GetBitrateAsync([ChannelTypes(ChannelType.Voice, ChannelType.Stage)] IChannel channel)
|
||||
=> await RespondAsync(text: $"This voice channel has a bitrate of {(channel as IVoiceChannel).Bitrate}");
|
||||
|
||||
// [Group] will create a command group. [SlashCommand]s and [ComponentInteraction]s will be registered with the group prefix
|
||||
[Group("test_group", "This is a command group")]
|
||||
public class GroupExample : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
// You can create command choices either by using the [Choice] attribute or by creating an enum. Every enum with 25 or less values will be registered as a multiple
|
||||
// choice option
|
||||
[SlashCommand("choice_example", "Enums create choices")]
|
||||
public async Task ChoiceExample(ExampleEnum input)
|
||||
=> await RespondAsync(input.ToString());
|
||||
}
|
||||
|
||||
// Use [ComponentInteraction] to handle message component interactions. Message component interaction with the matching customId will be executed.
|
||||
// Alternatively, you can create a wild card pattern using the '*' character. Interaction Service will perform a lazy regex search and capture the matching strings.
|
||||
// You can then access these capture groups from the method parameters, in the order they were captured. Using the wild card pattern, you can cherry pick component interactions.
|
||||
[ComponentInteraction("musicSelect:*,*")]
|
||||
public async Task ButtonPress(string id, string name)
|
||||
{
|
||||
// ...
|
||||
await RespondAsync($"Playing song: {name}/{id}");
|
||||
}
|
||||
|
||||
// Select Menu interactions, contain ids of the menu options that were selected by the user. You can access the option ids from the method parameters.
|
||||
// You can also use the wild card pattern with Select Menus, in that case, the wild card captures will be passed on to the method first, followed by the option ids.
|
||||
[ComponentInteraction("roleSelect")]
|
||||
public async Task RoleSelect(string[] selections)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// With the Attribute DoUserCheck you can make sure that only the user this button targets can click it. This is defined by the first wildcard: *.
|
||||
// See Attributes/DoUserCheckAttribute.cs for elaboration.
|
||||
[DoUserCheck]
|
||||
[ComponentInteraction("myButton:*")]
|
||||
public async Task ClickButtonAsync(string userId)
|
||||
=> await RespondAsync(text: ":thumbsup: Clicked!");
|
||||
|
||||
// This command will greet target user in the channel this was executed in.
|
||||
[UserCommand("greet")]
|
||||
public async Task GreetUserAsync(IUser user)
|
||||
=> await RespondAsync(text: $":wave: {Context.User} said hi to you, <@{user.Id}>!");
|
||||
|
||||
// Pins a message in the channel it is in.
|
||||
[MessageCommand("pin")]
|
||||
public async Task PinMessageAsync(IMessage message)
|
||||
{
|
||||
// make a safety cast to check if the message is ISystem- or IUserMessage
|
||||
if (message is not IUserMessage userMessage)
|
||||
await RespondAsync(text: ":x: You cant pin system messages!");
|
||||
|
||||
// if the pins in this channel are equal to or above 50, no more messages can be pinned.
|
||||
else if ((await Context.Channel.GetPinnedMessagesAsync()).Count >= 50)
|
||||
await RespondAsync(text: ":x: You cant pin any more messages, the max has already been reached in this channel!");
|
||||
|
||||
else
|
||||
{
|
||||
await userMessage.PinAsync();
|
||||
await RespondAsync(":white_check_mark: Successfully pinned message!");
|
||||
}
|
||||
await userMessage.PinAsync();
|
||||
await RespondAsync(":white_check_mark: Successfully pinned message!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,68 +7,50 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace InteractionFramework
|
||||
namespace InteractionFramework;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public class Program
|
||||
private static IConfiguration _configuration;
|
||||
private static IServiceProvider _services;
|
||||
|
||||
private static readonly DiscordSocketConfig _socketConfig = new()
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IServiceProvider _services;
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers,
|
||||
AlwaysDownloadUsers = true,
|
||||
};
|
||||
|
||||
private readonly DiscordSocketConfig _socketConfig = new()
|
||||
{
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers,
|
||||
AlwaysDownloadUsers = true,
|
||||
};
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
_configuration = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables(prefix: "DC_")
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.Build();
|
||||
|
||||
public Program()
|
||||
{
|
||||
_configuration = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables(prefix: "DC_")
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.Build();
|
||||
_services = new ServiceCollection()
|
||||
.AddSingleton(_configuration)
|
||||
.AddSingleton(_socketConfig)
|
||||
.AddSingleton<DiscordSocketClient>()
|
||||
.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordSocketClient>()))
|
||||
.AddSingleton<InteractionHandler>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
_services = new ServiceCollection()
|
||||
.AddSingleton(_configuration)
|
||||
.AddSingleton(_socketConfig)
|
||||
.AddSingleton<DiscordSocketClient>()
|
||||
.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordSocketClient>()))
|
||||
.AddSingleton<InteractionHandler>()
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
var client = _services.GetRequiredService<DiscordSocketClient>();
|
||||
|
||||
static void Main(string[] args)
|
||||
=> new Program().RunAsync()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
client.Log += LogAsync;
|
||||
|
||||
public async Task RunAsync()
|
||||
{
|
||||
var client = _services.GetRequiredService<DiscordSocketClient>();
|
||||
// Here we can initialize the service that will register and execute our commands
|
||||
await _services.GetRequiredService<InteractionHandler>()
|
||||
.InitializeAsync();
|
||||
|
||||
client.Log += LogAsync;
|
||||
// Bot token can be provided from the Configuration object we set up earlier
|
||||
await client.LoginAsync(TokenType.Bot, _configuration["token"]);
|
||||
await client.StartAsync();
|
||||
|
||||
// Here we can initialize the service that will register and execute our commands
|
||||
await _services.GetRequiredService<InteractionHandler>()
|
||||
.InitializeAsync();
|
||||
|
||||
// Bot token can be provided from the Configuration object we set up earlier
|
||||
await client.LoginAsync(TokenType.Bot, _configuration["token"]);
|
||||
await client.StartAsync();
|
||||
|
||||
// Never quit the program until manually forced to.
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
|
||||
private async Task LogAsync(LogMessage message)
|
||||
=> Console.WriteLine(message.ToString());
|
||||
|
||||
public static bool IsDebug()
|
||||
{
|
||||
#if DEBUG
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
// Never quit the program until manually forced to.
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
|
||||
private static async Task LogAsync(LogMessage message)
|
||||
=> Console.WriteLine(message.ToString());
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||
<PackageReference Include="Discord.Net.Interactions" Version="3.10.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageReference Include="Discord.Net.Interactions" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
using Discord.Interactions;
|
||||
using Discord.WebSocket;
|
||||
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.
|
||||
public class InteractionModule : InteractionModuleBase<ShardedInteractionContext>
|
||||
{
|
||||
// A display of portability, which shows how minimal the difference between the 2 frameworks is.
|
||||
public class InteractionModule : InteractionModuleBase<ShardedInteractionContext>
|
||||
[SlashCommand("info", "Information about this shard.")]
|
||||
public async Task InfoAsync()
|
||||
{
|
||||
[SlashCommand("info", "Information about this shard.")]
|
||||
public async Task InfoAsync()
|
||||
{
|
||||
var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards!
|
||||
var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards!
|
||||
This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}";
|
||||
await RespondAsync(msg);
|
||||
}
|
||||
await RespondAsync(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using Discord.Commands;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardedClient.Modules
|
||||
namespace ShardedClient.Modules;
|
||||
|
||||
// Remember to make your module reference the ShardedCommandContext
|
||||
public class PublicModule : ModuleBase<ShardedCommandContext>
|
||||
{
|
||||
// Remember to make your module reference the ShardedCommandContext
|
||||
public class PublicModule : ModuleBase<ShardedCommandContext>
|
||||
[Command("info")]
|
||||
public async Task InfoAsync()
|
||||
{
|
||||
[Command("info")]
|
||||
public async Task InfoAsync()
|
||||
{
|
||||
var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards!
|
||||
var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards!
|
||||
This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}";
|
||||
await ReplyAsync(msg);
|
||||
}
|
||||
await ReplyAsync(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,78 +8,70 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardedClient
|
||||
namespace ShardedClient;
|
||||
|
||||
// This is a minimal example of using Discord.Net's Sharded Client
|
||||
// The provided DiscordShardedClient class simplifies having multiple
|
||||
// DiscordSocketClient instances (or shards) to serve a large number of guilds.
|
||||
class Program
|
||||
{
|
||||
// This is a minimal example of using Discord.Net's Sharded Client
|
||||
// The provided DiscordShardedClient class simplifies having multiple
|
||||
// DiscordSocketClient instances (or shards) to serve a large number of guilds.
|
||||
class Program
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
static void 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
|
||||
// DiscordSocketConfig. Generally, it's recommended to
|
||||
// have 1 shard per 1500-2000 guilds your bot is in.
|
||||
var config = new DiscordSocketConfig
|
||||
{
|
||||
// You specify the amount of shards you'd like to have with the
|
||||
// DiscordSocketConfig. Generally, it's recommended to
|
||||
// have 1 shard per 1500-2000 guilds your bot is in.
|
||||
var config = new DiscordSocketConfig
|
||||
{
|
||||
TotalShards = 2,
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||
};
|
||||
TotalShards = 2,
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||
};
|
||||
|
||||
// You should dispose a service provider created using ASP.NET
|
||||
// when you are finished using it, at the end of your app's lifetime.
|
||||
// If you use another dependency injection framework, you should inspect
|
||||
// its documentation for the best way to do this.
|
||||
using (var services = ConfigureServices(config))
|
||||
{
|
||||
var client = services.GetRequiredService<DiscordShardedClient>();
|
||||
// You should dispose a service provider created using ASP.NET
|
||||
// when you are finished using it, at the end of your app's lifetime.
|
||||
// If you use another dependency injection framework, you should inspect
|
||||
// its documentation for the best way to do this.
|
||||
await using var services = ConfigureServices(config);
|
||||
|
||||
// The Sharded Client does not have a Ready event.
|
||||
// The ShardReady event is used instead, allowing for individual
|
||||
// control per shard.
|
||||
client.ShardReady += ReadyAsync;
|
||||
client.Log += LogAsync;
|
||||
var client = services.GetRequiredService<DiscordShardedClient>();
|
||||
|
||||
await services.GetRequiredService<InteractionHandlingService>()
|
||||
.InitializeAsync();
|
||||
// The Sharded Client does not have a Ready event.
|
||||
// The ShardReady event is used instead, allowing for individual
|
||||
// control per shard.
|
||||
client.ShardReady += ReadyAsync;
|
||||
client.Log += LogAsync;
|
||||
|
||||
await services.GetRequiredService<CommandHandlingService>()
|
||||
.InitializeAsync();
|
||||
await services.GetRequiredService<InteractionHandlingService>()
|
||||
.InitializeAsync();
|
||||
|
||||
// Tokens should be considered secret data, and never hard-coded.
|
||||
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
|
||||
await client.StartAsync();
|
||||
await services.GetRequiredService<CommandHandlingService>()
|
||||
.InitializeAsync();
|
||||
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
// Tokens should be considered secret data, and never hard-coded.
|
||||
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
|
||||
await client.StartAsync();
|
||||
|
||||
private ServiceProvider ConfigureServices(DiscordSocketConfig config)
|
||||
=> new ServiceCollection()
|
||||
.AddSingleton(new DiscordShardedClient(config))
|
||||
.AddSingleton<CommandService>()
|
||||
.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordShardedClient>()))
|
||||
.AddSingleton<CommandHandlingService>()
|
||||
.AddSingleton<InteractionHandlingService>()
|
||||
.BuildServiceProvider();
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
|
||||
private static ServiceProvider ConfigureServices(DiscordSocketConfig config)
|
||||
=> new ServiceCollection()
|
||||
.AddSingleton(new DiscordShardedClient(config))
|
||||
.AddSingleton<CommandService>()
|
||||
.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordShardedClient>()))
|
||||
.AddSingleton<CommandHandlingService>()
|
||||
.AddSingleton<InteractionHandlingService>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
|
||||
private Task ReadyAsync(DiscordSocketClient shard)
|
||||
{
|
||||
Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
private static Task ReadyAsync(DiscordSocketClient shard)
|
||||
{
|
||||
Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
private static Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,67 +6,66 @@ using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardedClient.Services
|
||||
namespace ShardedClient.Services;
|
||||
|
||||
public class CommandHandlingService
|
||||
{
|
||||
public class CommandHandlingService
|
||||
private readonly CommandService _commands;
|
||||
private readonly DiscordShardedClient _discord;
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public CommandHandlingService(IServiceProvider services)
|
||||
{
|
||||
private readonly CommandService _commands;
|
||||
private readonly DiscordShardedClient _discord;
|
||||
private readonly IServiceProvider _services;
|
||||
_commands = services.GetRequiredService<CommandService>();
|
||||
_discord = services.GetRequiredService<DiscordShardedClient>();
|
||||
_services = services;
|
||||
|
||||
public CommandHandlingService(IServiceProvider services)
|
||||
{
|
||||
_commands = services.GetRequiredService<CommandService>();
|
||||
_discord = services.GetRequiredService<DiscordShardedClient>();
|
||||
_services = services;
|
||||
_commands.CommandExecuted += CommandExecutedAsync;
|
||||
_commands.Log += LogAsync;
|
||||
_discord.MessageReceived += MessageReceivedAsync;
|
||||
}
|
||||
|
||||
_commands.CommandExecuted += CommandExecutedAsync;
|
||||
_commands.Log += LogAsync;
|
||||
_discord.MessageReceived += MessageReceivedAsync;
|
||||
}
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
}
|
||||
public async Task MessageReceivedAsync(SocketMessage rawMessage)
|
||||
{
|
||||
// Ignore system messages, or messages from other bots
|
||||
if (rawMessage is not SocketUserMessage message)
|
||||
return;
|
||||
if (message.Source != MessageSource.User)
|
||||
return;
|
||||
|
||||
public async Task MessageReceivedAsync(SocketMessage rawMessage)
|
||||
{
|
||||
// Ignore system messages, or messages from other bots
|
||||
if (rawMessage is not SocketUserMessage message)
|
||||
return;
|
||||
if (message.Source != MessageSource.User)
|
||||
return;
|
||||
// This value holds the offset where the prefix ends
|
||||
var argPos = 0;
|
||||
if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos))
|
||||
return;
|
||||
|
||||
// This value holds the offset where the prefix ends
|
||||
var argPos = 0;
|
||||
if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos))
|
||||
return;
|
||||
// A new kind of command context, ShardedCommandContext can be utilized with the commands framework
|
||||
var context = new ShardedCommandContext(_discord, message);
|
||||
await _commands.ExecuteAsync(context, argPos, _services);
|
||||
}
|
||||
|
||||
// A new kind of command context, ShardedCommandContext can be utilized with the commands framework
|
||||
var context = new ShardedCommandContext(_discord, message);
|
||||
await _commands.ExecuteAsync(context, argPos, _services);
|
||||
}
|
||||
public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
|
||||
{
|
||||
// command is unspecified when there was a search failure (command not found); we don't care about these errors
|
||||
if (!command.IsSpecified)
|
||||
return;
|
||||
|
||||
public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
|
||||
{
|
||||
// command is unspecified when there was a search failure (command not found); we don't care about these errors
|
||||
if (!command.IsSpecified)
|
||||
return;
|
||||
// the command was successful, we don't care about this result, unless we want to log that a command succeeded.
|
||||
if (result.IsSuccess)
|
||||
return;
|
||||
|
||||
// the command was successful, we don't care about this result, unless we want to log that a command succeeded.
|
||||
if (result.IsSuccess)
|
||||
return;
|
||||
// the command failed, let's notify the user that something happened.
|
||||
await context.Channel.SendMessageAsync($"error: {result}");
|
||||
}
|
||||
|
||||
// the command failed, let's notify the user that something happened.
|
||||
await context.Channel.SendMessageAsync($"error: {result}");
|
||||
}
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,65 @@
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardedClient.Services
|
||||
namespace ShardedClient.Services;
|
||||
|
||||
public class InteractionHandlingService
|
||||
{
|
||||
public class InteractionHandlingService
|
||||
private readonly InteractionService _service;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly IServiceProvider _provider;
|
||||
|
||||
public InteractionHandlingService(IServiceProvider services)
|
||||
{
|
||||
private readonly InteractionService _service;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly IServiceProvider _provider;
|
||||
_service = services.GetRequiredService<InteractionService>();
|
||||
_client = services.GetRequiredService<DiscordShardedClient>();
|
||||
_provider = services;
|
||||
|
||||
public InteractionHandlingService(IServiceProvider services)
|
||||
_service.Log += LogAsync;
|
||||
_client.InteractionCreated += OnInteractionAsync;
|
||||
_client.ShardReady += ReadyAsync;
|
||||
// For examples on how to handle post execution,
|
||||
// see the InteractionFramework samples.
|
||||
}
|
||||
|
||||
// Register all modules, and add the commands from these modules to either guild or globally depending on the build state.
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await _service.AddModulesAsync(typeof(InteractionHandlingService).Assembly, _provider);
|
||||
}
|
||||
|
||||
private async Task OnInteractionAsync(SocketInteraction interaction)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
_service = services.GetRequiredService<InteractionService>();
|
||||
_client = services.GetRequiredService<DiscordShardedClient>();
|
||||
_provider = services;
|
||||
var context = new ShardedInteractionContext(_client, interaction);
|
||||
await _service.ExecuteCommandAsync(context, _provider);
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
_service.Log += LogAsync;
|
||||
_client.InteractionCreated += OnInteractionAsync;
|
||||
_client.ShardReady += ReadyAsync;
|
||||
// For examples on how to handle post execution,
|
||||
// see the InteractionFramework samples.
|
||||
}
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
|
||||
// Register all modules, and add the commands from these modules to either guild or globally depending on the build state.
|
||||
public async Task InitializeAsync()
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private bool _hasRegistered = false;
|
||||
|
||||
private async Task ReadyAsync(DiscordSocketClient _)
|
||||
{
|
||||
// ShardReady is called for each shard; to avoid getting ratelimited we only want to register commands once.
|
||||
if (!_hasRegistered)
|
||||
{
|
||||
await _service.AddModulesAsync(typeof(InteractionHandlingService).Assembly, _provider);
|
||||
}
|
||||
|
||||
private async Task OnInteractionAsync(SocketInteraction interaction)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var context = new ShardedInteractionContext(_client, interaction);
|
||||
await _service.ExecuteCommandAsync(context, _provider);
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ReadyAsync(DiscordSocketClient _)
|
||||
{
|
||||
#if DEBUG
|
||||
await _service.RegisterCommandsToGuildAsync(1 /* implement */);
|
||||
#else
|
||||
await _service.RegisterCommandsGloballyAsync();
|
||||
#endif
|
||||
_hasRegistered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||
<PackageReference Include="Discord.Net" Version="3.10.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Discord.Net" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -4,66 +4,65 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using TextCommandFramework.Services;
|
||||
|
||||
namespace TextCommandFramework.Modules
|
||||
namespace TextCommandFramework.Modules;
|
||||
|
||||
// Modules must be public and inherit from an IModuleBase
|
||||
public class PublicModule : ModuleBase<SocketCommandContext>
|
||||
{
|
||||
// Modules must be public and inherit from an IModuleBase
|
||||
public class PublicModule : ModuleBase<SocketCommandContext>
|
||||
// Dependency Injection will fill this value in for us
|
||||
public PictureService PictureService { get; set; }
|
||||
|
||||
[Command("ping")]
|
||||
[Alias("pong", "hello")]
|
||||
public Task PingAsync()
|
||||
=> ReplyAsync("pong!");
|
||||
|
||||
[Command("cat")]
|
||||
public async Task CatAsync()
|
||||
{
|
||||
// Dependency Injection will fill this value in for us
|
||||
public PictureService PictureService { get; set; }
|
||||
|
||||
[Command("ping")]
|
||||
[Alias("pong", "hello")]
|
||||
public Task PingAsync()
|
||||
=> ReplyAsync("pong!");
|
||||
|
||||
[Command("cat")]
|
||||
public async Task CatAsync()
|
||||
{
|
||||
// Get a stream containing an image of a cat
|
||||
var stream = await PictureService.GetCatPictureAsync();
|
||||
// Streams must be seeked to their beginning before being uploaded!
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
await Context.Channel.SendFileAsync(stream, "cat.png");
|
||||
}
|
||||
|
||||
// Get info on a user, or the user who invoked the command if one is not specified
|
||||
[Command("userinfo")]
|
||||
public async Task UserInfoAsync(IUser user = null)
|
||||
{
|
||||
user ??= Context.User;
|
||||
|
||||
await ReplyAsync(user.ToString());
|
||||
}
|
||||
|
||||
// Ban a user
|
||||
[Command("ban")]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
// make sure the user invoking the command can ban
|
||||
[RequireUserPermission(GuildPermission.BanMembers)]
|
||||
// make sure the bot itself can ban
|
||||
[RequireBotPermission(GuildPermission.BanMembers)]
|
||||
public async Task BanUserAsync(IGuildUser user, [Remainder] string reason = null)
|
||||
{
|
||||
await user.Guild.AddBanAsync(user, reason: reason);
|
||||
await ReplyAsync("ok!");
|
||||
}
|
||||
|
||||
// [Remainder] takes the rest of the command's arguments as one argument, rather than splitting every space
|
||||
[Command("echo")]
|
||||
public Task EchoAsync([Remainder] string text)
|
||||
// Insert a ZWSP before the text to prevent triggering other bots!
|
||||
=> ReplyAsync('\u200B' + text);
|
||||
|
||||
// 'params' will parse space-separated elements into a list
|
||||
[Command("list")]
|
||||
public Task ListAsync(params string[] objects)
|
||||
=> ReplyAsync("You listed: " + string.Join("; ", objects));
|
||||
|
||||
// Setting a custom ErrorMessage property will help clarify the precondition error
|
||||
[Command("guild_only")]
|
||||
[RequireContext(ContextType.Guild, ErrorMessage = "Sorry, this command must be ran from within a server, not a DM!")]
|
||||
public Task GuildOnlyCommand()
|
||||
=> ReplyAsync("Nothing to see here!");
|
||||
// Get a stream containing an image of a cat
|
||||
var stream = await PictureService.GetCatPictureAsync();
|
||||
// Streams must be seeked to their beginning before being uploaded!
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
await Context.Channel.SendFileAsync(stream, "cat.png");
|
||||
}
|
||||
|
||||
// Get info on a user, or the user who invoked the command if one is not specified
|
||||
[Command("userinfo")]
|
||||
public async Task UserInfoAsync(IUser user = null)
|
||||
{
|
||||
user ??= Context.User;
|
||||
|
||||
await ReplyAsync(user.ToString());
|
||||
}
|
||||
|
||||
// Ban a user
|
||||
[Command("ban")]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
// make sure the user invoking the command can ban
|
||||
[RequireUserPermission(GuildPermission.BanMembers)]
|
||||
// make sure the bot itself can ban
|
||||
[RequireBotPermission(GuildPermission.BanMembers)]
|
||||
public async Task BanUserAsync(IGuildUser user, [Remainder] string reason = null)
|
||||
{
|
||||
await user.Guild.AddBanAsync(user, reason: reason);
|
||||
await ReplyAsync("ok!");
|
||||
}
|
||||
|
||||
// [Remainder] takes the rest of the command's arguments as one argument, rather than splitting every space
|
||||
[Command("echo")]
|
||||
public Task EchoAsync([Remainder] string text)
|
||||
// Insert a ZWSP before the text to prevent triggering other bots!
|
||||
=> ReplyAsync('\u200B' + text);
|
||||
|
||||
// 'params' will parse space-separated elements into a list
|
||||
[Command("list")]
|
||||
public Task ListAsync(params string[] objects)
|
||||
=> ReplyAsync("You listed: " + string.Join("; ", objects));
|
||||
|
||||
// Setting a custom ErrorMessage property will help clarify the precondition error
|
||||
[Command("guild_only")]
|
||||
[RequireContext(ContextType.Guild, ErrorMessage = "Sorry, this command must be ran from within a server, not a DM!")]
|
||||
public Task GuildOnlyCommand()
|
||||
=> ReplyAsync("Nothing to see here!");
|
||||
}
|
||||
|
||||
@@ -8,68 +8,62 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TextCommandFramework.Services;
|
||||
|
||||
namespace TextCommandFramework
|
||||
namespace TextCommandFramework;
|
||||
|
||||
// This is a minimal example of using Discord.Net's command
|
||||
// framework - by no means does it show everything the framework
|
||||
// is capable of.
|
||||
//
|
||||
// You can find samples of using the command framework:
|
||||
// - Here, under the 02_commands_framework sample
|
||||
// - https://github.com/foxbot/DiscordBotBase - a bare-bones bot template
|
||||
// - https://github.com/foxbot/patek - a more feature-filled bot, utilizing more aspects of the library
|
||||
class Program
|
||||
{
|
||||
// This is a minimal example of using Discord.Net's command
|
||||
// framework - by no means does it show everything the framework
|
||||
// is capable of.
|
||||
//
|
||||
// You can find samples of using the command framework:
|
||||
// - Here, under the 02_commands_framework sample
|
||||
// - https://github.com/foxbot/DiscordBotBase - a bare-bones bot template
|
||||
// - https://github.com/foxbot/patek - a more feature-filled bot, utilizing more aspects of the library
|
||||
class Program
|
||||
// There is no need to implement IDisposable like before as we are
|
||||
// using dependency injection, which handles calling Dispose for us.
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
// There is no need to implement IDisposable like before as we are
|
||||
// using dependency injection, which handles calling Dispose for us.
|
||||
static void Main(string[] args)
|
||||
=> new Program().MainAsync().GetAwaiter().GetResult();
|
||||
// You should dispose a service provider created using ASP.NET
|
||||
// when you are finished using it, at the end of your app's lifetime.
|
||||
// If you use another dependency injection framework, you should inspect
|
||||
// its documentation for the best way to do this.
|
||||
await using var services = ConfigureServices();
|
||||
var client = services.GetRequiredService<DiscordSocketClient>();
|
||||
|
||||
public async Task MainAsync()
|
||||
{
|
||||
// You should dispose a service provider created using ASP.NET
|
||||
// when you are finished using it, at the end of your app's lifetime.
|
||||
// If you use another dependency injection framework, you should inspect
|
||||
// its documentation for the best way to do this.
|
||||
using (var services = ConfigureServices())
|
||||
client.Log += LogAsync;
|
||||
services.GetRequiredService<CommandService>().Log += LogAsync;
|
||||
|
||||
// Tokens should be considered secret data and never hard-coded.
|
||||
// We can read from the environment variable to avoid hard coding.
|
||||
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
|
||||
await client.StartAsync();
|
||||
|
||||
// Here we initialize the logic required to register our commands.
|
||||
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();
|
||||
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
|
||||
private static Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static ServiceProvider ConfigureServices()
|
||||
{
|
||||
return new ServiceCollection()
|
||||
.AddSingleton(new DiscordSocketConfig
|
||||
{
|
||||
var client = services.GetRequiredService<DiscordSocketClient>();
|
||||
|
||||
client.Log += LogAsync;
|
||||
services.GetRequiredService<CommandService>().Log += LogAsync;
|
||||
|
||||
// Tokens should be considered secret data and never hard-coded.
|
||||
// We can read from the environment variable to avoid hard coding.
|
||||
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
|
||||
await client.StartAsync();
|
||||
|
||||
// Here we initialize the logic required to register our commands.
|
||||
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();
|
||||
|
||||
await Task.Delay(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
private Task LogAsync(LogMessage log)
|
||||
{
|
||||
Console.WriteLine(log.ToString());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private ServiceProvider ConfigureServices()
|
||||
{
|
||||
return new ServiceCollection()
|
||||
.AddSingleton(new DiscordSocketConfig
|
||||
{
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||
})
|
||||
.AddSingleton<DiscordSocketClient>()
|
||||
.AddSingleton<CommandService>()
|
||||
.AddSingleton<CommandHandlingService>()
|
||||
.AddSingleton<HttpClient>()
|
||||
.AddSingleton<PictureService>()
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||
})
|
||||
.AddSingleton<DiscordSocketClient>()
|
||||
.AddSingleton<CommandService>()
|
||||
.AddSingleton<CommandHandlingService>()
|
||||
.AddSingleton<HttpClient>()
|
||||
.AddSingleton<PictureService>()
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,70 +6,69 @@ using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TextCommandFramework.Services
|
||||
namespace TextCommandFramework.Services;
|
||||
|
||||
public class CommandHandlingService
|
||||
{
|
||||
public class CommandHandlingService
|
||||
private readonly CommandService _commands;
|
||||
private readonly DiscordSocketClient _discord;
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public CommandHandlingService(IServiceProvider services)
|
||||
{
|
||||
private readonly CommandService _commands;
|
||||
private readonly DiscordSocketClient _discord;
|
||||
private readonly IServiceProvider _services;
|
||||
_commands = services.GetRequiredService<CommandService>();
|
||||
_discord = services.GetRequiredService<DiscordSocketClient>();
|
||||
_services = services;
|
||||
|
||||
public CommandHandlingService(IServiceProvider services)
|
||||
{
|
||||
_commands = services.GetRequiredService<CommandService>();
|
||||
_discord = services.GetRequiredService<DiscordSocketClient>();
|
||||
_services = services;
|
||||
// Hook CommandExecuted to handle post-command-execution logic.
|
||||
_commands.CommandExecuted += CommandExecutedAsync;
|
||||
// Hook MessageReceived so we can process each message to see
|
||||
// if it qualifies as a command.
|
||||
_discord.MessageReceived += MessageReceivedAsync;
|
||||
}
|
||||
|
||||
// Hook CommandExecuted to handle post-command-execution logic.
|
||||
_commands.CommandExecuted += CommandExecutedAsync;
|
||||
// Hook MessageReceived so we can process each message to see
|
||||
// if it qualifies as a command.
|
||||
_discord.MessageReceived += MessageReceivedAsync;
|
||||
}
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Register modules that are public and inherit ModuleBase<T>.
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Register modules that are public and inherit ModuleBase<T>.
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||
}
|
||||
public async Task MessageReceivedAsync(SocketMessage rawMessage)
|
||||
{
|
||||
// Ignore system messages, or messages from other bots
|
||||
if (!(rawMessage is SocketUserMessage message))
|
||||
return;
|
||||
if (message.Source != MessageSource.User)
|
||||
return;
|
||||
|
||||
public async Task MessageReceivedAsync(SocketMessage rawMessage)
|
||||
{
|
||||
// Ignore system messages, or messages from other bots
|
||||
if (!(rawMessage is SocketUserMessage message))
|
||||
return;
|
||||
if (message.Source != MessageSource.User)
|
||||
return;
|
||||
// This value holds the offset where the prefix ends
|
||||
var argPos = 0;
|
||||
// Perform prefix check. You may want to replace this with
|
||||
// (!message.HasCharPrefix('!', ref argPos))
|
||||
// for a more traditional command format like !help.
|
||||
if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos))
|
||||
return;
|
||||
|
||||
// This value holds the offset where the prefix ends
|
||||
var argPos = 0;
|
||||
// Perform prefix check. You may want to replace this with
|
||||
// (!message.HasCharPrefix('!', ref argPos))
|
||||
// for a more traditional command format like !help.
|
||||
if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos))
|
||||
return;
|
||||
var context = new SocketCommandContext(_discord, message);
|
||||
// Perform the execution of the command. In this method,
|
||||
// the command service will perform precondition and parsing check
|
||||
// then execute the command if one is matched.
|
||||
await _commands.ExecuteAsync(context, argPos, _services);
|
||||
// Note that normally a result will be returned by this format, but here
|
||||
// we will handle the result in CommandExecutedAsync,
|
||||
}
|
||||
|
||||
var context = new SocketCommandContext(_discord, message);
|
||||
// Perform the execution of the command. In this method,
|
||||
// the command service will perform precondition and parsing check
|
||||
// then execute the command if one is matched.
|
||||
await _commands.ExecuteAsync(context, argPos, _services);
|
||||
// Note that normally a result will be returned by this format, but here
|
||||
// we will handle the result in CommandExecutedAsync,
|
||||
}
|
||||
public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
|
||||
{
|
||||
// command is unspecified when there was a search failure (command not found); we don't care about these errors
|
||||
if (!command.IsSpecified)
|
||||
return;
|
||||
|
||||
public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
|
||||
{
|
||||
// command is unspecified when there was a search failure (command not found); we don't care about these errors
|
||||
if (!command.IsSpecified)
|
||||
return;
|
||||
// the command was successful, we don't care about this result, unless we want to log that a command succeeded.
|
||||
if (result.IsSuccess)
|
||||
return;
|
||||
|
||||
// the command was successful, we don't care about this result, unless we want to log that a command succeeded.
|
||||
if (result.IsSuccess)
|
||||
return;
|
||||
|
||||
// the command failed, let's notify the user that something happened.
|
||||
await context.Channel.SendMessageAsync($"error: {result}");
|
||||
}
|
||||
// the command failed, let's notify the user that something happened.
|
||||
await context.Channel.SendMessageAsync($"error: {result}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,18 @@ using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TextCommandFramework.Services
|
||||
namespace TextCommandFramework.Services;
|
||||
|
||||
public class PictureService
|
||||
{
|
||||
public class PictureService
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public PictureService(HttpClient http)
|
||||
=> _http = http;
|
||||
|
||||
public async Task<Stream> GetCatPictureAsync()
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public PictureService(HttpClient http)
|
||||
=> _http = http;
|
||||
|
||||
public async Task<Stream> GetCatPictureAsync()
|
||||
{
|
||||
var resp = await _http.GetAsync("https://cataas.com/cat");
|
||||
return await resp.Content.ReadAsStreamAsync();
|
||||
}
|
||||
var resp = await _http.GetAsync("https://cataas.com/cat");
|
||||
return await resp.Content.ReadAsStreamAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||
<PackageReference Include="Discord.Net.Commands" Version="3.10.0" />
|
||||
<PackageReference Include="Discord.Net.Websocket" Version="3.10.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Discord.Net.Commands" Version="3.13.0" />
|
||||
<PackageReference Include="Discord.Net.Websocket" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,33 +2,28 @@ using Discord;
|
||||
using Discord.Webhook;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebHookClient
|
||||
namespace WebHookClient;
|
||||
|
||||
// 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
|
||||
// To a channel specific URL to send a message to that channel.
|
||||
class Program
|
||||
{
|
||||
// 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
|
||||
// To a channel specific URL to send a message to that channel.
|
||||
class Program
|
||||
public static async Task Main()
|
||||
{
|
||||
static void Main(string[] args)
|
||||
=> new Program().MainAsync().GetAwaiter().GetResult();
|
||||
// The webhook url follows the format https://discord.com/api/webhooks/{id}/{token}
|
||||
// Because anyone with the webhook URL can use your webhook
|
||||
// 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");
|
||||
|
||||
public async Task MainAsync()
|
||||
var embed = new EmbedBuilder
|
||||
{
|
||||
// The webhook url follows the format https://discord.com/api/webhooks/{id}/{token}
|
||||
// Because anyone with the webhook URL can use your webhook
|
||||
// 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"))
|
||||
{
|
||||
var embed = new EmbedBuilder
|
||||
{
|
||||
Title = "Test Embed",
|
||||
Description = "Test Description"
|
||||
};
|
||||
Title = "Test Embed",
|
||||
Description = "Test Description"
|
||||
};
|
||||
|
||||
// Webhooks are able to send multiple embeds per message
|
||||
// As such, your embeds must be passed as a collection.
|
||||
await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() });
|
||||
}
|
||||
}
|
||||
// Webhooks are able to send multiple embeds per message
|
||||
// As such, your embeds must be passed as a collection.
|
||||
await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.10.0" />
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user