From 2947f9fe97df03fd1cf32f2bc32a6bb113a436e6 Mon Sep 17 00:00:00 2001 From: Kagura <44108528+Kagura-Nanaa@users.noreply.github.com> Date: Sat, 6 Dec 2025 00:45:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=9A=E7=9F=A5=E6=B8=A0=E9=81=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=92=89=E9=92=89=20(#14884)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 通知渠道支持钉钉 * fix: 还原错误的更改 * chore: 加回丢掉的换行符 * fix: 还原错误删除的内容 --- src/MaaWpfGui/Constants/ConfigurationKeys.cs | 2 + src/MaaWpfGui/Res/Localizations/en-us.xaml | 2 + src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 2 + src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 2 + src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 2 + src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 2 + .../ExternalNotificationService.cs | 1 + .../DingTalkNotificationProvider.cs | 126 ++++++++++++++++++ ...nalNotificationSettingsUserControlModel.cs | 36 +++++ ...ternalNotificationSettingsUserControl.xaml | 32 ++++- 10 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 src/MaaWpfGui/Services/Notification/src/MaaWpfGui/Services/Notification/DingTalkNotificationProvider.cs diff --git a/src/MaaWpfGui/Constants/ConfigurationKeys.cs b/src/MaaWpfGui/Constants/ConfigurationKeys.cs index 52c365bc4b..be012461ab 100644 --- a/src/MaaWpfGui/Constants/ConfigurationKeys.cs +++ b/src/MaaWpfGui/Constants/ConfigurationKeys.cs @@ -290,6 +290,8 @@ public static class ConfigurationKeys public const string ExternalNotificationDiscordBotToken = "ExternalNotification.Discord.BotToken"; public const string ExternalNotificationDiscordUserId = "ExternalNotification.Discord.UserId"; public const string ExternalNotificationDiscordWebhookUrl = "ExternalNotification.Discord.WebhookUrl"; + public const string ExternalNotificationDingTalkAccessToken = "ExternalNotification.DingTalk.AccessToken"; + public const string ExternalNotificationDingTalkSecret = "ExternalNotification.DingTalk.Secret"; public const string ExternalNotificationTelegramBotToken = "ExternalNotification.Telegram.BotToken"; public const string ExternalNotificationTelegramChatId = "ExternalNotification.Telegram.ChatId"; public const string ExternalNotificationTelegramTopicId = "ExternalNotification.TelegramTopicId"; diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 77a57a2d32..13479b2316 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -62,6 +62,8 @@ Bot Token User ID Webhook URL + Access Token + Secret Bot Token Chat ID Topic ID (Optional) diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 7a47c45e92..3461d43c04 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -62,6 +62,8 @@ ボットトークン ユーザーID ウェブフック URL + アクセストークン + シークレット ボットトークン チャットID トピックID(任意) diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index 36b54663bf..fd3787dba5 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -62,6 +62,8 @@ 봇 토큰 사용자 ID 웹후크 URL + 액세스 토큰 + 시크릿 봇 토큰 채팅 ID 토픽 ID (선택사항) diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 6fc9713a5d..afb2cb811c 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -62,6 +62,8 @@ 机器人 Token 用户 ID Webhook URL + Access Token + 加签密钥 机器人 Token 聊天 ID 话题ID(可选) diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index dff50e4d28..5a6f90f401 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -62,6 +62,8 @@ 機器人 Token 用戶 ID Webhook URL + Access Token + 簽名密鑰 機器人 Token 聊天 ID 話題 ID(可選) diff --git a/src/MaaWpfGui/Services/Notification/ExternalNotificationService.cs b/src/MaaWpfGui/Services/Notification/ExternalNotificationService.cs index f4d38f1018..e2e001c67e 100644 --- a/src/MaaWpfGui/Services/Notification/ExternalNotificationService.cs +++ b/src/MaaWpfGui/Services/Notification/ExternalNotificationService.cs @@ -37,6 +37,7 @@ public static class ExternalNotificationService "ServerChan" => new ServerChanNotificationProvider(Instances.HttpService), "Telegram" => new TelegramNotificationProvider(Instances.HttpService), "Discord" => new DiscordNotificationProvider(Instances.HttpService), + "DingTalk" => new DingTalkNotificationProvider(Instances.HttpService), "Discord Webhook" => new DiscordWebhookNotificationProvider(Instances.HttpService), "Custom Webhook" => new CustomWebhookNotificationProvider(Instances.HttpService), "SMTP" => new SmtpNotificationProvider(), diff --git a/src/MaaWpfGui/Services/Notification/src/MaaWpfGui/Services/Notification/DingTalkNotificationProvider.cs b/src/MaaWpfGui/Services/Notification/src/MaaWpfGui/Services/Notification/DingTalkNotificationProvider.cs new file mode 100644 index 0000000000..550bfbd27f --- /dev/null +++ b/src/MaaWpfGui/Services/Notification/src/MaaWpfGui/Services/Notification/DingTalkNotificationProvider.cs @@ -0,0 +1,126 @@ +// +// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team) +// Copyright (C) 2021-2025 MaaAssistantArknights Contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License v3.0 only as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY +// + +#nullable enable + +using System; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using MaaWpfGui.Services.Web; +using MaaWpfGui.ViewModels.UI; +using Serilog; + +namespace MaaWpfGui.Services.Notification; + +public class DingTalkNotificationProvider(IHttpService httpService) : IExternalNotificationProvider +{ + private const string DingTalkWebhookEndpoint = "https://oapi.dingtalk.com/robot/send"; + private readonly ILogger _logger = Log.ForContext(); + + public async Task SendAsync(string title, string content) + { + var accessToken = SettingsViewModel.ExternalNotificationSettings.DingTalkAccessToken; + var secret = SettingsViewModel.ExternalNotificationSettings.DingTalkSecret; + + if (string.IsNullOrEmpty(accessToken)) + { + _logger.Warning("Failed to send DingTalk notification: Access Token is empty"); + return false; + } + + var webhook = BuildWebhookUrl(accessToken, secret); + var requestBody = new DingTalkPostContent + { + MessageType = "text", + Text = new DingTalkTextContent + { + Content = $"{title}: {content}" + } + }; + + var response = await httpService.PostAsJsonAsync(new Uri(webhook), requestBody); + if (response == null) + { + _logger.Warning("Failed to send DingTalk notification: response is null"); + return false; + } + + try + { + var responseData = JsonSerializer.Deserialize(response); + if (responseData?.ErrorCode == 0) + { + _logger.Information("DingTalk notification sent successfully."); + return true; + } + + _logger.Warning( + "Failed to send DingTalk notification, error code: {ErrorCode}, error message: {ErrorMessage}", + responseData?.ErrorCode, + responseData?.ErrorMessage); + return false; + } + catch (JsonException ex) + { + _logger.Warning(ex, "Failed to parse DingTalk response."); + return false; + } + } + + private static string BuildWebhookUrl(string accessToken, string? secret) + { + var builder = new StringBuilder($"{DingTalkWebhookEndpoint}?access_token={accessToken}"); + + if (string.IsNullOrEmpty(secret)) + { + return builder.ToString(); + } + + var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + var signBase = $"{timestamp}\n{secret}"; + using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); + var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signBase)); + var sign = WebUtility.UrlEncode(Convert.ToBase64String(hash)); + builder.Append($"×tamp={timestamp}&sign={sign}"); + + return builder.ToString(); + } + + private class DingTalkPostContent + { + [JsonPropertyName("msgtype")] + public string? MessageType { get; set; } + + [JsonPropertyName("text")] + public DingTalkTextContent? Text { get; set; } + } + + private class DingTalkTextContent + { + [JsonPropertyName("content")] + public string? Content { get; set; } + } + + private class DingTalkResponse + { + [JsonPropertyName("errcode")] + public int ErrorCode { get; set; } + + [JsonPropertyName("errmsg")] + public string? ErrorMessage { get; set; } + } +} diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/ExternalNotificationSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/ExternalNotificationSettingsUserControlModel.cs index 5098d948a0..4fc973e6e6 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/Settings/ExternalNotificationSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/ExternalNotificationSettingsUserControlModel.cs @@ -98,6 +98,7 @@ public class ExternalNotificationSettingsUserControlModel : PropertyChangedBase "ServerChan", "Telegram", "Discord", + "DingTalk", "Discord Webhook", "SMTP", "Bark", @@ -164,6 +165,14 @@ public class ExternalNotificationSettingsUserControlModel : PropertyChangedBase set => SetAndNotify(ref _discordEnabled, value); } + private bool _dingTalkEnabled = false; + + public bool DingTalkEnabled + { + get => _dingTalkEnabled; + set => SetAndNotify(ref _dingTalkEnabled, value); + } + private bool _discordWebhookEnabled = false; public bool DiscordWebhookEnabled @@ -209,6 +218,7 @@ public class ExternalNotificationSettingsUserControlModel : PropertyChangedBase ServerChanEnabled = _enabledExternalNotificationProviders.Contains("ServerChan"); TelegramEnabled = _enabledExternalNotificationProviders.Contains("Telegram"); DiscordEnabled = _enabledExternalNotificationProviders.Contains("Discord"); + DingTalkEnabled = _enabledExternalNotificationProviders.Contains("DingTalk"); DiscordWebhookEnabled = _enabledExternalNotificationProviders.Contains("Discord Webhook"); SmtpEnabled = _enabledExternalNotificationProviders.Contains("SMTP"); BarkEnabled = _enabledExternalNotificationProviders.Contains("Bark"); @@ -400,6 +410,32 @@ public class ExternalNotificationSettingsUserControlModel : PropertyChangedBase } } + private string _dingTalkAccessToken = SimpleEncryptionHelper.Decrypt(ConfigurationHelper.GetValue(ConfigurationKeys.ExternalNotificationDingTalkAccessToken, string.Empty)); + + public string DingTalkAccessToken + { + get => _dingTalkAccessToken; + set + { + SetAndNotify(ref _dingTalkAccessToken, value); + value = SimpleEncryptionHelper.Encrypt(value); + ConfigurationHelper.SetValue(ConfigurationKeys.ExternalNotificationDingTalkAccessToken, value); + } + } + + private string _dingTalkSecret = SimpleEncryptionHelper.Decrypt(ConfigurationHelper.GetValue(ConfigurationKeys.ExternalNotificationDingTalkSecret, string.Empty)); + + public string DingTalkSecret + { + get => _dingTalkSecret; + set + { + SetAndNotify(ref _dingTalkSecret, value); + value = SimpleEncryptionHelper.Encrypt(value); + ConfigurationHelper.SetValue(ConfigurationKeys.ExternalNotificationDingTalkSecret, value); + } + } + private string _telegramBotToken = SimpleEncryptionHelper.Decrypt(ConfigurationHelper.GetValue(ConfigurationKeys.ExternalNotificationTelegramBotToken, string.Empty)); public string TelegramBotToken diff --git a/src/MaaWpfGui/Views/UserControl/Settings/ExternalNotificationSettingsUserControl.xaml b/src/MaaWpfGui/Views/UserControl/Settings/ExternalNotificationSettingsUserControl.xaml index 50d047000f..f07ff5bd4e 100644 --- a/src/MaaWpfGui/Views/UserControl/Settings/ExternalNotificationSettingsUserControl.xaml +++ b/src/MaaWpfGui/Views/UserControl/Settings/ExternalNotificationSettingsUserControl.xaml @@ -29,6 +29,7 @@ +