Merge branch 'main' into refactor/captureMat

This commit is contained in:
辉鸭蛋
2025-03-15 13:17:59 +08:00
committed by GitHub
10 changed files with 928 additions and 390 deletions

View File

@@ -57,6 +57,8 @@ public class TpTask(CancellationToken ct)
public async Task TpToStatueOfTheSeven()
{
await CheckInBigMapUi();
// 提前调整至恰当的缩放以更快的传送
if (_tpConfig.MapZoomEnabled)
{
double currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
@@ -73,22 +75,23 @@ public class TpTask(CancellationToken ct)
string? area = _tpConfig.ReviveStatueOfTheSevenArea;
double x = _tpConfig.ReviveStatueOfTheSevenPointX;
double y = _tpConfig.ReviveStatueOfTheSevenPointY;
GiTpPosition revivePoint = _tpConfig.ReviveStatueOfTheSeven ?? GetNearestGoddess(x, y);
if (_tpConfig.IsReviveInNearestStatueOfTheSeven)
{
var center = GetBigMapCenterPoint();
var giTpPoint = GetNearestGoddess(center.X, center.Y);
if (giTpPoint != null)
{
country = giTpPoint.Country;
area = giTpPoint.Area;
x = giTpPoint.X;
y = giTpPoint.Y;
}
country = giTpPoint.Country;
area = giTpPoint.Area;
x = giTpPoint.X;
y = giTpPoint.Y;
revivePoint = giTpPoint;
}
Logger.LogInformation("将传送至 {country} {area} 七天神像", country, area);
await Tp(x, y);
if (_tpConfig.ShouldMove || _tpConfig.IsReviveInNearestStatueOfTheSeven)
{
(x, y) = GetClosestPoint(revivePoint.TranX, revivePoint.TranY, x, y, 5);
var waypoint = new Waypoint
{
X = x,
@@ -98,18 +101,45 @@ public class TpTask(CancellationToken ct)
};
var waypointForTrack = new WaypointForTrack(waypoint);
await new PathExecutor(ct).MoveTo(waypointForTrack);
Simulation.SendInput.SimulateAction(GIActions.Drop);
}
await Delay((int)(_tpConfig.HpRestoreDuration * 1000), ct);
}
/// <summary>
///
/// </summary>
/// <param name="tranX"> 传送后实际到达的点X坐标 </param>
/// <param name="tranY"> 传送后实际到达的点Y坐标 </param>
/// <param name="x"> 传送点 X 坐标 </param>
/// <param name="y"> 传送点 Y 坐标 </param>
/// <param name="d"> 期望最终离传送点的距离 </param>
/// <returns> </returns>
private static (double X, double Y) GetClosestPoint(double tranX, double tranY, double x, double y, double d)
{
double dx = x - tranX;
double dy = y - tranY;
double distanceSquared = dx * dx + dy * dy;
double distance = Math.Sqrt(distanceSquared);
d = d > 0 ? d : 0;
if (distance < d)
{
return (tranX, tranY);
}
double ratio = d / distance;
double px = (x - dx * ratio);
double py = (y - dy * ratio);
return (px, py);
}
/// <summary>
/// 获取离 x,y 最近的七天神像
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
private GiTpPosition? GetNearestGoddess(double x, double y)
private GiTpPosition GetNearestGoddess(double x, double y)
{
GiTpPosition? nearestGiTpPosition = null;
double minDistance = double.MaxValue;
@@ -123,7 +153,7 @@ public class TpTask(CancellationToken ct)
}
}
// 获取最近的神像位置
return nearestGiTpPosition;
return nearestGiTpPosition ?? throw new InvalidOperationException("没找到最近的七天神像");;
}
/// <summary>

View File

@@ -108,6 +108,7 @@ namespace LogParse
{
configTask.Fault.ReviveCount++;
}
//传送失败,重试 n 次
result = parseBgiLine($@"传送失败,重试 (\d+) 次", logstr);
if (result.Item1)
@@ -115,19 +116,31 @@ namespace LogParse
configTask.Fault.TeleportFailCount = int.Parse(result.Item2[1]);
}
//战斗超时结束
if (logstr == "战斗超时结束")
{
configTask.Fault.BattleTimeoutCount ++;
}
}
//重试一次路线或放弃此路线!
if (logstr.EndsWith("重试一次路线或放弃此路线!"))
{
configTask.Fault.RetryCount++;
configTask.Fault.RetryCount ++;
}
//疑似卡死,尝试脱离...
if (logstr == "疑似卡死,尝试脱离...")
{
configTask.Fault.StuckCount ++;
}
//One or more errors occurred
result = parseBgiLine(@"执行脚本时发生异常: ""(.+?)""", logstr);
if (result.Item1)
{
configTask.Fault.ErrCount ++;
}
if (logstr.StartsWith("→ 脚本执行结束: \"" + configTask.Name + "\""))
{
@@ -241,11 +254,14 @@ namespace LogParse
public int ReviveCount { get; set; } = 0;
//传送失败次数
public int TeleportFailCount { get; set; } = 0;
//疑似卡死次数
public int StuckCount { get; set; } = 0;
//重试次数
public int RetryCount { get; set; } = 0;
//战斗超时
public int BattleTimeoutCount { get; set; } = 0;
//异常发生次数
public int ErrCount { get; set; } = 0;
}
}
@@ -401,8 +417,10 @@ namespace LogParse
{
colConfigList.Add((name: "复活次数", value: task => FormatNumberWithStyle(task.Fault.ReviveCount)));
colConfigList.Add((name: "重试次数", value: task => FormatNumberWithStyle(task.Fault.RetryCount)));
colConfigList.Add((name: "疑似卡死次数", value: task => FormatNumberWithStyle(task.Fault.StuckCount)));
colConfigList.Add((name: "战斗超时次数", value: task => FormatNumberWithStyle(task.Fault.BattleTimeoutCount)));
colConfigList.Add((name: "传送失败次数", value: task => FormatNumberWithStyle(task.Fault.TeleportFailCount)));
colConfigList.Add((name: "异常发生次数", value: task => FormatNumberWithStyle(task.Fault.ErrCount)));
}

View File

@@ -1,6 +1,5 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.DirectoryServices.ActiveDirectory;
using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterGenshinImpact.Service.Notification;
@@ -10,187 +9,174 @@ namespace BetterGenshinImpact.Service.Notification;
[Serializable]
public partial class NotificationConfig : ObservableObject
{
[ObservableProperty]
private string _notificationEventSubscribe = string.Empty;
/// <summary>
///
/// </summary>
[ObservableProperty]
private bool _webhookEnabled;
/// <summary>
///
/// </summary>
[ObservableProperty]
private string _webhookEndpoint = string.Empty;
/// <summary>
/// 是否包含截图
/// </summary>
[ObservableProperty]
private bool _includeScreenShot = true;
/// <summary>
/// windows uwp 通知是否启用
/// </summary>
[ObservableProperty]
private bool _windowsUwpNotificationEnabled = false;
// 飞书通知
/// <summary>
/// 飞书通知是否启用
/// </summary>
[ObservableProperty]
private bool _feishuNotificationEnabled = false;
/// <summary>
/// 飞书通知地址
/// </summary>
[ObservableProperty]
private string _feishuWebhookUrl = string.Empty;
// 企业微信通知
/// <summary>
/// 企业微信通知是否启用
/// </summary>
[ObservableProperty]
private bool _workweixinNotificationEnabled = false;
/// <summary>
/// 企业微信通知通知地址
/// </summary>
[ObservableProperty]
private string _workweixinWebhookUrl = string.Empty;
[ObservableProperty]
bool _webSocketNotificationEnabled = false;
[ObservableProperty]
private string _webSocketEndpoint = string.Empty;
// Email 通知配置
[ObservableProperty]
private bool _emailNotificationEnabled = false;
[ObservableProperty]
private string _smtpServer = string.Empty;
[ObservableProperty]
private int _smtpPort;
[ObservableProperty]
private string _smtpUsername = string.Empty;
[ObservableProperty]
private string _smtpPassword = string.Empty;
[ObservableProperty]
private string _fromEmail = string.Empty;
[ObservableProperty]
private string _fromName = string.Empty;
[ObservableProperty]
private string _toEmail = string.Empty;
/// <summary>
/// Bark移动推送通知配置
/// </summary>
[ObservableProperty]
private bool _barkNotificationEnabled = false;
[ObservableProperty]
private string _barkApiEndpoint = string.Empty;
[ObservableProperty]
private string _barkDeviceKeys = string.Empty;
// Bark 通知附加参数配置
/// <summary>
/// 推送副标题
/// </summary>
[ObservableProperty]
private string _barkSubtitle = string.Empty;
/// <summary>
/// 推送中断级别critical(重要警告), active(默认值), timeSensitive(时效性通知), passive(仅添加到通知列表)
/// </summary>
[ObservableProperty]
private string _barkLevel = "active";
/// <summary>
/// 通知声音
/// </summary>
[ObservableProperty]
private string _barkSound = "minuet";
/// <summary>
/// 重要警告的通知音量,取值范围: 0-10
/// </summary>
[ObservableProperty]
private int _barkVolume = 5;
/// <summary>
/// 推送角标,可以是任意数字
/// </summary>
[ObservableProperty]
private int _barkBadge = 1;
/// <summary>
/// 通知铃声重复播放1为开启
/// </summary>
[ObservableProperty]
private string _barkCall = string.Empty;
/// <summary>
/// iOS14.5以下自动复制推送内容1为开启
/// </summary>
[ObservableProperty]
private string _barkAutoCopy = string.Empty;
/// <summary>
/// 复制推送时指定复制的内容
/// </summary>
[ObservableProperty]
private string _barkCopy = string.Empty;
/// <summary>
/// 为推送设置自定义图标URL
/// </summary>
[ObservableProperty]
private string _barkIcon = string.Empty;
/// <summary>
/// 对消息进行分组推送将按group分组显示在通知中心中
/// </summary>
[ObservableProperty]
private string _barkGroup = "default";
/// <summary>
/// 传1保存推送传其他的不保存推送
/// </summary>
[ObservableProperty]
private string _barkIsArchive = "1";
/// <summary>
/// 点击推送时跳转的URL
/// </summary>
[ObservableProperty]
private string _barkUrl = string.Empty;
/// <summary>
/// 传"none"时,点击推送不会弹窗
/// </summary>
[ObservableProperty]
private string _barkAction = string.Empty;
[ObservableProperty] private string _barkAction = string.Empty;
[ObservableProperty] private string _barkApiEndpoint = string.Empty;
/// <summary>
/// iOS14.5以下自动复制推送内容1为开启
/// </summary>
[ObservableProperty] private string _barkAutoCopy = string.Empty;
/// <summary>
/// 推送角标,可以是任意数字
/// </summary>
[ObservableProperty] private int _barkBadge = 1;
/// <summary>
/// 通知铃声重复播放1为开启
/// </summary>
[ObservableProperty] private string _barkCall = string.Empty;
[ObservableProperty] private string _barkCiphertext = string.Empty;
/// <summary>
/// 复制推送时指定复制的内容
/// </summary>
[ObservableProperty] private string _barkCopy = string.Empty;
[ObservableProperty] private string _barkDeviceKeys = string.Empty;
/// <summary>
/// 对消息进行分组推送将按group分组显示在通知中心中
/// </summary>
[ObservableProperty] private string _barkGroup = "default";
/// <summary>
/// 为推送设置自定义图标URL
/// </summary>
[ObservableProperty] private string _barkIcon = string.Empty;
/// <summary>
/// 传1保存推送传其他的不保存推送
/// </summary>
[ObservableProperty] private string _barkIsArchive = "1";
/// <summary>
/// 推送中断级别critical(重要警告), active(默认值), timeSensitive(时效性通知), passive(仅添加到通知列表)
/// </summary>
[ObservableProperty] private string _barkLevel = "active";
/// <summary>
/// Bark移动推送通知配置
/// </summary>
[ObservableProperty] private bool _barkNotificationEnabled;
/// <summary>
/// 通知声音
/// </summary>
[ObservableProperty] private string _barkSound = "minuet";
// Bark 通知附加参数配置
/// <summary>
/// 推送副标题
/// </summary>
[ObservableProperty] private string _barkSubtitle = string.Empty;
/// <summary>
/// 点击推送时跳转的URL
/// </summary>
[ObservableProperty] private string _barkUrl = string.Empty;
/// <summary>
/// 重要警告的通知音量,取值范围: 0-10
/// </summary>
[ObservableProperty] private int _barkVolume = 5;
// Email 通知配置
[ObservableProperty] private bool _emailNotificationEnabled;
// 飞书通知
/// <summary>
/// 飞书通知是否启用
/// </summary>
[ObservableProperty] private bool _feishuNotificationEnabled;
/// <summary>
/// 飞书通知地址
/// </summary>
[ObservableProperty] private string _feishuWebhookUrl = string.Empty;
[ObservableProperty] private string _fromEmail = string.Empty;
[ObservableProperty] private string _fromName = string.Empty;
/// <summary>
/// 是否包含截图
/// </summary>
[ObservableProperty] private bool _includeScreenShot = true;
[ObservableProperty] private string _notificationEventSubscribe = string.Empty;
[ObservableProperty] private string _smtpPassword = string.Empty;
[ObservableProperty] private int _smtpPort;
[ObservableProperty] private string _smtpServer = string.Empty;
[ObservableProperty] private string _smtpUsername = string.Empty;
/// <summary>
/// Telegram API基础URL(可选留空使用官方API)
/// </summary>
[ObservableProperty] private string _telegramApiBaseUrl = string.Empty;
/// <summary>
/// Telegram机器人Token
/// </summary>
[ObservableProperty] private string _telegramBotToken = string.Empty;
/// <summary>
/// Telegram聊天ID
/// </summary>
[ObservableProperty] private string _telegramChatId = string.Empty;
// Telegram通知
/// <summary>
/// Telegram通知是否启用
/// </summary>
[ObservableProperty] private bool _telegramNotificationEnabled;
[ObservableProperty] private string _toEmail = string.Empty;
/// <summary>
/// </summary>
[ObservableProperty] private bool _webhookEnabled;
/// <summary>
/// </summary>
[ObservableProperty] private string _webhookEndpoint = string.Empty;
[ObservableProperty] private string _webhookSendTo = string.Empty; // 修改属性名
[ObservableProperty] private string _webSocketEndpoint = string.Empty;
[ObservableProperty] private bool _webSocketNotificationEnabled;
/// <summary>
/// windows uwp 通知是否启用
/// </summary>
[ObservableProperty] private bool _windowsUwpNotificationEnabled;
// 企业微信通知
/// <summary>
/// 企业微信通知是否启用
/// </summary>
[ObservableProperty] private bool _workweixinNotificationEnabled;
/// <summary>
/// 企业微信通知通知地址
/// </summary>
[ObservableProperty] private string _workweixinWebhookUrl = string.Empty;
}

View File

@@ -6,11 +6,17 @@ using Microsoft.Extensions.Hosting;
using System;
using System.Drawing;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.Service.Notification.Model;
using BetterGenshinImpact.Service.Notification.Model.Enum;
using BetterGenshinImpact.Service.Notifier;
using BetterGenshinImpact.Service.Notifier.Exception;
using BetterGenshinImpact.Service.Notifier.Interface;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using OpenCvSharp.Extensions;
@@ -31,16 +37,6 @@ public class NotificationService : IHostedService
InitializeNotifiers();
}
public static NotificationService Instance()
{
if (_instance == null)
{
throw new Exception("Not instantiated");
}
return _instance;
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
@@ -51,12 +47,20 @@ public class NotificationService : IHostedService
return Task.CompletedTask;
}
public static NotificationService Instance()
{
if (_instance == null) throw new Exception("Not instantiated");
return _instance;
}
private void InitializeNotifiers()
{
if (TaskContext.Instance().Config.NotificationConfig.WebhookEnabled)
{
_notifierManager.RegisterNotifier(new WebhookNotifier(NotifyHttpClient, TaskContext.Instance().Config.NotificationConfig.WebhookEndpoint));
_notifierManager.RegisterNotifier(new WebhookNotifier(NotifyHttpClient,
TaskContext.Instance().Config.NotificationConfig));
}
if (TaskContext.Instance().Config.NotificationConfig.WindowsUwpNotificationEnabled)
@@ -66,37 +70,37 @@ public class NotificationService : IHostedService
if (TaskContext.Instance().Config.NotificationConfig.FeishuNotificationEnabled)
{
_notifierManager.RegisterNotifier(new FeishuNotifier(NotifyHttpClient, TaskContext.Instance().Config.NotificationConfig.FeishuWebhookUrl));
_notifierManager.RegisterNotifier(new FeishuNotifier(NotifyHttpClient,
TaskContext.Instance().Config.NotificationConfig.FeishuWebhookUrl));
}
if (TaskContext.Instance().Config.NotificationConfig.WorkweixinNotificationEnabled)
{
_notifierManager.RegisterNotifier(new WorkWeixinNotifier(NotifyHttpClient, TaskContext.Instance().Config.NotificationConfig.WorkweixinWebhookUrl));
_notifierManager.RegisterNotifier(new WorkWeixinNotifier(NotifyHttpClient,
TaskContext.Instance().Config.NotificationConfig.WorkweixinWebhookUrl));
}
// WebSocket通知初始化
if (TaskContext.Instance().Config.NotificationConfig.WebSocketNotificationEnabled)
// WebSocket通知初始化
if (TaskContext.Instance().Config.NotificationConfig.WebSocketNotificationEnabled)
{
var jsonSerializerOptions = new JsonSerializerOptions
{
var jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
};
var cts = new CancellationTokenSource();
_notifierManager.RegisterNotifier(new WebSocketNotifier(
TaskContext.Instance().Config.NotificationConfig.WebSocketEndpoint,
jsonSerializerOptions,
cts
));
}
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
var cts = new CancellationTokenSource();
_notifierManager.RegisterNotifier(new WebSocketNotifier(
TaskContext.Instance().Config.NotificationConfig.WebSocketEndpoint,
jsonSerializerOptions,
cts
));
}
// Bark通知初始化
if (TaskContext.Instance().Config.NotificationConfig.BarkNotificationEnabled)
{
_notifierManager.RegisterNotifier(new BarkNotifier(
TaskContext.Instance().Config.NotificationConfig.BarkDeviceKeys,
TaskContext.Instance().Config.NotificationConfig.BarkApiEndpoint
));
}
// Bark通知初始化
if (TaskContext.Instance().Config.NotificationConfig.BarkNotificationEnabled)
_notifierManager.RegisterNotifier(new BarkNotifier(
TaskContext.Instance().Config.NotificationConfig.BarkDeviceKeys,
TaskContext.Instance().Config.NotificationConfig.BarkApiEndpoint
));
// 邮件通知初始化
if (TaskContext.Instance().Config.NotificationConfig.EmailNotificationEnabled)
@@ -111,6 +115,15 @@ public class NotificationService : IHostedService
TaskContext.Instance().Config.NotificationConfig.ToEmail
));
}
// Telegram通知初始化
if (TaskContext.Instance().Config.NotificationConfig.TelegramNotificationEnabled)
_notifierManager.RegisterNotifier(new TelegramNotifier(
NotifyHttpClient, // 使用共享的HttpClient
TaskContext.Instance().Config.NotificationConfig.TelegramBotToken,
TaskContext.Instance().Config.NotificationConfig.TelegramChatId,
TaskContext.Instance().Config.NotificationConfig.TelegramApiBaseUrl
));
}
public void RefreshNotifiers()
@@ -191,4 +204,4 @@ public class NotificationService : IHostedService
{
NotifyHttpClient.Dispose();
}
}
}

View File

@@ -1,3 +1,6 @@
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
@@ -9,23 +12,15 @@ namespace BetterGenshinImpact.Service.Notifier
{
public class EmailNotifier : INotifier
{
public string Name { get; set; } = "Email";
// SMTP服务器配置
private readonly string _smtpServer;
private readonly int _smtpPort;
private readonly string _smtpUsername;
private readonly string _smtpPassword;
// 发件人配置
private readonly string _fromEmail;
private readonly string _fromName;
// 收件人邮箱
public string ToEmail { get; set; }
private readonly string _smtpPassword;
private readonly int _smtpPort;
// 提升 SmtpClient 为类的成员变量
private readonly SmtpClient _smtpClient;
// SMTP服务器配置
private readonly string _smtpServer;
private readonly string _smtpUsername;
public EmailNotifier(
string smtpServer,
@@ -44,14 +39,15 @@ namespace BetterGenshinImpact.Service.Notifier
_fromName = fromName;
ToEmail = toEmail;
// 在构造函数中初始化 SmtpClient
_smtpClient = new SmtpClient(_smtpServer, _smtpPort)
{
Credentials = new System.Net.NetworkCredential(_smtpUsername, _smtpPassword),
EnableSsl = true
};
// 忽略SSL证书验证错误
ServicePointManager.ServerCertificateValidationCallback =
delegate { return true; };
}
// 收件人邮箱
public string ToEmail { get; set; }
public string Name { get; set; } = "Email";
public async Task SendAsync(BaseNotificationData content)
{
if (string.IsNullOrEmpty(ToEmail))
@@ -59,24 +55,78 @@ namespace BetterGenshinImpact.Service.Notifier
throw new NotifierException("收件人邮箱地址为空");
}
try
// 创建一个新的SmtpClient实例不复用
using (var smtpClient = new SmtpClient())
{
using var mailMessage = new MailMessage
try
{
From = new MailAddress(_fromEmail, _fromName),
Subject = FormatEmailSubject(content),
Body = FormatEmailBody(content),
IsBodyHtml = true
};
mailMessage.To.Add(ToEmail);
// 配置SMTP客户端
smtpClient.Host = _smtpServer;
smtpClient.Port = _smtpPort;
smtpClient.EnableSsl = true;
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential(_smtpUsername, _smtpPassword);
smtpClient.Timeout = 30000; // 30秒超时
// 使用成员变量 _smtpClient 发送邮件
await _smtpClient.SendMailAsync(mailMessage);
}
catch (System.Exception ex)
{
throw new NotifierException($"发送邮件失败: {ex.Message}");
// 创建邮件
using (var mailMessage = new MailMessage())
{
mailMessage.From = new MailAddress(_fromEmail, _fromName);
mailMessage.To.Add(ToEmail);
mailMessage.Subject = FormatEmailSubject(content);
mailMessage.Body = FormatEmailBody(content);
mailMessage.IsBodyHtml = true;
mailMessage.BodyEncoding = Encoding.UTF8;
mailMessage.SubjectEncoding = Encoding.UTF8;
// 添加图片附件(如果存在)
if (content.Screenshot != null)
{
var tempPath = Path.GetTempFileName() + ".jpg";
try
{
// 保存图片到临时文件
content.Screenshot.Save(tempPath, ImageFormat.Jpeg);
// 从文件添加附件
var attachment = new Attachment(tempPath);
mailMessage.Attachments.Add(attachment);
// 发送邮件
await smtpClient.SendMailAsync(mailMessage);
// 清理附件和临时文件
attachment.Dispose();
if (File.Exists(tempPath)) File.Delete(tempPath);
}
catch (System.Exception ex)
{
// 尝试清理临时文件
try
{
if (File.Exists(tempPath)) File.Delete(tempPath);
}
catch
{
/* 忽略清理错误 */
}
throw new NotifierException($"发送邮件失败: {ex.Message}");
}
}
else
{
// 没有图片时直接发送
await smtpClient.SendMailAsync(mailMessage);
}
}
}
catch (System.Exception ex)
{
var errorMessage = $"发送邮件失败: {ex.Message}";
throw new NotifierException(errorMessage);
}
}
}
@@ -89,23 +139,29 @@ namespace BetterGenshinImpact.Service.Notifier
private string FormatEmailBody(BaseNotificationData content)
{
var builder = new StringBuilder();
builder.AppendLine("<html><body>");
builder.AppendLine("<html><body style='font-family: Arial, sans-serif;'>");
// 添加通知标题
builder.AppendLine("<h2>通知详情</h2>");
builder.AppendLine("<h2 style='color: #333;'>通知详情</h2>");
// 添加通知内容
foreach (var prop in content.GetType().GetProperties())
{
// 跳过Screenshot属性它会单独处理
if (prop.Name == "Screenshot")
continue;
var value = prop.GetValue(content);
if (value != null)
{
builder.AppendLine($"<p><strong>{prop.Name}:</strong> {value}</p>");
builder.AppendFormat("<p><strong>{0}:</strong> {1}</p>", prop.Name, value);
}
}
// 添加提示信息
builder.AppendLine("<p><em>如有截图,请查看附件。</em></p>");
builder.AppendLine("</body></html>");
return builder.ToString();
}
}
}
}

View File

@@ -0,0 +1,226 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BetterGenshinImpact.Service.Notification.Model;
using BetterGenshinImpact.Service.Notifier.Exception;
using BetterGenshinImpact.Service.Notifier.Interface;
namespace BetterGenshinImpact.Service.Notifier;
public class TelegramNotifier : INotifier, IDisposable
{
// 永远不更改此URL常量这是Telegram API的标准URL前缀
private const string TELEGRAM_API_URL = "https://api.telegram.org/bot";
private readonly bool _createdHttpClient;
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
/// <summary>
/// 创建一个新的Telegram通知器实例
/// </summary>
/// <param name="httpClient">可选的HttpClient如果不提供则创建新的</param>
/// <param name="telegramBotToken">Telegram机器人Token</param>
/// <param name="telegramChatId">Telegram聊天ID</param>
/// <param name="telegramApiBaseUrl">不再使用,保留参数仅为兼容性</param>
public TelegramNotifier(HttpClient httpClient = null, string telegramBotToken = "", string telegramChatId = "",
string telegramApiBaseUrl = "")
{
TelegramBotToken = telegramBotToken;
TelegramChatId = telegramChatId;
if (httpClient != null)
{
_httpClient = httpClient;
_createdHttpClient = false;
}
else
{
_httpClient = new HttpClient();
_createdHttpClient = true;
_httpClient.Timeout = TimeSpan.FromSeconds(30);
}
// 忽略自定义API URL始终使用标准Telegram API URL
TelegramApiBaseUrl = TELEGRAM_API_URL;
}
/// <summary>
/// Telegram机器人Token
/// </summary>
public string TelegramBotToken { get; set; }
/// <summary>
/// Telegram聊天ID
/// </summary>
public string TelegramChatId { get; set; }
/// <summary>
/// Telegram API基础URL - 内部使用
/// </summary>
private string TelegramApiBaseUrl { get; set; }
public void Dispose()
{
if (_createdHttpClient)
{
_httpClient?.Dispose();
}
}
/// <summary>
/// 通知器名称
/// </summary>
public string Name { get; set; } = "Telegram";
public async Task SendAsync(BaseNotificationData content)
{
if (string.IsNullOrEmpty(TelegramBotToken))
{
throw new NotifierException("Telegram bot token is not set");
}
if (string.IsNullOrEmpty(TelegramChatId))
{
throw new NotifierException("Telegram chat ID is not set");
}
try
{
var message = content.Message;
var fullMessage = !string.IsNullOrEmpty(message) ? message : "";
if (!string.IsNullOrEmpty(fullMessage))
{
await SendTextMessageAsync(fullMessage);
}
else
{
throw new NotifierException("No message content to send");
}
}
catch (HttpRequestException ex)
{
throw new NotifierException("Network error sending Telegram notification: " + ex.Message);
}
catch (TaskCanceledException)
{
throw new NotifierException("Telegram API request timed out. Check your internet connection.");
}
catch (NotifierException)
{
throw;
}
catch (System.Exception ex)
{
throw new NotifierException("Error sending Telegram notification: " + ex.Message);
}
}
private async Task SendTextMessageAsync(string message)
{
// 构建Telegram API URL - 固定格式https://api.telegram.org/bot{token}/sendMessage
var endpoint = $"{TELEGRAM_API_URL}{TelegramBotToken}/sendMessage";
try
{
var jsonContent = new
{
chat_id = TelegramChatId,
text = message,
disable_web_page_preview = true
};
var json = JsonSerializer.Serialize(jsonContent, _jsonSerializerOptions);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
{
Content = content
};
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("BetterGenshinImpact", "1.0"));
var response = await _httpClient.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new NotifierException(
$"Telegram message failed with code: {response.StatusCode}, Error: {responseContent}");
}
// Check for API errors in the response
var (isSuccess, errorCode, errorDescription) = ValidateApiResponse(responseContent);
if (!isSuccess)
{
if (errorCode == 400)
{
throw new NotifierException(
"Please send a message to the bot first and check that the chat ID is correct.");
}
if (errorCode == 401)
{
throw new NotifierException("Telegram bot token is incorrect.");
}
if (errorCode == 404)
throw new NotifierException(
$"Telegram API not found (404). Please verify your bot token is correct. URL: {endpoint}");
throw new NotifierException($"Telegram API error: {errorDescription} (Code: {errorCode})");
}
}
catch (System.Exception ex) when (!(ex is NotifierException))
{
throw new NotifierException("Error sending Telegram notification: " + ex.Message);
}
}
private (bool isSuccess, int errorCode, string errorDescription) ValidateApiResponse(string responseJson)
{
try
{
using (var doc = JsonDocument.Parse(responseJson))
{
var root = doc.RootElement;
// Telegram API returns "ok": true for success
if (root.TryGetProperty("ok", out var okElement))
{
var isOk = okElement.GetBoolean();
if (!isOk)
{
var errorDescription = "Unknown Telegram API error";
if (root.TryGetProperty("description", out var descriptionElement))
errorDescription = descriptionElement.GetString();
var errorCode = 0;
if (root.TryGetProperty("error_code", out var errorCodeElement))
errorCode = errorCodeElement.GetInt32();
return (false, errorCode, errorDescription);
}
return (true, 0, string.Empty);
}
return (false, 0, "Invalid API response: 'ok' field missing");
}
}
catch (JsonException ex)
{
return (false, 0, "Failed to parse API response: " + ex.Message);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Net.WebSockets;
using System.Text.Json;
using System.Threading;
@@ -53,6 +53,7 @@ namespace BetterGenshinImpact.Service.Notifier
var json = JsonSerializer.Serialize(notificationData, _jsonSerializerOptions);
var buffer = System.Text.Encoding.UTF8.GetBytes(json);
await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, _cts.Token);
await CloseAsync(); // 添加关闭连接的代码
}
catch (WebSocketException ex)
{
@@ -82,4 +83,4 @@ namespace BetterGenshinImpact.Service.Notifier
await SendAsync(notificationData);
}
}
}
}

View File

@@ -1,10 +1,13 @@
using BetterGenshinImpact.Service.Notifier.Exception;
using BetterGenshinImpact.Service.Notifier.Exception;
using BetterGenshinImpact.Service.Notifier.Interface;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BetterGenshinImpact.Service.Notification.Model;
using System.Collections.Generic;
using BetterGenshinImpact.Service.Notification; // 添加对 System.Collections.Generic 命名空间的引用
using BetterGenshinImpact.Service.Notification; // 添加对 NotificationConfig 类型的引用
namespace BetterGenshinImpact.Service.Notifier;
@@ -14,6 +17,9 @@ public class WebhookNotifier : INotifier
public string Endpoint { get; set; }
// 添加 send_to 属性
private string SendTo { get; set; }
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
@@ -21,10 +27,12 @@ public class WebhookNotifier : INotifier
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
};
public WebhookNotifier(HttpClient httpClient, string endpoint = "")
public WebhookNotifier(HttpClient httpClient, NotificationConfig config)
{
_httpClient = httpClient;
Endpoint = endpoint;
Endpoint = config.WebhookEndpoint;
SendTo = config.WebhookSendTo; // 初始化 send_to 属性
}
public async Task SendAsync(BaseNotificationData content)
@@ -53,11 +61,21 @@ public class WebhookNotifier : INotifier
}
}
private StringContent TransformData(BaseNotificationData notificationData)
{
// using object type here so it serializes the interface correctly
var serializedData = JsonSerializer.Serialize<object>(notificationData, _jsonSerializerOptions);
// 使用 SendTo 属性来设置 send_to 字段,并将 notification_data 的内容合并到外层字典
var dataToSend = new Dictionary<string, object>
{
{ "send_to", SendTo },
{ "event", notificationData.Event },
{ "result", notificationData.Result },
{ "timestamp", notificationData.Timestamp },
{ "screenshot", notificationData.Screenshot },
{ "message", notificationData.Message },
{ "data", notificationData.Data }
};
var serializedData = JsonSerializer.Serialize(dataToSend, _jsonSerializerOptions);
return new StringContent(serializedData, Encoding.UTF8, "application/json");
}

View File

@@ -15,7 +15,7 @@
FontFamily="{StaticResource TextThemeFontFamily}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
mc:Ignorable="d">
<StackPanel Margin="42,16,42,12">
<ui:TextBlock Margin="0,0,0,8"
FontTypography="BodyStrong"
@@ -605,7 +605,7 @@
</Grid>
</StackPanel>
</ui:CardExpander>
<!--回血相关设置-->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
<ui:CardExpander.Icon>
@@ -636,84 +636,86 @@
<StackPanel>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0" Grid.Column="0"
<ui:TextBlock Grid.Row="0" Grid.Column="0"
Text="是否就近七天神像恢复血量"
FontTypography="Body"/>
FontTypography="Body" />
<ui:TextBlock Grid.Row="1" Grid.Column="0"
Text="启用后将自动选择最近的七天神像,忽略下方指定位置"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"/>
<ui:ToggleSwitch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
IsChecked="{Binding Config.TpConfig.IsReviveInNearestStatueOfTheSeven}"/>
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}" />
<ui:ToggleSwitch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Margin="0,0,36,0"
IsChecked="{Binding Config.TpConfig.IsReviveInNearestStatueOfTheSeven}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0" Grid.Column="0"
<ui:TextBlock Grid.Row="0" Grid.Column="0"
Text="传送到七天神像之后是否需要移动后回血"
FontTypography="Body"/>
FontTypography="Body" />
<ui:TextBlock Grid.Row="1" Grid.Column="0"
Text="启用后将自动向七天神像方向移动"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"/>
<ui:ToggleSwitch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
IsChecked="{Binding Config.TpConfig.ShouldMove}"/>
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}" />
<ui:ToggleSwitch Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Margin="0,0,36,0"
IsChecked="{Binding Config.TpConfig.ShouldMove}" />
</Grid>
<!-- 指定国家 -->
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0" Grid.Column="0"
<ui:TextBlock Grid.Row="0" Grid.Column="0"
Text="七天神像国家"
FontTypography="Body"/>
FontTypography="Body" />
<ui:TextBlock Grid.Row="1" Grid.Column="0"
Text="选择七天神像所在国家"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"/>
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}" />
<ComboBox Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
ItemsSource="{Binding CountryList}"
SelectedItem="{Binding SelectedCountry}"
MinWidth="120"/>
Margin="0,0,36,0"
ItemsSource="{Binding CountryList}"
SelectedItem="{Binding SelectedCountry}"
MinWidth="120" />
</Grid>
<!-- 指定区域 -->
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0" Grid.Column="0"
<ui:TextBlock Grid.Row="0" Grid.Column="0"
Text="七天神像区域"
FontTypography="Body"/>
FontTypography="Body" />
<ui:TextBlock Grid.Row="1" Grid.Column="0"
Text="选择七天神像所在区域"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"/>
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}" />
<ComboBox Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
ItemsSource="{Binding Areas}"
SelectedItem="{Binding SelectedArea}"
MinWidth="120"/>
Margin="0,0,36,0"
ItemsSource="{Binding Areas}"
SelectedItem="{Binding SelectedArea}"
MinWidth="120" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -773,7 +775,7 @@
Command="{Binding ImportLocalScriptsRepoZipCommand}"
Content="导入" />
</Grid>
</ui:CardControl.Header>
</ui:CardControl>
@@ -800,16 +802,16 @@
Text="使按键绑定设置对外部脚本生效"
TextWrapping="Wrap" />
<ui:ToggleSwitch Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsChecked="{Binding Config.KeyBindingsConfig.GlobalKeyMappingEnabled}"/>
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsChecked="{Binding Config.KeyBindingsConfig.GlobalKeyMappingEnabled}" />
</Grid>
</ui:CardControl.Header>
</ui:CardControl>
<!-- 其他设置 -->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0" >
<!-- 其他设置 -->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
<ui:CardExpander.Icon>
<ui:FontIcon Glyph="&#xf141;" Style="{StaticResource FaFontIconStyle}" />
</ui:CardExpander.Icon>
@@ -861,7 +863,7 @@
Margin="0,0,36,0"
IsChecked="{Binding Config.OtherConfig.RestoreFocusOnLostEnabled, Mode=TwoWay}" />
</Grid>
</StackPanel>
</ui:CardExpander>
<ui:TextBlock Margin="0,0,0,8"
@@ -977,7 +979,7 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="Webhook相关设置"
Text="Webhook 相关设置"
TextWrapping="Wrap" />
<ui:ToggleSwitch Grid.Row="0"
Grid.RowSpan="2"
@@ -999,12 +1001,12 @@
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="Webhook端点"
Text="Webhook 端点"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="填写Webhook端点"
Text="填写 Webhook 端点"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
@@ -1015,6 +1017,34 @@
Text="{Binding Config.NotificationConfig.WebhookEndpoint, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="发送对象"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="填写发送对象"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.WebhookSendTo, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -1036,18 +1066,11 @@
TextWrapping="Wrap" />
<ui:Button Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
Margin="0,0,36,0"
HorizontalAlignment="Right"
Command="{Binding TestWebhookCommand}"
Content="发送"
IsEnabled="{Binding IsLoading, Converter={StaticResource InverseBooleanConverter}}" />
<ui:TextBlock Grid.Row="1"
Grid.Column="1"
Margin="0,0,36,0"
HorizontalAlignment="Right"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="{Binding WebhookStatus}"
TextWrapping="Wrap" />
Content="发送"/>
</Grid>
</StackPanel>
</ui:CardExpander>
@@ -1373,7 +1396,7 @@
Margin="0,0,36,0"
Command="{Binding TestWebSocketNotificationCommand}"
Content="发送" />
</Grid>
</Grid>
</StackPanel>
</ui:CardExpander>
<!-- Email通知 -->
@@ -1453,7 +1476,7 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="填写 SMTP 服务器端口"
Text="填写 SMTP 服务器端口一般为587"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
@@ -1693,7 +1716,7 @@
Text="{Binding Config.NotificationConfig.BarkApiEndpoint, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -1711,8 +1734,9 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="多个设备使用逗号、分号或空格分隔 (请使用英文输入法)"
Text="多个设备使用英文逗号、分号或空格分隔"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
@@ -1720,9 +1744,9 @@
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.BarkDeviceKeys, Mode=TwoWay}"
TextWrapping="NoWrap" />
TextWrapping="Wrap" />
</Grid>
<!-- 高级参数 -->
<Grid Margin="16">
<Grid.RowDefinitions>
@@ -1741,18 +1765,22 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="critical重要&#x0a;active默认&#x0a;timeSensitive专注下也会提醒&#x0a;passive通知但不提醒"
Text="修改推送级别"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.BarkLevel, Mode=TwoWay}"
TextWrapping="NoWrap" />
<ComboBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
SelectedItem="{Binding Config.NotificationConfig.BarkLevel}"
SelectedValuePath="Tag"
Margin="0,0,36,0"
MinWidth="120">
<ComboBoxItem Content="passive通知但不提醒" Tag="passive" />
<ComboBoxItem Content="critical重要通知" Tag="critical" />
<ComboBoxItem Content="active默认通知" Tag="active" />
<ComboBoxItem Content="sensitive专注通知" Tag="timeSensitive" />
</ComboBox>
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -1770,18 +1798,20 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="例如: minuet, silence, bell"
Text="选择通知声音"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.BarkSound, Mode=TwoWay}"
TextWrapping="NoWrap" />
<ComboBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
SelectedItem="{Binding Config.NotificationConfig.BarkSound}"
Margin="0,0,36,0"
MinWidth="120">
<ComboBoxItem Content="minuet" />
<ComboBoxItem Content="silence" />
<ComboBoxItem Content="bell" />
</ComboBox>
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -1810,7 +1840,7 @@
Text="{Binding Config.NotificationConfig.BarkGroup, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -1828,16 +1858,19 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="传1保存推送传其他不保存"
Text="选择是否保存推送历史"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.BarkIsArchive, Mode=TwoWay}"
TextWrapping="NoWrap" />
<ComboBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
SelectedValue="{Binding Config.NotificationConfig.BarkIsArchive, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="是" Tag="1" />
<ComboBoxItem Content="否" Tag="0" />
</ComboBox>
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
@@ -1896,6 +1929,154 @@
</Grid>
</StackPanel>
</ui:CardExpander>
<!-- Telegram通知 -->
<ui:CardExpander Margin="0,0,0,12"
ContentPadding="0"
Icon="{ui:SymbolIcon Send20}">
<ui:CardExpander.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="启用 Telegram 通知"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="Telegram 机器人相关设置"
TextWrapping="Wrap" />
<ui:ToggleSwitch Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,24,0"
IsChecked="{Binding Config.NotificationConfig.TelegramNotificationEnabled, Mode=TwoWay}" />
</Grid>
</ui:CardExpander.Header>
<StackPanel>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="机器人 Token"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="填写 Telegram 机器人的 Token"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.TelegramBotToken, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="聊天 ID"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="填写接收消息的聊天 ID"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.TelegramChatId, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="API 基础 URL可选"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="留空使用官方 API或填写第三方 API 地址"
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.NotificationConfig.TelegramApiBaseUrl, Mode=TwoWay}"
TextWrapping="NoWrap" />
</Grid>
<!-- 测试按钮 -->
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="测试 Telegram 通知"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="发送测试通知"
TextWrapping="Wrap" />
<ui:Button Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
Command="{Binding TestTelegramNotificationCommand}"
Content="发送" />
</Grid>
</StackPanel>
</ui:CardExpander>
<!-- 地图 -->
<!--<ui:CardControl Margin="0,0,0,12"
Icon="{ui:SymbolIcon Cursor24}"
@@ -1950,4 +2131,4 @@
IsChecked="{Binding Config.MaskWindowConfig.ShowLogBox, Mode=TwoWay}"/>
</ui:CardControl>-->
</StackPanel>
</Page>
</Page>

View File

@@ -33,13 +33,6 @@ public partial class CommonSettingsPageViewModel : ViewModel
private readonly INavigationService _navigationService;
private readonly NotificationService _notificationService;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string _webhookStatus = string.Empty;
public ObservableCollection<string> CountryList { get; } = new();
public ObservableCollection<string> Areas { get; } = new();
private readonly TpConfig _tpConfig = TaskContext.Instance().Config.TpConfig;
@@ -192,14 +185,15 @@ public partial class CommonSettingsPageViewModel : ViewModel
[RelayCommand]
private async Task OnTestWebhook()
{
IsLoading = true;
WebhookStatus = string.Empty;
var res = await _notificationService.TestNotifierAsync<WebhookNotifier>();
WebhookStatus = res.Message;
IsLoading = false;
if(res.IsSuccess)
{
Toast.Success(res.Message);
}
else
{
Toast.Error(res.Message);
}
}
[RelayCommand]
@@ -286,6 +280,21 @@ public partial class CommonSettingsPageViewModel : ViewModel
}
}
[RelayCommand]
private async Task OnTestTelegramNotification()
{
var res = await _notificationService.TestNotifierAsync<TelegramNotifier>();
if(res.IsSuccess)
{
Toast.Success(res.Message);
}
else
{
Toast.Error(res.Message);
}
}
[RelayCommand]
private void ImportLocalScriptsRepoZip()
{