浏览代码

feat: support group message now (close #46)

JustSong 2 年之前
父节点
当前提交
1cade7a218
共有 6 个文件被更改,包括 91 次插入8 次删除
  1. 3 1
      README.md
  2. 31 0
      channel/group.go
  3. 2 0
      channel/main.go
  4. 1 0
      model/channel.go
  5. 7 1
      web/src/constants/channel.constants.js
  6. 47 6
      web/src/pages/Channel/EditChannel.js

+ 3 - 1
README.md

@@ -57,6 +57,7 @@ _✨ 搭建专属于你的消息推送服务,支持多种消息推送方式,
    + WebSocket 客户端([官方客户端](https://github.com/songquanpeng/personal-assistant),[接入文档](./docs/API.md#websocket-客户端)),
    + Telegram 机器人,
    + Discord 群机器人,
+   + 群组消息,可以将多个推送通道组合成一个群组,然后向群组发送消息,可以实现一次性推送到多个渠道的功能。
 2. 多种用户登录注册方式:
    + 邮箱登录注册以及通过邮箱进行密码重置。
    + [GitHub 开放授权](https://github.com/settings/applications/new)。
@@ -172,7 +173,8 @@ proxy_send_timeout 300s;
       9. `telegram`:通过 Telegram 机器人进行推送(`description` 或 `content` 字段二选一,支持 Markdown 的子集)。
       10. `discord`:通过 Discord 群机器人进行推送(注意事项同上)。
       11. `one_api`:通过 OneAPI 协议推送消息到 QQ。
-      12. `none`:仅保存到数据库,不做推送。
+      12. `group`:通过预先配置的消息推送通道群组进行推送。
+      13. `none`:仅保存到数据库,不做推送。
    5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
    6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
    7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。

+ 31 - 0
channel/group.go

@@ -0,0 +1,31 @@
+package channel
+
+import (
+	"errors"
+	"message-pusher/model"
+	"strings"
+)
+
+func SendGroupMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
+	subChannels := strings.Split(channel_.AppId, "|")
+	subTargets := strings.Split(channel_.AccountId, "|")
+	if len(subChannels) != len(subTargets) {
+		return errors.New("无效的群组消息配置,子通道数量与子目标数量不一致")
+	}
+	for i := 0; i < len(subChannels); i++ {
+		message.To = subTargets[i]
+		message.Channel = subChannels[i]
+		subChannel, err := model.GetChannelByName(subChannels[i], user.Id)
+		if err != nil {
+			return errors.New("获取群组消息子通道失败:" + err.Error())
+		}
+		if subChannel.Type == model.TypeGroup {
+			return errors.New("群组消息子通道不能是群组消息")
+		}
+		err = SendMessage(message, user, subChannel)
+		if err != nil {
+			return errors.New("发送群组消息失败:" + err.Error())
+		}
+	}
+	return nil
+}

+ 2 - 0
channel/main.go

@@ -31,6 +31,8 @@ func SendMessage(message *model.Message, user *model.User, channel_ *model.Chann
 		return nil
 	case model.TypeOneBot:
 		return SendOneBotMessage(message, user, channel_)
+	case model.TypeGroup:
+		return SendGroupMessage(message, user, channel_)
 	default:
 		return errors.New("不支持的消息通道:" + channel_.Type)
 	}

+ 1 - 0
model/channel.go

@@ -18,6 +18,7 @@ const (
 	TypeClient            = "client"
 	TypeNone              = "none"
 	TypeOneBot            = "one_bot"
+	TypeGroup             = "group"
 )
 
 type Channel struct {

+ 7 - 1
web/src/constants/channel.constants.js

@@ -33,7 +33,13 @@ export const CHANNEL_OPTIONS = [
     key: 'one_bot',
     text: 'OneBot 协议',
     value: 'one_bot',
-    color: '#616cff',
+    color: '#76FF03',
+  },
+  {
+    key: 'group',
+    text: '群组消息',
+    value: 'group',
+    color: '#FF9800',
   },
   {
     key: 'none',

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

@@ -20,7 +20,7 @@ const EditChannel = () => {
     url: '',
     other: '',
     corp_id: '', // only for corp_app
-    agent_id: '', // only for corp_app
+    agent_id: '' // only for corp_app
   };
 
   const [inputs, setInputs] = useState(originInputs);
@@ -70,11 +70,22 @@ const EditChannel = () => {
           localInputs.url = localInputs.url.slice(0, -1);
         }
         break;
+      case 'group':
+        let channels = localInputs.app_id.split('|');
+        let targets = localInputs.account_id.split('|');
+        if (localInputs.account_id === '') {
+          for (let i = 0; i < channels.length - 1; i++) {
+            localInputs.account_id += '|';
+          }
+        } else if (channels.length !== targets.length) {
+          showError('群组通道的子通道数量与目标数量不匹配,对于不需要指定的目标请直接留空');
+          return;
+        }
     }
     if (isEditing) {
       res = await API.put(`/api/channel/`, {
         ...localInputs,
-        id: parseInt(channelId),
+        id: parseInt(channelId)
       });
     } else {
       res = await API.post(`/api/channel`, localInputs);
@@ -247,9 +258,9 @@ const EditChannel = () => {
                   {
                     key: 'plugin',
                     text: '微信中的企业微信插件',
-                    value: 'plugin',
+                    value: 'plugin'
                   },
-                  { key: 'app', text: '企业微信 APP', value: 'app' },
+                  { key: 'app', text: '企业微信 APP', value: 'app' }
                 ]}
                 value={inputs.other}
                 onChange={handleInputChange}
@@ -465,10 +476,11 @@ const EditChannel = () => {
         return (
           <>
             <Message>
-              通过 OneBot 协议进行推送,可以使用 <a href='https://github.com/Mrs4s/go-cqhttp' target='_blank'>cqhttp</a> 等实现。
+              通过 OneBot 协议进行推送,可以使用 <a href='https://github.com/Mrs4s/go-cqhttp'
+                                                   target='_blank'>cqhttp</a> 等实现。
               利用 OneBot 协议可以实现推送 QQ 消息。
             </Message>
-            <Form.Group widths={2}>
+            <Form.Group widths={3}>
               <Form.Input
                 label='服务器地址'
                 name='url'
@@ -497,6 +509,35 @@ const EditChannel = () => {
             </Form.Group>
           </>
         );
+      case 'group':
+        return (
+          <>
+            <Message>
+              对渠道进行分组,然后在推送时选择分组进行推送,可以实现一次性推送到多个渠道的功能。
+              <br />
+              <br />
+              推送目标如若不填,则使用子渠道的默认推送目标。如果填写,请务必全部按顺序填写,对于不需要指定的直接留空即可,例如 <code>123456789||@wechat</code>,两个连续的分隔符表示跳过该渠道。
+            </Message>
+            <Form.Group widths={2}>
+              <Form.Input
+                label='渠道列表'
+                name='app_id'
+                onChange={handleInputChange}
+                autoComplete='new-password'
+                value={inputs.app_id}
+                placeholder='在此填写渠道列表,使用 | 分割,例如 bark|telegram|wechat'
+              />
+              <Form.Input
+                label='默认推送目标'
+                name='account_id'
+                onChange={handleInputChange}
+                autoComplete='new-password'
+                value={inputs.account_id}
+                placeholder='在此填写默认推送目标,使用 | 分割,例如 123456789|@wechat|@wechat'
+              />
+            </Form.Group>
+          </>
+        );
       case 'none':
         return (
           <>