Merge pull request #123 from ygguorun/feature-bark

feat: add bark notification
This commit is contained in:
acgnhiki 2022-11-06 11:50:26 +08:00 committed by GitHub
commit 3a7facce0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 538 additions and 41 deletions

View File

@ -33,6 +33,7 @@ from .notification import (
PushdeerNotifier,
PushplusNotifier,
TelegramNotifier,
BarkNotifier,
)
from .webhook import WebHookEmitter
@ -331,11 +332,13 @@ class Application:
self._pushdeer_notifier = PushdeerNotifier()
self._pushplus_notifier = PushplusNotifier()
self._telegram_notifier = TelegramNotifier()
self._bark_notifier = BarkNotifier()
self._settings_manager.apply_email_notification_settings()
self._settings_manager.apply_serverchan_notification_settings()
self._settings_manager.apply_pushdeer_notification_settings()
self._settings_manager.apply_pushplus_notification_settings()
self._settings_manager.apply_telegram_notification_settings()
self._settings_manager.apply_bark_notification_settings()
def _setup_webhooks(self) -> None:
self._webhook_emitter = WebHookEmitter()
@ -367,11 +370,13 @@ class Application:
self._pushdeer_notifier.disable()
self._pushplus_notifier.disable()
self._telegram_notifier.disable()
self._bark_notifier.disable()
del self._email_notifier
del self._serverchan_notifier
del self._pushdeer_notifier
del self._pushplus_notifier
del self._telegram_notifier
del self._bark_notifier
def _destroy_webhooks(self) -> None:
self._webhook_emitter.disable()

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

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,6 @@
<body>
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
<script src="runtime.c6818dbcd7b06106.js" type="module"></script><script src="polyfills.4b08448aee19bb22.js" type="module"></script><script src="main.6da8ea192405b948.js" type="module"></script>
<script src="runtime.af0deb37647841b9.js" type="module"></script><script src="polyfills.4b08448aee19bb22.js" type="module"></script><script src="main.dbd09d2079405adc.js" type="module"></script>
</body></html>

View File

@ -1,6 +1,6 @@
{
"configVersion": 1,
"timestamp": 1661579095139,
"timestamp": 1666085654716,
"index": "/index.html",
"assetGroups": [
{
@ -14,15 +14,15 @@
"/103.5b5d2a6e5a8a7479.js",
"/146.5a8902910bda9e87.js",
"/183.ee55fc76717674c3.js",
"/205.cf2caa9b46b14212.js",
"/45.c90c3cea2bf1a66e.js",
"/91.cab8652a2fa56b1a.js",
"/548.5d742bcea753ec09.js",
"/91.3c224fe84835dadd.js",
"/common.858f777e9296e6f2.js",
"/index.html",
"/main.6da8ea192405b948.js",
"/main.dbd09d2079405adc.js",
"/manifest.webmanifest",
"/polyfills.4b08448aee19bb22.js",
"/runtime.c6818dbcd7b06106.js",
"/runtime.af0deb37647841b9.js",
"/styles.2e152d608221c2ee.css"
],
"patterns": []
@ -1637,9 +1637,9 @@
"/103.5b5d2a6e5a8a7479.js": "cc0240f217015b6d4ddcc14f31fcc42e1c1c282a",
"/146.5a8902910bda9e87.js": "d9c33c7073662699f00f46f3a384ae5b749fdef9",
"/183.ee55fc76717674c3.js": "2628c996ec80a6c6703d542d34ac95194283bcf8",
"/205.cf2caa9b46b14212.js": "749df896fbbd279dcf49318963f0ce074c5df87f",
"/45.c90c3cea2bf1a66e.js": "e5bfb8cf3803593e6b8ea14c90b3d3cb6a066764",
"/91.cab8652a2fa56b1a.js": "c11ebf28472c8a75653f7b27b5cffdec477830fe",
"/548.5d742bcea753ec09.js": "1ae82ced33a49e48520babe08fcc030fb8f595e6",
"/91.3c224fe84835dadd.js": "2e3cdb6c44a8cf3241fe8dd89b27c37f212768f8",
"/assets/animal/panda.js": "fec2868bb3053dd2da45f96bbcb86d5116ed72b1",
"/assets/animal/panda.svg": "bebd302cdc601e0ead3a6d2710acf8753f3d83b1",
"/assets/fill/.gitkeep": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
@ -3234,11 +3234,11 @@
"/assets/twotone/warning.js": "fb2d7ea232f3a99bf8f080dbc94c65699232ac01",
"/assets/twotone/warning.svg": "8c7a2d3e765a2e7dd58ac674870c6655cecb0068",
"/common.858f777e9296e6f2.js": "b68ca68e1e214a2537d96935c23410126cc564dd",
"/index.html": "80797fa46f33b7bcf402788a5d0d0516b77f23b1",
"/main.6da8ea192405b948.js": "b8995c7d8ccd465769b90936db5e0a337a827a58",
"/index.html": "77dfa5e5defa4b9918c19b2bef05ca991bdec934",
"/main.dbd09d2079405adc.js": "2f7284b616ed9fc433b612c9dca53dc06a0f3aa1",
"/manifest.webmanifest": "62c1cb8c5ad2af551a956b97013ab55ce77dd586",
"/polyfills.4b08448aee19bb22.js": "8e73f2d42cc13ca353cea5c886d930bd6da08d0d",
"/runtime.c6818dbcd7b06106.js": "00160f946c5d007a956f5f61293cbd3bed2756dc",
"/runtime.af0deb37647841b9.js": "79436e2995b15139193a457ad96e453a00af6844",
"/styles.2e152d608221c2ee.css": "9830389a46daa5b4511e0dd343aad23ca9f9690f"
},
"navigationUrls": [

View File

@ -0,0 +1 @@
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={exports:{}};return v[e].call(t.exports,t,t.exports,r),t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],c=!0,l=0;l<t.length;l++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(c=!1,o<a&&(a=o));if(c){e.splice(n--,1);var d=i();void 0!==d&&(f=d)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.n=e=>{var f=e&&e.__esModule?()=>e.default:()=>e;return r.d(f,{a:f}),f},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>(592===e?"common":e)+"."+{45:"c90c3cea2bf1a66e",91:"3c224fe84835dadd",103:"5b5d2a6e5a8a7479",146:"5a8902910bda9e87",183:"ee55fc76717674c3",548:"5d742bcea753ec09",592:"858f777e9296e6f2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="blrec:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,c;if(void 0!==o)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")==f+o){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",f+o),a.src=r.tu(t)),e[t]=[i];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=f=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(f))})(),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((u,s)=>n=e[i]=[u,s]);o.push(n[2]=a);var c=r.p+r.u(i),l=new Error;r.l(c,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+s+": "+p+")",l.name="ChunkLoadError",l.type=s,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var l,d,[n,a,c]=o,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(i&&i(o);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(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();

View File

@ -1 +0,0 @@
(()=>{"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,f,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,f,o]=e[n],c=!0,l=0;l<t.length;l++)(!1&o||a>=o)&&Object.keys(r.O).every(p=>r.O[p](t[l]))?t.splice(l--,1):(c=!1,o<a&&(a=o));if(c){e.splice(n--,1);var d=f();void 0!==d&&(i=d)}}return i}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,f,o]},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",91:"cab8652a2fa56b1a",103:"5b5d2a6e5a8a7479",146:"5a8902910bda9e87",183:"ee55fc76717674c3",205:"cf2caa9b46b14212",592:"858f777e9296e6f2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="blrec:";r.l=(t,f,o,n)=>{if(e[t])e[t].push(f);else{var a,c;if(void 0!==o)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+o){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+o),a.src=r.tu(t)),e[t]=[f];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=(f,o)=>{var n=r.o(e,f)?e[f]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=f){var a=new Promise((u,s)=>n=e[f]=[u,s]);o.push(n[2]=a);var c=r.p+r.u(f),l=new Error;r.l(c,u=>{if(r.o(e,f)&&(0!==(n=e[f])&&(e[f]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),b=u&&u.target&&u.target.src;l.message="Loading chunk "+f+" failed.\n("+s+": "+b+")",l.name="ChunkLoadError",l.type=s,l.request=b,n[1](l)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var i=(f,o)=>{var l,d,[n,a,c]=o,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(f&&f(o);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))})()})();

View File

@ -6,6 +6,7 @@ from .notifiers import (
PushdeerNotifier,
PushplusNotifier,
TelegramNotifier,
BarkNotifier,
)
from .providers import (
MessagingProvider,
@ -14,6 +15,7 @@ from .providers import (
Pushdeer,
Pushplus,
Telegram,
Bark,
)
@ -24,6 +26,7 @@ __all__ = (
'Pushdeer',
'Pushplus',
'Telegram',
'Bark',
'Notifier',
'MessageNotifier',
@ -32,4 +35,5 @@ __all__ = (
'PushdeerNotifier',
'PushplusNotifier',
'TelegramNotifier',
'BarkNotifier',
)

View File

@ -36,6 +36,7 @@ from .providers import (
Pushplus,
Serverchan,
Telegram,
Bark,
)
__all__ = (
@ -46,6 +47,7 @@ __all__ = (
'PushdeerNotifier',
'PushplusNotifier',
'TelegramNotifier',
'BarkNotifer',
)
@ -403,3 +405,15 @@ class TelegramNotifier(MessageNotifier):
def _get_error_message_content(self, msg_type: Optional[MessageType] = None) -> str:
return super()._get_error_message_content(msg_type='text')
class BarkNotifier(MessageNotifier):
provider = Bark.get_instance()
def _do_enable(self) -> None:
super()._do_enable()
logger.debug('Enabled Bark notifier')
def _do_disable(self) -> None:
super()._do_disable()
logger.debug('Disabled Bark notifier')

View File

@ -17,6 +17,7 @@ from ..setting.typing import (
PushplusMessageType,
ServerchanMessageType,
TelegramMessageType,
BarkMessageType,
)
from ..utils.patterns import Singleton
@ -27,6 +28,7 @@ __all__ = (
'Pushdeer',
'Pushplus',
'Telegram',
'Bark',
)
@ -251,3 +253,50 @@ class Telegram(MessagingProvider):
response['result']['error_code'],
response['result']['description'],
)
class BarkResponse(TypedDict):
code: int
message: str
timestamp: int
class Bark(MessagingProvider):
_server: Final = 'https://api.day.app'
_endpoint: Final = '/push'
def __init__(self, server: str = '', pushkey: str = '') -> None:
super().__init__()
self.server = server
self.pushkey = pushkey
async def send_message(
self, title: str, content: str, msg_type: MessageType
) -> None:
self._check_parameters()
await self._post_message(title, content, cast(BarkMessageType, 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, msg_type: BarkMessageType
) -> None:
url = urljoin(self.server or self._server, self._endpoint)
# content size is limited to a maximum size of 4 KB (4096 bytes)
if len(content.encode()) >= 4096:
content = content.encode()[:4090].decode(errors='ignore') + ' ...'
payload = {
"title": title,
"body": content,
"device_key": self.pushkey,
"badge": 1,
"icon": "https://raw.githubusercontent.com/acgnhiki/blrec/master/webapp/src/assets/icons/icon-72x72.png",
"group": "blrec",
}
async with aiohttp.ClientSession(raise_for_status=True) as session:
async with session.post(url, json=payload) as res:
response = cast(BarkResponse, await res.json())
if response['code'] != 200:
raise HTTPException(response['message'])

View File

@ -36,6 +36,9 @@ from .models import (
TelegramMessageTemplateSettings,
TelegramNotificationSettings,
TelegramSettings,
BarkMessageTemplateSettings,
BarkNotificationSettings,
BarkSettings,
WebHookSettings,
)
from .setting_manager import SettingsManager
@ -65,11 +68,13 @@ __all__ = (
'PushdeerMessageTemplateSettings',
'PushplusMessageTemplateSettings',
'TelegramMessageTemplateSettings',
'BarkMessageTemplateSettings',
'EmailSettings',
'ServerchanSettings',
'PushdeerSettings',
'PushplusSettings',
'TelegramSettings',
'BarkSettings',
'NotifierSettings',
'NotificationSettings',
'EmailNotificationSettings',
@ -77,6 +82,7 @@ __all__ = (
'PushdeerNotificationSettings',
'PushplusNotificationSettings',
'TelegramNotificationSettings',
'BarkNotificationSettings',
'WebHookSettings',
'update_settings',
'shadow_settings',

View File

@ -24,6 +24,7 @@ from .typing import (
RecordingMode,
ServerchanMessageType,
TelegramMessageType,
BarkMessageType,
)
logger = logging.getLogger(__name__)
@ -54,6 +55,7 @@ __all__ = (
'PushdeerSettings',
'PushplusSettings',
'TelegramSettings',
'BarkSettings'
'NotifierSettings',
'NotificationSettings',
'EmailMessageTemplateSettings',
@ -61,11 +63,13 @@ __all__ = (
'PushdeerMessageTemplateSettings',
'PushplusMessageTemplateSettings',
'TelegramMessageTemplateSettings',
'BarkMessageTemplateSettings',
'EmailNotificationSettings',
'ServerchanNotificationSettings',
'PushdeerNotificationSettings',
'PushplusNotificationSettings',
'TelegramNotificationSettings',
'BarkNotificationSettings',
'WebHookSettings',
)
@ -419,6 +423,23 @@ class TelegramSettings(BaseModel):
return value
class BarkSettings(BaseModel):
server: str = ''
pushkey: str = ''
@validator('server')
def _validate_server(cls, value: str) -> str:
if value != '' and not re.fullmatch(r'https?://.+', value):
raise ValueError('server is invalid')
return value
@validator('pushkey')
def _validate_pushkey(cls, value: str) -> str:
if value != '' and not re.fullmatch(r'[a-zA-Z\d]+', value):
raise ValueError('pushkey is invalid')
return value
class NotifierSettings(BaseModel):
enabled: bool = False
@ -520,6 +541,21 @@ class TelegramMessageTemplateSettings(MessageTemplateSettings):
error_message_content: str = ''
class BarkMessageTemplateSettings(MessageTemplateSettings):
began_message_type: BarkMessageType = 'markdown'
began_message_title: str = ''
began_message_content: str = ''
ended_message_type: BarkMessageType = 'markdown'
ended_message_title: str = ''
ended_message_content: str = ''
space_message_type: BarkMessageType = 'markdown'
space_message_title: str = ''
space_message_content: str = ''
error_message_type: BarkMessageType = 'markdown'
error_message_title: str = ''
error_message_content: str = ''
class EmailNotificationSettings(
EmailSettings, NotifierSettings, NotificationSettings, EmailMessageTemplateSettings
):
@ -562,6 +598,15 @@ class TelegramNotificationSettings(
pass
class BarkNotificationSettings(
BarkSettings,
NotifierSettings,
NotificationSettings,
BarkMessageTemplateSettings,
):
pass
class WebHookEventSettings(BaseModel):
live_began: bool = True
live_ended: bool = True
@ -607,6 +652,7 @@ class Settings(BaseModel):
pushdeer_notification: PushdeerNotificationSettings = PushdeerNotificationSettings()
pushplus_notification: PushplusNotificationSettings = PushplusNotificationSettings()
telegram_notification: TelegramNotificationSettings = TelegramNotificationSettings()
bark_notification: BarkNotificationSettings = BarkNotificationSettings()
webhooks: Annotated[List[WebHookSettings], Field(max_items=50)] = []
@classmethod
@ -655,6 +701,7 @@ class SettingsIn(BaseModel):
pushdeer_notification: Optional[PushdeerNotificationSettings] = None
pushplus_notification: Optional[PushplusNotificationSettings] = None
telegram_notification: Optional[TelegramNotificationSettings] = None
bark_notification: Optional[BarkNotificationSettings] = None
webhooks: Optional[List[WebHookSettings]] = None

View File

@ -12,6 +12,7 @@ from ..notification import (
Pushplus,
Serverchan,
Telegram,
Bark,
)
from ..webhook import WebHook
from .helpers import shadow_settings, update_settings
@ -345,6 +346,14 @@ class SettingsManager:
self._apply_notification_settings(notifier, settings)
self._apply_message_template_settings(notifier, settings)
def apply_bark_notification_settings(self) -> None:
notifier = self._app._bark_notifier
settings = self._settings.bark_notification
self._apply_bark_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]
self._app._webhook_emitter.webhooks = webhooks
@ -371,6 +380,10 @@ class SettingsManager:
telegram.token = self._settings.telegram_notification.token
telegram.chatid = self._settings.telegram_notification.chatid
def _apply_bark_settings(self, bark: Bark) -> None:
bark.server = self._settings.bark_notification.server
bark.pushkey = self._settings.bark_notification.pushkey
def _apply_notifier_settings(
self, notifier: Notifier, settings: NotifierSettings
) -> None:

View File

@ -12,6 +12,7 @@ ServerchanMessageType = MarkdownMessageType
PushdeerMessageType = Union[TextMessageType, MarkdownMessageType]
PushplusMessageType = Union[TextMessageType, MarkdownMessageType, HtmlMessageType]
TelegramMessageType = Union[MarkdownMessageType, HtmlMessageType]
BarkMessageType = Union[TextMessageType, MarkdownMessageType]
KeyOfSettings = Literal[
@ -30,6 +31,7 @@ KeyOfSettings = Literal[
'pushdeer_notification',
'pushplus_notification',
'telegram_notification',
'bark_notification',
'webhooks',
]

View File

@ -55,5 +55,6 @@ AliasKeyOfSettings = Literal[
'pushdeerNotification',
'pushplusNotification',
'telegramNotification',
'barkNotification',
'webhooks',
]

View File

@ -0,0 +1,20 @@
<app-sub-page pageTitle="Bark 通知">
<ng-template appSubPageContent>
<app-page-section>
<app-notifier-settings [settings]="notifierSettings" keyOfSettings="barkNotification"></app-notifier-settings>
</app-page-section>
<app-page-section name="Bark">
<app-bark-settings [settings]="barkSettings"></app-bark-settings>
</app-page-section>
<app-page-section name="事件">
<app-event-settings [settings]="notificationSettings" keyOfSettings="barkNotification"></app-event-settings>
</app-page-section>
<app-page-section name="消息">
<app-message-template-settings [settings]="messageTemplateSettings" keyOfSettings="barkNotification">
</app-message-template-settings>
</app-page-section>
</ng-template>
</app-sub-page>

View File

@ -0,0 +1,2 @@
@use '../../shared/styles/setting';

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BarkNotificationSettingsComponent } from './bark-notification-settings.component';
describe('BarkNotificationSettingsComponent', () => {
let component: BarkNotificationSettingsComponent;
let fixture: ComponentFixture<BarkNotificationSettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [BarkNotificationSettingsComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BarkNotificationSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,53 @@
import {
Component,
OnInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
} from '@angular/core';
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_BARK_SETTINGS,
MessageTemplateSettings,
NotificationSettings,
NotifierSettings,
BarkNotificationSettings,
BarkSettings,
} from '../../shared/setting.model';
@Component({
selector: 'app-bark-notification-settings',
templateUrl: './bark-notification-settings.component.html',
styleUrls: ['./bark-notification-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BarkNotificationSettingsComponent implements OnInit {
barkSettings!: BarkSettings;
notifierSettings!: NotifierSettings;
notificationSettings!: NotificationSettings;
messageTemplateSettings!: MessageTemplateSettings;
constructor(
private changeDetector: ChangeDetectorRef,
private route: ActivatedRoute
) { }
ngOnInit(): void {
this.route.data.subscribe((data) => {
const settings = data.settings as BarkNotificationSettings;
this.barkSettings = pick(settings, KEYS_OF_BARK_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();
});
}
}

View File

@ -0,0 +1,33 @@
<form nz-form [formGroup]="settingsForm">
<nz-form-item class="setting-item">
<nz-form-label class="setting-label align-required" nzFor="server" nzNoColon>server</nz-form-label>
<nz-form-control class="setting-control input" nzHasFeedback [nzErrorTip]="serverErrorTip"
[nzWarningTip]="syncFailedWarningTip" [nzValidateStatus]="
serverControl.valid && !syncStatus.server ? 'warning' : serverControl
">
<input id="server" type="url" placeholder="默认为官方服务器 https://api.day.app" nz-input formControlName="server" />
<ng-template #serverErrorTip let-control>
<ng-container *ngIf="control.hasError('pattern')">
server 无效
</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item class="setting-item">
<nz-form-label class="setting-label" nzFor="pushkey" nzNoColon nzRequired>pushkey</nz-form-label>
<nz-form-control class="setting-control input" nzHasFeedback [nzErrorTip]="pushkeyErrorTip"
[nzWarningTip]="syncFailedWarningTip" [nzValidateStatus]="
pushkeyControl.valid && !syncStatus.pushkey ? 'warning' : pushkeyControl
">
<input id="pushkey" type="text" placeholder="" required nz-input formControlName="pushkey" />
<ng-template #pushkeyErrorTip let-control>
<ng-container *ngIf="control.hasError('required')">
请输入 pushkey
</ng-container>
<ng-container *ngIf="control.hasError('pattern')">
pushkey 无效
</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
</form>

View File

@ -0,0 +1,6 @@
@use '../../../shared/styles/setting';
.setting-label {
max-width: 5em !important;
width: 5em !important;
}

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BarkSettingsComponent } from './bark-settings.component';
describe('BarkSettingsComponent', () => {
let component: BarkSettingsComponent;
let fixture: ComponentFixture<BarkSettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [BarkSettingsComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BarkSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,87 @@
import {
Component,
OnInit,
ChangeDetectionStrategy,
Input,
OnChanges,
ChangeDetectorRef,
} from '@angular/core';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from '@angular/forms';
import mapValues from 'lodash-es/mapValues';
import { BarkSettings } from '../../../shared/setting.model';
import { filterValueChanges } from '../../../shared/rx-operators';
import {
SettingsSyncService,
SyncStatus,
calcSyncStatus,
} from '../../../shared/services/settings-sync.service';
import { SYNC_FAILED_WARNING_TIP } from 'src/app/settings/shared/constants/form';
@Component({
selector: 'app-bark-settings',
templateUrl: './bark-settings.component.html',
styleUrls: ['./bark-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BarkSettingsComponent implements OnInit, OnChanges {
@Input() settings!: BarkSettings;
syncStatus!: SyncStatus<BarkSettings>;
readonly settingsForm: FormGroup;
readonly syncFailedWarningTip = SYNC_FAILED_WARNING_TIP;
constructor(
formBuilder: FormBuilder,
private changeDetector: ChangeDetectorRef,
private settingsSyncService: SettingsSyncService
) {
this.settingsForm = formBuilder.group({
server: ['', [Validators.pattern(/^https?:\/\/.+/)]],
pushkey: [
'',
[
Validators.required,
Validators.pattern(
/^[a-zA-Z\d]+$/
),
],
],
});
}
get serverControl() {
return this.settingsForm.get('server') as FormControl;
}
get pushkeyControl() {
return this.settingsForm.get('pushkey') as FormControl;
}
ngOnChanges(): void {
this.syncStatus = mapValues(this.settings, () => true);
console.log(this.settings);
this.settingsForm.setValue(this.settings);
}
ngOnInit(): void {
this.settingsSyncService
.syncSettings(
'barkNotification',
this.settings,
this.settingsForm.valueChanges.pipe(
filterValueChanges<Partial<BarkSettings>>(this.settingsForm)
)
)
.subscribe((detail) => {
this.syncStatus = { ...this.syncStatus, ...calcSyncStatus(detail) };
this.changeDetector.markForCheck();
});
}
}

View File

@ -1,20 +1,12 @@
<a class="setting-item" routerLink="email-notification"
><span class="setting-label">邮箱通知</span
><span class="setting-control"> <i nz-icon nzType="right"></i> </span
></a>
<a class="setting-item" routerLink="serverchan-notification"
><span class="setting-label">ServerChan 通知</span
><span class="setting-control"><i nz-icon nzType="right"></i></span
></a>
<a class="setting-item" routerLink="pushdeer-notification"
><span class="setting-label">PushDeer 通知</span
><span class="setting-control"><i nz-icon nzType="right"></i></span
></a>
<a class="setting-item" routerLink="pushplus-notification"
><span class="setting-label">pushplus 通知</span
><span class="setting-control"><i nz-icon nzType="right"></i></span
></a>
<a class="setting-item" routerLink="telegram-notification"
><span class="setting-label">telegram 通知</span
><span class="setting-control"><i nz-icon nzType="right"></i></span
></a>
<a class="setting-item" routerLink="email-notification"><span class="setting-label">邮箱通知</span><span
class="setting-control"> <i nz-icon nzType="right"></i> </span></a>
<a class="setting-item" routerLink="serverchan-notification"><span class="setting-label">ServerChan 通知</span><span
class="setting-control"><i nz-icon nzType="right"></i></span></a>
<a class="setting-item" routerLink="pushdeer-notification"><span class="setting-label">PushDeer 通知</span><span
class="setting-control"><i nz-icon nzType="right"></i></span></a>
<a class="setting-item" routerLink="pushplus-notification"><span class="setting-label">pushplus 通知</span><span
class="setting-control"><i nz-icon nzType="right"></i></span></a>
<a class="setting-item" routerLink="telegram-notification"><span class="setting-label">telegram 通知</span><span
class="setting-control"><i nz-icon nzType="right"></i></span></a>
<a class="setting-item" routerLink="bark-notification"><span class="setting-label">Bark 通知</span><span
class="setting-control"><i nz-icon nzType="right"></i></span></a>

View File

@ -66,6 +66,7 @@ export class PushdeerSettingsComponent implements OnInit, OnChanges {
ngOnChanges(): void {
this.syncStatus = mapValues(this.settings, () => true);
console.log(this.settings);
this.settingsForm.setValue(this.settings);
}

View File

@ -32,7 +32,8 @@ export class EventSettingsComponent implements OnInit, OnChanges {
| 'serverchanNotification'
| 'pushdeerNotification'
| 'pushplusNotification'
| 'telegramNotification';
| 'telegramNotification'
| 'barkNotification';
syncStatus!: SyncStatus<NotificationSettings>;

View File

@ -38,7 +38,8 @@ export class MessageTemplateSettingsComponent implements OnInit, OnChanges {
| 'serverchanNotification'
| 'pushdeerNotification'
| 'pushplusNotification'
| 'telegramNotification';
| 'telegramNotification'
| 'barkNotification';
messageTypes!: MessageType[];
beganMessageTemplateSettings!: CommonMessageTemplateSettings;
@ -50,7 +51,7 @@ export class MessageTemplateSettingsComponent implements OnInit, OnChanges {
private changeDetector: ChangeDetectorRef,
private message: NzMessageService,
private settingService: SettingService
) {}
) { }
ngOnInit(): void {
switch (this.keyOfSettings) {

View File

@ -32,7 +32,8 @@ export class NotifierSettingsComponent implements OnInit, OnChanges {
| 'serverchanNotification'
| 'pushdeerNotification'
| 'pushplusNotification'
| 'telegramNotification';
| 'telegramNotification'
| 'barkNotification';
syncStatus!: SyncStatus<NotifierSettings>;

View File

@ -7,6 +7,7 @@ import { PushplusNotificationSettingsResolver } from './shared/services/pushplus
import { TelegramNotificationSettingsResolver } from './shared/services/telegram-notification-settings.resolver';
import { ServerchanNotificationSettingsResolver } from './shared/services/serverchan-notification-settings.resolver';
import { PushdeerNotificationSettingsResolver } from './shared/services/pushdeer-notification-settings.resolver';
import { BarkNotificationSettingsResolver } from './shared/services/bark-notification-settings.resolver';
import { WebhookSettingsResolver } from './shared/services/webhook-settings.resolver';
import { SettingsComponent } from './settings.component';
import { EmailNotificationSettingsComponent } from './notification-settings/email-notification-settings/email-notification-settings.component';
@ -14,6 +15,7 @@ import { ServerchanNotificationSettingsComponent } from './notification-settings
import { PushdeerNotificationSettingsComponent } from './notification-settings/pushdeer-notification-settings/pushdeer-notification-settings.component';
import { PushplusNotificationSettingsComponent } from './notification-settings/pushplus-notification-settings/pushplus-notification-settings.component';
import { TelegramNotificationSettingsComponent } from './notification-settings/telegram-notification-settings/telegram-notification-settings.component';
import { BarkNotificationSettingsComponent } from './notification-settings/bark-notification-settings/bark-notification-settings.component';
import { WebhookManagerComponent } from './webhook-settings/webhook-manager/webhook-manager.component';
const routes: Routes = [
@ -52,6 +54,13 @@ const routes: Routes = [
settings: TelegramNotificationSettingsResolver,
},
},
{
path: 'bark-notification',
component: BarkNotificationSettingsComponent,
resolve: {
settings: BarkNotificationSettingsResolver,
},
},
{
path: 'webhooks',
component: WebhookManagerComponent,
@ -72,4 +81,4 @@ const routes: Routes = [
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class SettingsRoutingModule {}
export class SettingsRoutingModule { }

View File

@ -70,6 +70,9 @@ import { BiliApiSettingsComponent } from './bili-api-settings/bili-api-settings.
import { BaseApiUrlEditDialogComponent } from './bili-api-settings/base-api-url-edit-dialog/base-api-url-edit-dialog.component';
import { BaseLiveApiUrlEditDialogComponent } from './bili-api-settings/base-live-api-url-edit-dialog/base-live-api-url-edit-dialog.component';
import { BasePlayInfoApiUrlEditDialogComponent } from './bili-api-settings/base-play-info-api-url-edit-dialog/base-play-info-api-url-edit-dialog.component';
import { BarkNotificationSettingsComponent } from './notification-settings/bark-notification-settings/bark-notification-settings.component';
import { BarkSettingsComponent } from './notification-settings/bark-notification-settings/bark-settings/bark-settings.component';
import { BarkNotificationSettingsResolver } from './shared/services/bark-notification-settings.resolver';
@NgModule({
declarations: [
@ -98,6 +101,8 @@ import { BasePlayInfoApiUrlEditDialogComponent } from './bili-api-settings/base-
PushplusSettingsComponent,
TelegramNotificationSettingsComponent,
TelegramSettingsComponent,
BarkNotificationSettingsComponent,
BarkSettingsComponent,
NotifierSettingsComponent,
WebhookManagerComponent,
WebhookEditDialogComponent,
@ -147,7 +152,8 @@ import { BasePlayInfoApiUrlEditDialogComponent } from './bili-api-settings/base-
PushdeerNotificationSettingsResolver,
PushplusNotificationSettingsResolver,
TelegramNotificationSettingsResolver,
BarkNotificationSettingsResolver,
WebhookSettingsResolver,
],
})
export class SettingsModule {}
export class SettingsModule { }

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { BarkNotificationSettingsResolver } from './bark-notification-settings.resolver';
describe('TelegramNotificationSettingsResolverService', () => {
let service: BarkNotificationSettingsResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(BarkNotificationSettingsResolver);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,46 @@
import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import {
ActivatedRouteSnapshot,
Resolve,
RouterStateSnapshot,
} from '@angular/router';
import { Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { NGXLogger } from 'ngx-logger';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { retry } from '../../../shared/rx-operators';
import { BarkNotificationSettings } from '../setting.model';
import { SettingService } from './setting.service';
@Injectable()
export class BarkNotificationSettingsResolver
implements Resolve<BarkNotificationSettings>
{
constructor(
private logger: NGXLogger,
private notification: NzNotificationService,
private settingService: SettingService
) { }
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<BarkNotificationSettings> {
return this.settingService.getSettings(['barkNotification']).pipe(
map((settings) => settings.barkNotification),
retry(3, 300),
catchError((error: HttpErrorResponse) => {
this.logger.error(
'Failed to get bark notification settings:',
error
);
this.notification.error('获取 bark 通知设置出错', error.message, {
nzDuration: 0,
});
throw error;
})
);
}
}

View File

@ -169,7 +169,17 @@ export const KEYS_OF_TELEGRAM_SETTINGS = ['token', 'chatid'] as const;
export interface NotifierSettings {
enabled: boolean;
}
export interface BarkSettings {
server: string;
pushkey: string;
}
export const KEYS_OF_BARK_SETTINGS = ['server', 'pushkey'] as const;
export interface PushplusSettings {
token: string;
topic: string;
}
export const KEYS_OF_NOTIFIER_SETTINGS = ['enabled'] as const;
export interface NotificationSettings {
@ -202,6 +212,8 @@ export type PushplusMessageType =
| MarkdownMessageType
| HtmlMessageType;
export type TelegramMessageType = MarkdownMessageType | HtmlMessageType;
export type BarkMessageType = TextMessageType;
export interface MessageTemplateSettings {
beganMessageType: string;
@ -307,6 +319,20 @@ export interface TelegramMessageTemplateSettings {
errorMessageTitle: string;
errorMessageContent: string;
}
export interface BarkMessageTemplateSettings {
beganMessageType: BarkMessageType;
beganMessageTitle: string;
beganMessageContent: string;
endedMessageType: BarkMessageType;
endedMessageTitle: string;
endedMessageContent: string;
spaceMessageType: BarkMessageType;
spaceMessageTitle: string;
spaceMessageContent: string;
errorMessageType: BarkMessageType;
errorMessageTitle: string;
errorMessageContent: string;
}
export type EmailNotificationSettings = EmailSettings &
NotifierSettings &
@ -333,6 +359,11 @@ export type TelegramNotificationSettings = TelegramSettings &
NotificationSettings &
TelegramMessageTemplateSettings;
export type BarkNotificationSettings = BarkSettings &
NotifierSettings &
NotificationSettings &
BarkMessageTemplateSettings;
export interface WebhookEventSettings {
liveBegan: boolean;
liveEnded: boolean;
@ -371,6 +402,7 @@ export interface Settings {
pushdeerNotification: PushdeerNotificationSettings;
pushplusNotification: PushplusNotificationSettings;
telegramNotification: TelegramNotificationSettings;
barkNotification: BarkNotificationSettings;
webhooks: WebhookSettings[];
}