diff --git a/README.md b/README.md
index 6cbcaa7..99a85bc 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ _✨ 搭建专属于你的消息推送服务,支持多种消息推送方式,
+ WebSocket 客户端([官方客户端](https://github.com/songquanpeng/personal-assistant),[接入文档](./docs/API.md#websocket-客户端)),
+ Telegram 机器人,
+ Discord 群机器人,
+ + 腾讯云自定义告警:免费的短信提醒,
+ 群组消息:可以将多个推送通道组合成一个群组,然后向群组发送消息,可以实现一次性推送到多个渠道的功能,
+ 自定义消息,可以自定义消息请求 URL 和请求体格式,实现与其他服务的对接,支持[众多第三方服务](https://iamazing.cn/page/message-pusher-common-custom-templates)。
2. 支持**自定义 Webhook,反向适配各种调用平台**,你可以接入各种已有的系统,而无需修改其代码。
@@ -211,7 +212,8 @@ proxy_send_timeout 300s;
12. `one_api`:通过 OneAPI 协议推送消息到 QQ。
13. `group`:通过预先配置的消息推送通道群组进行推送。
14. `custom`:通过预先配置好的自定义推送通道进行推送。
- 15. `none`:仅保存到数据库,不做推送。
+ 15. `tencent_alarm`:通过腾讯云监控告警进行推送,仅支持 `description` 字段。
+ 16. `none`:仅保存到数据库,不做推送。
5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。
@@ -224,19 +226,20 @@ proxy_send_timeout 300s;
**各种通道的支持程度:**
-| 通道类型 | `title` | `description` | `content` | `url` | `to` | Markdown 支持 |
-|:----------:|:-------:|:-------------:|:---------:|:-----:|:----:|:-----------:|
-| `email` | ✅ | ✅ | ✅ | ❌ | ✅️ | ✅️ |
-| `test` | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
-| `corp_app` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
-| `corp` | ❌ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
-| `lark` | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ |
-| `lark_app` | ❌ | ✅ | ✅ | ❌️ | ✅ | ✅ |
-| `ding` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
-| `bark` | ✅ | ✅ | ✅ | ✅️ | ❌ | ✅ |
-| `client` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
-| `telegram` | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
-| `discord` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
+| 通道类型 | `title` | `description` | `content` | `url` | `to` | Markdown 支持 |
+|:---------------:|:-------:|:-------------:|:---------:|:-----:|:----:|:-----------:|
+| `email` | ✅ | ✅ | ✅ | ❌ | ✅️ | ✅️ |
+| `test` | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
+| `corp_app` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
+| `corp` | ❌ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
+| `lark` | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ |
+| `lark_app` | ❌ | ✅ | ✅ | ❌️ | ✅ | ✅ |
+| `ding` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
+| `bark` | ✅ | ✅ | ✅ | ✅️ | ❌ | ✅ |
+| `client` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
+| `telegram` | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
+| `discord` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
+| `tencent_alarm` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
注意:
1. 对于大部分通道,`description` 字段和 `content` 是不能同时存在的,如果你只需要文字消息,请使用 `description` 字段,如果你需要发送 Markdown 消息,请使用 `content` 字段。
diff --git a/channel/main.go b/channel/main.go
index ca65415..4dd0962 100644
--- a/channel/main.go
+++ b/channel/main.go
@@ -37,6 +37,8 @@ func SendMessage(message *model.Message, user *model.User, channel_ *model.Chann
return SendLarkAppMessage(message, user, channel_)
case model.TypeCustom:
return SendCustomMessage(message, user, channel_)
+ case model.TypeTencentAlarm:
+ return SendTencentAlarmMessage(message, user, channel_)
default:
return errors.New("不支持的消息通道:" + channel_.Type)
}
diff --git a/channel/tencent-alarm.go b/channel/tencent-alarm.go
new file mode 100644
index 0000000..beaadf2
--- /dev/null
+++ b/channel/tencent-alarm.go
@@ -0,0 +1,89 @@
+package channel
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "math/rand"
+ "message-pusher/model"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type TencentAlarmResponse struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ CodeDesc string `json:"codeDesc"`
+}
+
+func SendTencentAlarmMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
+ secretId := channel_.AppId
+ secretKey := channel_.Secret
+ policyId := channel_.AccountId
+ region := channel_.Other
+ if message.Description == "" {
+ message.Description = message.Content
+ }
+ params := map[string]string{
+ "Action": "SendCustomAlarmMsg",
+ "Region": region,
+ "Timestamp": strconv.FormatInt(time.Now().Unix(), 10),
+ "Nonce": strconv.Itoa(rand.Intn(65535)),
+ "SecretId": secretId,
+ "policyId": policyId,
+ "msg": message.Description,
+ }
+
+ keys := make([]string, 0, len(params))
+ for key := range params {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ srcStr := "GETmonitor.api.qcloud.com/v2/index.php?"
+ for _, key := range keys {
+ srcStr += key + "=" + params[key] + "&"
+ }
+ srcStr = srcStr[:len(srcStr)-1]
+
+ h := hmac.New(sha1.New, []byte(secretKey))
+ h.Write([]byte(srcStr))
+ signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
+
+ params["Signature"] = signature
+
+ urlStr := "https://monitor.api.qcloud.com/v2/index.php?" + urlEncode(params)
+
+ client := &http.Client{}
+ req, err := http.NewRequest("GET", urlStr, nil)
+ if err != nil {
+ return err
+ }
+ resp, err := client.Do(req)
+ if err != nil {
+ return err
+ }
+ var response TencentAlarmResponse
+ err = json.NewDecoder(resp.Body).Decode(&response)
+ if err != nil {
+ return err
+ }
+ if response.Code != 0 {
+ return errors.New(response.Message)
+ }
+ return nil
+}
+
+func urlEncode(params map[string]string) string {
+ var encodedParams []string
+ for key, value := range params {
+ encodedParams = append(encodedParams, url.QueryEscape(key)+"="+url.QueryEscape(value))
+ }
+ return strings.Join(encodedParams, "&")
+}
diff --git a/model/channel.go b/model/channel.go
index f0e6fd5..9df706a 100644
--- a/model/channel.go
+++ b/model/channel.go
@@ -21,6 +21,7 @@ const (
TypeGroup = "group"
TypeLarkApp = "lark_app"
TypeCustom = "custom"
+ TypeTencentAlarm = "tencent_alarm"
)
type Channel struct {
diff --git a/web/src/constants/channel.constants.js b/web/src/constants/channel.constants.js
index 316a776..9b35f85 100644
--- a/web/src/constants/channel.constants.js
+++ b/web/src/constants/channel.constants.js
@@ -53,6 +53,12 @@ export const CHANNEL_OPTIONS = [
value: 'group',
color: '#FF9800',
},
+ {
+ key: 'tencent_alarm',
+ text: '腾讯云消息告警',
+ value: 'tencent_alarm',
+ color: '#00a4ff',
+ },
{
key: 'none',
text: '不推送',
diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js
index fecfe2b..524d190 100644
--- a/web/src/pages/Channel/EditChannel.js
+++ b/web/src/pages/Channel/EditChannel.js
@@ -209,6 +209,58 @@ const EditChannel = () => {
>
);
+ case 'tencent_alarm':
+ return (
+ <>
+