mirror of
https://github.com/acgnhiki/blrec.git
synced 2024-12-27 17:10:18 +08:00
feat: add message template
This commit is contained in:
parent
1decffc266
commit
2a6498a910
@ -35,6 +35,7 @@ package_dir =
|
||||
include_package_data = True
|
||||
python_requires = >= 3.8
|
||||
install_requires =
|
||||
python-liquid >= 1.2.1, < 2.0.0
|
||||
typing-extensions >= 3.10.0.0
|
||||
ordered-set >= 4.1.0, < 5.0.0
|
||||
fastapi >= 0.70.0, < 0.71.0
|
||||
|
@ -24,9 +24,9 @@ from .setting import (
|
||||
Settings,
|
||||
SettingsIn,
|
||||
SettingsOut,
|
||||
KeySetOfSettings,
|
||||
TaskOptions,
|
||||
)
|
||||
from .setting.typing import KeySetOfSettings
|
||||
from .notification import (
|
||||
EmailNotifier,
|
||||
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="manifest" href="manifest.webmanifest">
|
||||
<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>
|
||||
<app-root></app-root>
|
||||
<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>
|
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,
|
||||
"timestamp": 1654692172512,
|
||||
"timestamp": 1655391613431,
|
||||
"index": "/index.html",
|
||||
"assetGroups": [
|
||||
{
|
||||
@ -13,17 +13,17 @@
|
||||
"urls": [
|
||||
"/103.5b5d2a6e5a8a7479.js",
|
||||
"/146.92e3b29c4c754544.js",
|
||||
"/183.8cf3b5282412a0ec.js",
|
||||
"/183.ae1a1102b7d5cbdb.js",
|
||||
"/202.e15e5ae9f06639b8.js",
|
||||
"/45.c90c3cea2bf1a66e.js",
|
||||
"/474.7f6529972e383566.js",
|
||||
"/66.31f5b9ae46ae9005.js",
|
||||
"/66.9faa0b5a6adf9602.js",
|
||||
"/common.858f777e9296e6f2.js",
|
||||
"/index.html",
|
||||
"/main.411b4a979eb179f8.js",
|
||||
"/main.888c50197ddf8040.js",
|
||||
"/manifest.webmanifest",
|
||||
"/polyfills.4b08448aee19bb22.js",
|
||||
"/runtime.dc2f7d56c437cd78.js",
|
||||
"/styles.1f581691b230dc4d.css"
|
||||
"/runtime.0ce129f346263990.js",
|
||||
"/styles.2e152d608221c2ee.css"
|
||||
],
|
||||
"patterns": []
|
||||
},
|
||||
@ -1636,10 +1636,10 @@
|
||||
"hashTable": {
|
||||
"/103.5b5d2a6e5a8a7479.js": "cc0240f217015b6d4ddcc14f31fcc42e1c1c282a",
|
||||
"/146.92e3b29c4c754544.js": "3824de681dd1f982ea69a065cdf54d7a1e781f4d",
|
||||
"/183.8cf3b5282412a0ec.js": "bf93e3b9baf3d6c0eb5e320c4916d5bf540c4cb0",
|
||||
"/183.ae1a1102b7d5cbdb.js": "6cb22d60b0a20214212e6050fbbf33926a4c1346",
|
||||
"/202.e15e5ae9f06639b8.js": "62335dc98644969539760565ff9c3c472d304287",
|
||||
"/45.c90c3cea2bf1a66e.js": "e5bfb8cf3803593e6b8ea14c90b3d3cb6a066764",
|
||||
"/474.7f6529972e383566.js": "1c74b5c6379705a3110c99767f97feddc42a0d54",
|
||||
"/66.31f5b9ae46ae9005.js": "cc22d2582d8e4c2a83e089d5a1ec32619e439ccd",
|
||||
"/66.9faa0b5a6adf9602.js": "c2f418ebb80f35402d9f24e5acaf8167c96f9eb3",
|
||||
"/assets/animal/panda.js": "fec2868bb3053dd2da45f96bbcb86d5116ed72b1",
|
||||
"/assets/animal/panda.svg": "bebd302cdc601e0ead3a6d2710acf8753f3d83b1",
|
||||
"/assets/fill/.gitkeep": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||
@ -3234,12 +3234,12 @@
|
||||
"/assets/twotone/warning.js": "fb2d7ea232f3a99bf8f080dbc94c65699232ac01",
|
||||
"/assets/twotone/warning.svg": "8c7a2d3e765a2e7dd58ac674870c6655cecb0068",
|
||||
"/common.858f777e9296e6f2.js": "b68ca68e1e214a2537d96935c23410126cc564dd",
|
||||
"/index.html": "e7c15c095d8d2a11a60d7211e17e8a38316adf0e",
|
||||
"/main.411b4a979eb179f8.js": "4c5e77b0589a77410f84441d0877c1d18cb1357f",
|
||||
"/index.html": "374ebd2a9b656c5ebcbc9f5a4402b345cd4c7c5c",
|
||||
"/main.888c50197ddf8040.js": "f506b85641a4598b002c21bc49c9a36e0c058326",
|
||||
"/manifest.webmanifest": "62c1cb8c5ad2af551a956b97013ab55ce77dd586",
|
||||
"/polyfills.4b08448aee19bb22.js": "8e73f2d42cc13ca353cea5c886d930bd6da08d0d",
|
||||
"/runtime.dc2f7d56c437cd78.js": "fcf22060f48d6229236a14de31cc63c3568db8b1",
|
||||
"/styles.1f581691b230dc4d.css": "6f5befbbad57c2b2e80aae855139744b8010d150"
|
||||
"/runtime.0ce129f346263990.js": "98698b10b3f873a761f1e1c7fb5a9bcd2f3830ee",
|
||||
"/styles.2e152d608221c2ee.css": "9830389a46daa5b4511e0dd343aad23ca9f9690f"
|
||||
},
|
||||
"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 logging
|
||||
import os
|
||||
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 (
|
||||
AsyncRetrying,
|
||||
wait_exponential,
|
||||
stop_after_delay,
|
||||
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 (
|
||||
Error,
|
||||
ErrorData,
|
||||
EventCenter,
|
||||
LiveBeganEvent,
|
||||
LiveEndedEvent,
|
||||
SpaceNoEnoughEvent,
|
||||
)
|
||||
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__ = (
|
||||
'Notifier',
|
||||
@ -55,12 +60,45 @@ class Notifier(SwitchableMixin, ABC):
|
||||
notify_ended: bool = False,
|
||||
notify_space: 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:
|
||||
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_ended = notify_ended
|
||||
self.notify_space = notify_space
|
||||
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:
|
||||
events = EventCenter.get_instance().events
|
||||
@ -87,7 +125,8 @@ class Notifier(SwitchableMixin, ABC):
|
||||
|
||||
def _on_exception(self, exc: BaseException) -> None:
|
||||
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:
|
||||
return self.notify_began
|
||||
@ -95,9 +134,7 @@ class Notifier(SwitchableMixin, ABC):
|
||||
def _should_notify_live_ended(self, event: LiveEndedEvent) -> bool:
|
||||
return self.notify_ended
|
||||
|
||||
def _should_notify_space_no_enough(
|
||||
self, event: SpaceNoEnoughEvent
|
||||
) -> bool:
|
||||
def _should_notify_space_no_enough(self, event: SpaceNoEnoughEvent) -> bool:
|
||||
return self.notify_space
|
||||
|
||||
def _should_notify_exception(self, exc: BaseException) -> bool:
|
||||
@ -116,56 +153,184 @@ class Notifier(SwitchableMixin, ABC):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def _notify_exception(self, exc: BaseException) -> None:
|
||||
def _notify_exception(self, event: Error) -> None:
|
||||
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):
|
||||
provider: MessagingProvider
|
||||
|
||||
def _notify_live_began(self, event: LiveBeganEvent) -> None:
|
||||
title = f'{event.data.user_info.name} 开播啦'
|
||||
content = make_live_info_content(
|
||||
event.data.user_info, event.data.room_info
|
||||
)
|
||||
self._send_message(title, content)
|
||||
title, content = self._make_began_message(event)
|
||||
self._send_message(title, content, self.began_message_type)
|
||||
|
||||
def _notify_live_ended(self, event: LiveEndedEvent) -> None:
|
||||
title = f'{event.data.user_info.name} 下播了'
|
||||
content = make_live_info_content(
|
||||
event.data.user_info, event.data.room_info
|
||||
)
|
||||
self._send_message(title, content)
|
||||
title, content = self._make_ended_message(event)
|
||||
self._send_message(title, content, self.ended_message_type)
|
||||
|
||||
def _notify_space_no_enough(self, event: SpaceNoEnoughEvent) -> None:
|
||||
title = '空间不足!'
|
||||
content = make_disk_usage_content(event.data)
|
||||
self._send_message(title, content)
|
||||
title, content = self._make_space_message(event)
|
||||
self._send_message(title, content, self.space_message_type)
|
||||
|
||||
def _notify_exception(self, exc: BaseException) -> None:
|
||||
title = '出错了~'
|
||||
content = make_exception_content(exc)
|
||||
self._send_message(title, content)
|
||||
def _notify_exception(self, event: Error) -> None:
|
||||
title, content = self._make_error_message(event)
|
||||
self._send_message(title, content, self.error_message_type)
|
||||
|
||||
def _send_message(self, title: str, content: str) -> None:
|
||||
asyncio.create_task(self._send_message_async(title, content))
|
||||
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
|
||||
|
||||
async def _send_message_async(self, title: str, content: str) -> None:
|
||||
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 = '空间不足!'
|
||||
content = format_exception(e)
|
||||
return title, content
|
||||
|
||||
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 = '出错了~'
|
||||
content = format_exception(e)
|
||||
return title, content
|
||||
|
||||
def _send_message(self, title: str, content: str, msg_type: MessageType) -> None:
|
||||
asyncio.create_task(self._send_message_async(title, content, msg_type))
|
||||
|
||||
async def _send_message_async(
|
||||
self, title: str, content: str, msg_type: MessageType
|
||||
) -> None:
|
||||
try:
|
||||
async for attempt in AsyncRetrying(
|
||||
reraise=True,
|
||||
stop=stop_after_delay(300),
|
||||
wait=wait_exponential(multiplier=0.1, max=10),
|
||||
retry=retry_if_exception(
|
||||
lambda e: not isinstance(e, ValueError)
|
||||
),
|
||||
retry=retry_if_exception(lambda e: not isinstance(e, ValueError)),
|
||||
):
|
||||
with attempt:
|
||||
await self.provider.send_message(title, content)
|
||||
await self.provider.send_message(title, content, msg_type)
|
||||
except Exception as e:
|
||||
logger.warning('Failed to send a message via {}: {}'.format(
|
||||
self.provider.__class__.__name__, repr(e)
|
||||
))
|
||||
logger.warning(
|
||||
'Failed to send a message via {}: {}'.format(
|
||||
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):
|
||||
@ -226,3 +391,15 @@ class TelegramNotifier(MessageNotifier):
|
||||
def _do_disable(self) -> None:
|
||||
super()._do_disable()
|
||||
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 email.message import EmailMessage
|
||||
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
|
||||
|
||||
import aiohttp
|
||||
|
||||
from ..setting.typing import (
|
||||
EmailMessageType,
|
||||
MessageType,
|
||||
PushdeerMessageType,
|
||||
PushplusMessageType,
|
||||
ServerchanMessageType,
|
||||
TelegramMessageType,
|
||||
)
|
||||
from ..utils.patterns import Singleton
|
||||
|
||||
__all__ = (
|
||||
@ -30,13 +38,12 @@ class MessagingProvider(Singleton, ABC):
|
||||
super().__init__()
|
||||
|
||||
@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):
|
||||
def __init__(
|
||||
self,
|
||||
@ -54,7 +61,7 @@ class EmailService(MessagingProvider):
|
||||
self.smtp_port = smtp_port
|
||||
|
||||
async def send_message(
|
||||
self, subject: str, content: str, msg_type: MSG_TYPE = 'plain'
|
||||
self, subject: str, content: str, msg_type: MessageType
|
||||
) -> None:
|
||||
self._check_parameters()
|
||||
await asyncio.get_running_loop().run_in_executor(
|
||||
@ -62,13 +69,14 @@ class EmailService(MessagingProvider):
|
||||
)
|
||||
|
||||
def _send_email(
|
||||
self, subject: str, content: str, msg_type: MSG_TYPE = 'plain'
|
||||
self, subject: str, content: str, msg_type: EmailMessageType
|
||||
) -> None:
|
||||
msg = EmailMessage()
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = self.src_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:
|
||||
with smtplib.SMTP_SSL(self.smtp_host, self.smtp_port) as smtp:
|
||||
@ -97,15 +105,19 @@ class Serverchan(MessagingProvider):
|
||||
super().__init__()
|
||||
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()
|
||||
await self._post_message(title, content)
|
||||
await self._post_message(title, content, cast(ServerchanMessageType, msg_type))
|
||||
|
||||
def _check_parameters(self) -> None:
|
||||
if not self.sendkey:
|
||||
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'
|
||||
payload = {'text': title, 'desp': content}
|
||||
|
||||
@ -129,21 +141,25 @@ class Pushdeer(MessagingProvider):
|
||||
self.server = server
|
||||
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()
|
||||
await self._post_message(title, content)
|
||||
await self._post_message(title, content, cast(PushdeerMessageType, msg_type))
|
||||
|
||||
def _check_parameters(self) -> None:
|
||||
if not self.pushkey:
|
||||
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)
|
||||
payload = {
|
||||
'pushkey': self.pushkey,
|
||||
'text': title,
|
||||
'desp': content,
|
||||
'type': 'text',
|
||||
'type': msg_type,
|
||||
}
|
||||
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
||||
async with session.post(url, json=payload) as res:
|
||||
@ -166,21 +182,25 @@ class Pushplus(MessagingProvider):
|
||||
self.token = token
|
||||
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()
|
||||
await self._post_message(title, content)
|
||||
await self._post_message(title, content, msg_type)
|
||||
|
||||
def _check_parameters(self) -> None:
|
||||
if not self.token:
|
||||
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 = {
|
||||
'title': title,
|
||||
'content': content,
|
||||
'token': self.token,
|
||||
'topic': self.topic,
|
||||
'template': 'html',
|
||||
'template': msg_type,
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
||||
@ -201,9 +221,11 @@ class Telegram(MessagingProvider):
|
||||
self.token = token
|
||||
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()
|
||||
await self._post_message(title, content)
|
||||
await self._post_message(title, content, cast(TelegramMessageType, msg_type))
|
||||
|
||||
def _check_parameters(self) -> None:
|
||||
if not self.token:
|
||||
@ -211,12 +233,14 @@ class Telegram(MessagingProvider):
|
||||
if not self.chatid:
|
||||
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'
|
||||
payload = {
|
||||
'chat_id': self.chatid,
|
||||
'text': title + '\n' + content,
|
||||
'parse_mode': 'HTML',
|
||||
'text': title + '\n\n' + content,
|
||||
'parse_mode': 'MarkdownV2' if msg_type == 'markdown' else 'HTML',
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession(raise_for_status=True) as session:
|
||||
|
@ -1,55 +1,50 @@
|
||||
from .helpers import shadow_settings, update_settings
|
||||
from .models import (
|
||||
DEFAULT_SETTINGS_FILE,
|
||||
|
||||
DanmakuOptions,
|
||||
DanmakuSettings,
|
||||
EmailMessageTemplateSettings,
|
||||
EmailNotificationSettings,
|
||||
EmailSettings,
|
||||
EnvSettings,
|
||||
HeaderOptions,
|
||||
HeaderSettings,
|
||||
LoggingSettings,
|
||||
NotificationSettings,
|
||||
NotifierSettings,
|
||||
OutputSettings,
|
||||
PostprocessingOptions,
|
||||
PostprocessingSettings,
|
||||
PushdeerMessageTemplateSettings,
|
||||
PushdeerNotificationSettings,
|
||||
PushdeerSettings,
|
||||
PushplusMessageTemplateSettings,
|
||||
PushplusNotificationSettings,
|
||||
PushplusSettings,
|
||||
RecorderOptions,
|
||||
RecorderSettings,
|
||||
ServerchanMessageTemplateSettings,
|
||||
ServerchanNotificationSettings,
|
||||
ServerchanSettings,
|
||||
Settings,
|
||||
SettingsIn,
|
||||
SettingsOut,
|
||||
|
||||
HeaderOptions,
|
||||
HeaderSettings,
|
||||
DanmakuOptions,
|
||||
DanmakuSettings,
|
||||
RecorderOptions,
|
||||
RecorderSettings,
|
||||
PostprocessingSettings,
|
||||
PostprocessingOptions,
|
||||
|
||||
SpaceSettings,
|
||||
TaskOptions,
|
||||
TaskSettings,
|
||||
OutputSettings,
|
||||
LoggingSettings,
|
||||
SpaceSettings,
|
||||
EmailSettings,
|
||||
ServerchanSettings,
|
||||
PushdeerSettings,
|
||||
PushplusSettings,
|
||||
TelegramSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
EmailNotificationSettings,
|
||||
ServerchanNotificationSettings,
|
||||
PushdeerNotificationSettings,
|
||||
PushplusNotificationSettings,
|
||||
TelegramMessageTemplateSettings,
|
||||
TelegramNotificationSettings,
|
||||
TelegramSettings,
|
||||
WebHookSettings,
|
||||
)
|
||||
from .typing import KeyOfSettings, KeySetOfSettings
|
||||
from .helpers import update_settings, shadow_settings
|
||||
from .setting_manager import SettingsManager
|
||||
|
||||
|
||||
__all__ = (
|
||||
'DEFAULT_SETTINGS_FILE',
|
||||
|
||||
'EnvSettings',
|
||||
'Settings',
|
||||
'SettingsIn',
|
||||
'SettingsOut',
|
||||
|
||||
'KeyOfSettings',
|
||||
'KeySetOfSettings',
|
||||
|
||||
'HeaderOptions',
|
||||
'HeaderSettings',
|
||||
'DanmakuOptions',
|
||||
@ -58,12 +53,16 @@ __all__ = (
|
||||
'RecorderSettings',
|
||||
'PostprocessingSettings',
|
||||
'PostprocessingOptions',
|
||||
|
||||
'TaskOptions',
|
||||
'TaskSettings',
|
||||
'OutputSettings',
|
||||
'LoggingSettings',
|
||||
'SpaceSettings',
|
||||
'EmailMessageTemplateSettings',
|
||||
'ServerchanMessageTemplateSettings',
|
||||
'PushdeerMessageTemplateSettings',
|
||||
'PushplusMessageTemplateSettings',
|
||||
'TelegramMessageTemplateSettings',
|
||||
'EmailSettings',
|
||||
'ServerchanSettings',
|
||||
'PushdeerSettings',
|
||||
@ -77,7 +76,6 @@ __all__ = (
|
||||
'PushplusNotificationSettings',
|
||||
'TelegramNotificationSettings',
|
||||
'WebHookSettings',
|
||||
|
||||
'update_settings',
|
||||
'shadow_settings',
|
||||
'SettingsManager',
|
||||
|
@ -1,41 +1,38 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
from typing_extensions import Annotated
|
||||
from typing import (
|
||||
ClassVar,
|
||||
Collection,
|
||||
Final,
|
||||
List,
|
||||
Optional,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
from typing import ClassVar, Collection, Final, List, Optional, TypeVar
|
||||
|
||||
import toml
|
||||
from pydantic import BaseModel as PydanticBaseModel
|
||||
from pydantic import Field, BaseSettings, validator, PrivateAttr
|
||||
from pydantic.networks import HttpUrl, EmailStr
|
||||
from pydantic import BaseSettings, Field, PrivateAttr, validator
|
||||
from pydantic.networks import EmailStr, HttpUrl
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from ..bili.typing import StreamFormat, QualityNumber
|
||||
from ..postprocess import DeleteStrategy
|
||||
from ..bili.typing import QualityNumber, StreamFormat
|
||||
from ..core.cover_downloader import CoverSaveStrategy
|
||||
from ..logging.typing import LOG_LEVEL
|
||||
from ..postprocess import DeleteStrategy
|
||||
from ..utils.string import camel_case
|
||||
|
||||
from .typing import (
|
||||
EmailMessageType,
|
||||
PushdeerMessageType,
|
||||
PushplusMessageType,
|
||||
ServerchanMessageType,
|
||||
TelegramMessageType,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'DEFAULT_SETTINGS_FILE',
|
||||
|
||||
'EnvSettings',
|
||||
'Settings',
|
||||
'SettingsIn',
|
||||
'SettingsOut',
|
||||
|
||||
'HeaderOptions',
|
||||
'HeaderSettings',
|
||||
'DanmakuOptions',
|
||||
@ -44,7 +41,6 @@ __all__ = (
|
||||
'RecorderSettings',
|
||||
'PostprocessingSettings',
|
||||
'PostprocessingOptions',
|
||||
|
||||
'TaskOptions',
|
||||
'TaskSettings',
|
||||
'OutputSettings',
|
||||
@ -57,6 +53,11 @@ __all__ = (
|
||||
'TelegramSettings',
|
||||
'NotifierSettings',
|
||||
'NotificationSettings',
|
||||
'EmailMessageTemplateSettings',
|
||||
'ServerchanMessageTemplateSettings',
|
||||
'PushdeerMessageTemplateSettings',
|
||||
'PushplusMessageTemplateSettings',
|
||||
'TelegramMessageTemplateSettings',
|
||||
'EmailNotificationSettings',
|
||||
'ServerchanNotificationSettings',
|
||||
'PushdeerNotificationSettings',
|
||||
@ -67,9 +68,7 @@ __all__ = (
|
||||
|
||||
|
||||
DEFAULT_OUT_DIR: Final[str] = os.environ.get('DEFAULT_OUT_DIR', '.')
|
||||
DEFAULT_LOG_DIR: Final[str] = os.environ.get(
|
||||
'DEFAULT_LOG_DIR', '~/.blrec/logs/'
|
||||
)
|
||||
DEFAULT_LOG_DIR: Final[str] = os.environ.get('DEFAULT_LOG_DIR', '~/.blrec/logs/')
|
||||
DEFAULT_SETTINGS_FILE: Final[str] = os.environ.get(
|
||||
'DEFAULT_SETTINGS_FILE', '~/.blrec/settings.toml'
|
||||
)
|
||||
@ -80,8 +79,7 @@ class EnvSettings(BaseSettings):
|
||||
out_dir: Optional[str] = None
|
||||
log_dir: Optional[str] = None
|
||||
api_key: Annotated[
|
||||
Optional[str],
|
||||
Field(min_length=8, max_length=80, regex=r'[a-zA-Z\d\-]{8,80}'),
|
||||
Optional[str], Field(min_length=8, max_length=80, regex=r'[a-zA-Z\d\-]{8,80}'),
|
||||
] = None
|
||||
|
||||
class Config:
|
||||
@ -102,9 +100,7 @@ class BaseModel(PydanticBaseModel):
|
||||
return camel_case(string)
|
||||
|
||||
@staticmethod
|
||||
def _validate_with_collection(
|
||||
value: _V, allowed_values: Collection[_V]
|
||||
) -> None:
|
||||
def _validate_with_collection(value: _V, allowed_values: Collection[_V]) -> None:
|
||||
if value not in allowed_values:
|
||||
raise ValueError(
|
||||
f'the value {value} does not be allowed, '
|
||||
@ -118,9 +114,11 @@ class HeaderOptions(BaseModel):
|
||||
|
||||
|
||||
class HeaderSettings(HeaderOptions):
|
||||
user_agent: str = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
|
||||
'AppleWebKit/537.36 (KHTML, like Gecko) ' \
|
||||
user_agent: str = (
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
|
||||
'AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
'Chrome/89.0.4389.114 Safari/537.36'
|
||||
)
|
||||
cookie: str = ''
|
||||
|
||||
|
||||
@ -149,7 +147,7 @@ class RecorderOptions(BaseModel):
|
||||
read_timeout: Optional[int] # seconds
|
||||
disconnection_timeout: Optional[int] # seconds
|
||||
buffer_size: Annotated[ # bytes
|
||||
Optional[int], Field(ge=4096, le=1024 ** 2 * 512, multiple_of=2)
|
||||
Optional[int], Field(ge=4096, le=1024**2 * 512, multiple_of=2)
|
||||
]
|
||||
save_cover: Optional[bool]
|
||||
cover_save_strategy: Optional[CoverSaveStrategy]
|
||||
@ -169,9 +167,7 @@ class RecorderOptions(BaseModel):
|
||||
return value
|
||||
|
||||
@validator('disconnection_timeout')
|
||||
def _validate_disconnection_timeout(
|
||||
cls, value: Optional[int]
|
||||
) -> Optional[int]:
|
||||
def _validate_disconnection_timeout(cls, value: Optional[int]) -> Optional[int]:
|
||||
if value is not None:
|
||||
allowed_values = frozenset(60 * i for i in (3, 5, 10, 15, 20, 30))
|
||||
cls._validate_with_collection(value, allowed_values)
|
||||
@ -185,7 +181,7 @@ class RecorderSettings(RecorderOptions):
|
||||
read_timeout: int = 3
|
||||
disconnection_timeout: int = 600
|
||||
buffer_size: Annotated[
|
||||
int, Field(ge=4096, le=1024 ** 2 * 512, multiple_of=2)
|
||||
int, Field(ge=4096, le=1024**2 * 512, multiple_of=2)
|
||||
] = 8192
|
||||
save_cover: bool = False
|
||||
cover_save_strategy: CoverSaveStrategy = CoverSaveStrategy.DEFAULT
|
||||
@ -244,7 +240,7 @@ class OutputOptions(BaseModel):
|
||||
def _validate_filesize_limit(cls, value: Optional[int]) -> Optional[int]:
|
||||
# allowed 1 ~ 20 GB, 0 indicates not limit.
|
||||
if value is not None:
|
||||
allowed_values = frozenset(1024 ** 3 * i for i in range(0, 21))
|
||||
allowed_values = frozenset(1024**3 * i for i in range(0, 21))
|
||||
cls._validate_with_collection(value, allowed_values)
|
||||
return value
|
||||
|
||||
@ -288,11 +284,11 @@ class TaskOptions(BaseModel):
|
||||
|
||||
@classmethod
|
||||
def from_settings(cls, settings: TaskSettings) -> TaskOptions:
|
||||
return cls(**settings.dict(
|
||||
include={
|
||||
'output', 'header', 'danmaku', 'recorder', 'postprocessing'
|
||||
}
|
||||
))
|
||||
return cls(
|
||||
**settings.dict(
|
||||
include={'output', 'header', 'danmaku', 'recorder', 'postprocessing'}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TaskSettings(TaskOptions):
|
||||
@ -312,8 +308,10 @@ class LoggingSettings(BaseModel):
|
||||
log_dir: Annotated[str, Field(default_factory=log_dir_factory)]
|
||||
console_log_level: LOG_LEVEL = 'INFO'
|
||||
max_bytes: Annotated[
|
||||
int, Field(ge=1024 ** 2, le=1024 ** 2 * 10, multiple_of=1024 ** 2)
|
||||
] = 1024 ** 2 * 10 # allowed 1 ~ 10 MB
|
||||
int, Field(ge=1024**2, le=1024**2 * 10, multiple_of=1024**2)
|
||||
] = (
|
||||
1024**2 * 10
|
||||
) # allowed 1 ~ 10 MB
|
||||
backup_count: Annotated[int, Field(ge=1, le=30)] = 30
|
||||
|
||||
@validator('log_dir')
|
||||
@ -325,7 +323,7 @@ class LoggingSettings(BaseModel):
|
||||
|
||||
class SpaceSettings(BaseModel):
|
||||
check_interval: int = 60 # 1 minutes
|
||||
space_threshold: int = 1024 ** 3 # 1 GB
|
||||
space_threshold: int = 1024**3 # 1 GB
|
||||
recycle_records: bool = False
|
||||
|
||||
@validator('check_interval')
|
||||
@ -336,7 +334,7 @@ class SpaceSettings(BaseModel):
|
||||
|
||||
@validator('space_threshold')
|
||||
def _validate_threshold(cls, value: int) -> int:
|
||||
allowed_values = frozenset(1024 ** 3 * i for i in (1, 3, 5, 10, 20))
|
||||
allowed_values = frozenset(1024**3 * i for i in (1, 3, 5, 10, 20))
|
||||
cls._validate_with_collection(value, allowed_values)
|
||||
return value
|
||||
|
||||
@ -395,9 +393,7 @@ class TelegramSettings(BaseModel):
|
||||
|
||||
@validator('token')
|
||||
def _validate_token(cls, value: str) -> str:
|
||||
if value != '' and not re.fullmatch(
|
||||
r'[0-9]{8,10}:[a-zA-Z0-9_-]{35}', value
|
||||
):
|
||||
if value != '' and not re.fullmatch(r'[0-9]{8,10}:[a-zA-Z0-9_-]{35}', value):
|
||||
raise ValueError('token is invalid')
|
||||
return value
|
||||
|
||||
@ -419,32 +415,134 @@ class NotificationSettings(BaseModel):
|
||||
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(
|
||||
EmailSettings, NotifierSettings, NotificationSettings
|
||||
EmailSettings, NotifierSettings, NotificationSettings, EmailMessageTemplateSettings
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ServerchanNotificationSettings(
|
||||
ServerchanSettings, NotifierSettings, NotificationSettings
|
||||
ServerchanSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
ServerchanMessageTemplateSettings,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class PushdeerNotificationSettings(
|
||||
PushdeerSettings, NotifierSettings, NotificationSettings
|
||||
PushdeerSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
PushplusMessageTemplateSettings,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class PushplusNotificationSettings(
|
||||
PushplusSettings, NotifierSettings, NotificationSettings
|
||||
PushplusSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
PushplusMessageTemplateSettings,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class TelegramNotificationSettings(
|
||||
TelegramSettings, NotifierSettings, NotificationSettings
|
||||
TelegramSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
TelegramMessageTemplateSettings,
|
||||
):
|
||||
pass
|
||||
|
||||
@ -487,14 +585,12 @@ class Settings(BaseModel):
|
||||
postprocessing: PostprocessingSettings = PostprocessingSettings()
|
||||
space: SpaceSettings = SpaceSettings()
|
||||
email_notification: EmailNotificationSettings = EmailNotificationSettings()
|
||||
serverchan_notification: ServerchanNotificationSettings = \
|
||||
serverchan_notification: ServerchanNotificationSettings = (
|
||||
ServerchanNotificationSettings()
|
||||
pushdeer_notification: PushdeerNotificationSettings = \
|
||||
PushdeerNotificationSettings()
|
||||
pushplus_notification: PushplusNotificationSettings = \
|
||||
PushplusNotificationSettings()
|
||||
telegram_notification: TelegramNotificationSettings = \
|
||||
TelegramNotificationSettings()
|
||||
)
|
||||
pushdeer_notification: PushdeerNotificationSettings = PushdeerNotificationSettings()
|
||||
pushplus_notification: PushplusNotificationSettings = PushplusNotificationSettings()
|
||||
telegram_notification: TelegramNotificationSettings = TelegramNotificationSettings()
|
||||
webhooks: Annotated[List[WebHookSettings], Field(max_items=50)] = []
|
||||
|
||||
@classmethod
|
||||
@ -525,9 +621,7 @@ class Settings(BaseModel):
|
||||
cls, webhooks: List[WebHookSettings]
|
||||
) -> List[WebHookSettings]:
|
||||
if len(webhooks) >= cls._MAX_WEBHOOKS:
|
||||
raise ValueError(
|
||||
f'Out of max webhooks limits: {cls._MAX_WEBHOOKS}'
|
||||
)
|
||||
raise ValueError(f'Out of max webhooks limits: {cls._MAX_WEBHOOKS}')
|
||||
return webhooks
|
||||
|
||||
|
||||
|
@ -1,31 +1,37 @@
|
||||
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 (
|
||||
DanmakuOptions,
|
||||
HeaderOptions,
|
||||
MessageTemplateSettings,
|
||||
NotificationSettings,
|
||||
NotifierSettings,
|
||||
OutputOptions,
|
||||
PostprocessingOptions,
|
||||
RecorderOptions,
|
||||
Settings,
|
||||
SettingsIn,
|
||||
SettingsOut,
|
||||
|
||||
HeaderOptions,
|
||||
DanmakuOptions,
|
||||
RecorderOptions,
|
||||
PostprocessingOptions,
|
||||
|
||||
TaskOptions,
|
||||
TaskSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
)
|
||||
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:
|
||||
from ..application import Application
|
||||
|
||||
@ -40,9 +46,7 @@ class SettingsManager:
|
||||
include: Optional[KeySetOfSettings] = None,
|
||||
exclude: Optional[KeySetOfSettings] = None,
|
||||
) -> SettingsOut:
|
||||
return SettingsOut(
|
||||
**self._settings.dict(include=include, exclude=exclude)
|
||||
)
|
||||
return SettingsOut(**self._settings.dict(include=include, exclude=exclude))
|
||||
|
||||
async def change_settings(self, settings: SettingsIn) -> SettingsOut:
|
||||
changed = False
|
||||
@ -70,19 +74,15 @@ class SettingsManager:
|
||||
if changed:
|
||||
await self.dump_settings()
|
||||
|
||||
return self.get_settings(
|
||||
cast(KeySetOfSettings, settings.__fields_set__)
|
||||
)
|
||||
return self.get_settings(cast(KeySetOfSettings, settings.__fields_set__))
|
||||
|
||||
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)
|
||||
raise NotFoundError(f'task settings of room {room_id} not found')
|
||||
|
||||
async def change_task_options(
|
||||
self,
|
||||
room_id: int,
|
||||
options: TaskOptions,
|
||||
self, room_id: int, options: TaskOptions
|
||||
) -> TaskOptions:
|
||||
settings = self.find_task_settings(room_id)
|
||||
assert settings is not None
|
||||
@ -211,11 +211,7 @@ class SettingsManager:
|
||||
await self.dump_settings()
|
||||
|
||||
async def apply_task_header_settings(
|
||||
self,
|
||||
room_id: int,
|
||||
options: HeaderOptions,
|
||||
*,
|
||||
update_session: bool = True,
|
||||
self, room_id: int, options: HeaderOptions, *, update_session: bool = True
|
||||
) -> None:
|
||||
final_settings = self._settings.header.copy()
|
||||
shadow_settings(options, final_settings)
|
||||
@ -224,42 +220,26 @@ class SettingsManager:
|
||||
)
|
||||
|
||||
def apply_task_danmaku_settings(
|
||||
self,
|
||||
room_id: int,
|
||||
options: DanmakuOptions,
|
||||
self, room_id: int, options: DanmakuOptions
|
||||
) -> None:
|
||||
final_settings = self._settings.danmaku.copy()
|
||||
shadow_settings(options, final_settings)
|
||||
self._app._task_manager.apply_task_danmaku_settings(
|
||||
room_id, final_settings
|
||||
)
|
||||
self._app._task_manager.apply_task_danmaku_settings(room_id, final_settings)
|
||||
|
||||
def apply_task_recorder_settings(
|
||||
self,
|
||||
room_id: int,
|
||||
options: RecorderOptions,
|
||||
self, room_id: int, options: RecorderOptions
|
||||
) -> None:
|
||||
final_settings = self._settings.recorder.copy()
|
||||
shadow_settings(options, final_settings)
|
||||
self._app._task_manager.apply_task_recorder_settings(
|
||||
room_id, final_settings
|
||||
)
|
||||
self._app._task_manager.apply_task_recorder_settings(room_id, final_settings)
|
||||
|
||||
def apply_task_output_settings(
|
||||
self,
|
||||
room_id: int,
|
||||
options: OutputOptions,
|
||||
) -> None:
|
||||
def apply_task_output_settings(self, room_id: int, options: OutputOptions) -> None:
|
||||
final_settings = self._settings.output.copy()
|
||||
shadow_settings(options, final_settings)
|
||||
self._app._task_manager.apply_task_output_settings(
|
||||
room_id, final_settings
|
||||
)
|
||||
self._app._task_manager.apply_task_output_settings(room_id, final_settings)
|
||||
|
||||
def apply_task_postprocessing_settings(
|
||||
self,
|
||||
room_id: int,
|
||||
options: PostprocessingOptions,
|
||||
self, room_id: int, options: PostprocessingOptions
|
||||
) -> None:
|
||||
final_settings = self._settings.postprocessing.copy()
|
||||
shadow_settings(options, final_settings)
|
||||
@ -286,21 +266,15 @@ class SettingsManager:
|
||||
|
||||
async def apply_header_settings(self) -> None:
|
||||
for settings in self._settings.tasks:
|
||||
await self.apply_task_header_settings(
|
||||
settings.room_id, settings.header
|
||||
)
|
||||
await self.apply_task_header_settings(settings.room_id, settings.header)
|
||||
|
||||
def apply_danmaku_settings(self) -> None:
|
||||
for settings in self._settings.tasks:
|
||||
self.apply_task_danmaku_settings(
|
||||
settings.room_id, settings.danmaku
|
||||
)
|
||||
self.apply_task_danmaku_settings(settings.room_id, settings.danmaku)
|
||||
|
||||
def apply_recorder_settings(self) -> None:
|
||||
for settings in self._settings.tasks:
|
||||
self.apply_task_recorder_settings(
|
||||
settings.room_id, settings.recorder
|
||||
)
|
||||
self.apply_task_recorder_settings(settings.room_id, settings.recorder)
|
||||
|
||||
def apply_postprocessing_settings(self) -> None:
|
||||
for settings in self._settings.tasks:
|
||||
@ -327,6 +301,7 @@ class SettingsManager:
|
||||
self._apply_email_settings(notifier.provider)
|
||||
self._apply_notifier_settings(notifier, settings)
|
||||
self._apply_notification_settings(notifier, settings)
|
||||
self._apply_message_template_settings(notifier, settings)
|
||||
|
||||
def apply_serverchan_notification_settings(self) -> None:
|
||||
notifier = self._app._serverchan_notifier
|
||||
@ -334,6 +309,7 @@ class SettingsManager:
|
||||
self._apply_serverchan_settings(notifier.provider)
|
||||
self._apply_notifier_settings(notifier, settings)
|
||||
self._apply_notification_settings(notifier, settings)
|
||||
self._apply_message_template_settings(notifier, settings)
|
||||
|
||||
def apply_pushdeer_notification_settings(self) -> None:
|
||||
notifier = self._app._pushdeer_notifier
|
||||
@ -341,6 +317,7 @@ class SettingsManager:
|
||||
self._apply_pushdeer_settings(notifier.provider)
|
||||
self._apply_notifier_settings(notifier, settings)
|
||||
self._apply_notification_settings(notifier, settings)
|
||||
self._apply_message_template_settings(notifier, settings)
|
||||
|
||||
def apply_pushplus_notification_settings(self) -> None:
|
||||
notifier = self._app._pushplus_notifier
|
||||
@ -348,6 +325,7 @@ class SettingsManager:
|
||||
self._apply_pushplus_settings(notifier.provider)
|
||||
self._apply_notifier_settings(notifier, settings)
|
||||
self._apply_notification_settings(notifier, settings)
|
||||
self._apply_message_template_settings(notifier, settings)
|
||||
|
||||
def apply_telegram_notification_settings(self) -> None:
|
||||
notifier = self._app._telegram_notifier
|
||||
@ -355,6 +333,7 @@ class SettingsManager:
|
||||
self._apply_telegram_settings(notifier.provider)
|
||||
self._apply_notifier_settings(notifier, settings)
|
||||
self._apply_notification_settings(notifier, settings)
|
||||
self._apply_message_template_settings(notifier, settings)
|
||||
|
||||
def apply_webhooks_settings(self) -> None:
|
||||
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_error = settings.notify_error
|
||||
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[
|
||||
|
@ -3,7 +3,7 @@ from typing import Callable, Iterable, Iterator, List, Optional, cast
|
||||
|
||||
from fastapi import Query
|
||||
|
||||
from blrec.setting import KeyOfSettings, KeySetOfSettings
|
||||
from blrec.setting.typing import KeyOfSettings, KeySetOfSettings
|
||||
|
||||
from .schemas import DataSelection, AliasKeyOfSettings
|
||||
from ..bili.models import LiveStatus
|
||||
|
@ -7,9 +7,9 @@ from ..dependencies import settings_include_set, settings_exclude_set
|
||||
from ...setting import (
|
||||
SettingsIn,
|
||||
SettingsOut,
|
||||
KeySetOfSettings,
|
||||
TaskOptions,
|
||||
)
|
||||
from ...setting.typing import KeySetOfSettings
|
||||
from ...application import Application
|
||||
|
||||
|
||||
|
@ -17,5 +17,12 @@
|
||||
keyOfSettings="emailNotification"
|
||||
></app-event-settings>
|
||||
</app-page-section>
|
||||
|
||||
<app-page-section name="消息">
|
||||
<app-message-template-settings
|
||||
[settings]="messageTemplateSettings"
|
||||
keyOfSettings="emailNotification"
|
||||
></app-message-template-settings>
|
||||
</app-page-section>
|
||||
</ng-template>
|
||||
</app-sub-page>
|
||||
|
@ -13,9 +13,11 @@ import {
|
||||
EmailSettings,
|
||||
NotifierSettings,
|
||||
NotificationSettings,
|
||||
MessageTemplateSettings,
|
||||
KEYS_OF_EMAIL_SETTINGS,
|
||||
KEYS_OF_NOTIFIER_SETTINGS,
|
||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||
} from '../../shared/setting.model';
|
||||
|
||||
@Component({
|
||||
@ -28,6 +30,7 @@ export class EmailNotificationSettingsComponent implements OnInit {
|
||||
emailSettings!: EmailSettings;
|
||||
notifierSettings!: NotifierSettings;
|
||||
notificationSettings!: NotificationSettings;
|
||||
messageTemplateSettings!: MessageTemplateSettings;
|
||||
|
||||
constructor(
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
@ -40,6 +43,10 @@ export class EmailNotificationSettingsComponent implements OnInit {
|
||||
this.emailSettings = pick(settings, KEYS_OF_EMAIL_SETTINGS);
|
||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||
this.messageTemplateSettings = pick(
|
||||
settings,
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||
);
|
||||
this.changeDetector.markForCheck();
|
||||
});
|
||||
}
|
||||
|
@ -19,6 +19,13 @@
|
||||
keyOfSettings="pushdeerNotification"
|
||||
></app-event-settings>
|
||||
</app-page-section>
|
||||
|
||||
<app-page-section name="消息">
|
||||
<app-message-template-settings
|
||||
[settings]="messageTemplateSettings"
|
||||
keyOfSettings="pushdeerNotification"
|
||||
></app-message-template-settings>
|
||||
</app-page-section>
|
||||
</ng-template>
|
||||
</app-sub-page>
|
||||
|
||||
|
@ -9,9 +9,11 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import pick from 'lodash-es/pick';
|
||||
|
||||
import {
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||
KEYS_OF_NOTIFIER_SETTINGS,
|
||||
KEYS_OF_PUSHDEER_SETTINGS,
|
||||
MessageTemplateSettings,
|
||||
NotificationSettings,
|
||||
NotifierSettings,
|
||||
PushdeerNotificationSettings,
|
||||
@ -28,6 +30,7 @@ export class PushdeerNotificationSettingsComponent implements OnInit {
|
||||
pushdeerSettings!: PushdeerSettings;
|
||||
notifierSettings!: NotifierSettings;
|
||||
notificationSettings!: NotificationSettings;
|
||||
messageTemplateSettings!: MessageTemplateSettings;
|
||||
|
||||
constructor(
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
@ -40,6 +43,10 @@ export class PushdeerNotificationSettingsComponent implements OnInit {
|
||||
this.pushdeerSettings = pick(settings, KEYS_OF_PUSHDEER_SETTINGS);
|
||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_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="pushplusNotification"
|
||||
></app-event-settings>
|
||||
</app-page-section>
|
||||
|
||||
<app-page-section name="消息">
|
||||
<app-message-template-settings
|
||||
[settings]="messageTemplateSettings"
|
||||
keyOfSettings="pushplusNotification"
|
||||
></app-message-template-settings>
|
||||
</app-page-section>
|
||||
</ng-template>
|
||||
</app-sub-page>
|
||||
|
@ -16,6 +16,8 @@ import {
|
||||
KEYS_OF_PUSHPLUS_SETTINGS,
|
||||
KEYS_OF_NOTIFIER_SETTINGS,
|
||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||
MessageTemplateSettings,
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||
} from '../../shared/setting.model';
|
||||
|
||||
@Component({
|
||||
@ -28,6 +30,7 @@ export class PushplusNotificationSettingsComponent implements OnInit {
|
||||
pushplusSettings!: PushplusSettings;
|
||||
notifierSettings!: NotifierSettings;
|
||||
notificationSettings!: NotificationSettings;
|
||||
messageTemplateSettings!: MessageTemplateSettings;
|
||||
|
||||
constructor(
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
@ -37,10 +40,14 @@ export class PushplusNotificationSettingsComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
this.route.data.subscribe((data) => {
|
||||
const settings = data.settings as PushplusNotificationSettings;
|
||||
this.changeDetector.markForCheck();
|
||||
this.pushplusSettings = pick(settings, KEYS_OF_PUSHPLUS_SETTINGS);
|
||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_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"
|
||||
></app-event-settings>
|
||||
</app-page-section>
|
||||
|
||||
<app-page-section name="消息">
|
||||
<app-message-template-settings
|
||||
[settings]="messageTemplateSettings"
|
||||
keyOfSettings="serverchanNotification"
|
||||
></app-message-template-settings>
|
||||
</app-page-section>
|
||||
</ng-template>
|
||||
</app-sub-page>
|
||||
|
@ -9,9 +9,11 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import pick from 'lodash-es/pick';
|
||||
|
||||
import {
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||
KEYS_OF_NOTIFIER_SETTINGS,
|
||||
KEYS_OF_SERVERCHAN_SETTINGS,
|
||||
MessageTemplateSettings,
|
||||
NotificationSettings,
|
||||
NotifierSettings,
|
||||
ServerchanNotificationSettings,
|
||||
@ -28,6 +30,7 @@ export class ServerchanNotificationSettingsComponent implements OnInit {
|
||||
serverchanSettings!: ServerchanSettings;
|
||||
notifierSettings!: NotifierSettings;
|
||||
notificationSettings!: NotificationSettings;
|
||||
messageTemplateSettings!: MessageTemplateSettings;
|
||||
|
||||
constructor(
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
@ -40,6 +43,10 @@ export class ServerchanNotificationSettingsComponent implements OnInit {
|
||||
this.serverchanSettings = pick(settings, KEYS_OF_SERVERCHAN_SETTINGS);
|
||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_SETTINGS);
|
||||
this.notificationSettings = pick(settings, KEYS_OF_NOTIFICATION_SETTINGS);
|
||||
this.messageTemplateSettings = pick(
|
||||
settings,
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS
|
||||
);
|
||||
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"
|
||||
></app-event-settings>
|
||||
</app-page-section>
|
||||
|
||||
<app-page-section name="消息">
|
||||
<app-message-template-settings
|
||||
[settings]="messageTemplateSettings"
|
||||
keyOfSettings="telegramNotification"
|
||||
></app-message-template-settings>
|
||||
</app-page-section>
|
||||
</ng-template>
|
||||
</app-sub-page>
|
||||
|
@ -16,6 +16,8 @@ import {
|
||||
KEYS_OF_TELEGRAM_SETTINGS,
|
||||
KEYS_OF_NOTIFIER_SETTINGS,
|
||||
KEYS_OF_NOTIFICATION_SETTINGS,
|
||||
MessageTemplateSettings,
|
||||
KEYS_OF_MESSAGE_TEMPLATE_SETTINGS,
|
||||
} from '../../shared/setting.model';
|
||||
|
||||
@Component({
|
||||
@ -28,6 +30,7 @@ export class TelegramNotificationSettingsComponent implements OnInit {
|
||||
telegramSettings!: TelegramSettings;
|
||||
notifierSettings!: NotifierSettings;
|
||||
notificationSettings!: NotificationSettings;
|
||||
messageTemplateSettings!: MessageTemplateSettings;
|
||||
|
||||
constructor(
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
@ -37,10 +40,14 @@ export class TelegramNotificationSettingsComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
this.route.data.subscribe((data) => {
|
||||
const settings = data.settings as TelegramNotificationSettings;
|
||||
this.changeDetector.markForCheck();
|
||||
this.telegramSettings = pick(settings, KEYS_OF_TELEGRAM_SETTINGS);
|
||||
this.notifierSettings = pick(settings, KEYS_OF_NOTIFIER_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 { 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 { 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({
|
||||
declarations: [
|
||||
@ -97,6 +99,8 @@ import { PathTemplateEditDialogComponent } from './output-settings/path-template
|
||||
OutdirEditDialogComponent,
|
||||
LogdirEditDialogComponent,
|
||||
PathTemplateEditDialogComponent,
|
||||
MessageTemplateSettingsComponent,
|
||||
MessageTemplateEditDialogComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -175,25 +175,152 @@ export const KEYS_OF_NOTIFICATION_SETTINGS = [
|
||||
'notifySpace',
|
||||
] 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 &
|
||||
NotifierSettings &
|
||||
NotificationSettings;
|
||||
NotificationSettings &
|
||||
EmailMessageTemplateSettings;
|
||||
|
||||
export type ServerchanNotificationSettings = ServerchanSettings &
|
||||
NotifierSettings &
|
||||
NotificationSettings;
|
||||
NotificationSettings &
|
||||
ServerchanMessageTemplateSettings;
|
||||
|
||||
export type PushdeerNotificationSettings = PushdeerSettings &
|
||||
NotifierSettings &
|
||||
NotificationSettings;
|
||||
NotificationSettings &
|
||||
PushdeerMessageTemplateSettings;
|
||||
|
||||
export type PushplusNotificationSettings = PushplusSettings &
|
||||
NotifierSettings &
|
||||
NotificationSettings;
|
||||
NotificationSettings &
|
||||
PushdeerMessageTemplateSettings;
|
||||
|
||||
export type TelegramNotificationSettings = TelegramSettings &
|
||||
NotifierSettings &
|
||||
NotificationSettings;
|
||||
NotificationSettings &
|
||||
TelegramMessageTemplateSettings;
|
||||
|
||||
export interface WebhookEventSettings {
|
||||
liveBegan: boolean;
|
||||
|
@ -1,5 +1,5 @@
|
||||
@use '../../../shared/styles/layout';
|
||||
@use '../../../shared/styles/list';
|
||||
@use "../../../shared/styles/layout";
|
||||
@use "../../../shared/styles/list";
|
||||
|
||||
.settings-page {
|
||||
@extend %inner-page;
|
||||
@ -43,6 +43,11 @@ a.setting-item {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
@include actionable;
|
||||
|
||||
height: 60px;
|
||||
&:not(:first-child) {
|
||||
height: 61px;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
@ -90,6 +95,11 @@ a.setting-item {
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
margin-left: 0;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 332px) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
&::-webkit-scrollbar {
|
||||
background-color: transparent;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
|
@ -2,6 +2,7 @@
|
||||
@media screen and (min-width: 768px) {
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
|
Loading…
Reference in New Issue
Block a user