-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
134 changed files
with
6,369 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
samples/QQBot.Net.Samples.TextCommands/Modules/PublicModule.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
using QQBot.Commands; | ||
using QQBot.Net.Samples.TextCommands.Services; | ||
using QQBot.WebSocket; | ||
|
||
namespace QQBot.Net.Samples.TextCommands.Modules; | ||
|
||
// Modules must be public and inherit from an IModuleBase | ||
public class PublicModule : ModuleBase<SocketCommandContext> | ||
{ | ||
// Dependency Injection will fill this value in for us | ||
public required PictureService PictureService { get; set; } | ||
|
||
[Command("ping")] | ||
[Alias("pong", "hello", "test")] | ||
public Task PingAsync() => | ||
ReplyAsync("pong!"); | ||
|
||
[Command("cat", RunMode = RunMode.Async)] | ||
[RequireContext(ContextType.Guild | ContextType.DM)] | ||
public async Task CatAsync() | ||
{ | ||
// Get a stream containing an image of a cat | ||
Stream stream = await PictureService.GetCatPictureAsync(); | ||
// Streams must be seeked to their beginning before being uploaded! | ||
stream.Seek(0, SeekOrigin.Begin); | ||
FileAttachment attachment = new(stream, "cat.png"); | ||
await ReplyAsync(attachment: attachment); | ||
} | ||
|
||
// 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() ?? user.Id); | ||
// } | ||
|
||
// [Command("emoji")] | ||
// public async Task Emoji([Remainder] string? _) => | ||
// await Context.Message.AddReactionAsync(new Emoji("\uD83D\uDC4C")); | ||
|
||
// [Command("image")] | ||
// public async Task Image(Uri image) | ||
// { | ||
// if (Context.Message.MaybeTextImageMixedMessage() | ||
// && image.IsAbsoluteUri) | ||
// await ReplyFileAsync(new FileAttachment(image, "image.png", AttachmentType.Image)); | ||
// } | ||
|
||
// // 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!"); | ||
|
||
// [Command("per")] | ||
// public async Task ModifyCategoryPermissions() | ||
// { | ||
// if (Context.Guild is not { } guild) return; | ||
// if (Context.Channel is not IGuildChannel guildChannel) return; | ||
// await guildChannel.AddPermissionOverwriteAsync((IGuildUser)Context.User); | ||
// if (guildChannel is SocketChannel socketChannel) | ||
// await socketChannel.UpdateAsync(); | ||
// if (guild.GetChannel(Context.Channel.Id) is { } socketGuildChannel) | ||
// { | ||
// await socketGuildChannel.ModifyPermissionOverwriteAsync((IGuildUser)Context.User, | ||
// permissions => permissions.Modify( | ||
// viewChannel: PermValue.Allow, | ||
// sendMessages: PermValue.Deny, | ||
// attachFiles: PermValue.Allow)); | ||
// } | ||
// } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using QQBot; | ||
using QQBot.Commands; | ||
using QQBot.Net.Samples.TextCommands.Services; | ||
using QQBot.WebSocket; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
|
||
// 这是一个使用 QQBot.Net 的文本命令框架的简单示例 | ||
|
||
// 此处使用了 .NET 通用主机承载 KOOK Bot 服务,该主机将帮助我们管理应用程序与服务的依赖注入与生命周期。 | ||
// 有关 .NET 通用主机的更多信息,请参阅 https://learn.microsoft.com/dotnet/core/extensions/generic-host | ||
// 如果您使用另一个依赖注入框架,应该查阅其文档以找到最佳的处理方式。 | ||
|
||
// 您可以在以下位置找到使用命令框架的文档: | ||
// - https://kooknet.dev/guides/text_commands/intro.html | ||
|
||
HostApplicationBuilder builder = Host.CreateEmptyApplicationBuilder(new HostApplicationBuilderSettings()); | ||
|
||
builder.Services.AddSingleton<QQBotSocketConfig>(_ => new QQBotSocketConfig | ||
{ | ||
LogLevel = LogSeverity.Debug, | ||
AccessEnvironment = AccessEnvironment.Sandbox, | ||
GatewayIntents = GatewayIntents.All | ||
}); | ||
builder.Services.AddSingleton<QQBotSocketClient>(provider => | ||
{ | ||
QQBotSocketConfig config = provider.GetRequiredService<QQBotSocketConfig>(); | ||
return new QQBotSocketClient(config); | ||
}); | ||
builder.Services.AddSingleton<CommandService>(); | ||
builder.Services.AddSingleton<CommandHandlingService>(); | ||
builder.Services.AddHostedService<QQBotClientService>(); | ||
builder.Services.AddSingleton<PictureService>(); | ||
builder.Services.AddHttpClient("Pictures"); | ||
|
||
IHost app = builder.Build(); | ||
await app.RunAsync(); |
12 changes: 12 additions & 0 deletions
12
samples/QQBot.Net.Samples.TextCommands/QQBot.Net.Samples.TextCommands.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="../../QQBot.Net.Sample.props" /> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" /> | ||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" /> | ||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" /> | ||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
75 changes: 75 additions & 0 deletions
75
samples/QQBot.Net.Samples.TextCommands/Services/CommandHandlingService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using System.Reflection; | ||
using QQBot.Commands; | ||
using QQBot.WebSocket; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace QQBot.Net.Samples.TextCommands.Services; | ||
|
||
public class CommandHandlingService | ||
{ | ||
private readonly CommandService _commands; | ||
private readonly QQBotSocketClient _client; | ||
private readonly IServiceProvider _services; | ||
|
||
public CommandHandlingService(IServiceProvider services) | ||
{ | ||
_commands = services.GetRequiredService<CommandService>(); | ||
_client = services.GetRequiredService<QQBotSocketClient>(); | ||
_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. | ||
_client.MessageReceived += MessageReceivedAsync; | ||
} | ||
|
||
public async Task InitializeAsync() | ||
{ | ||
if (Assembly.GetEntryAssembly() is not { } assembly) return; | ||
// Register modules that are public and inherit ModuleBase<T>. | ||
await _commands.AddModulesAsync(assembly, _services); | ||
} | ||
|
||
public async Task MessageReceivedAsync(SocketUserMessage message) | ||
{ | ||
if (_client.CurrentUser is null) return; | ||
|
||
// Ignore system messages, or messages from other bots | ||
if (message.Source is not MessageSource.User) return; | ||
|
||
// This value holds the offset where the prefix ends | ||
int argPos = 0; | ||
// Perform prefix check. The default formats in various contexts are: | ||
if (message.Channel is IGuildChannel) | ||
if (!message.HasMentionPrefix(_client.CurrentUser, ref argPos)) return; | ||
if (message.Channel is IGroupChannel or IGuildChannel) | ||
if (!message.HasCharPrefix('/', ref argPos)) return; | ||
if (!message.HasCharPrefix('/', ref argPos)) return; | ||
// for a more traditional command format like !help. | ||
// if (!message.HasMentionPrefix(_client.CurrentUser, ref argPos)) | ||
// return; | ||
|
||
SocketCommandContext context = new(_client, 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(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 is null) | ||
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.Message.ReplyAsync($"error: {result}"); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
samples/QQBot.Net.Samples.TextCommands/Services/PictureService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace QQBot.Net.Samples.TextCommands.Services; | ||
|
||
public class PictureService(IHttpClientFactory httpClientFactory) | ||
{ | ||
public async Task<Stream> GetCatPictureAsync() | ||
{ | ||
HttpClient httpClient = httpClientFactory.CreateClient("Pictures"); | ||
HttpResponseMessage resp = await httpClient.GetAsync("https://cataas.com/cat"); | ||
return await resp.Content.ReadAsStreamAsync(); | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
samples/QQBot.Net.Samples.TextCommands/Services/QQBotClientService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using QQBot.Commands; | ||
using QQBot.WebSocket; | ||
using Microsoft.Extensions.Hosting; | ||
|
||
namespace QQBot.Net.Samples.TextCommands.Services; | ||
|
||
public class QQBotClientService : IHostedService | ||
{ | ||
private readonly QQBotSocketClient _client; | ||
private readonly CommandService _commandService; | ||
private readonly CommandHandlingService _commandHandlingService; | ||
|
||
public QQBotClientService(QQBotSocketClient client, | ||
CommandService commandService, CommandHandlingService commandHandlingService) | ||
{ | ||
_client = client; | ||
_commandService = commandService; | ||
_commandHandlingService = commandHandlingService; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task StartAsync(CancellationToken cancellationToken) | ||
{ | ||
_client.Log += LogAsync; | ||
_commandService.Log += LogAsync; | ||
|
||
// 令牌(Tokens)应被视为机密数据,永远不应硬编码在代码中 | ||
// 在实际开发中,为了保护令牌的安全性,建议将令牌存储在安全的环境中 | ||
// 例如本地 .json、.yaml、.xml、.txt 文件、环境变量或密钥管理系统 | ||
// 这样可以避免将敏感信息直接暴露在代码中,以防止令牌被滥用或泄露 | ||
string token = Environment.GetEnvironmentVariable("QQBotDebugToken", EnvironmentVariableTarget.User) | ||
?? throw new InvalidOperationException("Token not found"); | ||
await _client.LoginAsync(0, TokenType.BotToken, token); | ||
await _client.StartAsync(); | ||
await _commandHandlingService.InitializeAsync(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task StopAsync(CancellationToken cancellationToken) | ||
{ | ||
await _client.StopAsync(); | ||
await _client.LogoutAsync(); | ||
} | ||
|
||
/// <summary> | ||
/// Log 事件,此处以直接输出到控制台为例 | ||
/// </summary> | ||
private static Task LogAsync(LogMessage log) | ||
{ | ||
Console.WriteLine(log.ToString()); | ||
return Task.CompletedTask; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
using System.Runtime.CompilerServices; | ||
|
||
[assembly: InternalsVisibleTo("QQBot.Net.Tests.Unit")] | ||
[assembly: InternalsVisibleTo("QQBot.Net.Tests.Integration.Rest")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
namespace QQBot.Commands; | ||
|
||
/// <summary> | ||
/// 为命令指定别名。 | ||
/// </summary> | ||
/// <remarks> | ||
/// 此特性允许命令具有一个或多个别名,在指定命令的基本名称的同时,还可以指定多个别名,以便用户可以使用多个熟悉的词汇来触发相同的命令。 | ||
/// </remarks> | ||
/// <example> | ||
/// 以下示例中,要调用此命令,除了可以使用基本名称“stats”,还使用“stat”或“info”。 | ||
/// <code language="cs"> | ||
/// [Command("stats")] | ||
/// [Alias("stat", "info")] | ||
/// public async Task GetStatsAsync(IUser user) | ||
/// { | ||
/// await ReplyTextAsync($"{user.Username} has 1000 score!"); | ||
/// } | ||
/// </code> | ||
/// </example> | ||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] | ||
public class AliasAttribute : Attribute | ||
{ | ||
/// <summary> | ||
/// 获取为命令定义的别名。 | ||
/// </summary> | ||
public string[] Aliases { get; } | ||
|
||
/// <summary> | ||
/// 初始化一个 <see cref="AliasAttribute"/> 类的新实例。 | ||
/// </summary> | ||
public AliasAttribute(params string[] aliases) | ||
{ | ||
Aliases = aliases; | ||
} | ||
} |
Oops, something went wrong.