Browse Source

feat: support custom channel now (close #72, close #75, #71)

JustSong 2 years ago
parent
commit
82068de143

+ 3 - 1
README.md

@@ -59,6 +59,7 @@ _✨ 搭建专属于你的消息推送服务,支持多种消息推送方式,
    + Telegram 机器人,
    + Discord 群机器人,
    + 群组消息,可以将多个推送通道组合成一个群组,然后向群组发送消息,可以实现一次性推送到多个渠道的功能。
+   + 自定义消息,可以自定义消息请求 URL 和请求体格式,实现与其他服务的对接,支持[众多第三方服务](https://iamazing.cn/page/message-pusher-common-custom-templates)。
 2. 支持在 Web 端管理发送的消息,支持自动刷新。
 3. 支持异步消息。
 4. 多种用户登录注册方式:
@@ -177,7 +178,8 @@ proxy_send_timeout 300s;
       11. `discord`:通过 Discord 群机器人进行推送(注意事项同上)。
       12. `one_api`:通过 OneAPI 协议推送消息到 QQ。
       13. `group`:通过预先配置的消息推送通道群组进行推送。
-      14. `none`:仅保存到数据库,不做推送。
+      14. `custom`:通过预先配置好的自定义推送通道进行推送。
+      15. `none`:仅保存到数据库,不做推送。
    5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
    6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
    7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。

+ 31 - 0
channel/custom.go

@@ -0,0 +1,31 @@
+package channel
+
+import (
+	"bytes"
+	"errors"
+	"message-pusher/model"
+	"net/http"
+	"strings"
+)
+
+func SendCustomMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
+	url := channel_.URL
+	if strings.HasPrefix(url, "http:") {
+		return errors.New("自定义通道必须使用 HTTPS 协议")
+	}
+	template := channel_.Other
+	template = strings.Replace(template, "$url", message.URL, -1)
+	template = strings.Replace(template, "$to", message.To, -1)
+	template = strings.Replace(template, "$title", message.Title, -1)
+	template = strings.Replace(template, "$description", message.Description, -1)
+	template = strings.Replace(template, "$content", message.Content, -1)
+	reqBody := []byte(template)
+	resp, err := http.Post(url, "application/json", bytes.NewReader(reqBody))
+	if err != nil {
+		return err
+	}
+	if resp.StatusCode != 200 {
+		return errors.New(resp.Status)
+	}
+	return nil
+}

+ 2 - 0
channel/main.go

@@ -35,6 +35,8 @@ func SendMessage(message *model.Message, user *model.User, channel_ *model.Chann
 		return SendGroupMessage(message, user, channel_)
 	case model.TypeLarkApp:
 		return SendLarkAppMessage(message, user, channel_)
+	case model.TypeCustom:
+		return SendCustomMessage(message, user, channel_)
 	default:
 		return errors.New("不支持的消息通道:" + channel_.Type)
 	}

+ 1 - 0
model/channel.go

@@ -20,6 +20,7 @@ const (
 	TypeOneBot            = "one_bot"
 	TypeGroup             = "group"
 	TypeLarkApp           = "lark_app"
+	TypeCustom            = "custom"
 )
 
 type Channel struct {

+ 6 - 0
web/src/constants/channel.constants.js

@@ -41,6 +41,12 @@ export const CHANNEL_OPTIONS = [
     value: 'one_bot',
     color: '#76FF03',
   },
+  {
+    key: 'custom',
+    text: '自定义消息通道',
+    value: 'custom',
+    color: '#cc33ff',
+  },
   {
     key: 'group',
     text: '群组消息',

+ 47 - 1
web/src/pages/Channel/EditChannel.js

@@ -83,6 +83,19 @@ const EditChannel = () => {
           );
           return;
         }
+        break;
+      case 'custom':
+        if (!localInputs.url.startsWith('https://')) {
+          showError('自定义通道的 URL 必须以 https:// 开头!');
+          return;
+        }
+        try {
+          JSON.parse(localInputs.other);
+        }catch (e) {
+          showError('JSON 格式错误:' + e.message);
+          return;
+        }
+        break;
     }
     if (isEditing) {
       res = await API.put(`/api/channel/`, {
@@ -601,7 +614,40 @@ const EditChannel = () => {
             </Form.Group>
           </>
         );
-
+      case 'custom':
+        return (
+          <>
+            <Message>
+              自定义推送,目前仅支持 POST 请求,请求体为 JSON 格式。
+              <br/>
+              支持以下模板变量:<code>$title</code>,<code>$description</code>,<code>$content</code>,<code>$url</code>,<code>$to</code>。
+              <br/>
+              <a href="https://iamazing.cn/page/message-pusher-common-custom-templates" target='_blank'>这个页面</a>给出了常见的第三方平台的配置实例,你可以参考这些示例进行配置。
+              <br/>
+              注意,为了防止攻击者利用本功能访问内部网络,也为了你的信息安全,请求地址必须使用 HTTPS 协议。
+            </Message>
+            <Form.Group widths={2}>
+              <Form.Input
+                label='请求地址'
+                name='url'
+                onChange={handleInputChange}
+                autoComplete='new-password'
+                value={inputs.url}
+                placeholder='在此填写完整的请求地址,必须使用 HTTPS 协议'
+              />
+            </Form.Group>
+            <Form.Group widths='equal'>
+              <Form.TextArea
+                label='请求体'
+                placeholder='在此输入请求体,支持模板变量,必须为合法的 JSON 格式'
+                value={inputs.other}
+                name='other'
+                onChange={handleInputChange}
+                style={{ minHeight: 200, fontFamily: 'JetBrains Mono, Consolas' }}
+              />
+            </Form.Group>
+          </>
+        );
       case 'none':
         return (
           <>