diff --git a/samples/Kook.Net.Samples.Docker/.dockerignore b/samples/Kook.Net.Samples.Docker/.dockerignore new file mode 100644 index 00000000..2f32bfe4 --- /dev/null +++ b/samples/Kook.Net.Samples.Docker/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/samples/Kook.Net.Samples.Docker/Dockerfile b/samples/Kook.Net.Samples.Docker/Dockerfile new file mode 100644 index 00000000..0f85e7c0 --- /dev/null +++ b/samples/Kook.Net.Samples.Docker/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/runtime:7.0 AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["Kook.Net.Samples.Docker/Kook.Net.Samples.Docker.csproj", "Kook.Net.Samples.Docker/"] +RUN dotnet restore "Kook.Net.Samples.Docker/Kook.Net.Samples.Docker.csproj" +COPY . . +WORKDIR "/src/Kook.Net.Samples.Docker" +RUN dotnet build "Kook.Net.Samples.Docker.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Kook.Net.Samples.Docker.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Kook.Net.Samples.Docker.dll"] diff --git a/samples/Kook.Net.Samples.Docker/Kook.Net.Samples.Docker.csproj b/samples/Kook.Net.Samples.Docker/Kook.Net.Samples.Docker.csproj new file mode 100644 index 00000000..5d79da3e --- /dev/null +++ b/samples/Kook.Net.Samples.Docker/Kook.Net.Samples.Docker.csproj @@ -0,0 +1,15 @@ + + + + Exe + net7.0 + enable + enable + Linux + + + + + + + diff --git a/samples/Kook.Net.Samples.Docker/Program.cs b/samples/Kook.Net.Samples.Docker/Program.cs new file mode 100644 index 00000000..3f6f6822 --- /dev/null +++ b/samples/Kook.Net.Samples.Docker/Program.cs @@ -0,0 +1,165 @@ +// See https://aka.ms/new-console-template for more information + +// KookSocketConfig 是 KookSocketClient 的配置类 + +using Kook; +using Kook.WebSocket; + +KookSocketConfig config = new() +{ + AlwaysDownloadUsers = false, + AlwaysDownloadVoiceStates = false, + AlwaysDownloadBoostSubscriptions = false, + MessageCacheSize = 100, + LogLevel = LogSeverity.Debug +}; + +// 在使用完 Kook.Net 的客户端后,建议在应用程序的生命周期结束时进行 Dispose 操作 +KookSocketClient client = new(config); + +// 此处列举了 Kook.Net 的 KookSocketClient 的所有事件 + +#region BaseKookClient + +client.Log += LogAsync; +client.LoggedIn += () => Task.CompletedTask; +client.LoggedOut += () => Task.CompletedTask; + +#endregion + +#region KookSocketClient + +client.Connected += () => Task.CompletedTask; +client.Disconnected += exception => Task.CompletedTask; +client.Ready += ReadyAsync; +client.LatencyUpdated += (before, after) => Task.CompletedTask; + +#endregion + +#region BaseSocketClient + +client.ChannelCreated += channel => Task.CompletedTask; +client.ChannelDestroyed += channel => Task.CompletedTask; +client.ChannelUpdated += (before, after) => Task.CompletedTask; + +client.ReactionAdded += (message, channel, user, reaction) => Task.CompletedTask; +client.ReactionRemoved += (message, channel, user, reaction) => Task.CompletedTask; +client.DirectReactionAdded += (message, channel, user, reaction) => Task.CompletedTask; +client.DirectReactionRemoved += (message, channel, user, reaction) => Task.CompletedTask; + +client.MessageReceived += MessageReceivedAsync; +client.MessageDeleted += (message, channel) => Task.CompletedTask; +client.MessageUpdated += (before, after, channel) => Task.CompletedTask; +client.MessagePinned += (before, after, channel, @operator) => Task.CompletedTask; +client.MessageUnpinned += (before, after, channel, @operator) => Task.CompletedTask; + +client.DirectMessageReceived += (message, author, channel) => Task.CompletedTask; +client.DirectMessageDeleted += (message, author, channel) => Task.CompletedTask; +client.DirectMessageUpdated += (before, after, author, channel) => Task.CompletedTask; + +client.UserJoined += (user, time) => Task.CompletedTask; +client.UserLeft += (guild, user, time) => Task.CompletedTask; +client.UserBanned += (users, @operator, guild, reason) => Task.CompletedTask; +client.UserUnbanned += (users, @operator, guild) => Task.CompletedTask; +client.UserUpdated += (before, after) => Task.CompletedTask; +client.CurrentUserUpdated += (before, after) => Task.CompletedTask; +client.GuildMemberUpdated += (before, after) => Task.CompletedTask; +client.GuildMemberOnline += (users, time) => Task.CompletedTask; +client.GuildMemberOffline += (users, time) => Task.CompletedTask; + +client.UserConnected += (user, channel, time) => Task.CompletedTask; +client.UserDisconnected += (user, channel, time) => Task.CompletedTask; + +client.RoleCreated += role => Task.CompletedTask; +client.RoleDeleted += role => Task.CompletedTask; +client.RoleUpdated += (before, after) => Task.CompletedTask; + +client.EmoteCreated += (emote, guild) => Task.CompletedTask; +client.EmoteDeleted += (emote, guild) => Task.CompletedTask; +client.EmoteUpdated += (before, after, guild) => Task.CompletedTask; + +client.JoinedGuild += guild => Task.CompletedTask; +client.LeftGuild += guild => Task.CompletedTask; +client.GuildUpdated += (before, after) => Task.CompletedTask; +client.GuildAvailable += guild => Task.CompletedTask; +client.GuildUnavailable += guild => Task.CompletedTask; + +client.MessageButtonClicked += MessageButtonClickedAsync; +client.DirectMessageButtonClicked += (value, user, message, channel) => Task.CompletedTask; + +#endregion + +// Log 事件,此处以直接输出到控制台为例 +Task LogAsync(LogMessage log) +{ + Console.WriteLine(log.ToString()); + return Task.CompletedTask; +} + +// Ready 事件表示客户端已经建立了连接,现在可以安全地访问缓存 +Task ReadyAsync() +{ + Console.WriteLine($"{client.CurrentUser} 已连接!"); + return Task.CompletedTask; +} + +// 并不建议以这样的方式实现 Bot 的命令交互功能 +// 请参阅 Kook.Net.Samples.TextCommands 示例项目及其文档 +async Task MessageReceivedAsync(SocketMessage message, + SocketGuildUser author, + SocketTextChannel channel) +{ + // Bot 永远不应该响应自己的消息 + if (author.Id == client.CurrentUser.Id) + return; + + + if (message.Content == "!ping") + { + // 创建一个 CardBuilder,卡片将会包含一个文本模块和一个按钮模块 + CardBuilder builder = new CardBuilder() + .AddModule(s => + s.WithText("pong!")) + .AddModule(a => a + .AddElement(b => b + .WithClick(ButtonClickEventType.ReturnValue) + .WithText("点我!") + .WithValue("unique-id") + .WithTheme(ButtonTheme.Primary))); + + // 发送一条卡片形式的消息,内容包含文本 pong!,以及一个按钮 + // 在调用时,需要先调用 .Build() 方法来构建卡片 + await channel.SendCardAsync(builder.Build()); + } +} + +// 当按钮被点击时,会触发 MessageButtonClicked 事件 +async Task MessageButtonClickedAsync(string value, + Cacheable user, + Cacheable message, + SocketTextChannel channel) +{ + // 检查按钮的值是否为之前的代码中设置的值 + if (value == "unique-id") + { + IMessage messageEntity = await message.GetOrDownloadAsync(); + if (messageEntity is IUserMessage userMessage) + await userMessage.ReplyTextAsync("按钮被点击了!", isQuote: true); + } + + else + Console.WriteLine("接收到了一个没有对应处理程序的按钮值!"); +} + +// 令牌(Tokens)应被视为机密数据,永远不应硬编码在代码中 +// 在实际开发中,为了保护令牌的安全性,建议将令牌存储在安全的环境中 +// 例如本地 .json、.yaml、.xml、.txt 文件、环境变量或密钥管理系统 +// 这样可以避免将敏感信息直接暴露在代码中,以防止令牌被滥用或泄露 +string token = Environment.GetEnvironmentVariable("KookDebugToken") + ?? throw new ArgumentNullException("KookDebugToken"); + +await client.LoginAsync(TokenType.Bot, token); +await client.StartAsync(); + +// 阻塞程序直到关闭 +await Task.Delay(Timeout.Infinite); diff --git a/src/Kook.Net.sln b/src/Kook.Net.sln index 1ec11487..961f47f0 100644 --- a/src/Kook.Net.sln +++ b/src/Kook.Net.sln @@ -32,7 +32,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Experimental", "Experimenta EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kook.Net.Experimental", "Kook.Net.Experimental\Kook.Net.Experimental.csproj", "{95881FD6-BAF5-43A6-9D75-9F9181FC9D21}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{55F21D65-BA1A-4A4B-B370-FD2CA0204EB2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "SolutionItems", "{55F21D65-BA1A-4A4B-B370-FD2CA0204EB2}" ProjectSection(SolutionItems) = preProject Kook.Net.targets = Kook.Net.targets EndProjectSection @@ -41,6 +41,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Kook.Net.Samples.VisualBasi EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Kook.Net.Samples.FSharp", "..\samples\Kook.Net.Samples.FSharp\Kook.Net.Samples.FSharp.fsproj", "{DCD692BE-9D91-4DE1-8603-CD8923C59B6A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kook.Net.Samples.Docker", "..\samples\Kook.Net.Samples.Docker\Kook.Net.Samples.Docker.csproj", "{B8A50094-A959-4807-AB36-6256A1EEEE61}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,6 +101,10 @@ Global {DCD692BE-9D91-4DE1-8603-CD8923C59B6A}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCD692BE-9D91-4DE1-8603-CD8923C59B6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {DCD692BE-9D91-4DE1-8603-CD8923C59B6A}.Release|Any CPU.Build.0 = Release|Any CPU + {B8A50094-A959-4807-AB36-6256A1EEEE61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8A50094-A959-4807-AB36-6256A1EEEE61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8A50094-A959-4807-AB36-6256A1EEEE61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8A50094-A959-4807-AB36-6256A1EEEE61}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {57F76D67-0E55-4609-A61C-3A4C3073CA0E} = {58B166AB-82DD-472F-AC70-9318DA4F881E} @@ -112,5 +118,6 @@ Global {95881FD6-BAF5-43A6-9D75-9F9181FC9D21} = {BB39595B-5DCE-4B1C-8740-A013C261E317} {43C91115-7D09-4A28-95CD-380C3B00EAE0} = {8DE556F9-829D-48D3-A41C-FA57207CAE72} {DCD692BE-9D91-4DE1-8603-CD8923C59B6A} = {8DE556F9-829D-48D3-A41C-FA57207CAE72} + {B8A50094-A959-4807-AB36-6256A1EEEE61} = {8DE556F9-829D-48D3-A41C-FA57207CAE72} EndGlobalSection EndGlobal