Update sample projects & samples in docs (#2823)
* update them all * more docs * moar docs
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user