feat: support tencent custom alarm now (close #87)
This commit is contained in:
@@ -58,6 +58,7 @@ _✨ 搭建专属于你的消息推送服务,支持多种消息推送方式,
|
|||||||
+ WebSocket 客户端([官方客户端](https://github.com/songquanpeng/personal-assistant),[接入文档](./docs/API.md#websocket-客户端)),
|
+ WebSocket 客户端([官方客户端](https://github.com/songquanpeng/personal-assistant),[接入文档](./docs/API.md#websocket-客户端)),
|
||||||
+ Telegram 机器人,
|
+ Telegram 机器人,
|
||||||
+ Discord 群机器人,
|
+ Discord 群机器人,
|
||||||
|
+ 腾讯云自定义告警:免费的短信提醒,
|
||||||
+ 群组消息:可以将多个推送通道组合成一个群组,然后向群组发送消息,可以实现一次性推送到多个渠道的功能,
|
+ 群组消息:可以将多个推送通道组合成一个群组,然后向群组发送消息,可以实现一次性推送到多个渠道的功能,
|
||||||
+ 自定义消息,可以自定义消息请求 URL 和请求体格式,实现与其他服务的对接,支持[众多第三方服务](https://iamazing.cn/page/message-pusher-common-custom-templates)。
|
+ 自定义消息,可以自定义消息请求 URL 和请求体格式,实现与其他服务的对接,支持[众多第三方服务](https://iamazing.cn/page/message-pusher-common-custom-templates)。
|
||||||
2. 支持**自定义 Webhook,反向适配各种调用平台**,你可以接入各种已有的系统,而无需修改其代码。
|
2. 支持**自定义 Webhook,反向适配各种调用平台**,你可以接入各种已有的系统,而无需修改其代码。
|
||||||
@@ -211,7 +212,8 @@ proxy_send_timeout 300s;
|
|||||||
12. `one_api`:通过 OneAPI 协议推送消息到 QQ。
|
12. `one_api`:通过 OneAPI 协议推送消息到 QQ。
|
||||||
13. `group`:通过预先配置的消息推送通道群组进行推送。
|
13. `group`:通过预先配置的消息推送通道群组进行推送。
|
||||||
14. `custom`:通过预先配置好的自定义推送通道进行推送。
|
14. `custom`:通过预先配置好的自定义推送通道进行推送。
|
||||||
15. `none`:仅保存到数据库,不做推送。
|
15. `tencent_alarm`:通过腾讯云监控告警进行推送,仅支持 `description` 字段。
|
||||||
|
16. `none`:仅保存到数据库,不做推送。
|
||||||
5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
|
5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
|
||||||
6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
|
6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
|
||||||
7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。
|
7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。
|
||||||
@@ -224,19 +226,20 @@ proxy_send_timeout 300s;
|
|||||||
|
|
||||||
**各种通道的支持程度:**
|
**各种通道的支持程度:**
|
||||||
|
|
||||||
| 通道类型 | `title` | `description` | `content` | `url` | `to` | Markdown 支持 |
|
| 通道类型 | `title` | `description` | `content` | `url` | `to` | Markdown 支持 |
|
||||||
|:----------:|:-------:|:-------------:|:---------:|:-----:|:----:|:-----------:|
|
|:---------------:|:-------:|:-------------:|:---------:|:-----:|:----:|:-----------:|
|
||||||
| `email` | ✅ | ✅ | ✅ | ❌ | ✅️ | ✅️ |
|
| `email` | ✅ | ✅ | ✅ | ❌ | ✅️ | ✅️ |
|
||||||
| `test` | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
|
| `test` | ✅ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
|
||||||
| `corp_app` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
|
| `corp_app` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
|
||||||
| `corp` | ❌ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
|
| `corp` | ❌ | ✅ | ✅ | ✅️ | ✅️ | ✅ |
|
||||||
| `lark` | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ |
|
| `lark` | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ |
|
||||||
| `lark_app` | ❌ | ✅ | ✅ | ❌️ | ✅ | ✅ |
|
| `lark_app` | ❌ | ✅ | ✅ | ❌️ | ✅ | ✅ |
|
||||||
| `ding` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
|
| `ding` | ✅ | ✅ | ✅ | ✅️ | ✅ | ✅ |
|
||||||
| `bark` | ✅ | ✅ | ✅ | ✅️ | ❌ | ✅ |
|
| `bark` | ✅ | ✅ | ✅ | ✅️ | ❌ | ✅ |
|
||||||
| `client` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
| `client` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||||
| `telegram` | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
|
| `telegram` | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
|
||||||
| `discord` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
|
| `discord` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
|
||||||
|
| `tencent_alarm` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
|
||||||
注意:
|
注意:
|
||||||
1. 对于大部分通道,`description` 字段和 `content` 是不能同时存在的,如果你只需要文字消息,请使用 `description` 字段,如果你需要发送 Markdown 消息,请使用 `content` 字段。
|
1. 对于大部分通道,`description` 字段和 `content` 是不能同时存在的,如果你只需要文字消息,请使用 `description` 字段,如果你需要发送 Markdown 消息,请使用 `content` 字段。
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ func SendMessage(message *model.Message, user *model.User, channel_ *model.Chann
|
|||||||
return SendLarkAppMessage(message, user, channel_)
|
return SendLarkAppMessage(message, user, channel_)
|
||||||
case model.TypeCustom:
|
case model.TypeCustom:
|
||||||
return SendCustomMessage(message, user, channel_)
|
return SendCustomMessage(message, user, channel_)
|
||||||
|
case model.TypeTencentAlarm:
|
||||||
|
return SendTencentAlarmMessage(message, user, channel_)
|
||||||
default:
|
default:
|
||||||
return errors.New("不支持的消息通道:" + channel_.Type)
|
return errors.New("不支持的消息通道:" + channel_.Type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, "&")
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ const (
|
|||||||
TypeGroup = "group"
|
TypeGroup = "group"
|
||||||
TypeLarkApp = "lark_app"
|
TypeLarkApp = "lark_app"
|
||||||
TypeCustom = "custom"
|
TypeCustom = "custom"
|
||||||
|
TypeTencentAlarm = "tencent_alarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ export const CHANNEL_OPTIONS = [
|
|||||||
value: 'group',
|
value: 'group',
|
||||||
color: '#FF9800',
|
color: '#FF9800',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'tencent_alarm',
|
||||||
|
text: '腾讯云消息告警',
|
||||||
|
value: 'tencent_alarm',
|
||||||
|
color: '#00a4ff',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'none',
|
key: 'none',
|
||||||
text: '不推送',
|
text: '不推送',
|
||||||
|
|||||||
@@ -209,6 +209,58 @@ const EditChannel = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
case 'tencent_alarm':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Message>
|
||||||
|
通过腾讯云自定义消息告警进行推送,
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
href='https://github.com/songquanpeng/message-pusher/issues/87#issuecomment-1547971847'
|
||||||
|
>
|
||||||
|
配置教程
|
||||||
|
</a>
|
||||||
|
。
|
||||||
|
</Message>
|
||||||
|
<Form.Group widths={3}>
|
||||||
|
<Form.Input
|
||||||
|
label='SecretId'
|
||||||
|
name='app_id'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.app_id}
|
||||||
|
placeholder='子账号的 SecretId'
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='SecretKey'
|
||||||
|
name='secret'
|
||||||
|
type='password'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.secret}
|
||||||
|
placeholder='子账号的 SecretKey'
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='消息策略 ID'
|
||||||
|
name='account_id'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.account_id}
|
||||||
|
placeholder='例如:cm-6gl3pq19'
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group widths={3}>
|
||||||
|
<Form.Input
|
||||||
|
label='区域'
|
||||||
|
name='other'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.other}
|
||||||
|
placeholder='例如:ap-shanghai'
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
case 'corp_app':
|
case 'corp_app':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user