Skip to content

Commit

Permalink
Merge pull request #2 from viazure/feature_接入消息通知服务
Browse files Browse the repository at this point in the history
Feature 接入消息通知服务
  • Loading branch information
viazure authored Oct 4, 2024
2 parents 865e282 + fffaac7 commit 82d3fe8
Show file tree
Hide file tree
Showing 23 changed files with 445 additions and 123 deletions.
62 changes: 31 additions & 31 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="AngleSharp" Version="1.1.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.2.2" />
<PackageVersion Include="MSTest.TestFramework" Version="3.2.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit" Version="2.7.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
</ItemGroup>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.2" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="AngleSharp" Version="1.1.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.6.1" />
<PackageVersion Include="MSTest.TestFramework" Version="3.6.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
</ItemGroup>
</Project>
68 changes: 56 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

欧路词典默认的生词本 ID 是 `0`。若希望使用特定的生词本,需按照以下步骤操作:

1. 使用 API 测试工具向 <https://api.frdic.com/api/open/v1/studylist/category?language=en> 发起请求。
1. 使用 API 测试工具向 [https://api.frdic.com/api/open/v1/studylist/category?language=en](https://api.frdic.com/api/open/v1/studylist/category?language=en) 发起请求。
2. 查看响应数据中的生词本列表,获取所需生词本的 ID。

#### 获取墨墨背单词云词库 ID
Expand Down Expand Up @@ -74,30 +74,74 @@
"Username": "your_username",
"Password": "your_password",
"DefaultNotepadId": "0"
},
"Notification": {
"Enabled": false,
"Url": "",
"RequestBody": "",
"Headers": ""
}
}
```

**字段说明:**

| 来源 | 字段名 | 说明 | 必填 |
| ---------- | ---------------- | ---------------------------------------- | ---- |
| 欧路词典 | Authorization | 接口授权,有了这个授权才能请求后续的接口 | True |
| 欧路词典 | DefaultBookId | 默认同步的生词本 Id,默认生词本 id 为 0 | True |
| 墨墨背单词 | Username | 用于登录的用户名(邮箱或手机号) | True |
| 墨墨背单词 | Password | 用于登录的密码 | True |
| 墨墨背单词 | DefaultNotepadId | 默认同步的云词库 id | True |
| 来源 | 字段名 | 说明 | 必填 |
| ---------- | ---------------- | ----------------------------------------------- | ----- |
| 欧路词典 | Authorization | 接口授权,有了这个授权才能请求后续的接口 | True |
| 欧路词典 | DefaultBookId | 默认同步的生词本 Id,默认生词本 id 为 0 | True |
| 墨墨背单词 | Username | 用于登录的用户名(邮箱或手机号) | True |
| 墨墨背单词 | Password | 用于登录的密码 | True |
| 墨墨背单词 | DefaultNotepadId | 默认同步的云词库 id | True |
| 通知服务 | Enabled | 是否启用通知 | False |
| 通知服务 | Url | 通知地址 | False |
| 通知服务 | RequestBody | 通知请求体,需将 JSON 字符串转义 | False |
| 通知服务 | Headers | 通知请求头,格式:"key1=value1;key2=value2;..." | False |

日志文件默认存储在 `%PROGRAMDATA%\EudicSyncToMaiMemo\logs` 目录中,若需要更改日志文件的存放位置,可以修改配置文件中的 Serilog 节点,具体操作是调整 WriteTo 部分中 Args 下的 path 参数。

### 配置通知服务

消息通知服务的接入逻辑参考了 [jeessy2/ddns-go](https://github.com/jeessy2/ddns-go) 项目的通知服务实现,特此表示感谢。

#### 配置步骤

1. 打开配置文件 `appsettings.json`,找到 `Notification` 节点。
2.`Enabled` 设置为 `true`,启用消息通知服务。
3. 根据您使用的服务继续设置该节点下的 `Url``RequestBody``Headers`
4. RequestBody 为转义后的 JSON 字符串,例如:`{\"message\":\"同步完成:{content} \"}`。如果此内容为空,调用通知服务会使用 GET 请求,否则使用 POST 请求。

**支持的变量:**

| 变量名 | 说明 |
| --------- | ---------------------------------------------------------------------------------- |
| {content} | 消息通知内容。若同步成功且单词数量大于 0,内容为单词列表,否则为其他服务状态通知。 |

**示例:**

[Server 酱](https://sct.ftqq.com/)

```url
https://sctapi.ftqq.com/****************.send?title=单词同步&desp={content}
```

[AnPush](https://anpush.com/)

```url
https://api.anpush.com/push/[your_token]?title=单词同步&content={content}&channel=[your_channel]
```

更多消息通知配置可以参考:[ddns-go Webhook 配置参考](https://github.com/jeessy2/ddns-go/issues/327)

## 项目依赖

- [.NET 8 SDK](https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0)
- Visual Studio 2022 版本 17.8 或更高版本
- [Visual Studio 2022](https://visualstudio.microsoft.com/) 版本 17.8 或更高版本

## Todo

- [x] MVP 版本:默认词库自动同步(控制台程序)
- [x] 可注册为 Windows 服务,并定期执行
- [ ] 接入消息通知服务
- [X] MVP 版本:默认词库自动同步(控制台程序)
- [X] 可注册为 Windows 服务,并定期执行
- [X] 接入消息通知服务
- [ ] 接入 墨墨开放 API,替换现有的网页解析方案
- [ ] 接入 Telegram Bot,用于手动选择词库同步
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ public static IServiceCollection AddAdditionalServices(this IServiceCollection s
{
services.AddScoped<IEudicService, EudicService>();
services.AddScoped<IMaiMemoService, MaiMemoService>();
services.AddScoped<INotificationService, NotificationService>();
services.AddScoped<IDictionarySyncService, DictionarySyncService>();

services.AddSingleton<INotificationService, NotificationService>();

return services;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace EudicSyncToMaiMemo.Infrastructure.Exceptions
{
/// <summary>
/// 配置信息异常
/// </summary>
[Serializable]
public class ConfigurationException : Exception
{
public ConfigurationException()
{
}

public ConfigurationException(string? message) : base(message)
{
}

public ConfigurationException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace EudicSyncToMaiMemo.Infrastructure.Exceptions
{
/// <summary>
/// 消息通知异常
/// </summary>
[Serializable]
public class NotificationException : Exception
{
public NotificationException()
{
}

public NotificationException(string? message) : base(message)
{
}

public NotificationException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}
49 changes: 46 additions & 3 deletions src/EudicSyncToMaiMemo.Infrastructure/Helpers/JsonHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,65 @@ namespace EudicSyncToMaiMemo.Infrastructure.Helpers
/// </summary>
public class JsonHelper
{
/// <summary>
/// 校验是否为有效的 JSON 字符串
/// </summary>
/// <param name="json">JSON 字符串</param>
/// <returns>是否有效</returns>
public static bool IsValidJson(string json)
{
if (string.IsNullOrWhiteSpace(json))
{
return false;
}

json = json.Trim();
if ((json.StartsWith("{") && json.EndsWith("}")) || // For object
(json.StartsWith("[") && json.EndsWith("]"))) // For array
{
try
{
var obj = JsonConvert.DeserializeObject(json);
return true;
}
catch (JsonException)
{
return false;
}
}
else
{
return false;
}
}

/// <summary>
/// 将对象序列化为 JSON 字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="value">对象实例</param>
/// <returns>JSON 字符串</returns>
public static string ObjToJson<T>(T value)
{
return JsonConvert.SerializeObject(value);
}

public static T? JsonToObj<T>(string jsonString)
/// <summary>
/// 将 JSON 字符串反序列化为对象
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="json">JSON 字符串</param>
/// <returns>对象实例</returns>
public static T? JsonToObj<T>(string json)
{
if (string.IsNullOrEmpty(jsonString))
if (string.IsNullOrEmpty(json))
{
return default;
}

try
{
return JsonConvert.DeserializeObject<T>(jsonString);
return JsonConvert.DeserializeObject<T>(json);
}
catch (JsonException)
{
Expand Down
2 changes: 1 addition & 1 deletion src/EudicSyncToMaiMemo.Installer/Package.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!-- Define the variables in "$(var.*) expressions" -->
<?define Name = "Eudic Sync To MaiMemo Service" ?>
<?define Manufacturer = "viazure" ?>
<?define Version = "1.0.0.0" ?>
<?define Version = "1.1.0.0" ?>
<?define Source = "W:\_publish\EudicSyncToMaiMemo" ?>
<?define UpgradeCode = "FD9B9913-E79C-47E2-9B47-84F19D8388B3" ?>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace EudicSyncToMaiMemo.Models.DTOs.Eudic
{
/// <summary>
/// 欧路词典接口响应
/// 欧路词典 API 返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
public class ApiResponse<T>
public class ApiResponseDto<T>
{
[JsonProperty("data")]
public List<T>? Data { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace EudicSyncToMaiMemo.Models.DTOs.MaiMemo
{
public class ApiResponse
/// <summary>
/// 墨墨背单词 API 返回结果
/// </summary>
public class ApiResponseDto
{
[JsonProperty("valid")]
public int Valid { get; set; } = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace EudicSyncToMaiMemo.Models.DTOs.Notification
{
/// <summary>
/// 消息通知 DTO
/// </summary>
public class NotificationMessageDto
{
/// <summary>
/// 通知消息内容
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// 同步的单词总数量
/// </summary>
public int Total { get; set; }
}
}
Loading

0 comments on commit 82d3fe8

Please sign in to comment.