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 ( + <> + + 通过腾讯云自定义消息告警进行推送, + + 配置教程 + + 。 + + + + + + + + + + + ); case 'corp_app': return ( <>