Skip to content

Commit

Permalink
feat: PushDeer competable API (#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
LeslieLeung authored Sep 14, 2024
1 parent 47f97a1 commit 3a647a1
Show file tree
Hide file tree
Showing 5 changed files with 496 additions and 868 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Heimdallr 是一个非常轻量的通知网关,可以聚合各种推送渠道
- 支持各种常见的推送渠道,如Bark、企业微信等
- 支持 [Apprise](https://github.com/caronc/apprise),从此再也不缺通知渠道(完全列表见[链接](https://github.com/caronc/apprise#supported-notifications)
- 完全兼容 Bark 的路由,任意支持 Bark 的地方,都可以使用 Heimdallr 同时发送到更多渠道
- 提供兼容 [message-pusher](https://github.com/songquanpeng/message-pusher) 的路由。
- 提供兼容 [message-pusher](https://github.com/songquanpeng/message-pusher)[PushDeer](http://pushdeer.com) 的路由。
- 支持发送图片(仅 Apprise)
- 支持多通知渠道和分组配置
- 支持 Serverless 部署,几乎零成本运行
Expand Down Expand Up @@ -82,6 +82,10 @@ Heimdallr 提供与 Bark 完全兼容的接口以及与 message-pusher 兼容的

message-pusher 的接口形如 `https://<domain>/push/<username>`,替换成 `https://<domain>/competable/message-pusher/push` 即可,token 填 Heimdallr 分组的 token。

### PushDeer 兼容接口

PushDeer 的接口形如 `https://<domain>/message/push`,替换成 `https://<domain>/competable/pushdeer/message/push` 即可,pushkey 填 PushDeer 的 token。

## Markdown 支持

在一些支持 Markdown 的服务上,格式化的文本可以以 Markdown 格式呈现。在请求时,通过 query 参数或 json 传入 `msg_type = markdown` 即可。
Expand Down
10 changes: 7 additions & 3 deletions heimdallr/api/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import asyncio
import inspect
import logging
from typing import Dict
from typing import Any, Dict

from heimdallr.channel.base import Channel
from heimdallr.channel.factory import build_message
from heimdallr.config.config import is_debug
from heimdallr.exception import AuthException
Expand All @@ -12,7 +13,8 @@
logger = logging.getLogger(__name__)


async def serve_channels_async(key: str, title: str = "", body: str = "", **kwargs):
async def serve_channels_async(key: str, title: str = "", body: str = "", **kwargs) -> Dict[str, Any]:
raise_exception_on_error = kwargs.pop("raise_exception_on_error", False)
# log the caller of this function
if is_debug():
logger.debug(f"Serving caller: {inspect.stack()[1].function}")
Expand All @@ -31,10 +33,12 @@ async def serve_channels_async(key: str, title: str = "", body: str = "", **kwar
err_msg = ""
for err in errors.items():
err_msg += f"{err[0]} return: {err[1]}."
if raise_exception_on_error:
raise RuntimeError(err_msg)
return Response(code=1, message=err_msg).render()


async def send_to_channel(chan, title, body, errors, kwargs):
async def send_to_channel(chan: Channel, title: str, body: str, errors: Dict[str, str], kwargs: Dict[str, Any]) -> None:
logger.info(f"channel: {chan.get_name()}, channel_type: {chan.get_type()}")
message = build_message(chan.get_name(), title, body, **kwargs)
rs, err = chan.send(message)
Expand Down
38 changes: 37 additions & 1 deletion heimdallr/api/competable.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
from fastapi import APIRouter, Form, Query
from fastapi import APIRouter, Form, Query, Request
from fastapi.responses import JSONResponse

from heimdallr.api.base import serve_channels_async

competable_router = APIRouter(prefix="/competable")


@competable_router.post("/pushdeer/message/push")
async def pushdeer_message_push(request: Request):
content_type = request.headers.get("Content-Type", "")

if "application/json" in content_type:
data = await request.json()
text = data.get("text")
desp = data.get("desp")
pushkey = data.get("pushkey")
elif "application/x-www-form-urlencoded" in content_type:
form_data = await request.form()
text = form_data.get("text")
desp = form_data.get("desp")
pushkey = form_data.get("pushkey")
else:
return JSONResponse(status_code=415, content={"error": "Unsupported Media Type"})

if not all([text, desp, pushkey]):
return JSONResponse(status_code=400, content={"error": "Missing required fields"})

try:
await serve_channels_async(str(pushkey), str(text), str(desp))
except Exception as e:
return JSONResponse(status_code=500, content={"error": str(e)})

return {
"code": 0,
"content": {
"result": [
'{"counts":1,"logs":[],"success":"ok"}',
]
},
}


@competable_router.get("/message-pusher/push")
async def message_pusher_get(
title: str = Query(...),
Expand Down
Loading

0 comments on commit 3a647a1

Please sign in to comment.