Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for feishu/lark webhook #23

Merged
merged 3 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ channel_9_TYPE=telegram
channel_9_TELEGRAM_TOKEN=<YOUR_TELEGRAM_TOKEN>
channel_9_TELEGRAM_CHAT_ID=<YOUR_TELEGRAM_CHAT_ID>

# channel_10(Ntfy)
# channel_10(ntfy)
channel_10_TYPE=ntfy
channel_10_NTFY_HOST=<YOUR_NTFY_HOST>
channel_10_NTFY_TOPIC=<YOUR_NTFY_TOPIC>
channel_10_NTFY_TOPIC=<YOUR_NTFY_TOPIC>

# channel_11(lark)
channel_11_TYPE=lark_webhook
channel_11_LARK_TOKEN=<YOUR_LARK_TOKEN>
20 changes: 0 additions & 20 deletions .github/workflows/sonarqube-scan.yml

This file was deleted.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ Heimdallr 是一个非常轻量的通知网关,可以聚合各种推送渠道
- [Discord(webhook)](https://discord.com/developers/docs/resources/webhook#execute-webhook)
- [Telegram Bot](https://core.telegram.org/bots/api#sendmessage)
- [ntfy](https://docs.ntfy.sh/)
- [飞书/Lark](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot)


### 可能会支持的推送方式
- [ ] 钉钉
- [ ] 飞书/Lark
- [ ] ...

> 如果有需要的通知方式,请提交 [issue](https://github.com/LeslieLeung/heimdallr/issues/new?assignees=LeslieLeung&labels=enhancement&template=feature_request.md&title=)
Expand Down
2 changes: 2 additions & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# 更新日志
## v2.0.2
- 通知渠道新增 飞书/Lark Webhook
## v2.0.1
- 通知渠道新增 ntfy
- 新增 `/push/form` 接口,支持 form-data
Expand Down
79 changes: 41 additions & 38 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,47 +62,50 @@ channel_3_WECOM_WEBHOOK_KEY=<YOUR_WECOM_WEBHOOK_KEY>

#### 通知渠道类型

| 渠道名称 | 渠道类型(上面的`<CHANNEL_NAME>_TYPE` |
| ---------------- | ------------------------------------- |
| Bark | bark |
| 企业微信 Webhook | wecom_webhook |
| 企业微信应用 | wecom_app |
| Pushover | pushover |
| PushDeer | pushdeer |
| Chanify | chanify |
| SMTP(邮件) | email |
| Discord | discord_webhook |
| Telegram | telegram |
| ntfy | ntfy |
| 渠道名称 | 渠道类型(上面的`<CHANNEL_NAME>_TYPE` |
| ----------------- | ------------------------------------- |
| Bark | bark |
| 企业微信 Webhook | wecom_webhook |
| 企业微信应用 | wecom_app |
| Pushover | pushover |
| PushDeer | pushdeer |
| Chanify | chanify |
| SMTP(邮件) | email |
| Discord | discord_webhook |
| Telegram | telegram |
| ntfy | ntfy |
| 飞书/Lark Webhook | lark_webhook |


#### 渠道配置后缀
| 后缀名 | 通知渠道 | 后缀说明 |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `BARK_URL` | Bark | Bark服务器地址,如`https://api.day.app` |
| `BARK_KEY` | Bark | Bark的推送 key,如 `qy7s8qnhjhphuNDHJNFxQE` |
| `WECOM_KEY` | 企业微信 | 企业微信机器人的 key,见 [企业微信机器人webhook](https://developer.work.weixin.qq.com/document/path/91770) |
| `WECOM_CORP_ID` | 企业微信 | 企业微信应用的 corp_id,见 [企业微信应用消息](https://developer.work.weixin.qq.com/document/path/90236) |
| `WECOM_AGENT_ID` | 企业微信 | 企业微信应用的 agent_id |
| `WECOM_SECRET` | 企业微信 | 企业微信应用的 secret |
| `PUSHOVER_TOKEN` | Pushover | Pushover 的 token,见 [Pushover API](https://pushover.net/api) |
| `PUSHOVER_USER` | Pushover | Pushover 的 user |
| `PUSHDEER_TOKEN` | PushDeer | PushDeer 的 token,见 [Pushdeer API](http://pushdeer.com) |
| `CHANIFY_ENDPOINT` | Chanify | Chanify 的 endpoint,见 [Chanify](https://github.com/chanify/chanify#as-sender-client),可不填,默认为 `https://api.chanify.net` |
| `CHANIFY_TOKEN` | Chanify | Chanify 的 token |
| `EMAIL_HOST` | Email | Email 服务器地址,如 `smtp.gmail.com` |
| `EMAIL_PORT` | Email | Email 服务器端口,如 `465` |
| `EMAIL_USER` | Email | Email 用户名 |
| `EMAIL_PASSWORD` | Email | Email 密码 |
| `EMAIL_SENDER` | Email | Email 发件人名称 |
| `EMAIL_TO` | Email | Email 收件人 |
| `EMAIL_STARTTLS` | Email | Email 是否使用 TLS |
| `DISCORD_WEBHOOK_ID` | Discord | Discord 的 Webhook ID,见 [Discord 文档](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) |
| `DISCORD_WEBHOOK_TOKEN` | Discord | Discord 的 Webhook Token |
| `TELEGRAM_TOKEN` | Telegram | Telegram 的 Token,见 [这里](https://github.com/pppscn/SmsForwarder/wiki/2.%E5%8F%91%E9%80%81%E9%80%9A%E9%81%93#tele%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91) |
| `TELEGRAM_CHAT_ID` | Telegram | Telegram 的 Chat ID,见 [这里](https://github.com/pppscn/SmsForwarder/issues/319) |
| `NTFY_HOST` | ntfy | ntfy 的服务端地址 |
| `NTFY_TOPIC` | ntfy | ntfy 的 topic,见 [这里](https://docs.ntfy.sh/) |
| 后缀名 | 通知渠道 | 后缀说明 |
| ----------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `BARK_URL` | Bark | Bark服务器地址,如`https://api.day.app` |
| `BARK_KEY` | Bark | Bark的推送 key,如 `qy7s8qnhjhphuNDHJNFxQE` |
| `WECOM_KEY` | 企业微信 | 企业微信机器人的 key,见 [企业微信机器人webhook](https://developer.work.weixin.qq.com/document/path/91770) |
| `WECOM_CORP_ID` | 企业微信 | 企业微信应用的 corp_id,见 [企业微信应用消息](https://developer.work.weixin.qq.com/document/path/90236) |
| `WECOM_AGENT_ID` | 企业微信 | 企业微信应用的 agent_id |
| `WECOM_SECRET` | 企业微信 | 企业微信应用的 secret |
| `PUSHOVER_TOKEN` | Pushover | Pushover 的 token,见 [Pushover API](https://pushover.net/api) |
| `PUSHOVER_USER` | Pushover | Pushover 的 user |
| `PUSHDEER_TOKEN` | PushDeer | PushDeer 的 token,见 [Pushdeer API](http://pushdeer.com) |
| `CHANIFY_ENDPOINT` | Chanify | Chanify 的 endpoint,见 [Chanify](https://github.com/chanify/chanify#as-sender-client),可不填,默认为 `https://api.chanify.net` |
| `CHANIFY_TOKEN` | Chanify | Chanify 的 token |
| `EMAIL_HOST` | Email | Email 服务器地址,如 `smtp.gmail.com` |
| `EMAIL_PORT` | Email | Email 服务器端口,如 `465` |
| `EMAIL_USER` | Email | Email 用户名 |
| `EMAIL_PASSWORD` | Email | Email 密码 |
| `EMAIL_SENDER` | Email | Email 发件人名称 |
| `EMAIL_TO` | Email | Email 收件人 |
| `EMAIL_STARTTLS` | Email | Email 是否使用 TLS |
| `DISCORD_WEBHOOK_ID` | Discord | Discord 的 Webhook ID,见 [Discord 文档](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) |
| `DISCORD_WEBHOOK_TOKEN` | Discord | Discord 的 Webhook Token |
| `TELEGRAM_TOKEN` | Telegram | Telegram 的 Token,见 [这里](https://github.com/pppscn/SmsForwarder/wiki/2.%E5%8F%91%E9%80%81%E9%80%9A%E9%81%93#tele%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91) |
| `TELEGRAM_CHAT_ID` | Telegram | Telegram 的 Chat ID,见 [这里](https://github.com/pppscn/SmsForwarder/issues/319) |
| `NTFY_HOST` | ntfy | ntfy 的服务端地址 |
| `NTFY_TOPIC` | ntfy | ntfy 的 topic,见 [这里](https://docs.ntfy.sh/) |
| `LARK_HOST` | 飞书/Lark | 飞书/Lark 的接口地址,默认可以留空。如果使用 Lark, 则为 https://open.larksuite.com/open-apis/bot/v2/hook/ |
| `LARK_TOKEN` | 飞书/Lark | 飞书/Lark 的 Token |


## 腾讯云 Serverless 环境变量设置
Expand Down
22 changes: 0 additions & 22 deletions heimdallr/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,6 @@
logger = logging.getLogger(__name__)


def serve(key: str, title: str = "", body: str = "", **kwargs):
try:
group = config.get_group(key)
except AuthException as e:
return Response(code=-1, message=str(e)).render()
logger.info(f"group: {group.name}, token: {group.token}")
errors = {}
for chan in group.channels:
logger.info(f"channel: {chan.get_name()}, channel_type: {chan.get_type()}")
message = build_message(chan.get_name(), title, body, **kwargs)
rs, msg = chan.send(message)
if not rs:
errors[chan.get_name()] = msg

if len(errors) == 0:
return success()
err_msg = ""
for err in errors.items():
err_msg += f"{err[0]} return: {err[1]}."
return Response(code=1, message=err_msg).render()


async def serve_channels_async(key: str, title: str = "", body: str = "", **kwargs):
try:
group = config.get_group(key)
Expand Down
6 changes: 6 additions & 0 deletions heimdallr/channel/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from heimdallr.channel.chanify import Chanify, ChanifyMessage
from heimdallr.channel.discord import DiscordWebhook, DiscordWebhookMessage
from heimdallr.channel.email import Email, EmailMessage
from heimdallr.channel.lark import LarkWebhook, LarkWebhookMessage
from heimdallr.channel.ntfy import Ntfy, NtfyMessage
from heimdallr.channel.pushdeer import PushDeer, PushDeerMessage
from heimdallr.channel.pushover import Pushover, PushoverMessage
Expand Down Expand Up @@ -32,6 +33,7 @@
CHANNEL_DISCORD_WEBHOOK = "discord_webhook"
CHANNEL_TELEGRAM = "telegram"
CHANNEL_NTFY = "ntfy"
CHANNEL_LARK_WEBHOOK = "lark_webhook"


def _get_channel_type_by_name(name: str) -> str:
Expand Down Expand Up @@ -69,6 +71,8 @@ def build_channel(name: str) -> Channel:
return Telegram(name, channel_type)
elif channel_type == CHANNEL_NTFY:
return Ntfy(name, channel_type)
elif channel_type == CHANNEL_LARK_WEBHOOK:
return LarkWebhook(name, channel_type)
else:
raise ParamException(f"Channel {name} type {channel_type} not supported.")

Expand Down Expand Up @@ -99,5 +103,7 @@ def build_message(name: str, title: str, body: str, **kwargs) -> Message:
return TelegramMessage(title, body, **kwargs)
elif channel_type == CHANNEL_NTFY:
return NtfyMessage(title, body, **kwargs)
elif channel_type == CHANNEL_LARK_WEBHOOK:
return LarkWebhookMessage(title, body, **kwargs)
else:
raise ParamException(f"Channel type {channel_type} not supported.")
49 changes: 49 additions & 0 deletions heimdallr/channel/lark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import logging
from typing import Any, Tuple

import requests

from heimdallr.channel.base import Channel, Message
from heimdallr.config.config import get_config_str
from heimdallr.config.definition import SUFFIX_LARK_HOST, SUFFIX_LARK_TOKEN
from heimdallr.exception import ParamException

logger = logging.getLogger(__name__)


class LarkWebhookMessage(Message):
def __init__(self, title: str, body: str, **kwargs) -> None:
super().__init__(title, body)

def render_message(self) -> Any:
return {"msg_type": "text", "content": {"text": f"{self.title}\n{self.body}"}}


class LarkWebhook(Channel):
def __init__(self, name: str, type: str) -> None:
super().__init__(name, type)
self.base_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/"
self.token: str = ""
self._build_channel()

def _build_channel(self) -> None:
self.base_url = get_config_str(self.get_name(), SUFFIX_LARK_HOST, self.base_url)
self.token = get_config_str(self.get_name(), SUFFIX_LARK_TOKEN, "")
if self.token == "":
raise ParamException("LarkWebhook key not set")

def send(self, message: Message) -> Tuple[bool, str]:
if not isinstance(message, LarkWebhookMessage):
raise ParamException("Invalid message type")

url = f"{self.base_url}{self.token}"
rs = requests.post(
url,
json=message.render_message(),
headers={"Content-Type": "application/json"},
).json()
logger.debug(f"LarkWebhook response: {rs}")
if rs["code"] != 0:
logger.error(f"LarkWebhook error: {rs['msg']}")
return False, rs["msg"]
return True, rs["msg"]
Loading