From da23eee4ec63b8d1316939e7b866fa0fee838461 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Mon, 9 Feb 2026 03:42:51 +0000 Subject: [PATCH] feat: add Bark iOS push support with HTTP requests --- backend/__pycache__/main.cpython-310.pyc | Bin 1471 -> 1543 bytes .../notify_service.cpython-310.pyc | Bin 3962 -> 5020 bytes backend/main.py | 14 ++-- backend/notify_service.py | 73 +++++++++++++++--- 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/backend/__pycache__/main.cpython-310.pyc b/backend/__pycache__/main.cpython-310.pyc index 57491678d735d0f8b2664f2cb355826a917341c5..512d33ec9f39f2c4040e53cbd781f8ba8cf372c4 100644 GIT binary patch delta 514 zcmYjN!EVz)5Z&2bH%?+Fsgt-#AwZ~vss@lax2oWR`VA|D6*QA#*LINYO7zr9E$W3F zkZ+vo58%WH^doxnwZ|U%2Z)WSQ08ghzMav|o89Z?r8Sw`c7y4-JHPaOgR$G?VKtW3 zw|b=?e@ymIjj*ojR1X?gO=M*1-CALrS_M?#z>wiu*p51?Q|Vf`9y!X1x~d!XR1X*d znj~f%>)5!E=b-u|u!$Dh)W_CBDwo_~gKF451ngAYKGkx4Vd%^5)qtE}SipnsuIS!> zy@^f%*u~xj(*}>KQ6E1HtVhK@x`j}qD)WR!1tdSU=~LVwgGSnPu;A$8a3QqIGu%9C z^W>L)ml2NDn8v~L-v=-OXyqBd$x{(HJIjxfn)sQ?lc5&*d+{>A7w=#zeS5XX128@o-DwD!wbwR#kC@h*sh_z*#eg_&y7CY3abc(I`1sRs#ow!VTF zZ$5!1Z#j7J=FtZbH`Rl)?C_gq=RY5}=1avLS(d?+Jx^|fcfr_45&XB33U<{UZ{4{& zR|~^(WOxQJWLO$jBGWS?%d;Zevw;zyB;MDsjK+<)2CqsSD`=ucRkWwVt5Gdnq!Lz- z0c&$#(Bf2AxwVHnnPFoFe{B6@`;X;O*nDV_RK871Gr&4FX54e;FU#c2Ah}c}I@lzg z995z<)v$r>DW8k3V3FhC@(okXt0oezTXdq~T({Zjxqq8{8`7x!xq7!}F-dE}-God&VvR8ex?r;+vkeUIKOK5}m{sA!M BYBc}= diff --git a/backend/__pycache__/notify_service.cpython-310.pyc b/backend/__pycache__/notify_service.cpython-310.pyc index 52cb2fbf8ead9728b696596abe18936ddfc37f7b..ec6ecf7776ea3a8c402ea44ce93c9be4db0e6d0f 100644 GIT binary patch delta 1954 zcmY*a&2Jk;6yKR$uh*OPN1WJklQeZcn#OJtr}Qi72cjPVD$zEjs#*}pY&=7}j=Oee z*G+2eVkA_7Dn%+}mnu8Stp1fi24Jk&%l6*>zFK zWxXNk=jXSSvTfI`BZb0Nm@^=(2X^(PWu2}1%Cc_JVuMwEmC)-(i56Gs4PPl1xwTmI zQlV@##(20a$zj?%8QLf3IL&*t(B7qUKvk?_4M|8w(v<3JNJ9!rAZbi}$ocjck0B|e z{<`hgr~b~_Ad+J0BMqrycb0^`hoQIP8NMhy-@s#~Men@aZrw)Bs3TWoqOIcFh($@1 zplZZbgHd&Rz}C~&NQ}fgxE&>l4ubr2wO+2dkfC;rBs+*Xz~UrH`gjh_NTBz1kad&v zIof~#^Li?YO0S)Cr1ubsll~3twqlOR?)+;IpBhYXaN|y#ed1_$1d?!~0mcK2-4Q+x zy9rbMpN*>_H(>~FuM;9EUR08Hdda{s!BjxWfbt#L(N&t0WRMJz;TG9>S9Ft(TG5;Y zm`0o=*|j0LeNJDck7QS|+i&-iQT_&7kHMHCh>YLFI|SGc&Vhu>f;leMPjGAd1X-w3%Iky=_d>)lEr^^)oSX4B!;5IY zS5S`S_rpbE^Bfic{BWI`#4uNK5<9@fgB%WVnD;(X4$573?}3tPaep?&0dWX!ewwpE z4tWlf9QFX@kS|&K+tioJ#!A_j^^$E|r+%nPuhXh8H`od_ZC@!d!!}BKwRz}lt!Y%N zdg0*gTz1M>D%VVUG<$04Qnt&p=P$Oh`wkT6vQ=Z1X3y#+P_kEY%~8Exufh_xQ8Nn_ zt7cATZMtq3>Q&tUo-gE^1I_ZxsdLlK^3iL@=VlkCsTq*@oY+(^S(MG32!fGMluER2 zH{;!L<`vysY3M6-lmGm3qTiD1I@7OOe%Lmy(ptmz!)2-y%B;J34J2Vs8z3- z7Cn+};_2)r0n3X`Eih5WYK-god{fHjRW*l#bVH^f8IZ9sAB(yTwFo-Qf2BGG%Xp)a z5wz2LA+jLP!7H2bT9NUVuP}P8K`q@mKGfFdF7PPc~ZU1=$Sz6_4mBX{CT8J*874KGb0)6j&7JVJ< z^VHZv?h1DcN*)I0i%=6LaV^AwFU>HAb*~y5&7TIU2ntGz_^`AE@jt;Lek@D)ZydsZ TVHt0!Vf9a;h11?wvHSl3xM}lH delta 876 zcmY+CPe>F|9LL}9&D+`8w==VUl<*&=Y1*#onxSHtMP;Q11_qKB6KAtsYn7Q1ZJABF z6jVa+gAg=W>EC&OdvU+bbu^Hy`zVrLN-}n6<@2xa#?Kh_l zgA?(*oV}N6s$4e%6o#;zcH%{9nYu{G>>2Snqxg!pLN$J-Cj^_7Il-q&z2IA=OYpZ+ zp<30$BvFHdY_lS-U3c9X=XffW4@xD39Tq+~6{t0I+Z57{OEltA;3#ecn$DdSznpTa z0|OOUE#K5Z2MwaYs`w|VcV2oBSd@R+nHT2Yq^bg@@@*Y(K)o7~_OkQW;&UDYzEFz` z`5`w{JzflZ+A09?SRw2wcG}WCWtmup$Ck+x8RJ>q3tQ$Q6m(Cw@46%#^uP;d zH7nv8MRS!{(M8rzEVw{~i<(tDYDG%<*a{mV!^DagLM|!(tD+vRgbtJs6Gq&~t#?2Q z29SKM8}$r)5bBBpc8OyGesCt8vog8K1hZRZ zFeT9@(T)qe+qcK6BDJHvxW7j$`Al zFfN_Uu94k|{dyh5a7yo1lX9gdT+{3El+n=Qhx+Cx(lc@eUvXSJVg5baBXU~FVNa8I z$7raLo3IT5KX^0kIOCIPU$xWD>EL@ diff --git a/backend/main.py b/backend/main.py index 048f88a..fd9ddec 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3,6 +3,10 @@ from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware import os +import sys + +# 添加项目根目录到路径 +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from backend.database import init_db from backend.routers import channels, notify, history @@ -35,15 +39,15 @@ app.include_router(channels.router) app.include_router(notify.router) app.include_router(history.router) -# 静态文件(前端) -frontend_path = os.path.join(os.path.dirname(__file__), "..", "frontend") -if os.path.exists(frontend_path): - app.mount("/", StaticFiles(directory=frontend_path, html=True), name="frontend") - @app.get("/api/health") async def health_check(): return {"status": "ok"} +# 静态文件(前端)- 放在最后 +frontend_path = os.path.join(os.path.dirname(__file__), "..", "frontend") +if os.path.exists(frontend_path): + app.mount("/", StaticFiles(directory=frontend_path, html=True), name="frontend") + if __name__ == "__main__": import uvicorn uvicorn.run("backend.main:app", host="0.0.0.0", port=8000, reload=True) diff --git a/backend/notify_service.py b/backend/notify_service.py index e096ca7..1331513 100644 --- a/backend/notify_service.py +++ b/backend/notify_service.py @@ -48,6 +48,14 @@ class NotifyService: elif channel.type == "apprise": return config.get("url", "") + # Bark iOS 推送 - 在 send_notification 中直接处理 + elif channel.type == "bark": + base_url = config.get("base_url", "") + device_key = config.get("device_key", "") + if base_url and device_key: + # 返回特殊标记,实际发送在 send_notification 中处理 + return "__bark__" + return None async def send_notification( @@ -101,17 +109,60 @@ class NotifyService: } try: - # 发送通知 - apobj = apprise.Apprise() - apobj.add(apprise_url) - - # 构建消息 - message = body - if title: - message = f"**{title}**\n\n{body}" - - # 发送 - result = apobj.notify(body=message) + # Bark 特殊处理 - 使用 requests + if channel.type == "bark": + import requests + import urllib.parse + + base_url = channel.config.get("base_url", "").rstrip("/") + device_key = channel.config.get("device_key", "") + + # 强制使用 HTTP(因为你的 Bark 服务器 SSL 有问题) + if base_url.startswith("https://"): + base_url = base_url.replace("https://", "http://") + elif not base_url.startswith("http://"): + base_url = "http://" + base_url + + # 构建 URL + encoded_body = urllib.parse.quote(str(body), safe='') + encoded_title = urllib.parse.quote(str(title), safe='') if title else "" + + if encoded_title: + bark_url = f"{base_url}/{device_key}/{encoded_title}/{encoded_body}" + else: + bark_url = f"{base_url}/{device_key}/{encoded_body}" + + # 添加参数 + params = {} + if priority == "high": + params["level"] = "active" + elif priority == "urgent": + params["level"] = "critical" + + try: + headers = { + 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + } + response = requests.get(bark_url, params=params, timeout=10, headers=headers) + result = response.status_code == 200 + print(f"Bark response: {response.status_code}, {response.text[:100]}") + except Exception as e: + result = False + print(f"Bark error: {e}") + else: + # 使用 apprise 发送通知 + apobj = apprise.Apprise() + apobj.add(apprise_url) + + # 构建消息 + message = body + if title: + message = f"**{title}**\n\n{body}" + + # 发送 + result = apobj.notify(body=message) if result: await crud.update_notification_status(