feat: add message template
This commit is contained in:
parent
1decffc266
commit
2a6498a910
@ -35,6 +35,7 @@ package_dir =
|
|||||||
include_package_data = True
|
include_package_data = True
|
||||||
python_requires = >= 3.8
|
python_requires = >= 3.8
|
||||||
install_requires =
|
install_requires =
|
||||||
|
python-liquid >= 1.2.1, < 2.0.0
|
||||||
typing-extensions >= 3.10.0.0
|
typing-extensions >= 3.10.0.0
|
||||||
ordered-set >= 4.1.0, < 5.0.0
|
ordered-set >= 4.1.0, < 5.0.0
|
||||||
fastapi >= 0.70.0, < 0.71.0
|
fastapi >= 0.70.0, < 0.71.0
|
||||||
|
@ -24,9 +24,9 @@ from .setting import (
|
|||||||
Settings,
|
Settings,
|
||||||
SettingsIn,
|
SettingsIn,
|
||||||
SettingsOut,
|
SettingsOut,
|
||||||
KeySetOfSettings,
|
|
||||||
TaskOptions,
|
TaskOptions,
|
||||||
)
|
)
|
||||||
|
from .setting.typing import KeySetOfSettings
|
||||||
from .notification import (
|
from .notification import (
|
||||||
EmailNotifier,
|
EmailNotifier,
|
||||||
ServerchanNotifier,
|
ServerchanNotifier,
|
||||||
|
16
src/blrec/data/message_templates/html/error.html
Normal file
16
src/blrec/data/message_templates/html/error.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<h2>异常信息:</h2>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{{ event.data.detail }}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span><b>事件 ID</b>:</span>
|
||||||
|
<span>{{ event.id }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>事件时间</b>:</span>
|
||||||
|
<span>{{ event.date }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
70
src/blrec/data/message_templates/html/live-began.html
Normal file
70
src/blrec/data/message_templates/html/live-began.html
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<p>
|
||||||
|
<span><b>主播</b>:</span>
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://space.bilibili.com/{{ event.data.user_info.uid }}"
|
||||||
|
_blank
|
||||||
|
title="打开主播个人空间页面"
|
||||||
|
>{{ event.data.user_info.name }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>标题</b>:</span>
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/{{ event.data.room_info.room_id }}"
|
||||||
|
_blank
|
||||||
|
title="打开直播间页面"
|
||||||
|
>{{ event.data.room_info.title }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>分区</b>:</span>
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/p/eden/area-tags?parentAreaId={{
|
||||||
|
event.data.room_info.parent_area_id
|
||||||
|
}}&areaId={{ event.data.room_info.area_id }}"
|
||||||
|
_blank
|
||||||
|
title="打开直播分区页面"
|
||||||
|
>{{ event.data.room_info.parent_area_name }}·{{
|
||||||
|
event.data.room_info.area_name }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>房间</b>:</span>
|
||||||
|
{%- if event.data.room_info.short_room_id > 0 -%}
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/{{ event.data.room_info.short_room_id }}"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ event.data.room_info.short_room_id }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span>, </span>
|
||||||
|
{%- endif -%}
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/{{ event.data.room_info.room_id }}"
|
||||||
|
_blank
|
||||||
|
title="打开直播间页面"
|
||||||
|
>{{ event.data.room_info.room_id }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>开播</b>:</span>
|
||||||
|
<span
|
||||||
|
>{{ event.data.room_info.live_start_time | datetimestring | date: "%Y-%m-%d
|
||||||
|
%H:%M:%S" }}</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<a href="{{ event.data.room_info.cover }}" _blank title="打开封面图片">
|
||||||
|
<img src="{{ event.data.room_info.cover }}" />
|
||||||
|
</a>
|
||||||
|
</div>
|
63
src/blrec/data/message_templates/html/live-ended.html
Normal file
63
src/blrec/data/message_templates/html/live-ended.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<p>
|
||||||
|
<span><b>主播</b>:</span>
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://space.bilibili.com/{{ event.data.user_info.uid }}"
|
||||||
|
_blank
|
||||||
|
title="打开主播个人空间页面"
|
||||||
|
>{{ event.data.user_info.name }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>标题</b>:</span>
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/{{ event.data.room_info.room_id }}"
|
||||||
|
_blank
|
||||||
|
title="打开直播间页面"
|
||||||
|
>{{ event.data.room_info.title }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>分区</b>:</span>
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/p/eden/area-tags?parentAreaId={{
|
||||||
|
event.data.room_info.parent_area_id
|
||||||
|
}}&areaId={{ event.data.room_info.area_id }}"
|
||||||
|
_blank
|
||||||
|
title="打开直播分区页面"
|
||||||
|
>{{ event.data.room_info.parent_area_name }}·{{
|
||||||
|
event.data.room_info.area_name }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>房间</b>:</span>
|
||||||
|
{%- if event.data.room_info.short_room_id > 0 -%}
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/{{ event.data.room_info.short_room_id }}"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ event.data.room_info.short_room_id }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span>, </span>
|
||||||
|
{%- endif -%}
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://live.bilibili.com/{{ event.data.room_info.room_id }}"
|
||||||
|
_blank
|
||||||
|
title="打开直播间页面"
|
||||||
|
>{{ event.data.room_info.room_id }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<a href="{{ event.data.room_info.cover }}" _blank title="打开封面图片">
|
||||||
|
<img src="{{ event.data.room_info.cover }}" />
|
||||||
|
</a>
|
||||||
|
</div>
|
31
src/blrec/data/message_templates/html/space-no-enough.html
Normal file
31
src/blrec/data/message_templates/html/space-no-enough.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<p>
|
||||||
|
<span><b>路径</b>:</span>
|
||||||
|
<span>{{ event.data.path }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>阈值</b>:</span>
|
||||||
|
<span>{{ event.data.threshold | naturalsize }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>硬盘容量</b>:</span>
|
||||||
|
<span>{{ event.data.usage.total | naturalsize }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>已用空间</b>:</span>
|
||||||
|
<span>{{ event.data.usage.used | naturalsize }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>可用空间</b>:</span>
|
||||||
|
<span>{{ event.data.usage.free | naturalsize }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span><b>事件 ID</b>:</span>
|
||||||
|
<span>{{ event.id }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span><b>事件时间</b>:</span>
|
||||||
|
<span>{{ event.date }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
5
src/blrec/data/message_templates/markdown/error.md
Normal file
5
src/blrec/data/message_templates/markdown/error.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
异常信息:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{{ event.data.detail }}
|
||||||
|
```
|
16
src/blrec/data/message_templates/markdown/live-began.md
Normal file
16
src/blrec/data/message_templates/markdown/live-began.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
主播:[{{ event.data.user_info.name }}](https://space.bilibili.com/{{ event.data.user_info.uid }})
|
||||||
|
|
||||||
|
标题:[{{ event.data.room_info.title }}](https://live.bilibili.com/{{ event.data.room_info.room_id }})
|
||||||
|
|
||||||
|
分区:[{{ event.data.room_info.parent_area_name }}·{{ event.data.room_info.area_name }}](https://live.bilibili.com/p/eden/area-tags?parentAreaId={{ event.data.room_info.parent_area_id }}&areaId={{ event.data.room_info.area_id }})
|
||||||
|
|
||||||
|
房间:[
|
||||||
|
{%- if event.data.room_info.short_room_id > 0 -%}
|
||||||
|
{{ event.data.room_info.short_room_id }}{% raw %}, {% endraw %}
|
||||||
|
{%- endif -%}
|
||||||
|
{{ event.data.room_info.room_id }}
|
||||||
|
](https://live.bilibili.com/{{ event.data.room_info.room_id }})
|
||||||
|
{% if event.data.room_info.live_start_time > 0 %}
|
||||||
|
开播:{{ event.data.room_info.live_start_time | datetimestring }}
|
||||||
|
{% endif %}
|
||||||
|
![直播间封面]({{ event.data.room_info.cover }})
|
14
src/blrec/data/message_templates/markdown/live-ended.md
Normal file
14
src/blrec/data/message_templates/markdown/live-ended.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
主播:[{{ event.data.user_info.name }}](https://space.bilibili.com/{{ event.data.user_info.uid }})
|
||||||
|
|
||||||
|
标题:[{{ event.data.room_info.title }}](https://live.bilibili.com/{{ event.data.room_info.room_id }})
|
||||||
|
|
||||||
|
分区:[{{ event.data.room_info.parent_area_name }}·{{ event.data.room_info.area_name }}](https://live.bilibili.com/p/eden/area-tags?parentAreaId={{ event.data.room_info.parent_area_id }}&areaId={{ event.data.room_info.area_id }})
|
||||||
|
|
||||||
|
房间:[
|
||||||
|
{%- if event.data.room_info.short_room_id > 0 -%}
|
||||||
|
{{ event.data.room_info.short_room_id }}{% raw %}, {% endraw %}
|
||||||
|
{%- endif -%}
|
||||||
|
{{ event.data.room_info.room_id }}
|
||||||
|
](https://live.bilibili.com/{{ event.data.room_info.room_id }})
|
||||||
|
|
||||||
|
![直播间封面]({{ event.data.room_info.cover }})
|
@ -0,0 +1,9 @@
|
|||||||
|
路径:{{ event.data.path }}
|
||||||
|
|
||||||
|
阈值:{{ event.data.threshold | naturalsize }}
|
||||||
|
|
||||||
|
硬盘容量:{{ event.data.usage.total | naturalsize }}
|
||||||
|
|
||||||
|
已用空间:{{ event.data.usage.used | naturalsize }}
|
||||||
|
|
||||||
|
可用空间:{{ event.data.usage.free | naturalsize }}
|
3
src/blrec/data/message_templates/text/error.txt
Normal file
3
src/blrec/data/message_templates/text/error.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
异常信息:
|
||||||
|
|
||||||
|
{{ event.data.detail }}
|
14
src/blrec/data/message_templates/text/live-began.txt
Normal file
14
src/blrec/data/message_templates/text/live-began.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
主播:{{ event.data.user_info.name }}
|
||||||
|
|
||||||
|
标题:{{ event.data.room_info.title }}
|
||||||
|
|
||||||
|
分区:{{ event.data.room_info.parent_area_name }}·{{ event.data.room_info.area_name }}
|
||||||
|
|
||||||
|
房间:
|
||||||
|
{%- if event.data.room_info.short_room_id > 0 -%}
|
||||||
|
{{ event.data.room_info.short_room_id }}{% raw %}, {% endraw %}
|
||||||
|
{%- endif -%}
|
||||||
|
{{ event.data.room_info.room_id }}
|
||||||
|
{% if event.data.room_info.live_start_time > 0 %}
|
||||||
|
开播:{{ event.data.room_info.live_start_time | datetimestring }}
|
||||||
|
{% endif %}
|
11
src/blrec/data/message_templates/text/live-ended.txt
Normal file
11
src/blrec/data/message_templates/text/live-ended.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
主播:{{ event.data.user_info.name }}
|
||||||
|
|
||||||
|
标题:{{ event.data.room_info.title }}
|
||||||
|
|
||||||
|
分区:{{ event.data.room_info.parent_area_name }}·{{ event.data.room_info.area_name }}
|
||||||
|
|
||||||
|
房间:
|
||||||
|
{%- if event.data.room_info.short_room_id > 0 -%}
|
||||||
|
{{ event.data.room_info.short_room_id }}{% raw %}, {% endraw %}
|
||||||
|
{%- endif -%}
|
||||||
|
{{ event.data.room_info.room_id }}
|
@ -0,0 +1,9 @@
|
|||||||
|
路径:{{ event.data.path }}
|
||||||
|
|
||||||
|
阈值:{{ event.data.threshold | naturalsize }}
|
||||||
|
|
||||||
|
硬盘容量:{{ event.data.usage.total | naturalsize }}
|
||||||
|
|
||||||
|
已用空间:{{ event.data.usage.used | naturalsize }}
|
||||||
|
|
||||||
|
可用空间:{{ event.data.usage.free | naturalsize }}
|
File diff suppressed because one or more lines are too long
1
src/blrec/data/webapp/183.ae1a1102b7d5cbdb.js
Normal file
1
src/blrec/data/webapp/183.ae1a1102b7d5cbdb.js
Normal file
File diff suppressed because one or more lines are too long
1
src/blrec/data/webapp/202.e15e5ae9f06639b8.js
Normal file
1
src/blrec/data/webapp/202.e15e5ae9f06639b8.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/blrec/data/webapp/66.9faa0b5a6adf9602.js
Normal file
1
src/blrec/data/webapp/66.9faa0b5a6adf9602.js
Normal file
File diff suppressed because one or more lines are too long
@ -6,10 +6,10 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="assets/images/logo.png">
|
<link rel="icon" type="image/x-icon" href="assets/images/logo.png">
|
||||||
<link rel="manifest" href="manifest.webmanifest">
|
<link rel="manifest" href="manifest.webmanifest">
|
||||||
<meta name="theme-color" content="#1976d2">
|
<meta name="theme-color" content="#1976d2">
|
||||||
<style>html,body{width:100%;height:100%}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0;color:#000000d9;font-size:14px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-variant:tabular-nums;line-height:1.5715;background-color:#fff;font-feature-settings:"tnum","tnum"}html{--antd-wave-shadow-color:#1890ff;--scroll-bar:0}</style><link rel="stylesheet" href="styles.1f581691b230dc4d.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.1f581691b230dc4d.css"></noscript></head>
|
<style>html,body{width:100%;height:100%}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0;color:#000000d9;font-size:14px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-variant:tabular-nums;line-height:1.5715;background-color:#fff;font-feature-settings:"tnum","tnum"}html{--antd-wave-shadow-color:#1890ff;--scroll-bar:0}</style><link rel="stylesheet" href="styles.2e152d608221c2ee.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.2e152d608221c2ee.css"></noscript></head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||||
<script src="runtime.dc2f7d56c437cd78.js" type="module"></script><script src="polyfills.4b08448aee19bb22.js" type="module"></script><script src="main.411b4a979eb179f8.js" type="module"></script>
|
<script src="runtime.0ce129f346263990.js" type="module"></script><script src="polyfills.4b08448aee19bb22.js" type="module"></script><script src="main.888c50197ddf8040.js" type="module"></script>
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
File diff suppressed because one or more lines are too long
1
src/blrec/data/webapp/main.888c50197ddf8040.js
Normal file
1
src/blrec/data/webapp/main.888c50197ddf8040.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"configVersion": 1,
|
"configVersion": 1,
|
||||||
"timestamp": 1654692172512,
|
"timestamp": 1655391613431,
|
||||||
"index": "/index.html",
|
"index": "/index.html",
|
||||||
"assetGroups": [
|
"assetGroups": [
|
||||||
{
|
{
|
||||||
@ -13,17 +13,17 @@
|
|||||||
"urls": [
|
"urls": [
|
||||||
"/103.5b5d2a6e5a8a7479.js",
|
"/103.5b5d2a6e5a8a7479.js",
|
||||||
"/146.92e3b29c4c754544.js",
|
"/146.92e3b29c4c754544.js",
|
||||||
"/183.8cf3b5282412a0ec.js",
|
"/183.ae1a1102b7d5cbdb.js",
|
||||||
|
"/202.e15e5ae9f06639b8.js",
|
||||||
"/45.c90c3cea2bf1a66e.js",
|
"/45.c90c3cea2bf1a66e.js",
|
||||||
"/474.7f6529972e383566.js",
|
"/66.9faa0b5a6adf9602.js",
|
||||||
"/66.31f5b9ae46ae9005.js",
|
|
||||||
"/common.858f777e9296e6f2.js",
|
"/common.858f777e9296e6f2.js",
|
||||||
"/index.html",
|
"/index.html",
|
||||||
"/main.411b4a979eb179f8.js",
|
"/main.888c50197ddf8040.js",
|
||||||
"/manifest.webmanifest",
|
"/manifest.webmanifest",
|
||||||
"/polyfills.4b08448aee19bb22.js",
|
"/polyfills.4b08448aee19bb22.js",
|
||||||
"/runtime.dc2f7d56c437cd78.js",
|
"/runtime.0ce129f346263990.js",
|
||||||
"/styles.1f581691b230dc4d.css"
|
"/styles.2e152d608221c2ee.css"
|
||||||
],
|
],
|
||||||
"patterns": []
|
"patterns": []
|
||||||
},
|
},
|
||||||
@ -1636,10 +1636,10 @@
|
|||||||
"hashTable": {
|
"hashTable": {
|
||||||
"/103.5b5d2a6e5a8a7479.js": "cc0240f217015b6d4ddcc14f31fcc42e1c1c282a",
|
"/103.5b5d2a6e5a8a7479.js": "cc0240f217015b6d4ddcc14f31fcc42e1c1c282a",
|
||||||
"/146.92e3b29c4c754544.js": "3824de681dd1f982ea69a065cdf54d7a1e781f4d",
|
"/146.92e3b29c4c754544.js": "3824de681dd1f982ea69a065cdf54d7a1e781f4d",
|
||||||
"/183.8cf3b5282412a0ec.js": "bf93e3b9baf3d6c0eb5e320c4916d5bf540c4cb0",
|
"/183.ae1a1102b7d5cbdb.js": "6cb22d60b0a20214212e6050fbbf33926a4c1346",
|
||||||
|
"/202.e15e5ae9f06639b8.js": "62335dc98644969539760565ff9c3c472d304287",
|
||||||
"/45.c90c3cea2bf1a66e.js": "e5bfb8cf3803593e6b8ea14c90b3d3cb6a066764",
|
"/45.c90c3cea2bf1a66e.js": "e5bfb8cf3803593e6b8ea14c90b3d3cb6a066764",
|
||||||
"/474.7f6529972e383566.js": "1c74b5c6379705a3110c99767f97feddc42a0d54",
|
"/66.9faa0b5a6adf9602.js": "c2f418ebb80f35402d9f24e5acaf8167c96f9eb3",
|
||||||
"/66.31f5b9ae46ae9005.js": "cc22d2582d8e4c2a83e089d5a1ec32619e439ccd",
|
|
||||||
"/assets/animal/panda.js": "fec2868bb3053dd2da45f96bbcb86d5116ed72b1",
|
"/assets/animal/panda.js": "fec2868bb3053dd2da45f96bbcb86d5116ed72b1",
|
||||||
"/assets/animal/panda.svg": "bebd302cdc601e0ead3a6d2710acf8753f3d83b1",
|
"/assets/animal/panda.svg": "bebd302cdc601e0ead3a6d2710acf8753f3d83b1",
|
||||||
"/assets/fill/.gitkeep": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
"/assets/fill/.gitkeep": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||||
@ -3234,12 +3234,12 @@
|
|||||||
"/assets/twotone/warning.js": "fb2d7ea232f3a99bf8f080dbc94c65699232ac01",
|
"/assets/twotone/warning.js": "fb2d7ea232f3a99bf8f080dbc94c65699232ac01",
|
||||||
"/assets/twotone/warning.svg": "8c7a2d3e765a2e7dd58ac674870c6655cecb0068",
|
"/assets/twotone/warning.svg": "8c7a2d3e765a2e7dd58ac674870c6655cecb0068",
|
||||||
"/common.858f777e9296e6f2.js": "b68ca68e1e214a2537d96935c23410126cc564dd",
|
"/common.858f777e9296e6f2.js": "b68ca68e1e214a2537d96935c23410126cc564dd",
|
||||||
"/index.html": "e7c15c095d8d2a11a60d7211e17e8a38316adf0e",
|
"/index.html": "374ebd2a9b656c5ebcbc9f5a4402b345cd4c7c5c",
|
||||||
"/main.411b4a979eb179f8.js": "4c5e77b0589a77410f84441d0877c1d18cb1357f",
|
"/main.888c50197ddf8040.js": "f506b85641a4598b002c21bc49c9a36e0c058326",
|
||||||
"/manifest.webmanifest": "62c1cb8c5ad2af551a956b97013ab55ce77dd586",
|
"/manifest.webmanifest": "62c1cb8c5ad2af551a956b97013ab55ce77dd586",
|
||||||
"/polyfills.4b08448aee19bb22.js": "8e73f2d42cc13ca353cea5c886d930bd6da08d0d",
|
"/polyfills.4b08448aee19bb22.js": "8e73f2d42cc13ca353cea5c886d930bd6da08d0d",
|
||||||
"/runtime.dc2f7d56c437cd78.js": "fcf22060f48d6229236a14de31cc63c3568db8b1",
|
"/runtime.0ce129f346263990.js": "98698b10b3f873a761f1e1c7fb5a9bcd2f3830ee",
|
||||||
"/styles.1f581691b230dc4d.css": "6f5befbbad57c2b2e80aae855139744b8010d150"
|
"/styles.2e152d608221c2ee.css": "9830389a46daa5b4511e0dd343aad23ca9f9690f"
|
||||||
},
|
},
|
||||||
"navigationUrls": [
|
"navigationUrls": [
|
||||||
{
|
{
|
||||||
|
@ -1 +1 @@
|
|||||||
(()=>{"use strict";var e,v={},m={};function r(e){var i=m[e];if(void 0!==i)return i.exports;var t=m[e]={exports:{}};return v[e].call(t.exports,t,t.exports,r),t.exports}r.m=v,e=[],r.O=(i,t,o,f)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,o,f]=e[n],c=!0,l=0;l<t.length;l++)(!1&f||a>=f)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(c=!1,f<a&&(a=f));if(c){e.splice(n--,1);var d=o();void 0!==d&&(i=d)}}return i}f=f||0;for(var n=e.length;n>0&&e[n-1][2]>f;n--)e[n]=e[n-1];e[n]=[t,o,f]},r.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return r.d(i,{a:i}),i},r.d=(e,i)=>{for(var t in i)r.o(i,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((i,t)=>(r.f[t](e,i),i),[])),r.u=e=>(592===e?"common":e)+"."+{45:"c90c3cea2bf1a66e",66:"31f5b9ae46ae9005",103:"5b5d2a6e5a8a7479",146:"92e3b29c4c754544",183:"8cf3b5282412a0ec",474:"7f6529972e383566",592:"858f777e9296e6f2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="blrec:";r.l=(t,o,f,n)=>{if(e[t])e[t].push(o);else{var a,c;if(void 0!==f)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==i+f){a=u;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",i+f),a.src=r.tu(t)),e[t]=[o];var s=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),_&&_.forEach(h=>h(b)),g)return g(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;r.tu=i=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(i))})(),r.p="",(()=>{var e={666:0};r.f.j=(o,f)=>{var n=r.o(e,o)?e[o]:void 0;if(0!==n)if(n)f.push(n[2]);else if(666!=o){var a=new Promise((u,s)=>n=e[o]=[u,s]);f.push(n[2]=a);var c=r.p+r.u(o),l=new Error;r.l(c,u=>{if(r.o(e,o)&&(0!==(n=e[o])&&(e[o]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+o+" failed.\n("+s+": "+p+")",l.name="ChunkLoadError",l.type=s,l.request=p,n[1](l)}},"chunk-"+o,o)}else e[o]=0},r.O.j=o=>0===e[o];var i=(o,f)=>{var l,d,[n,a,c]=f,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(c)var s=c(r)}for(o&&o(f);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[n[u]]=0;return r.O(s)},t=self.webpackChunkblrec=self.webpackChunkblrec||[];t.forEach(i.bind(null,0)),t.push=i.bind(null,t.push.bind(t))})()})();
|
(()=>{"use strict";var e,v={},m={};function r(e){var i=m[e];if(void 0!==i)return i.exports;var t=m[e]={exports:{}};return v[e].call(t.exports,t,t.exports,r),t.exports}r.m=v,e=[],r.O=(i,t,o,f)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,o,f]=e[n],c=!0,l=0;l<t.length;l++)(!1&f||a>=f)&&Object.keys(r.O).every(p=>r.O[p](t[l]))?t.splice(l--,1):(c=!1,f<a&&(a=f));if(c){e.splice(n--,1);var d=o();void 0!==d&&(i=d)}}return i}f=f||0;for(var n=e.length;n>0&&e[n-1][2]>f;n--)e[n]=e[n-1];e[n]=[t,o,f]},r.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return r.d(i,{a:i}),i},r.d=(e,i)=>{for(var t in i)r.o(i,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((i,t)=>(r.f[t](e,i),i),[])),r.u=e=>(592===e?"common":e)+"."+{45:"c90c3cea2bf1a66e",66:"9faa0b5a6adf9602",103:"5b5d2a6e5a8a7479",146:"92e3b29c4c754544",183:"ae1a1102b7d5cbdb",202:"e15e5ae9f06639b8",592:"858f777e9296e6f2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="blrec:";r.l=(t,o,f,n)=>{if(e[t])e[t].push(o);else{var a,c;if(void 0!==f)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==i+f){a=u;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",i+f),a.src=r.tu(t)),e[t]=[o];var s=(g,p)=>{a.onerror=a.onload=null,clearTimeout(b);var _=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),_&&_.forEach(h=>h(p)),g)return g(p)},b=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;r.tu=i=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(i))})(),r.p="",(()=>{var e={666:0};r.f.j=(o,f)=>{var n=r.o(e,o)?e[o]:void 0;if(0!==n)if(n)f.push(n[2]);else if(666!=o){var a=new Promise((u,s)=>n=e[o]=[u,s]);f.push(n[2]=a);var c=r.p+r.u(o),l=new Error;r.l(c,u=>{if(r.o(e,o)&&(0!==(n=e[o])&&(e[o]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),b=u&&u.target&&u.target.src;l.message="Loading chunk "+o+" failed.\n("+s+": "+b+")",l.name="ChunkLoadError",l.type=s,l.request=b,n[1](l)}},"chunk-"+o,o)}else e[o]=0},r.O.j=o=>0===e[o];var i=(o,f)=>{var l,d,[n,a,c]=f,u=0;if(n.some(b=>0!==e[b])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(c)var s=c(r)}for(o&&o(f);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[n[u]]=0;return r.O(s)},t=self.webpackChunkblrec=self.webpackChunkblrec||[];t.forEach(i.bind(null,0)),t.push=i.bind(null,t.push.bind(t))})()})();
|
File diff suppressed because one or more lines are too long
@ -1,61 +0,0 @@
|
|||||||
|
|
||||||
import humanize
|
|
||||||
|
|
||||||
from ..event import SpaceNoEnoughEventData
|
|
||||||
|
|
||||||
from ..bili.models import UserInfo, RoomInfo
|
|
||||||
from ..exception import format_exception
|
|
||||||
|
|
||||||
|
|
||||||
live_info_template = """
|
|
||||||
主播: {user_name}
|
|
||||||
|
|
||||||
标题: {room_title}
|
|
||||||
|
|
||||||
分区: {area_name}
|
|
||||||
|
|
||||||
房间: {room_id}
|
|
||||||
"""
|
|
||||||
|
|
||||||
disk_usage_template = """
|
|
||||||
路径: {path}
|
|
||||||
|
|
||||||
阈值: {threshold}
|
|
||||||
|
|
||||||
硬盘容量: {usage_total}
|
|
||||||
|
|
||||||
已用空间: {usage_used}
|
|
||||||
|
|
||||||
可用空间: {usage_free}
|
|
||||||
"""
|
|
||||||
|
|
||||||
exception_template = """
|
|
||||||
异常信息:
|
|
||||||
|
|
||||||
{exception_message}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def make_live_info_content(user_info: UserInfo, room_info: RoomInfo) -> str:
|
|
||||||
return live_info_template.format(
|
|
||||||
user_name=user_info.name,
|
|
||||||
room_title=room_info.title,
|
|
||||||
area_name=room_info.area_name,
|
|
||||||
room_id=room_info.room_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_disk_usage_content(data: SpaceNoEnoughEventData) -> str:
|
|
||||||
return disk_usage_template.format(
|
|
||||||
path=data.path,
|
|
||||||
threshold=humanize.naturalsize(data.threshold),
|
|
||||||
usage_total=humanize.naturalsize(data.usage.total),
|
|
||||||
usage_used=humanize.naturalsize(data.usage.used),
|
|
||||||
usage_free=humanize.naturalsize(data.usage.free),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_exception_content(exc: BaseException) -> str:
|
|
||||||
return exception_template.format(
|
|
||||||
exception_message=format_exception(exc),
|
|
||||||
)
|
|
@ -1,37 +1,42 @@
|
|||||||
import logging
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Final, Optional, Tuple
|
||||||
|
|
||||||
|
import attr
|
||||||
|
import humanize
|
||||||
|
from liquid import Environment
|
||||||
|
from liquid.filter import math_filter
|
||||||
|
from pkg_resources import resource_string
|
||||||
from tenacity import (
|
from tenacity import (
|
||||||
AsyncRetrying,
|
AsyncRetrying,
|
||||||
wait_exponential,
|
|
||||||
stop_after_delay,
|
|
||||||
retry_if_exception,
|
retry_if_exception,
|
||||||
|
stop_after_delay,
|
||||||
|
wait_exponential,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .providers import (
|
|
||||||
EmailService,
|
|
||||||
MessagingProvider,
|
|
||||||
Serverchan,
|
|
||||||
Pushdeer,
|
|
||||||
Pushplus,
|
|
||||||
Telegram,
|
|
||||||
)
|
|
||||||
from .message import (
|
|
||||||
make_live_info_content,
|
|
||||||
make_disk_usage_content,
|
|
||||||
make_exception_content,
|
|
||||||
)
|
|
||||||
from ..utils.mixins import SwitchableMixin
|
|
||||||
from ..exception import ExceptionCenter
|
|
||||||
from ..event import (
|
from ..event import (
|
||||||
|
Error,
|
||||||
|
ErrorData,
|
||||||
EventCenter,
|
EventCenter,
|
||||||
LiveBeganEvent,
|
LiveBeganEvent,
|
||||||
LiveEndedEvent,
|
LiveEndedEvent,
|
||||||
SpaceNoEnoughEvent,
|
SpaceNoEnoughEvent,
|
||||||
)
|
)
|
||||||
from ..event.typing import Event
|
from ..event.typing import Event
|
||||||
|
from ..exception import ExceptionCenter, format_exception
|
||||||
|
from ..setting.typing import MessageType
|
||||||
|
from ..utils.mixins import SwitchableMixin
|
||||||
|
from .providers import (
|
||||||
|
EmailService,
|
||||||
|
MessagingProvider,
|
||||||
|
Pushdeer,
|
||||||
|
Pushplus,
|
||||||
|
Serverchan,
|
||||||
|
Telegram,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Notifier',
|
'Notifier',
|
||||||
@ -55,12 +60,45 @@ class Notifier(SwitchableMixin, ABC):
|
|||||||
notify_ended: bool = False,
|
notify_ended: bool = False,
|
||||||
notify_space: bool = False,
|
notify_space: bool = False,
|
||||||
notify_error: bool = False,
|
notify_error: bool = False,
|
||||||
|
began_message_type: MessageType = 'text',
|
||||||
|
began_message_title: str = '',
|
||||||
|
began_message_content: str = '',
|
||||||
|
ended_message_type: MessageType = 'text',
|
||||||
|
ended_message_title: str = '',
|
||||||
|
ended_message_content: str = '',
|
||||||
|
space_message_type: MessageType = 'text',
|
||||||
|
space_message_title: str = '',
|
||||||
|
space_message_content: str = '',
|
||||||
|
error_message_type: MessageType = 'text',
|
||||||
|
error_message_title: str = '',
|
||||||
|
error_message_content: str = '',
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
self._liquid_env = Environment()
|
||||||
|
self._liquid_env.add_filter('intcomma', math_filter(humanize.intcomma))
|
||||||
|
self._liquid_env.add_filter('naturalsize', math_filter(humanize.naturalsize))
|
||||||
|
self._liquid_env.add_filter(
|
||||||
|
'datetimestring',
|
||||||
|
math_filter(lambda n: datetime.fromtimestamp(n).isoformat()),
|
||||||
|
)
|
||||||
|
|
||||||
self.notify_began = notify_began
|
self.notify_began = notify_began
|
||||||
self.notify_ended = notify_ended
|
self.notify_ended = notify_ended
|
||||||
self.notify_space = notify_space
|
self.notify_space = notify_space
|
||||||
self.notify_error = notify_error
|
self.notify_error = notify_error
|
||||||
|
self.began_message_type = began_message_type
|
||||||
|
self.began_message_title = began_message_title
|
||||||
|
self.began_message_content = began_message_content
|
||||||
|
self.ended_message_type = ended_message_type
|
||||||
|
self.ended_message_title = ended_message_title
|
||||||
|
self.ended_message_content = ended_message_content
|
||||||
|
self.space_message_type = space_message_type
|
||||||
|
self.space_message_title = space_message_title
|
||||||
|
self.space_message_content = space_message_content
|
||||||
|
self.error_message_type = error_message_type
|
||||||
|
self.error_message_title = error_message_title
|
||||||
|
self.error_message_content = error_message_content
|
||||||
|
|
||||||
def _do_enable(self) -> None:
|
def _do_enable(self) -> None:
|
||||||
events = EventCenter.get_instance().events
|
events = EventCenter.get_instance().events
|
||||||
@ -87,7 +125,8 @@ class Notifier(SwitchableMixin, ABC):
|
|||||||
|
|
||||||
def _on_exception(self, exc: BaseException) -> None:
|
def _on_exception(self, exc: BaseException) -> None:
|
||||||
if self._should_notify_exception(exc):
|
if self._should_notify_exception(exc):
|
||||||
self._notify_exception(exc)
|
error = Error.from_data(ErrorData.from_exc(exc))
|
||||||
|
self._notify_exception(error)
|
||||||
|
|
||||||
def _should_notify_live_began(self, event: LiveBeganEvent) -> bool:
|
def _should_notify_live_began(self, event: LiveBeganEvent) -> bool:
|
||||||
return self.notify_began
|
return self.notify_began
|
||||||
@ -95,9 +134,7 @@ class Notifier(SwitchableMixin, ABC):
|
|||||||
def _should_notify_live_ended(self, event: LiveEndedEvent) -> bool:
|
def _should_notify_live_ended(self, event: LiveEndedEvent) -> bool:
|
||||||
return self.notify_ended
|
return self.notify_ended
|
||||||
|
|
||||||
def _should_notify_space_no_enough(
|
def _should_notify_space_no_enough(self, event: SpaceNoEnoughEvent) -> bool:
|
||||||
self, event: SpaceNoEnoughEvent
|
|
||||||
) -> bool:
|
|
||||||
return self.notify_space
|
return self.notify_space
|
||||||
|
|
||||||
def _should_notify_exception(self, exc: BaseException) -> bool:
|
def _should_notify_exception(self, exc: BaseException) -> bool:
|
||||||
@ -116,56 +153,184 @@ class Notifier(SwitchableMixin, ABC):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _notify_exception(self, exc: BaseException) -> None:
|
def _notify_exception(self, event: Error) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _make_began_message(self, event: LiveBeganEvent) -> Tuple[str, str]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _make_ended_message(self, event: LiveEndedEvent) -> Tuple[str, str]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _make_space_message(self, event: SpaceNoEnoughEvent) -> Tuple[str, str]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _make_error_message(self, event: Error) -> Tuple[str, str]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
BEGAN_MESSAGE_TITLE: Final[str] = '{{ event.data.user_info.name }} 开播啦'
|
||||||
|
ENDED_MESSAGE_TITLE: Final[str] = '{{ event.data.user_info.name }} 下播了'
|
||||||
|
SPACE_MESSAGE_TITLE: Final[str] = '空间不足!'
|
||||||
|
ERROR_MESSAGE_TITLE: Final[str] = '出错了~'
|
||||||
|
|
||||||
|
|
||||||
class MessageNotifier(Notifier, ABC):
|
class MessageNotifier(Notifier, ABC):
|
||||||
provider: MessagingProvider
|
provider: MessagingProvider
|
||||||
|
|
||||||
def _notify_live_began(self, event: LiveBeganEvent) -> None:
|
def _notify_live_began(self, event: LiveBeganEvent) -> None:
|
||||||
title = f'{event.data.user_info.name} 开播啦'
|
title, content = self._make_began_message(event)
|
||||||
content = make_live_info_content(
|
self._send_message(title, content, self.began_message_type)
|
||||||
event.data.user_info, event.data.room_info
|
|
||||||
)
|
|
||||||
self._send_message(title, content)
|
|
||||||
|
|
||||||
def _notify_live_ended(self, event: LiveEndedEvent) -> None:
|
def _notify_live_ended(self, event: LiveEndedEvent) -> None:
|
||||||
title = f'{event.data.user_info.name} 下播了'
|
title, content = self._make_ended_message(event)
|
||||||
content = make_live_info_content(
|
self._send_message(title, content, self.ended_message_type)
|
||||||
event.data.user_info, event.data.room_info
|
|
||||||
)
|
|
||||||
self._send_message(title, content)
|
|
||||||
|
|
||||||
def _notify_space_no_enough(self, event: SpaceNoEnoughEvent) -> None:
|
def _notify_space_no_enough(self, event: SpaceNoEnoughEvent) -> None:
|
||||||
|
title, content = self._make_space_message(event)
|
||||||
|
self._send_message(title, content, self.space_message_type)
|
||||||
|
|
||||||
|
def _notify_exception(self, event: Error) -> None:
|
||||||
|
title, content = self._make_error_message(event)
|
||||||
|
self._send_message(title, content, self.error_message_type)
|
||||||
|
|
||||||
|
def _make_began_message(self, event: LiveBeganEvent) -> Tuple[str, str]:
|
||||||
|
env = os.environ.copy()
|
||||||
|
try:
|
||||||
|
template = self._liquid_env.from_string(self._get_began_message_title())
|
||||||
|
title = template.render(event=attr.asdict(event), env=env)
|
||||||
|
template = self._liquid_env.from_string(self._get_began_message_content())
|
||||||
|
content = template.render(event=attr.asdict(event), env=env)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f'Failed to render began message template: {repr(e)}')
|
||||||
|
title = f'{event.data.user_info.name} 开播啦'
|
||||||
|
content = format_exception(e)
|
||||||
|
return title, content
|
||||||
|
|
||||||
|
def _make_ended_message(self, event: LiveEndedEvent) -> Tuple[str, str]:
|
||||||
|
env = os.environ.copy()
|
||||||
|
try:
|
||||||
|
template = self._liquid_env.from_string(self._get_ended_message_title())
|
||||||
|
title = template.render(event=attr.asdict(event), env=env)
|
||||||
|
template = self._liquid_env.from_string(self._get_ended_message_content())
|
||||||
|
content = template.render(event=attr.asdict(event), env=env)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f'Failed to render ended message template: {repr(e)}')
|
||||||
|
title = f'{event.data.user_info.name} 下播了'
|
||||||
|
content = format_exception(e)
|
||||||
|
return title, content
|
||||||
|
|
||||||
|
def _make_space_message(self, event: SpaceNoEnoughEvent) -> Tuple[str, str]:
|
||||||
|
env = os.environ.copy()
|
||||||
|
try:
|
||||||
|
template = self._liquid_env.from_string(self._get_space_message_title())
|
||||||
|
title = template.render(event=attr.asdict(event), env=env)
|
||||||
|
template = self._liquid_env.from_string(self._get_space_message_content())
|
||||||
|
content = template.render(event=attr.asdict(event), env=env)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f'Failed to render space message template: {repr(e)}')
|
||||||
title = '空间不足!'
|
title = '空间不足!'
|
||||||
content = make_disk_usage_content(event.data)
|
content = format_exception(e)
|
||||||
self._send_message(title, content)
|
return title, content
|
||||||
|
|
||||||
def _notify_exception(self, exc: BaseException) -> None:
|
def _make_error_message(self, event: Error) -> Tuple[str, str]:
|
||||||
|
env = os.environ.copy()
|
||||||
|
try:
|
||||||
|
template = self._liquid_env.from_string(self._get_error_message_title())
|
||||||
|
title = template.render(event=attr.asdict(event), env=env)
|
||||||
|
template = self._liquid_env.from_string(self._get_error_message_content())
|
||||||
|
content = template.render(event=attr.asdict(event), env=env)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f'Failed to render error message template: {repr(e)}')
|
||||||
title = '出错了~'
|
title = '出错了~'
|
||||||
content = make_exception_content(exc)
|
content = format_exception(e)
|
||||||
self._send_message(title, content)
|
return title, content
|
||||||
|
|
||||||
def _send_message(self, title: str, content: str) -> None:
|
def _send_message(self, title: str, content: str, msg_type: MessageType) -> None:
|
||||||
asyncio.create_task(self._send_message_async(title, content))
|
asyncio.create_task(self._send_message_async(title, content, msg_type))
|
||||||
|
|
||||||
async def _send_message_async(self, title: str, content: str) -> None:
|
async def _send_message_async(
|
||||||
|
self, title: str, content: str, msg_type: MessageType
|
||||||
|
) -> None:
|
||||||
try:
|
try:
|
||||||
async for attempt in AsyncRetrying(
|
async for attempt in AsyncRetrying(
|
||||||
reraise=True,
|
reraise=True,
|
||||||
stop=stop_after_delay(300),
|
stop=stop_after_delay(300),
|
||||||
wait=wait_exponential(multiplier=0.1, max=10),
|
wait=wait_exponential(multiplier=0.1, max=10),
|
||||||
retry=retry_if_exception(
|
retry=retry_if_exception(lambda e: not isinstance(e, ValueError)),
|
||||||
lambda e: not isinstance(e, ValueError)
|
|
||||||
),
|
|
||||||
):
|
):
|
||||||
with attempt:
|
with attempt:
|
||||||
await self.provider.send_message(title, content)
|
await self.provider.send_message(title, content, msg_type)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('Failed to send a message via {}: {}'.format(
|
logger.warning(
|
||||||
|
'Failed to send a message via {}: {}'.format(
|
||||||
self.provider.__class__.__name__, repr(e)
|
self.provider.__class__.__name__, repr(e)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_began_message_title(self) -> str:
|
||||||
|
return self.began_message_title or BEGAN_MESSAGE_TITLE
|
||||||
|
|
||||||
|
def _get_began_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
if self.began_message_content:
|
||||||
|
return self.began_message_content
|
||||||
|
msg_type = msg_type or self.began_message_type
|
||||||
|
if msg_type == 'markdown':
|
||||||
|
relpath = '../data/message_templates/markdown/live-began.md'
|
||||||
|
elif msg_type == 'html':
|
||||||
|
relpath = '../data/message_templates/html/live-began.html'
|
||||||
|
else:
|
||||||
|
relpath = '../data/message_templates/text/live-began.txt'
|
||||||
|
return resource_string(__name__, relpath).decode('utf-8')
|
||||||
|
|
||||||
|
def _get_ended_message_title(self) -> str:
|
||||||
|
return self.ended_message_title or ENDED_MESSAGE_TITLE
|
||||||
|
|
||||||
|
def _get_ended_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
if self.ended_message_content:
|
||||||
|
return self.ended_message_content
|
||||||
|
msg_type = msg_type or self.ended_message_type
|
||||||
|
if msg_type == 'markdown':
|
||||||
|
relpath = '../data/message_templates/markdown/live-ended.md'
|
||||||
|
elif msg_type == 'html':
|
||||||
|
relpath = '../data/message_templates/html/live-ended.html'
|
||||||
|
else:
|
||||||
|
relpath = '../data/message_templates/text/live-ended.txt'
|
||||||
|
return resource_string(__name__, relpath).decode('utf-8')
|
||||||
|
|
||||||
|
def _get_space_message_title(self) -> str:
|
||||||
|
return self.space_message_title or SPACE_MESSAGE_TITLE
|
||||||
|
|
||||||
|
def _get_space_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
if self.space_message_content:
|
||||||
|
return self.space_message_content
|
||||||
|
msg_type = msg_type or self.space_message_type
|
||||||
|
if msg_type == 'markdown':
|
||||||
|
relpath = '../data/message_templates/markdown/space-no-enough.md'
|
||||||
|
elif msg_type == 'html':
|
||||||
|
relpath = '../data/message_templates/html/space-no-enough.html'
|
||||||
|
else:
|
||||||
|
relpath = '../data/message_templates/text/space-no-enough.txt'
|
||||||
|
return resource_string(__name__, relpath).decode('utf-8')
|
||||||
|
|
||||||
|
def _get_error_message_title(self) -> str:
|
||||||
|
return self.error_message_title or ERROR_MESSAGE_TITLE
|
||||||
|
|
||||||
|
def _get_error_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
if self.error_message_content:
|
||||||
|
return self.error_message_content
|
||||||
|
msg_type = msg_type or self.error_message_type
|
||||||
|
if msg_type == 'markdown':
|
||||||
|
relpath = '../data/message_templates/markdown/error.md'
|
||||||
|
elif msg_type == 'html':
|
||||||
|
relpath = '../data/message_templates/html/error.html'
|
||||||
|
else:
|
||||||
|
relpath = '../data/message_templates/text/error.txt'
|
||||||
|
return resource_string(__name__, relpath).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
class EmailNotifier(MessageNotifier):
|
class EmailNotifier(MessageNotifier):
|
||||||
@ -226,3 +391,15 @@ class TelegramNotifier(MessageNotifier):
|
|||||||
def _do_disable(self) -> None:
|
def _do_disable(self) -> None:
|
||||||
super()._do_disable()
|
super()._do_disable()
|
||||||
logger.debug('Disabled Telegram notifier')
|
logger.debug('Disabled Telegram notifier')
|
||||||
|
|
||||||
|
def _get_began_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
return super()._get_began_message_content(msg_type='text')
|
||||||
|
|
||||||
|
def _get_ended_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
return super()._get_ended_message_content(msg_type='text')
|
||||||
|
|
||||||
|
def _get_space_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
return super()._get_space_message_content(msg_type='text')
|
||||||
|
|
||||||
|
def _get_error_message_content(self, msg_type: Optional[MessageType] = None) -> str:
|
||||||
|
return super()._get_error_message_content(msg_type='text')
|
||||||
|
@ -5,11 +5,19 @@ import ssl
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
from http.client import HTTPException
|
from http.client import HTTPException
|
||||||
from typing import Final, Literal, TypedDict, Dict, Any, cast
|
from typing import Any, Dict, Final, TypedDict, cast
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
|
from ..setting.typing import (
|
||||||
|
EmailMessageType,
|
||||||
|
MessageType,
|
||||||
|
PushdeerMessageType,
|
||||||
|
PushplusMessageType,
|
||||||
|
ServerchanMessageType,
|
||||||
|
TelegramMessageType,
|
||||||
|
)
|
||||||
from ..utils.patterns import Singleton
|
from ..utils.patterns import Singleton
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -30,13 +38,12 @@ class MessagingProvider(Singleton, ABC):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def send_message(self, title: str, content: str) -> None:
|
async def send_message(
|
||||||
|
self, title: str, content: str, msg_type: MessageType
|
||||||
|
) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
MSG_TYPE = Literal['plain', 'html']
|
|
||||||
|
|
||||||
|
|
||||||
class EmailService(MessagingProvider):
|
class EmailService(MessagingProvider):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -54,7 +61,7 @@ class EmailService(MessagingProvider):
|
|||||||
self.smtp_port = smtp_port
|
self.smtp_port = smtp_port
|
||||||
|
|
||||||
async def send_message(
|
async def send_message(
|
||||||
self, subject: str, content: str, msg_type: MSG_TYPE = 'plain'
|
self, subject: str, content: str, msg_type: MessageType
|
||||||
) -> None:
|
) -> None:
|
||||||
self._check_parameters()
|
self._check_parameters()
|
||||||
await asyncio.get_running_loop().run_in_executor(
|
await asyncio.get_running_loop().run_in_executor(
|
||||||
@ -62,13 +69,14 @@ class EmailService(MessagingProvider):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _send_email(
|
def _send_email(
|
||||||
self, subject: str, content: str, msg_type: MSG_TYPE = 'plain'
|
self, subject: str, content: str, msg_type: EmailMessageType
|
||||||
) -> None:
|
) -> None:
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
msg['Subject'] = subject
|
msg['Subject'] = subject
|
||||||
msg['From'] = self.src_addr
|
msg['From'] = self.src_addr
|
||||||
msg['To'] = self.dst_addr
|
msg['To'] = self.dst_addr
|
||||||
msg.set_content(content, subtype=msg_type, charset='utf-8')
|
subtype = 'html' if msg_type == 'html' else 'plain'
|
||||||
|
msg.set_content(content, subtype=subtype, charset='utf-8')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with smtplib.SMTP_SSL(self.smtp_host, self.smtp_port) as smtp:
|
with smtplib.SMTP_SSL(self.smtp_host, self.smtp_port) as smtp:
|
||||||
@ -97,15 +105,19 @@ class Serverchan(MessagingProvider):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.sendkey = sendkey
|
self.sendkey = sendkey
|
||||||
|
|
||||||
async def send_message(self, title: str, content: str) -> None:
|
async def send_message(
|
||||||
|
self, title: str, content: str, msg_type: MessageType
|
||||||
|
) -> None:
|
||||||
self._check_parameters()
|
self._check_parameters()
|
||||||
await self._post_message(title, content)
|
await self._post_message(title, content, cast(ServerchanMessageType, msg_type))
|
||||||
|
|
||||||
def _check_parameters(self) -> None:
|
def _check_parameters(self) -> None:
|
||||||
if not self.sendkey:
|
if not self.sendkey:
|
||||||
raise ValueError('No sendkey supplied')
|
raise ValueError('No sendkey supplied')
|
||||||
|
|
||||||
async def _post_message(self, title: str, content: str) -> None:
|
async def _post_message(
|
||||||
|
self, title: str, content: str, msg_type: ServerchanMessageType
|
||||||
|
) -> None:
|
||||||
url = f'https://sctapi.ftqq.com/{self.sendkey}.send'
|
url = f'https://sctapi.ftqq.com/{self.sendkey}.send'
|
||||||
payload = {'text': title, 'desp': content}
|
payload = {'text': title, 'desp': content}
|
||||||
|
|
||||||
@ -129,21 +141,25 @@ class Pushdeer(MessagingProvider):
|
|||||||
self.server = server
|
self.server = server
|
||||||
self.pushkey = pushkey
|
self.pushkey = pushkey
|
||||||
|
|
||||||
async def send_message(self, title: str, content: str) -> None:
|
async def send_message(
|
||||||
|
self, title: str, content: str, msg_type: MessageType
|
||||||
|
) -> None:
|
||||||
self._check_parameters()
|
self._check_parameters()
|
||||||
await self._post_message(title, content)
|
await self._post_message(title, content, cast(PushdeerMessageType, msg_type))
|
||||||
|
|
||||||
def _check_parameters(self) -> None:
|
def _check_parameters(self) -> None:
|
||||||
if not self.pushkey:
|
if not self.pushkey:
|
||||||
raise ValueError('No pushkey supplied')
|
raise ValueError('No pushkey supplied')
|
||||||
|
|
||||||
async def _post_message(self, title: str, content: str) -> None:
|
async def _post_message(
|
||||||
|
self, title: str, content: str, msg_type: PushdeerMessageType
|
||||||
|
) -> None:
|
||||||
url = urljoin(self.server or self._server, self._endpoint)
|
url = urljoin(self.server or self._server, self._endpoint)
|
||||||
payload = {
|
payload = {
|
||||||
'pushkey': self.pushkey,
|
'pushkey': self.pushkey,
|
||||||
'text': title,
|
'text': title,
|
||||||
'desp': content,
|
'desp': content,
|
||||||
'type': 'text',
|
'type': msg_type,
|
||||||
}
|
}
|
||||||
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
||||||
async with session.post(url, json=payload) as res:
|
async with session.post(url, json=payload) as res:
|
||||||
@ -166,21 +182,25 @@ class Pushplus(MessagingProvider):
|
|||||||
self.token = token
|
self.token = token
|
||||||
self.topic = topic
|
self.topic = topic
|
||||||
|
|
||||||
async def send_message(self, title: str, content: str) -> None:
|
async def send_message(
|
||||||
|
self, title: str, content: str, msg_type: MessageType
|
||||||
|
) -> None:
|
||||||
self._check_parameters()
|
self._check_parameters()
|
||||||
await self._post_message(title, content)
|
await self._post_message(title, content, msg_type)
|
||||||
|
|
||||||
def _check_parameters(self) -> None:
|
def _check_parameters(self) -> None:
|
||||||
if not self.token:
|
if not self.token:
|
||||||
raise ValueError('No token supplied')
|
raise ValueError('No token supplied')
|
||||||
|
|
||||||
async def _post_message(self, title: str, content: str) -> None:
|
async def _post_message(
|
||||||
|
self, title: str, content: str, msg_type: PushplusMessageType
|
||||||
|
) -> None:
|
||||||
payload = {
|
payload = {
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content,
|
'content': content,
|
||||||
'token': self.token,
|
'token': self.token,
|
||||||
'topic': self.topic,
|
'topic': self.topic,
|
||||||
'template': 'html',
|
'template': msg_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
||||||
@ -201,9 +221,11 @@ class Telegram(MessagingProvider):
|
|||||||
self.token = token
|
self.token = token
|
||||||
self.chatid = chatid
|
self.chatid = chatid
|
||||||
|
|
||||||
async def send_message(self, title: str, content: str) -> None:
|
async def send_message(
|
||||||
|
self, title: str, content: str, msg_type: MessageType
|
||||||
|
) -> None:
|
||||||
self._check_parameters()
|
self._check_parameters()
|
||||||
await self._post_message(title, content)
|
await self._post_message(title, content, cast(TelegramMessageType, msg_type))
|
||||||
|
|
||||||
def _check_parameters(self) -> None:
|
def _check_parameters(self) -> None:
|
||||||
if not self.token:
|
if not self.token:
|
||||||
@ -211,12 +233,14 @@ class Telegram(MessagingProvider):
|
|||||||
if not self.chatid:
|
if not self.chatid:
|
||||||
raise ValueError('No chatid supplied')
|
raise ValueError('No chatid supplied')
|
||||||
|
|
||||||
async def _post_message(self, title: str, content: str) -> None:
|
async def _post_message(
|
||||||
|
self, title: str, content: str, msg_type: TelegramMessageType
|
||||||
|
) -> None:
|
||||||
url = f'https://api.telegram.org/bot{self.token}/sendMessage'
|
url = f'https://api.telegram.org/bot{self.token}/sendMessage'
|
||||||
payload = {
|
payload = {
|
||||||
'chat_id': self.chatid,
|
'chat_id': self.chatid,
|
||||||
'text': title + '\n' + content,
|
'text': title + '\n\n' + content,
|
||||||
'parse_mode': 'HTML',
|
'parse_mode': 'MarkdownV2' if msg_type == 'markdown' else 'HTML',
|
||||||
}
|
}
|
||||||
|
|
||||||
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
||||||
|
@ -1,55 +1,50 @@
|
|||||||
|
from .helpers import shadow_settings, update_settings
|
||||||
from .models import (
|
from .models import (
|
||||||
DEFAULT_SETTINGS_FILE,
|
DEFAULT_SETTINGS_FILE,
|
||||||
|
DanmakuOptions,
|
||||||
|
DanmakuSettings,
|
||||||
|
EmailMessageTemplateSettings,
|
||||||
|
EmailNotificationSettings,
|
||||||
|
EmailSettings,
|
||||||
EnvSettings,
|
EnvSettings,
|
||||||
|
HeaderOptions,
|
||||||
|
HeaderSettings,
|
||||||
|
LoggingSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
NotifierSettings,
|
||||||
|
OutputSettings,
|
||||||
|
PostprocessingOptions,
|
||||||
|
PostprocessingSettings,
|
||||||
|
PushdeerMessageTemplateSettings,
|
||||||
|
PushdeerNotificationSettings,
|
||||||
|
PushdeerSettings,
|
||||||
|
PushplusMessageTemplateSettings,
|
||||||
|
PushplusNotificationSettings,
|
||||||
|
PushplusSettings,
|
||||||
|
RecorderOptions,
|
||||||
|
RecorderSettings,
|
||||||
|
ServerchanMessageTemplateSettings,
|
||||||
|
ServerchanNotificationSettings,
|
||||||
|
ServerchanSettings,
|
||||||
Settings,
|
Settings,
|
||||||
SettingsIn,
|
SettingsIn,
|
||||||
SettingsOut,
|
SettingsOut,
|
||||||
|
SpaceSettings,
|
||||||
HeaderOptions,
|
|
||||||
HeaderSettings,
|
|
||||||
DanmakuOptions,
|
|
||||||
DanmakuSettings,
|
|
||||||
RecorderOptions,
|
|
||||||
RecorderSettings,
|
|
||||||
PostprocessingSettings,
|
|
||||||
PostprocessingOptions,
|
|
||||||
|
|
||||||
TaskOptions,
|
TaskOptions,
|
||||||
TaskSettings,
|
TaskSettings,
|
||||||
OutputSettings,
|
TelegramMessageTemplateSettings,
|
||||||
LoggingSettings,
|
|
||||||
SpaceSettings,
|
|
||||||
EmailSettings,
|
|
||||||
ServerchanSettings,
|
|
||||||
PushdeerSettings,
|
|
||||||
PushplusSettings,
|
|
||||||
TelegramSettings,
|
|
||||||
NotifierSettings,
|
|
||||||
NotificationSettings,
|
|
||||||
EmailNotificationSettings,
|
|
||||||
ServerchanNotificationSettings,
|
|
||||||
PushdeerNotificationSettings,
|
|
||||||
PushplusNotificationSettings,
|
|
||||||
TelegramNotificationSettings,
|
TelegramNotificationSettings,
|
||||||
|
TelegramSettings,
|
||||||
WebHookSettings,
|
WebHookSettings,
|
||||||
)
|
)
|
||||||
from .typing import KeyOfSettings, KeySetOfSettings
|
|
||||||
from .helpers import update_settings, shadow_settings
|
|
||||||
from .setting_manager import SettingsManager
|
from .setting_manager import SettingsManager
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'DEFAULT_SETTINGS_FILE',
|
'DEFAULT_SETTINGS_FILE',
|
||||||
|
|
||||||
'EnvSettings',
|
'EnvSettings',
|
||||||
'Settings',
|
'Settings',
|
||||||
'SettingsIn',
|
'SettingsIn',
|
||||||
'SettingsOut',
|
'SettingsOut',
|
||||||
|
|
||||||
'KeyOfSettings',
|
|
||||||
'KeySetOfSettings',
|
|
||||||
|
|
||||||
'HeaderOptions',
|
'HeaderOptions',
|
||||||
'HeaderSettings',
|
'HeaderSettings',
|
||||||
'DanmakuOptions',
|
'DanmakuOptions',
|
||||||
@ -58,12 +53,16 @@ __all__ = (
|
|||||||
'RecorderSettings',
|
'RecorderSettings',
|
||||||
'PostprocessingSettings',
|
'PostprocessingSettings',
|
||||||
'PostprocessingOptions',
|
'PostprocessingOptions',
|
||||||
|
|
||||||
'TaskOptions',
|
'TaskOptions',
|
||||||
'TaskSettings',
|
'TaskSettings',
|
||||||
'OutputSettings',
|
'OutputSettings',
|
||||||
'LoggingSettings',
|
'LoggingSettings',
|
||||||
'SpaceSettings',
|
'SpaceSettings',
|
||||||
|
'EmailMessageTemplateSettings',
|
||||||
|
'ServerchanMessageTemplateSettings',
|
||||||
|
'PushdeerMessageTemplateSettings',
|
||||||
|
'PushplusMessageTemplateSettings',
|
||||||
|
'TelegramMessageTemplateSettings',
|
||||||
'EmailSettings',
|
'EmailSettings',
|
||||||
'ServerchanSettings',
|
'ServerchanSettings',
|
||||||
'PushdeerSettings',
|
'PushdeerSettings',
|
||||||
@ -77,7 +76,6 @@ __all__ = (
|
|||||||
'PushplusNotificationSettings',
|
'PushplusNotificationSettings',
|
||||||
'TelegramNotificationSettings',
|
'TelegramNotificationSettings',
|
||||||
'WebHookSettings',
|
'WebHookSettings',
|
||||||
|
|
||||||
'update_settings',
|
'update_settings',
|
||||||
'shadow_settings',
|
'shadow_settings',
|
||||||
'SettingsManager',
|
'SettingsManager',
|
||||||
|
@ -1,41 +1,38 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import logging
|
from typing import ClassVar, Collection, Final, List, Optional, TypeVar
|
||||||
from typing_extensions import Annotated
|
|
||||||
from typing import (
|
|
||||||
ClassVar,
|
|
||||||
Collection,
|
|
||||||
Final,
|
|
||||||
List,
|
|
||||||
Optional,
|
|
||||||
TypeVar,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
from pydantic import BaseModel as PydanticBaseModel
|
from pydantic import BaseModel as PydanticBaseModel
|
||||||
from pydantic import Field, BaseSettings, validator, PrivateAttr
|
from pydantic import BaseSettings, Field, PrivateAttr, validator
|
||||||
from pydantic.networks import HttpUrl, EmailStr
|
from pydantic.networks import EmailStr, HttpUrl
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
|
||||||
from ..bili.typing import StreamFormat, QualityNumber
|
from ..bili.typing import QualityNumber, StreamFormat
|
||||||
from ..postprocess import DeleteStrategy
|
|
||||||
from ..core.cover_downloader import CoverSaveStrategy
|
from ..core.cover_downloader import CoverSaveStrategy
|
||||||
from ..logging.typing import LOG_LEVEL
|
from ..logging.typing import LOG_LEVEL
|
||||||
|
from ..postprocess import DeleteStrategy
|
||||||
from ..utils.string import camel_case
|
from ..utils.string import camel_case
|
||||||
|
from .typing import (
|
||||||
|
EmailMessageType,
|
||||||
|
PushdeerMessageType,
|
||||||
|
PushplusMessageType,
|
||||||
|
ServerchanMessageType,
|
||||||
|
TelegramMessageType,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'DEFAULT_SETTINGS_FILE',
|
'DEFAULT_SETTINGS_FILE',
|
||||||
|
|
||||||
'EnvSettings',
|
'EnvSettings',
|
||||||
'Settings',
|
'Settings',
|
||||||
'SettingsIn',
|
'SettingsIn',
|
||||||
'SettingsOut',
|
'SettingsOut',
|
||||||
|
|
||||||
'HeaderOptions',
|
'HeaderOptions',
|
||||||
'HeaderSettings',
|
'HeaderSettings',
|
||||||
'DanmakuOptions',
|
'DanmakuOptions',
|
||||||
@ -44,7 +41,6 @@ __all__ = (
|
|||||||
'RecorderSettings',
|
'RecorderSettings',
|
||||||
'PostprocessingSettings',
|
'PostprocessingSettings',
|
||||||
'PostprocessingOptions',
|
'PostprocessingOptions',
|
||||||
|
|
||||||
'TaskOptions',
|
'TaskOptions',
|
||||||
'TaskSettings',
|
'TaskSettings',
|
||||||
'OutputSettings',
|
'OutputSettings',
|
||||||
@ -57,6 +53,11 @@ __all__ = (
|
|||||||
'TelegramSettings',
|
'TelegramSettings',
|
||||||
'NotifierSettings',
|
'NotifierSettings',
|
||||||
'NotificationSettings',
|
'NotificationSettings',
|
||||||
|
'EmailMessageTemplateSettings',
|
||||||
|
'ServerchanMessageTemplateSettings',
|
||||||
|
'PushdeerMessageTemplateSettings',
|
||||||
|
'PushplusMessageTemplateSettings',
|
||||||
|
'TelegramMessageTemplateSettings',
|
||||||
'EmailNotificationSettings',
|
'EmailNotificationSettings',
|
||||||
'ServerchanNotificationSettings',
|
'ServerchanNotificationSettings',
|
||||||
'PushdeerNotificationSettings',
|
'PushdeerNotificationSettings',
|
||||||
@ -67,9 +68,7 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_OUT_DIR: Final[str] = os.environ.get('DEFAULT_OUT_DIR', '.')
|
DEFAULT_OUT_DIR: Final[str] = os.environ.get('DEFAULT_OUT_DIR', '.')
|
||||||
DEFAULT_LOG_DIR: Final[str] = os.environ.get(
|
DEFAULT_LOG_DIR: Final[str] = os.environ.get('DEFAULT_LOG_DIR', '~/.blrec/logs/')
|
||||||
'DEFAULT_LOG_DIR', '~/.blrec/logs/'
|
|
||||||
)
|
|
||||||
DEFAULT_SETTINGS_FILE: Final[str] = os.environ.get(
|
DEFAULT_SETTINGS_FILE: Final[str] = os.environ.get(
|
||||||
'DEFAULT_SETTINGS_FILE', '~/.blrec/settings.toml'
|
'DEFAULT_SETTINGS_FILE', '~/.blrec/settings.toml'
|
||||||
)
|
)
|
||||||
@ -80,8 +79,7 @@ class EnvSettings(BaseSettings):
|
|||||||
out_dir: Optional[str] = None
|
out_dir: Optional[str] = None
|
||||||
log_dir: Optional[str] = None
|
log_dir: Optional[str] = None
|
||||||
api_key: Annotated[
|
api_key: Annotated[
|
||||||
Optional[str],
|
Optional[str], Field(min_length=8, max_length=80, regex=r'[a-zA-Z\d\-]{8,80}'),
|
||||||
Field(min_length=8, max_length=80, regex=r'[a-zA-Z\d\-]{8,80}'),
|
|
||||||
] = None
|
] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
@ -102,9 +100,7 @@ class BaseModel(PydanticBaseModel):
|
|||||||
return camel_case(string)
|
return camel_case(string)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_with_collection(
|
def _validate_with_collection(value: _V, allowed_values: Collection[_V]) -> None:
|
||||||
value: _V, allowed_values: Collection[_V]
|
|
||||||
) -> None:
|
|
||||||
if value not in allowed_values:
|
if value not in allowed_values:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f'the value {value} does not be allowed, '
|
f'the value {value} does not be allowed, '
|
||||||
@ -118,9 +114,11 @@ class HeaderOptions(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class HeaderSettings(HeaderOptions):
|
class HeaderSettings(HeaderOptions):
|
||||||
user_agent: str = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
|
user_agent: str = (
|
||||||
'AppleWebKit/537.36 (KHTML, like Gecko) ' \
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
|
||||||
|
'AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||||
'Chrome/89.0.4389.114 Safari/537.36'
|
'Chrome/89.0.4389.114 Safari/537.36'
|
||||||
|
)
|
||||||
cookie: str = ''
|
cookie: str = ''
|
||||||
|
|
||||||
|
|
||||||
@ -169,9 +167,7 @@ class RecorderOptions(BaseModel):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
@validator('disconnection_timeout')
|
@validator('disconnection_timeout')
|
||||||
def _validate_disconnection_timeout(
|
def _validate_disconnection_timeout(cls, value: Optional[int]) -> Optional[int]:
|
||||||
cls, value: Optional[int]
|
|
||||||
) -> Optional[int]:
|
|
||||||
if value is not None:
|
if value is not None:
|
||||||
allowed_values = frozenset(60 * i for i in (3, 5, 10, 15, 20, 30))
|
allowed_values = frozenset(60 * i for i in (3, 5, 10, 15, 20, 30))
|
||||||
cls._validate_with_collection(value, allowed_values)
|
cls._validate_with_collection(value, allowed_values)
|
||||||
@ -288,11 +284,11 @@ class TaskOptions(BaseModel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_settings(cls, settings: TaskSettings) -> TaskOptions:
|
def from_settings(cls, settings: TaskSettings) -> TaskOptions:
|
||||||
return cls(**settings.dict(
|
return cls(
|
||||||
include={
|
**settings.dict(
|
||||||
'output', 'header', 'danmaku', 'recorder', 'postprocessing'
|
include={'output', 'header', 'danmaku', 'recorder', 'postprocessing'}
|
||||||
}
|
)
|
||||||
))
|
)
|
||||||
|
|
||||||
|
|
||||||
class TaskSettings(TaskOptions):
|
class TaskSettings(TaskOptions):
|
||||||
@ -313,7 +309,9 @@ class LoggingSettings(BaseModel):
|
|||||||
console_log_level: LOG_LEVEL = 'INFO'
|
console_log_level: LOG_LEVEL = 'INFO'
|
||||||
max_bytes: Annotated[
|
max_bytes: Annotated[
|
||||||
int, Field(ge=1024**2, le=1024**2 * 10, multiple_of=1024**2)
|
int, Field(ge=1024**2, le=1024**2 * 10, multiple_of=1024**2)
|
||||||
] = 1024 ** 2 * 10 # allowed 1 ~ 10 MB
|
] = (
|
||||||
|
1024**2 * 10
|
||||||
|
) # allowed 1 ~ 10 MB
|
||||||
backup_count: Annotated[int, Field(ge=1, le=30)] = 30
|
backup_count: Annotated[int, Field(ge=1, le=30)] = 30
|
||||||
|
|
||||||
@validator('log_dir')
|
@validator('log_dir')
|
||||||
@ -395,9 +393,7 @@ class TelegramSettings(BaseModel):
|
|||||||
|
|
||||||
@validator('token')
|
@validator('token')
|
||||||
def _validate_token(cls, value: str) -> str:
|
def _validate_token(cls, value: str) -> str:
|
||||||
if value != '' and not re.fullmatch(
|
if value != '' and not re.fullmatch(r'[0-9]{8,10}:[a-zA-Z0-9_-]{35}', value):
|
||||||
r'[0-9]{8,10}:[a-zA-Z0-9_-]{35}', value
|
|
||||||
):
|
|
||||||
raise ValueError('token is invalid')
|
raise ValueError('token is invalid')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -419,32 +415,134 @@ class NotificationSettings(BaseModel):
|
|||||||
notify_space: bool = True
|
notify_space: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
class MessageTemplateSettings(BaseModel):
|
||||||
|
began_message_type: str
|
||||||
|
began_message_title: str
|
||||||
|
began_message_content: str
|
||||||
|
ended_message_type: str
|
||||||
|
ended_message_title: str
|
||||||
|
ended_message_content: str
|
||||||
|
space_message_type: str
|
||||||
|
space_message_title: str
|
||||||
|
space_message_content: str
|
||||||
|
error_message_type: str
|
||||||
|
error_message_title: str
|
||||||
|
error_message_content: str
|
||||||
|
|
||||||
|
|
||||||
|
class EmailMessageTemplateSettings(MessageTemplateSettings):
|
||||||
|
began_message_type: EmailMessageType = 'html'
|
||||||
|
began_message_title: str = ''
|
||||||
|
began_message_content: str = ''
|
||||||
|
ended_message_type: EmailMessageType = 'html'
|
||||||
|
ended_message_title: str = ''
|
||||||
|
ended_message_content: str = ''
|
||||||
|
space_message_type: EmailMessageType = 'html'
|
||||||
|
space_message_title: str = ''
|
||||||
|
space_message_content: str = ''
|
||||||
|
error_message_type: EmailMessageType = 'html'
|
||||||
|
error_message_title: str = ''
|
||||||
|
error_message_content: str = ''
|
||||||
|
|
||||||
|
|
||||||
|
class ServerchanMessageTemplateSettings(MessageTemplateSettings):
|
||||||
|
began_message_type: ServerchanMessageType = 'markdown'
|
||||||
|
began_message_title: str = ''
|
||||||
|
began_message_content: str = ''
|
||||||
|
ended_message_type: ServerchanMessageType = 'markdown'
|
||||||
|
ended_message_title: str = ''
|
||||||
|
ended_message_content: str = ''
|
||||||
|
space_message_type: ServerchanMessageType = 'markdown'
|
||||||
|
space_message_title: str = ''
|
||||||
|
space_message_content: str = ''
|
||||||
|
error_message_type: ServerchanMessageType = 'markdown'
|
||||||
|
error_message_title: str = ''
|
||||||
|
error_message_content: str = ''
|
||||||
|
|
||||||
|
|
||||||
|
class PushdeerMessageTemplateSettings(MessageTemplateSettings):
|
||||||
|
began_message_type: PushdeerMessageType = 'markdown'
|
||||||
|
began_message_title: str = ''
|
||||||
|
began_message_content: str = ''
|
||||||
|
ended_message_type: PushdeerMessageType = 'markdown'
|
||||||
|
ended_message_title: str = ''
|
||||||
|
ended_message_content: str = ''
|
||||||
|
space_message_type: PushdeerMessageType = 'markdown'
|
||||||
|
space_message_title: str = ''
|
||||||
|
space_message_content: str = ''
|
||||||
|
error_message_type: PushdeerMessageType = 'markdown'
|
||||||
|
error_message_title: str = ''
|
||||||
|
error_message_content: str = ''
|
||||||
|
|
||||||
|
|
||||||
|
class PushplusMessageTemplateSettings(MessageTemplateSettings):
|
||||||
|
began_message_type: PushplusMessageType = 'markdown'
|
||||||
|
began_message_title: str = ''
|
||||||
|
began_message_content: str = ''
|
||||||
|
ended_message_type: PushplusMessageType = 'markdown'
|
||||||
|
ended_message_title: str = ''
|
||||||
|
ended_message_content: str = ''
|
||||||
|
space_message_type: PushplusMessageType = 'markdown'
|
||||||
|
space_message_title: str = ''
|
||||||
|
space_message_content: str = ''
|
||||||
|
error_message_type: PushplusMessageType = 'markdown'
|
||||||
|
error_message_title: str = ''
|
||||||
|
error_message_content: str = ''
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramMessageTemplateSettings(MessageTemplateSettings):
|
||||||
|
began_message_type: TelegramMessageType = 'html'
|
||||||
|
began_message_title: str = ''
|
||||||
|
began_message_content: str = ''
|
||||||
|
ended_message_type: TelegramMessageType = 'html'
|
||||||
|
ended_message_title: str = ''
|
||||||
|
ended_message_content: str = ''
|
||||||
|
space_message_type: TelegramMessageType = 'html'
|
||||||
|
space_message_title: str = ''
|
||||||
|
space_message_content: str = ''
|
||||||
|
error_message_type: TelegramMessageType = 'html'
|
||||||
|
error_message_title: str = ''
|
||||||
|
error_message_content: str = ''
|
||||||
|
|
||||||
|
|
||||||
class EmailNotificationSettings(
|
class EmailNotificationSettings(
|
||||||
EmailSettings, NotifierSettings, NotificationSettings
|
EmailSettings, NotifierSettings, NotificationSettings, EmailMessageTemplateSettings
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ServerchanNotificationSettings(
|
class ServerchanNotificationSettings(
|
||||||
ServerchanSettings, NotifierSettings, NotificationSettings
|
ServerchanSettings,
|
||||||
|
NotifierSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
ServerchanMessageTemplateSettings,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PushdeerNotificationSettings(
|
class PushdeerNotificationSettings(
|
||||||
PushdeerSettings, NotifierSettings, NotificationSettings
|
PushdeerSettings,
|
||||||
|
NotifierSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
PushplusMessageTemplateSettings,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PushplusNotificationSettings(
|
class PushplusNotificationSettings(
|
||||||
PushplusSettings, NotifierSettings, NotificationSettings
|
PushplusSettings,
|
||||||
|
NotifierSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
PushplusMessageTemplateSettings,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TelegramNotificationSettings(
|
class TelegramNotificationSettings(
|
||||||
TelegramSettings, NotifierSettings, NotificationSettings
|
TelegramSettings,
|
||||||
|
NotifierSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
TelegramMessageTemplateSettings,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -487,14 +585,12 @@ class Settings(BaseModel):
|
|||||||
postprocessing: PostprocessingSettings = PostprocessingSettings()
|
postprocessing: PostprocessingSettings = PostprocessingSettings()
|
||||||
space: SpaceSettings = SpaceSettings()
|
space: SpaceSettings = SpaceSettings()
|
||||||
email_notification: EmailNotificationSettings = EmailNotificationSettings()
|
email_notification: EmailNotificationSettings = EmailNotificationSettings()
|
||||||
serverchan_notification: ServerchanNotificationSettings = \
|
serverchan_notification: ServerchanNotificationSettings = (
|
||||||
ServerchanNotificationSettings()
|
ServerchanNotificationSettings()
|
||||||
pushdeer_notification: PushdeerNotificationSettings = \
|
)
|
||||||
PushdeerNotificationSettings()
|
pushdeer_notification: PushdeerNotificationSettings = PushdeerNotificationSettings()
|
||||||
pushplus_notification: PushplusNotificationSettings = \
|
pushplus_notification: PushplusNotificationSettings = PushplusNotificationSettings()
|
||||||
PushplusNotificationSettings()
|
telegram_notification: TelegramNotificationSettings = TelegramNotificationSettings()
|
||||||
telegram_notification: TelegramNotificationSettings = \
|
|
||||||
TelegramNotificationSettings()
|
|
||||||
webhooks: Annotated[List[WebHookSettings], Field(max_items=50)] = []
|
webhooks: Annotated[List[WebHookSettings], Field(max_items=50)] = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -525,9 +621,7 @@ class Settings(BaseModel):
|
|||||||
cls, webhooks: List[WebHookSettings]
|
cls, webhooks: List[WebHookSettings]
|
||||||
) -> List[WebHookSettings]:
|
) -> List[WebHookSettings]:
|
||||||
if len(webhooks) >= cls._MAX_WEBHOOKS:
|
if len(webhooks) >= cls._MAX_WEBHOOKS:
|
||||||
raise ValueError(
|
raise ValueError(f'Out of max webhooks limits: {cls._MAX_WEBHOOKS}')
|
||||||
f'Out of max webhooks limits: {cls._MAX_WEBHOOKS}'
|
|
||||||
)
|
|
||||||
return webhooks
|
return webhooks
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,31 +1,37 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import asyncio
|
|
||||||
from typing import Optional, TYPE_CHECKING, cast
|
|
||||||
|
|
||||||
from .helpers import update_settings, shadow_settings
|
import asyncio
|
||||||
|
from typing import TYPE_CHECKING, Optional, cast
|
||||||
|
|
||||||
|
from ..exception import NotFoundError
|
||||||
|
from ..logging import configure_logger
|
||||||
|
from ..notification import (
|
||||||
|
EmailService,
|
||||||
|
Notifier,
|
||||||
|
Pushdeer,
|
||||||
|
Pushplus,
|
||||||
|
Serverchan,
|
||||||
|
Telegram,
|
||||||
|
)
|
||||||
|
from ..webhook import WebHook
|
||||||
|
from .helpers import shadow_settings, update_settings
|
||||||
from .models import (
|
from .models import (
|
||||||
|
DanmakuOptions,
|
||||||
|
HeaderOptions,
|
||||||
|
MessageTemplateSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
NotifierSettings,
|
||||||
OutputOptions,
|
OutputOptions,
|
||||||
|
PostprocessingOptions,
|
||||||
|
RecorderOptions,
|
||||||
Settings,
|
Settings,
|
||||||
SettingsIn,
|
SettingsIn,
|
||||||
SettingsOut,
|
SettingsOut,
|
||||||
|
|
||||||
HeaderOptions,
|
|
||||||
DanmakuOptions,
|
|
||||||
RecorderOptions,
|
|
||||||
PostprocessingOptions,
|
|
||||||
|
|
||||||
TaskOptions,
|
TaskOptions,
|
||||||
TaskSettings,
|
TaskSettings,
|
||||||
NotifierSettings,
|
|
||||||
NotificationSettings,
|
|
||||||
)
|
)
|
||||||
from .typing import KeySetOfSettings
|
from .typing import KeySetOfSettings
|
||||||
from ..webhook import WebHook
|
|
||||||
from ..notification import (
|
|
||||||
Notifier, EmailService, Serverchan, Pushdeer, Pushplus, Telegram
|
|
||||||
)
|
|
||||||
from ..logging import configure_logger
|
|
||||||
from ..exception import NotFoundError
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..application import Application
|
from ..application import Application
|
||||||
|
|
||||||
@ -40,9 +46,7 @@ class SettingsManager:
|
|||||||
include: Optional[KeySetOfSettings] = None,
|
include: Optional[KeySetOfSettings] = None,
|
||||||
exclude: Optional[KeySetOfSettings] = None,
|
exclude: Optional[KeySetOfSettings] = None,
|
||||||
) -> SettingsOut:
|
) -> SettingsOut:
|
||||||
return SettingsOut(
|
return SettingsOut(**self._settings.dict(include=include, exclude=exclude))
|
||||||
**self._settings.dict(include=include, exclude=exclude)
|
|
||||||
)
|
|
||||||
|
|
||||||
async def change_settings(self, settings: SettingsIn) -> SettingsOut:
|
async def change_settings(self, settings: SettingsIn) -> SettingsOut:
|
||||||
changed = False
|
changed = False
|
||||||
@ -70,19 +74,15 @@ class SettingsManager:
|
|||||||
if changed:
|
if changed:
|
||||||
await self.dump_settings()
|
await self.dump_settings()
|
||||||
|
|
||||||
return self.get_settings(
|
return self.get_settings(cast(KeySetOfSettings, settings.__fields_set__))
|
||||||
cast(KeySetOfSettings, settings.__fields_set__)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_task_options(self, room_id: int) -> TaskOptions:
|
def get_task_options(self, room_id: int) -> TaskOptions:
|
||||||
if (settings := self.find_task_settings(room_id)):
|
if settings := self.find_task_settings(room_id):
|
||||||
return TaskOptions.from_settings(settings)
|
return TaskOptions.from_settings(settings)
|
||||||
raise NotFoundError(f'task settings of room {room_id} not found')
|
raise NotFoundError(f'task settings of room {room_id} not found')
|
||||||
|
|
||||||
async def change_task_options(
|
async def change_task_options(
|
||||||
self,
|
self, room_id: int, options: TaskOptions
|
||||||
room_id: int,
|
|
||||||
options: TaskOptions,
|
|
||||||
) -> TaskOptions:
|
) -> TaskOptions:
|
||||||
settings = self.find_task_settings(room_id)
|
settings = self.find_task_settings(room_id)
|
||||||
assert settings is not None
|
assert settings is not None
|
||||||
@ -211,11 +211,7 @@ class SettingsManager:
|
|||||||
await self.dump_settings()
|
await self.dump_settings()
|
||||||
|
|
||||||
async def apply_task_header_settings(
|
async def apply_task_header_settings(
|
||||||
self,
|
self, room_id: int, options: HeaderOptions, *, update_session: bool = True
|
||||||
room_id: int,
|
|
||||||
options: HeaderOptions,
|
|
||||||
*,
|
|
||||||
update_session: bool = True,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
final_settings = self._settings.header.copy()
|
final_settings = self._settings.header.copy()
|
||||||
shadow_settings(options, final_settings)
|
shadow_settings(options, final_settings)
|
||||||
@ -224,42 +220,26 @@ class SettingsManager:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def apply_task_danmaku_settings(
|
def apply_task_danmaku_settings(
|
||||||
self,
|
self, room_id: int, options: DanmakuOptions
|
||||||
room_id: int,
|
|
||||||
options: DanmakuOptions,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
final_settings = self._settings.danmaku.copy()
|
final_settings = self._settings.danmaku.copy()
|
||||||
shadow_settings(options, final_settings)
|
shadow_settings(options, final_settings)
|
||||||
self._app._task_manager.apply_task_danmaku_settings(
|
self._app._task_manager.apply_task_danmaku_settings(room_id, final_settings)
|
||||||
room_id, final_settings
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_task_recorder_settings(
|
def apply_task_recorder_settings(
|
||||||
self,
|
self, room_id: int, options: RecorderOptions
|
||||||
room_id: int,
|
|
||||||
options: RecorderOptions,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
final_settings = self._settings.recorder.copy()
|
final_settings = self._settings.recorder.copy()
|
||||||
shadow_settings(options, final_settings)
|
shadow_settings(options, final_settings)
|
||||||
self._app._task_manager.apply_task_recorder_settings(
|
self._app._task_manager.apply_task_recorder_settings(room_id, final_settings)
|
||||||
room_id, final_settings
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_task_output_settings(
|
def apply_task_output_settings(self, room_id: int, options: OutputOptions) -> None:
|
||||||
self,
|
|
||||||
room_id: int,
|
|
||||||
options: OutputOptions,
|
|
||||||
) -> None:
|
|
||||||
final_settings = self._settings.output.copy()
|
final_settings = self._settings.output.copy()
|
||||||
shadow_settings(options, final_settings)
|
shadow_settings(options, final_settings)
|
||||||
self._app._task_manager.apply_task_output_settings(
|
self._app._task_manager.apply_task_output_settings(room_id, final_settings)
|
||||||
room_id, final_settings
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_task_postprocessing_settings(
|
def apply_task_postprocessing_settings(
|
||||||
self,
|
self, room_id: int, options: PostprocessingOptions
|
||||||
room_id: int,
|
|
||||||
options: PostprocessingOptions,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
final_settings = self._settings.postprocessing.copy()
|
final_settings = self._settings.postprocessing.copy()
|
||||||
shadow_settings(options, final_settings)
|
shadow_settings(options, final_settings)
|
||||||
@ -286,21 +266,15 @@ class SettingsManager:
|
|||||||
|
|
||||||
async def apply_header_settings(self) -> None:
|
async def apply_header_settings(self) -> None:
|
||||||
for settings in self._settings.tasks:
|
for settings in self._settings.tasks:
|
||||||
await self.apply_task_header_settings(
|
await self.apply_task_header_settings(settings.room_id, settings.header)
|
||||||
settings.room_id, settings.header
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_danmaku_settings(self) -> None:
|
def apply_danmaku_settings(self) -> None:
|
||||||
for settings in self._settings.tasks:
|
for settings in self._settings.tasks:
|
||||||
self.apply_task_danmaku_settings(
|
self.apply_task_danmaku_settings(settings.room_id, settings.danmaku)
|
||||||
settings.room_id, settings.danmaku
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_recorder_settings(self) -> None:
|
def apply_recorder_settings(self) -> None:
|
||||||
for settings in self._settings.tasks:
|
for settings in self._settings.tasks:
|
||||||
self.apply_task_recorder_settings(
|
self.apply_task_recorder_settings(settings.room_id, settings.recorder)
|
||||||
settings.room_id, settings.recorder
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_postprocessing_settings(self) -> None:
|
def apply_postprocessing_settings(self) -> None:
|
||||||
for settings in self._settings.tasks:
|
for settings in self._settings.tasks:
|
||||||
@ -327,6 +301,7 @@ class SettingsManager:
|
|||||||
self._apply_email_settings(notifier.provider)
|
self._apply_email_settings(notifier.provider)
|
||||||
self._apply_notifier_settings(notifier, settings)
|
self._apply_notifier_settings(notifier, settings)
|
||||||
self._apply_notification_settings(notifier, settings)
|
self._apply_notification_settings(notifier, settings)
|
||||||
|
self._apply_message_template_settings(notifier, settings)
|
||||||
|
|
||||||
def apply_serverchan_notification_settings(self) -> None:
|
def apply_serverchan_notification_settings(self) -> None:
|
||||||
notifier = self._app._serverchan_notifier
|
notifier = self._app._serverchan_notifier
|
||||||
@ -334,6 +309,7 @@ class SettingsManager:
|
|||||||
self._apply_serverchan_settings(notifier.provider)
|
self._apply_serverchan_settings(notifier.provider)
|
||||||
self._apply_notifier_settings(notifier, settings)
|
self._apply_notifier_settings(notifier, settings)
|
||||||
self._apply_notification_settings(notifier, settings)
|
self._apply_notification_settings(notifier, settings)
|
||||||
|
self._apply_message_template_settings(notifier, settings)
|
||||||
|
|
||||||
def apply_pushdeer_notification_settings(self) -> None:
|
def apply_pushdeer_notification_settings(self) -> None:
|
||||||
notifier = self._app._pushdeer_notifier
|
notifier = self._app._pushdeer_notifier
|
||||||
@ -341,6 +317,7 @@ class SettingsManager:
|
|||||||
self._apply_pushdeer_settings(notifier.provider)
|
self._apply_pushdeer_settings(notifier.provider)
|
||||||
self._apply_notifier_settings(notifier, settings)
|
self._apply_notifier_settings(notifier, settings)
|
||||||
self._apply_notification_settings(notifier, settings)
|
self._apply_notification_settings(notifier, settings)
|
||||||
|
self._apply_message_template_settings(notifier, settings)
|
||||||
|
|
||||||
def apply_pushplus_notification_settings(self) -> None:
|
def apply_pushplus_notification_settings(self) -> None:
|
||||||
notifier = self._app._pushplus_notifier
|
notifier = self._app._pushplus_notifier
|
||||||
@ -348,6 +325,7 @@ class SettingsManager:
|
|||||||
self._apply_pushplus_settings(notifier.provider)
|
self._apply_pushplus_settings(notifier.provider)
|
||||||
self._apply_notifier_settings(notifier, settings)
|
self._apply_notifier_settings(notifier, settings)
|
||||||
self._apply_notification_settings(notifier, settings)
|
self._apply_notification_settings(notifier, settings)
|
||||||
|
self._apply_message_template_settings(notifier, settings)
|
||||||
|
|
||||||
def apply_telegram_notification_settings(self) -> None:
|
def apply_telegram_notification_settings(self) -> None:
|
||||||
notifier = self._app._telegram_notifier
|
notifier = self._app._telegram_notifier
|
||||||
@ -355,6 +333,7 @@ class SettingsManager:
|
|||||||
self._apply_telegram_settings(notifier.provider)
|
self._apply_telegram_settings(notifier.provider)
|
||||||
self._apply_notifier_settings(notifier, settings)
|
self._apply_notifier_settings(notifier, settings)
|
||||||
self._apply_notification_settings(notifier, settings)
|
self._apply_notification_settings(notifier, settings)
|
||||||
|
self._apply_message_template_settings(notifier, settings)
|
||||||
|
|
||||||
def apply_webhooks_settings(self) -> None:
|
def apply_webhooks_settings(self) -> None:
|
||||||
webhooks = [WebHook.from_settings(s) for s in self._settings.webhooks]
|
webhooks = [WebHook.from_settings(s) for s in self._settings.webhooks]
|
||||||
@ -397,3 +376,19 @@ class SettingsManager:
|
|||||||
notifier.notify_ended = settings.notify_ended
|
notifier.notify_ended = settings.notify_ended
|
||||||
notifier.notify_error = settings.notify_error
|
notifier.notify_error = settings.notify_error
|
||||||
notifier.notify_space = settings.notify_space
|
notifier.notify_space = settings.notify_space
|
||||||
|
|
||||||
|
def _apply_message_template_settings(
|
||||||
|
self, notifier: Notifier, settings: MessageTemplateSettings
|
||||||
|
) -> None:
|
||||||
|
notifier.began_message_type = settings.began_message_type
|
||||||
|
notifier.began_message_title = settings.began_message_title
|
||||||
|
notifier.began_message_content = settings.began_message_content
|
||||||
|
notifier.ended_message_type = settings.ended_message_type
|
||||||
|
notifier.ended_message_title = settings.ended_message_title
|
||||||
|
notifier.ended_message_content = settings.ended_message_content
|
||||||
|
notifier.space_message_type = settings.space_message_type
|
||||||
|
notifier.space_message_title = settings.space_message_title
|
||||||
|
notifier.space_message_content = settings.space_message_content
|
||||||
|
notifier.error_message_type = settings.error_message_type
|
||||||
|
notifier.error_message_title = settings.error_message_title
|
||||||
|
notifier.error_message_content = settings.error_message_content
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
from typing import AbstractSet, Literal
|
from typing import AbstractSet, Literal, Union
|
||||||
|
|
||||||
|
TextMessageType = Literal['text']
|
||||||
|
HtmlMessageType = Literal['html']
|
||||||
|
MarkdownMessageType = Literal['markdown']
|
||||||
|
MessageType = Union[TextMessageType, MarkdownMessageType, HtmlMessageType]
|
||||||
|
|
||||||
|
EmailMessageType = Union[TextMessageType, HtmlMessageType]
|
||||||
|
ServerchanMessageType = MarkdownMessageType
|
||||||
|
PushdeerMessageType = Union[TextMessageType, MarkdownMessageType]
|
||||||
|
PushplusMessageType = Union[TextMessageType, MarkdownMessageType, HtmlMessageType]
|
||||||
|
TelegramMessageType = Union[MarkdownMessageType, HtmlMessageType]
|
||||||
|
|
||||||
|
|
||||||
KeyOfSettings = Literal[
|
KeyOfSettings = Literal[
|
||||||
|
@ -3,7 +3,7 @@ from typing import Callable, Iterable, Iterator, List, Optional, cast
|
|||||||
|
|
||||||
from fastapi import Query
|
from fastapi import Query
|
||||||
|
|
||||||
from blrec.setting import KeyOfSettings, KeySetOfSettings
|
from blrec.setting.typing import KeyOfSettings, KeySetOfSettings
|
||||||
|
|
||||||
from .schemas import DataSelection, AliasKeyOfSettings
|
from .schemas import DataSelection, AliasKeyOfSettings
|
||||||
from ..bili.models import LiveStatus
|
from ..bili.models import LiveStatus
|
||||||
|
@ -7,9 +7,9 @@ from ..dependencies import settings_include_set, settings_exclude_set
|
|||||||
from ...setting import (
|
from ...setting import (
|
||||||
SettingsIn,
|
SettingsIn,
|
||||||
SettingsOut,
|
SettingsOut,
|
||||||
KeySetOfSettings,
|
|
||||||
TaskOptions,
|
TaskOptions,
|
||||||
)
|
)
|
||||||
|
from ...setting.typing import KeySetOfSettings
|
||||||
from ...application import Application
|
from ...application import Application
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,5 +17,12 @@
|
|||||||
keyOfSettings="emailNotification"
|
keyOfSettings="emailNotification"
|
||||||
></app-event-settings>
|
></app-event-settings>
|
||||||
</app-page-section>
|
</app-page-section>
|
||||||
|
|
||||||
|
<app-page-section name="消息">
|
||||||
|
<app-message-template-settings
|
||||||
|
[settings]="messageTemplateSettings"
|
||||||
|
keyOfSettings="emailNotification"
|
||||||
|
></app-message-template-settings>
|
||||||
|
</app-page-section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-sub-page>
|
</app-sub-page>
|
||||||
|
@ -13,9 +13,11 @@ import {
|
|||||||
EmailSettings,
|
EmailSettings,
|
||||||
NotifierSettings,
|
NotifierSettings,
|
||||||
NotificationSettings,
|
NotificationSettings,
|
||||||
|
MessageTemplateSettings,
|
||||||
KEYS_OF_EMAIL_SETTINGS,
|
KEYS_OF_EMAIL_SETTINGS,
|
||||||
KEYS_OF_NOTIFIER_SETTINGS,
|
KEYS_OF_NOTIFIER_SETTINGS,
|
||||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||||
} from '../../shared/setting.model';
|
} from '../../shared/setting.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -28,6 +30,7 @@ export class EmailNotificationSettingsComponent implements OnInit {
|
|||||||
emailSettings!: EmailSettings;
|
emailSettings!: EmailSettings;
|
||||||
notifierSettings!: NotifierSettings;
|
notifierSettings!: NotifierSettings;
|
||||||
notificationSettings!: NotificationSettings;
|
notificationSettings!: NotificationSettings;
|
||||||
|
messageTemplateSettings!: MessageTemplateSettings;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -40,6 +43,10 @@ export class EmailNotificationSettingsComponent implements OnInit {
|
|||||||
this.emailSettings = pick(settings, KEYS_OF_EMAIL_SETTINGS);
|
this.emailSettings = pick(settings, KEYS_OF_EMAIL_SETTINGS);
|
||||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||||
|
this.messageTemplateSettings = pick(
|
||||||
|
settings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||||
|
);
|
||||||
this.changeDetector.markForCheck();
|
this.changeDetector.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,13 @@
|
|||||||
keyOfSettings="pushdeerNotification"
|
keyOfSettings="pushdeerNotification"
|
||||||
></app-event-settings>
|
></app-event-settings>
|
||||||
</app-page-section>
|
</app-page-section>
|
||||||
|
|
||||||
|
<app-page-section name="消息">
|
||||||
|
<app-message-template-settings
|
||||||
|
[settings]="messageTemplateSettings"
|
||||||
|
keyOfSettings="pushdeerNotification"
|
||||||
|
></app-message-template-settings>
|
||||||
|
</app-page-section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-sub-page>
|
</app-sub-page>
|
||||||
|
|
||||||
|
@ -9,9 +9,11 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import pick from 'lodash-es/pick';
|
import pick from 'lodash-es/pick';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||||
KEYS_OF_NOTIFIER_SETTINGS,
|
KEYS_OF_NOTIFIER_SETTINGS,
|
||||||
KEYS_OF_PUSHDEER_SETTINGS,
|
KEYS_OF_PUSHDEER_SETTINGS,
|
||||||
|
MessageTemplateSettings,
|
||||||
NotificationSettings,
|
NotificationSettings,
|
||||||
NotifierSettings,
|
NotifierSettings,
|
||||||
PushdeerNotificationSettings,
|
PushdeerNotificationSettings,
|
||||||
@ -28,6 +30,7 @@ export class PushdeerNotificationSettingsComponent implements OnInit {
|
|||||||
pushdeerSettings!: PushdeerSettings;
|
pushdeerSettings!: PushdeerSettings;
|
||||||
notifierSettings!: NotifierSettings;
|
notifierSettings!: NotifierSettings;
|
||||||
notificationSettings!: NotificationSettings;
|
notificationSettings!: NotificationSettings;
|
||||||
|
messageTemplateSettings!: MessageTemplateSettings;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -40,6 +43,10 @@ export class PushdeerNotificationSettingsComponent implements OnInit {
|
|||||||
this.pushdeerSettings = pick(settings, KEYS_OF_PUSHDEER_SETTINGS);
|
this.pushdeerSettings = pick(settings, KEYS_OF_PUSHDEER_SETTINGS);
|
||||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||||
|
this.messageTemplateSettings = pick(
|
||||||
|
settings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||||
|
);
|
||||||
this.changeDetector.markForCheck();
|
this.changeDetector.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,12 @@
|
|||||||
keyOfSettings="pushplusNotification"
|
keyOfSettings="pushplusNotification"
|
||||||
></app-event-settings>
|
></app-event-settings>
|
||||||
</app-page-section>
|
</app-page-section>
|
||||||
|
|
||||||
|
<app-page-section name="消息">
|
||||||
|
<app-message-template-settings
|
||||||
|
[settings]="messageTemplateSettings"
|
||||||
|
keyOfSettings="pushplusNotification"
|
||||||
|
></app-message-template-settings>
|
||||||
|
</app-page-section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-sub-page>
|
</app-sub-page>
|
||||||
|
@ -16,6 +16,8 @@ import {
|
|||||||
KEYS_OF_PUSHPLUS_SETTINGS,
|
KEYS_OF_PUSHPLUS_SETTINGS,
|
||||||
KEYS_OF_NOTIFIER_SETTINGS,
|
KEYS_OF_NOTIFIER_SETTINGS,
|
||||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||||
|
MessageTemplateSettings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||||
} from '../../shared/setting.model';
|
} from '../../shared/setting.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -28,6 +30,7 @@ export class PushplusNotificationSettingsComponent implements OnInit {
|
|||||||
pushplusSettings!: PushplusSettings;
|
pushplusSettings!: PushplusSettings;
|
||||||
notifierSettings!: NotifierSettings;
|
notifierSettings!: NotifierSettings;
|
||||||
notificationSettings!: NotificationSettings;
|
notificationSettings!: NotificationSettings;
|
||||||
|
messageTemplateSettings!: MessageTemplateSettings;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -37,10 +40,14 @@ export class PushplusNotificationSettingsComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.data.subscribe((data) => {
|
this.route.data.subscribe((data) => {
|
||||||
const settings = data.settings as PushplusNotificationSettings;
|
const settings = data.settings as PushplusNotificationSettings;
|
||||||
this.changeDetector.markForCheck();
|
|
||||||
this.pushplusSettings = pick(settings, KEYS_OF_PUSHPLUS_SETTINGS);
|
this.pushplusSettings = pick(settings, KEYS_OF_PUSHPLUS_SETTINGS);
|
||||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||||
|
this.messageTemplateSettings = pick(
|
||||||
|
settings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||||
|
);
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,12 @@
|
|||||||
keyOfSettings="serverchanNotification"
|
keyOfSettings="serverchanNotification"
|
||||||
></app-event-settings>
|
></app-event-settings>
|
||||||
</app-page-section>
|
</app-page-section>
|
||||||
|
|
||||||
|
<app-page-section name="消息">
|
||||||
|
<app-message-template-settings
|
||||||
|
[settings]="messageTemplateSettings"
|
||||||
|
keyOfSettings="serverchanNotification"
|
||||||
|
></app-message-template-settings>
|
||||||
|
</app-page-section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-sub-page>
|
</app-sub-page>
|
||||||
|
@ -9,9 +9,11 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import pick from 'lodash-es/pick';
|
import pick from 'lodash-es/pick';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||||
KEYS_OF_NOTIFIER_SETTINGS,
|
KEYS_OF_NOTIFIER_SETTINGS,
|
||||||
KEYS_OF_SERVERCHAN_SETTINGS,
|
KEYS_OF_SERVERCHAN_SETTINGS,
|
||||||
|
MessageTemplateSettings,
|
||||||
NotificationSettings,
|
NotificationSettings,
|
||||||
NotifierSettings,
|
NotifierSettings,
|
||||||
ServerchanNotificationSettings,
|
ServerchanNotificationSettings,
|
||||||
@ -28,6 +30,7 @@ export class ServerchanNotificationSettingsComponent implements OnInit {
|
|||||||
serverchanSettings!: ServerchanSettings;
|
serverchanSettings!: ServerchanSettings;
|
||||||
notifierSettings!: NotifierSettings;
|
notifierSettings!: NotifierSettings;
|
||||||
notificationSettings!: NotificationSettings;
|
notificationSettings!: NotificationSettings;
|
||||||
|
messageTemplateSettings!: MessageTemplateSettings;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -40,6 +43,10 @@ export class ServerchanNotificationSettingsComponent implements OnInit {
|
|||||||
this.serverchanSettings = pick(settings, KEYS_OF_SERVERCHAN_SETTINGS);
|
this.serverchanSettings = pick(settings, KEYS_OF_SERVERCHAN_SETTINGS);
|
||||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||||
|
this.messageTemplateSettings = pick(
|
||||||
|
settings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||||
|
);
|
||||||
this.changeDetector.markForCheck();
|
this.changeDetector.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
<nz-modal
|
||||||
|
[nzTitle]="title"
|
||||||
|
nzCentered
|
||||||
|
[(nzVisible)]="visible"
|
||||||
|
[nzOkDisabled]="settingsForm.invalid"
|
||||||
|
(nzOnOk)="handleConfirm()"
|
||||||
|
(nzOnCancel)="handleCancel()"
|
||||||
|
>
|
||||||
|
<ng-container *nzModalContent>
|
||||||
|
<ng-template #messageTemplateTip>
|
||||||
|
<p>
|
||||||
|
语法、变量参考
|
||||||
|
<a href="https://github.com/acgnhiki/blrec/wiki/MessageTemplate" _blank
|
||||||
|
>wiki</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>空值将使用默认消息模板</p>
|
||||||
|
</ng-template>
|
||||||
|
<form nz-form [nzLayout]="'vertical'" [formGroup]="settingsForm">
|
||||||
|
<nz-form-item class="setting-item input">
|
||||||
|
<nz-form-label
|
||||||
|
class="setting-label"
|
||||||
|
nzFor="messageTitle"
|
||||||
|
nzNoColon
|
||||||
|
[nzTooltipTitle]="messageTemplateTip"
|
||||||
|
>
|
||||||
|
消息标题
|
||||||
|
</nz-form-label>
|
||||||
|
<nz-form-control class="setting-control input">
|
||||||
|
<input type="text" nz-input formControlName="messageTitle" />
|
||||||
|
</nz-form-control>
|
||||||
|
</nz-form-item>
|
||||||
|
<nz-form-item class="setting-item switch">
|
||||||
|
<nz-form-label class="setting-label" nzFor="messageType" nzNoColon>
|
||||||
|
消息类型
|
||||||
|
</nz-form-label>
|
||||||
|
<nz-form-control class="setting-control select">
|
||||||
|
<nz-select
|
||||||
|
formControlName="messageType"
|
||||||
|
[nzOptions]="MESSAGE_TYPE_OPTIONS"
|
||||||
|
>
|
||||||
|
</nz-select>
|
||||||
|
</nz-form-control>
|
||||||
|
</nz-form-item>
|
||||||
|
<nz-form-item class="setting-item textarea">
|
||||||
|
<nz-form-label
|
||||||
|
class="setting-label"
|
||||||
|
nzFor="messageTitle"
|
||||||
|
nzNoColon
|
||||||
|
[nzTooltipTitle]="messageTemplateTip"
|
||||||
|
>
|
||||||
|
消息内容
|
||||||
|
</nz-form-label>
|
||||||
|
<nz-form-control class="setting-control textarea">
|
||||||
|
<textarea
|
||||||
|
nz-input
|
||||||
|
[rows]="10"
|
||||||
|
wrap="off"
|
||||||
|
formControlName="messageContent"
|
||||||
|
></textarea>
|
||||||
|
</nz-form-control>
|
||||||
|
</nz-form-item>
|
||||||
|
</form>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #modalFooter>
|
||||||
|
<button nz-button nzType="default" (click)="handleCancel()">取消</button>
|
||||||
|
<button
|
||||||
|
nz-button
|
||||||
|
nzType="default"
|
||||||
|
(click)="handleConfirm()"
|
||||||
|
[disabled]="settingsForm.invalid"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
</nz-modal>
|
@ -0,0 +1,6 @@
|
|||||||
|
textarea {
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MessageTemplateEditDialogComponent } from './message-template-edit-dialog.component';
|
||||||
|
|
||||||
|
describe('MessageTemplateEditDialogComponent', () => {
|
||||||
|
let component: MessageTemplateEditDialogComponent;
|
||||||
|
let fixture: ComponentFixture<MessageTemplateEditDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ MessageTemplateEditDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MessageTemplateEditDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,102 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnChanges,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
|
import { NzSelectOptionInterface } from 'ng-zorro-antd/select/select.types';
|
||||||
|
import { MessageType } from 'src/app/settings/shared/setting.model';
|
||||||
|
|
||||||
|
import { CommonMessageTemplateSettings } from '../message-template-settings.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-message-template-edit-dialog',
|
||||||
|
templateUrl: './message-template-edit-dialog.component.html',
|
||||||
|
styleUrls: ['./message-template-edit-dialog.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class MessageTemplateEditDialogComponent implements OnInit, OnChanges {
|
||||||
|
@Input() value!: CommonMessageTemplateSettings;
|
||||||
|
@Input() messageTypes: MessageType[] = [];
|
||||||
|
|
||||||
|
@Input() title: string = '修改消息模板';
|
||||||
|
@Input() visible: boolean = false;
|
||||||
|
@Output() visibleChange = new EventEmitter<boolean>();
|
||||||
|
@Output() cancel = new EventEmitter<undefined>();
|
||||||
|
@Output() confirm = new EventEmitter<CommonMessageTemplateSettings>();
|
||||||
|
|
||||||
|
readonly settingsForm: FormGroup;
|
||||||
|
MESSAGE_TYPE_OPTIONS: NzSelectOptionInterface[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
formBuilder: FormBuilder,
|
||||||
|
private changeDetector: ChangeDetectorRef
|
||||||
|
) {
|
||||||
|
this.settingsForm = formBuilder.group({
|
||||||
|
messageType: [''],
|
||||||
|
messageTitle: [''],
|
||||||
|
messageContent: [''],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get messageTypeControl() {
|
||||||
|
return this.settingsForm.get('messageType') as FormControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
get messageTitleControl() {
|
||||||
|
return this.settingsForm.get('messageTitle') as FormControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
get messageContentControl() {
|
||||||
|
return this.settingsForm.get('messageContent') as FormControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.MESSAGE_TYPE_OPTIONS = Array.from(new Set(this.messageTypes)).map(
|
||||||
|
(type: MessageType) => ({
|
||||||
|
label: type,
|
||||||
|
value: type,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
this.setValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
open(): void {
|
||||||
|
this.setValue();
|
||||||
|
this.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisible(visible: boolean): void {
|
||||||
|
this.visible = visible;
|
||||||
|
this.visibleChange.emit(visible);
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(): void {
|
||||||
|
this.settingsForm.setValue(this.value);
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCancel(): void {
|
||||||
|
this.cancel.emit();
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirm(): void {
|
||||||
|
this.confirm.emit(this.settingsForm.value);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<a
|
||||||
|
class="setting-item actionable"
|
||||||
|
(click)="beganMessageTemplateEditDialog.open()"
|
||||||
|
><span class="setting-label">开播消息模板</span></a
|
||||||
|
>
|
||||||
|
<app-message-template-edit-dialog
|
||||||
|
#beganMessageTemplateEditDialog
|
||||||
|
[title]="'修改开播消息模板'"
|
||||||
|
[value]="beganMessageTemplateSettings"
|
||||||
|
[messageTypes]="messageTypes"
|
||||||
|
(confirm)="changeBeganMessageTemplateSettings($event)"
|
||||||
|
></app-message-template-edit-dialog>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="setting-item actionable"
|
||||||
|
(click)="endedMessageTemplateEditDialog.open()"
|
||||||
|
><span class="setting-label">下播消息模板</span></a
|
||||||
|
>
|
||||||
|
<app-message-template-edit-dialog
|
||||||
|
#endedMessageTemplateEditDialog
|
||||||
|
[title]="'修改下播消息模板'"
|
||||||
|
[value]="endedMessageTemplateSettings"
|
||||||
|
[messageTypes]="messageTypes"
|
||||||
|
(confirm)="changeEndedMessageTemplateSettings($event)"
|
||||||
|
></app-message-template-edit-dialog>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="setting-item actionable"
|
||||||
|
(click)="errorMessageTemplateEditDialog.open()"
|
||||||
|
><span class="setting-label">异常消息模板</span></a
|
||||||
|
>
|
||||||
|
<app-message-template-edit-dialog
|
||||||
|
#errorMessageTemplateEditDialog
|
||||||
|
[title]="'修改异常消息模板'"
|
||||||
|
[value]="errorMessageTemplateSettings"
|
||||||
|
[messageTypes]="messageTypes"
|
||||||
|
(confirm)="changeErrorMessageTemplateSettings($event)"
|
||||||
|
></app-message-template-edit-dialog>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="setting-item actionable"
|
||||||
|
(click)="spaceMessageTemplateEditDialog.open()"
|
||||||
|
><span class="setting-label">空间不足消息模板</span></a
|
||||||
|
>
|
||||||
|
<app-message-template-edit-dialog
|
||||||
|
#spaceMessageTemplateEditDialog
|
||||||
|
[title]="'修改空间不足消息模板'"
|
||||||
|
[value]="spaceMessageTemplateSettings"
|
||||||
|
[messageTypes]="messageTypes"
|
||||||
|
(confirm)="changeSpaceMessageTemplateSettings($event)"
|
||||||
|
></app-message-template-edit-dialog>
|
@ -0,0 +1 @@
|
|||||||
|
@use "src/app/settings/shared/styles/_setting.scss";
|
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MessageTemplateSettingsComponent } from './message-template-settings.component';
|
||||||
|
|
||||||
|
describe('MessageTemplateSettingsComponent', () => {
|
||||||
|
let component: MessageTemplateSettingsComponent;
|
||||||
|
let fixture: ComponentFixture<MessageTemplateSettingsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ MessageTemplateSettingsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MessageTemplateSettingsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,167 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
OnInit,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
|
||||||
|
import { retry } from 'src/app/shared/rx-operators';
|
||||||
|
import {
|
||||||
|
MessageTemplateSettings,
|
||||||
|
MessageType,
|
||||||
|
} from 'src/app/settings/shared/setting.model';
|
||||||
|
import { SettingService } from 'src/app/settings/shared/services/setting.service';
|
||||||
|
|
||||||
|
export interface CommonMessageTemplateSettings {
|
||||||
|
messageType: string;
|
||||||
|
messageTitle: string;
|
||||||
|
messageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-message-template-settings',
|
||||||
|
templateUrl: './message-template-settings.component.html',
|
||||||
|
styleUrls: ['./message-template-settings.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class MessageTemplateSettingsComponent implements OnInit, OnChanges {
|
||||||
|
@Input() settings!: MessageTemplateSettings;
|
||||||
|
@Input() keyOfSettings!:
|
||||||
|
| 'emailNotification'
|
||||||
|
| 'serverchanNotification'
|
||||||
|
| 'pushdeerNotification'
|
||||||
|
| 'pushplusNotification'
|
||||||
|
| 'telegramNotification';
|
||||||
|
|
||||||
|
messageTypes!: MessageType[];
|
||||||
|
beganMessageTemplateSettings!: CommonMessageTemplateSettings;
|
||||||
|
endedMessageTemplateSettings!: CommonMessageTemplateSettings;
|
||||||
|
spaceMessageTemplateSettings!: CommonMessageTemplateSettings;
|
||||||
|
errorMessageTemplateSettings!: CommonMessageTemplateSettings;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private changeDetector: ChangeDetectorRef,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private settingService: SettingService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
switch (this.keyOfSettings) {
|
||||||
|
case 'emailNotification':
|
||||||
|
this.messageTypes = ['text', 'html'];
|
||||||
|
break;
|
||||||
|
case 'serverchanNotification':
|
||||||
|
this.messageTypes = ['markdown'];
|
||||||
|
break;
|
||||||
|
case 'pushdeerNotification':
|
||||||
|
this.messageTypes = ['markdown', 'text'];
|
||||||
|
break;
|
||||||
|
case 'pushplusNotification':
|
||||||
|
this.messageTypes = ['markdown', 'text', 'html'];
|
||||||
|
break;
|
||||||
|
case 'telegramNotification':
|
||||||
|
this.messageTypes = ['markdown', 'html'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
this.updateCommonSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeBeganMessageTemplateSettings(
|
||||||
|
settings: CommonMessageTemplateSettings
|
||||||
|
): void {
|
||||||
|
const settingsToChange = {
|
||||||
|
beganMessageType: settings.messageType,
|
||||||
|
beganMessageTitle: settings.messageTitle,
|
||||||
|
beganMessageContent: settings.messageContent,
|
||||||
|
};
|
||||||
|
this.changeMessageTemplateSettings(settingsToChange).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeEndedMessageTemplateSettings(
|
||||||
|
settings: CommonMessageTemplateSettings
|
||||||
|
): void {
|
||||||
|
const settingsToChange = {
|
||||||
|
endedMessageType: settings.messageType,
|
||||||
|
endedMessageTitle: settings.messageTitle,
|
||||||
|
endedMessageContent: settings.messageContent,
|
||||||
|
};
|
||||||
|
this.changeMessageTemplateSettings(settingsToChange).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeSpaceMessageTemplateSettings(
|
||||||
|
settings: CommonMessageTemplateSettings
|
||||||
|
): void {
|
||||||
|
const settingsToChange = {
|
||||||
|
spaceMessageType: settings.messageType,
|
||||||
|
spaceMessageTitle: settings.messageTitle,
|
||||||
|
spaceMessageContent: settings.messageContent,
|
||||||
|
};
|
||||||
|
this.changeMessageTemplateSettings(settingsToChange).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeErrorMessageTemplateSettings(
|
||||||
|
settings: CommonMessageTemplateSettings
|
||||||
|
): void {
|
||||||
|
const settingsToChange = {
|
||||||
|
errorMessageType: settings.messageType,
|
||||||
|
errorMessageTitle: settings.messageTitle,
|
||||||
|
errorMessageContent: settings.messageContent,
|
||||||
|
};
|
||||||
|
this.changeMessageTemplateSettings(settingsToChange).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeMessageTemplateSettings(settings: Partial<MessageTemplateSettings>) {
|
||||||
|
return this.settingService
|
||||||
|
.changeSettings({ [this.keyOfSettings]: settings })
|
||||||
|
.pipe(
|
||||||
|
retry(3, 300),
|
||||||
|
tap(
|
||||||
|
(settings) => {
|
||||||
|
this.message.success('修改消息模板设置成功');
|
||||||
|
this.settings = {
|
||||||
|
...this.settings,
|
||||||
|
...settings[this.keyOfSettings],
|
||||||
|
};
|
||||||
|
this.updateCommonSettings();
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.error(`修改消息模板设置出错: ${error.message}`);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateCommonSettings(): void {
|
||||||
|
this.beganMessageTemplateSettings = {
|
||||||
|
messageType: this.settings.beganMessageType,
|
||||||
|
messageTitle: this.settings.beganMessageTitle,
|
||||||
|
messageContent: this.settings.beganMessageContent,
|
||||||
|
};
|
||||||
|
this.endedMessageTemplateSettings = {
|
||||||
|
messageType: this.settings.endedMessageType,
|
||||||
|
messageTitle: this.settings.endedMessageTitle,
|
||||||
|
messageContent: this.settings.endedMessageContent,
|
||||||
|
};
|
||||||
|
this.spaceMessageTemplateSettings = {
|
||||||
|
messageType: this.settings.spaceMessageType,
|
||||||
|
messageTitle: this.settings.spaceMessageTitle,
|
||||||
|
messageContent: this.settings.spaceMessageContent,
|
||||||
|
};
|
||||||
|
this.errorMessageTemplateSettings = {
|
||||||
|
messageType: this.settings.errorMessageType,
|
||||||
|
messageTitle: this.settings.errorMessageTitle,
|
||||||
|
messageContent: this.settings.errorMessageContent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -19,5 +19,12 @@
|
|||||||
keyOfSettings="telegramNotification"
|
keyOfSettings="telegramNotification"
|
||||||
></app-event-settings>
|
></app-event-settings>
|
||||||
</app-page-section>
|
</app-page-section>
|
||||||
|
|
||||||
|
<app-page-section name="消息">
|
||||||
|
<app-message-template-settings
|
||||||
|
[settings]="messageTemplateSettings"
|
||||||
|
keyOfSettings="telegramNotification"
|
||||||
|
></app-message-template-settings>
|
||||||
|
</app-page-section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-sub-page>
|
</app-sub-page>
|
||||||
|
@ -16,6 +16,8 @@ import {
|
|||||||
KEYS_OF_TELEGRAM_SETTINGS,
|
KEYS_OF_TELEGRAM_SETTINGS,
|
||||||
KEYS_OF_NOTIFIER_SETTINGS,
|
KEYS_OF_NOTIFIER_SETTINGS,
|
||||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||||
|
MessageTemplateSettings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||||
} from '../../shared/setting.model';
|
} from '../../shared/setting.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -28,6 +30,7 @@ export class TelegramNotificationSettingsComponent implements OnInit {
|
|||||||
telegramSettings!: TelegramSettings;
|
telegramSettings!: TelegramSettings;
|
||||||
notifierSettings!: NotifierSettings;
|
notifierSettings!: NotifierSettings;
|
||||||
notificationSettings!: NotificationSettings;
|
notificationSettings!: NotificationSettings;
|
||||||
|
messageTemplateSettings!: MessageTemplateSettings;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -37,10 +40,14 @@ export class TelegramNotificationSettingsComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.data.subscribe((data) => {
|
this.route.data.subscribe((data) => {
|
||||||
const settings = data.settings as TelegramNotificationSettings;
|
const settings = data.settings as TelegramNotificationSettings;
|
||||||
this.changeDetector.markForCheck();
|
|
||||||
this.telegramSettings = pick(settings, KEYS_OF_TELEGRAM_SETTINGS);
|
this.telegramSettings = pick(settings, KEYS_OF_TELEGRAM_SETTINGS);
|
||||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||||
|
this.messageTemplateSettings = pick(
|
||||||
|
settings,
|
||||||
|
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||||
|
);
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ import { WebhookListComponent } from './webhook-settings/webhook-list/webhook-li
|
|||||||
import { OutdirEditDialogComponent } from './output-settings/outdir-edit-dialog/outdir-edit-dialog.component';
|
import { OutdirEditDialogComponent } from './output-settings/outdir-edit-dialog/outdir-edit-dialog.component';
|
||||||
import { LogdirEditDialogComponent } from './logging-settings/logdir-edit-dialog/logdir-edit-dialog.component';
|
import { LogdirEditDialogComponent } from './logging-settings/logdir-edit-dialog/logdir-edit-dialog.component';
|
||||||
import { PathTemplateEditDialogComponent } from './output-settings/path-template-edit-dialog/path-template-edit-dialog.component';
|
import { PathTemplateEditDialogComponent } from './output-settings/path-template-edit-dialog/path-template-edit-dialog.component';
|
||||||
|
import { MessageTemplateSettingsComponent } from './notification-settings/shared/components/message-template-settings/message-template-settings.component';
|
||||||
|
import { MessageTemplateEditDialogComponent } from './notification-settings/shared/components/message-template-settings/message-template-edit-dialog/message-template-edit-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -97,6 +99,8 @@ import { PathTemplateEditDialogComponent } from './output-settings/path-template
|
|||||||
OutdirEditDialogComponent,
|
OutdirEditDialogComponent,
|
||||||
LogdirEditDialogComponent,
|
LogdirEditDialogComponent,
|
||||||
PathTemplateEditDialogComponent,
|
PathTemplateEditDialogComponent,
|
||||||
|
MessageTemplateSettingsComponent,
|
||||||
|
MessageTemplateEditDialogComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -175,25 +175,152 @@ export const KEYS_OF_NOTIFICATION_SETTINGS = [
|
|||||||
'notifySpace',
|
'notifySpace',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
export type TextMessageType = 'text';
|
||||||
|
export type HtmlMessageType = 'html';
|
||||||
|
export type MarkdownMessageType = 'markdown';
|
||||||
|
export type MessageType =
|
||||||
|
| TextMessageType
|
||||||
|
| MarkdownMessageType
|
||||||
|
| HtmlMessageType;
|
||||||
|
|
||||||
|
export type EmailMessageType = TextMessageType | HtmlMessageType;
|
||||||
|
export type ServerchanMessageType = MarkdownMessageType;
|
||||||
|
export type PushdeerMessageType = TextMessageType | MarkdownMessageType;
|
||||||
|
export type PushplusMessageType =
|
||||||
|
| TextMessageType
|
||||||
|
| MarkdownMessageType
|
||||||
|
| HtmlMessageType;
|
||||||
|
export type TelegramMessageType = MarkdownMessageType | HtmlMessageType;
|
||||||
|
|
||||||
|
export interface MessageTemplateSettings {
|
||||||
|
beganMessageType: string;
|
||||||
|
beganMessageTitle: string;
|
||||||
|
beganMessageContent: string;
|
||||||
|
endedMessageType: string;
|
||||||
|
endedMessageTitle: string;
|
||||||
|
endedMessageContent: string;
|
||||||
|
spaceMessageType: string;
|
||||||
|
spaceMessageTitle: string;
|
||||||
|
spaceMessageContent: string;
|
||||||
|
errorMessageType: string;
|
||||||
|
errorMessageTitle: string;
|
||||||
|
errorMessageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KEYS_OF_MESSAGE_TEMPLATE_SETTINGS = [
|
||||||
|
'beganMessageType',
|
||||||
|
'beganMessageTitle',
|
||||||
|
'beganMessageContent',
|
||||||
|
'endedMessageType',
|
||||||
|
'endedMessageTitle',
|
||||||
|
'endedMessageContent',
|
||||||
|
'spaceMessageType',
|
||||||
|
'spaceMessageTitle',
|
||||||
|
'spaceMessageContent',
|
||||||
|
'errorMessageType',
|
||||||
|
'errorMessageTitle',
|
||||||
|
'errorMessageContent',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export interface EmailMessageTemplateSettings {
|
||||||
|
beganMessageType: EmailMessageType;
|
||||||
|
beganMessageTitle: string;
|
||||||
|
beganMessageContent: string;
|
||||||
|
endedMessageType: EmailMessageType;
|
||||||
|
endedMessageTitle: string;
|
||||||
|
endedMessageContent: string;
|
||||||
|
spaceMessageType: EmailMessageType;
|
||||||
|
spaceMessageTitle: string;
|
||||||
|
spaceMessageContent: string;
|
||||||
|
errorMessageType: EmailMessageType;
|
||||||
|
errorMessageTitle: string;
|
||||||
|
errorMessageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServerchanMessageTemplateSettings {
|
||||||
|
beganMessageType: ServerchanMessageType;
|
||||||
|
beganMessageTitle: string;
|
||||||
|
beganMessageContent: string;
|
||||||
|
endedMessageType: ServerchanMessageType;
|
||||||
|
endedMessageTitle: string;
|
||||||
|
endedMessageContent: string;
|
||||||
|
spaceMessageType: ServerchanMessageType;
|
||||||
|
spaceMessageTitle: string;
|
||||||
|
spaceMessageContent: string;
|
||||||
|
errorMessageType: ServerchanMessageType;
|
||||||
|
errorMessageTitle: string;
|
||||||
|
errorMessageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushdeerMessageTemplateSettings {
|
||||||
|
beganMessageType: PushdeerMessageType;
|
||||||
|
beganMessageTitle: string;
|
||||||
|
beganMessageContent: string;
|
||||||
|
endedMessageType: PushdeerMessageType;
|
||||||
|
endedMessageTitle: string;
|
||||||
|
endedMessageContent: string;
|
||||||
|
spaceMessageType: PushdeerMessageType;
|
||||||
|
spaceMessageTitle: string;
|
||||||
|
spaceMessageContent: string;
|
||||||
|
errorMessageType: PushdeerMessageType;
|
||||||
|
errorMessageTitle: string;
|
||||||
|
errorMessageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushplusMessageTemplateSettings {
|
||||||
|
beganMessageType: PushplusMessageType;
|
||||||
|
beganMessageTitle: string;
|
||||||
|
beganMessageContent: string;
|
||||||
|
endedMessageType: PushplusMessageType;
|
||||||
|
endedMessageTitle: string;
|
||||||
|
endedMessageContent: string;
|
||||||
|
spaceMessageType: PushplusMessageType;
|
||||||
|
spaceMessageTitle: string;
|
||||||
|
spaceMessageContent: string;
|
||||||
|
errorMessageType: PushplusMessageType;
|
||||||
|
errorMessageTitle: string;
|
||||||
|
errorMessageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TelegramMessageTemplateSettings {
|
||||||
|
beganMessageType: PushplusMessageType;
|
||||||
|
beganMessageTitle: string;
|
||||||
|
beganMessageContent: string;
|
||||||
|
endedMessageType: PushplusMessageType;
|
||||||
|
endedMessageTitle: string;
|
||||||
|
endedMessageContent: string;
|
||||||
|
spaceMessageType: PushplusMessageType;
|
||||||
|
spaceMessageTitle: string;
|
||||||
|
spaceMessageContent: string;
|
||||||
|
errorMessageType: PushplusMessageType;
|
||||||
|
errorMessageTitle: string;
|
||||||
|
errorMessageContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type EmailNotificationSettings = EmailSettings &
|
export type EmailNotificationSettings = EmailSettings &
|
||||||
NotifierSettings &
|
NotifierSettings &
|
||||||
NotificationSettings;
|
NotificationSettings &
|
||||||
|
EmailMessageTemplateSettings;
|
||||||
|
|
||||||
export type ServerchanNotificationSettings = ServerchanSettings &
|
export type ServerchanNotificationSettings = ServerchanSettings &
|
||||||
NotifierSettings &
|
NotifierSettings &
|
||||||
NotificationSettings;
|
NotificationSettings &
|
||||||
|
ServerchanMessageTemplateSettings;
|
||||||
|
|
||||||
export type PushdeerNotificationSettings = PushdeerSettings &
|
export type PushdeerNotificationSettings = PushdeerSettings &
|
||||||
NotifierSettings &
|
NotifierSettings &
|
||||||
NotificationSettings;
|
NotificationSettings &
|
||||||
|
PushdeerMessageTemplateSettings;
|
||||||
|
|
||||||
export type PushplusNotificationSettings = PushplusSettings &
|
export type PushplusNotificationSettings = PushplusSettings &
|
||||||
NotifierSettings &
|
NotifierSettings &
|
||||||
NotificationSettings;
|
NotificationSettings &
|
||||||
|
PushdeerMessageTemplateSettings;
|
||||||
|
|
||||||
export type TelegramNotificationSettings = TelegramSettings &
|
export type TelegramNotificationSettings = TelegramSettings &
|
||||||
NotifierSettings &
|
NotifierSettings &
|
||||||
NotificationSettings;
|
NotificationSettings &
|
||||||
|
TelegramMessageTemplateSettings;
|
||||||
|
|
||||||
export interface WebhookEventSettings {
|
export interface WebhookEventSettings {
|
||||||
liveBegan: boolean;
|
liveBegan: boolean;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
@use '../../../shared/styles/layout';
|
@use "../../../shared/styles/layout";
|
||||||
@use '../../../shared/styles/list';
|
@use "../../../shared/styles/list";
|
||||||
|
|
||||||
.settings-page {
|
.settings-page {
|
||||||
@extend %inner-page;
|
@extend %inner-page;
|
||||||
@ -43,6 +43,11 @@ a.setting-item {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@include actionable;
|
@include actionable;
|
||||||
|
|
||||||
|
height: 60px;
|
||||||
|
&:not(:first-child) {
|
||||||
|
height: 61px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-label {
|
.setting-label {
|
||||||
@ -90,6 +95,11 @@ a.setting-item {
|
|||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 332px) {
|
@media screen and (max-width: 332px) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
|
Loading…
Reference in New Issue
Block a user