Browse Source

feat: support channel OneBot now (close #64)

JustSong 2 years ago
parent
commit
2fd800b0a3

+ 2 - 0
channel/main.go

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

+ 85 - 0
channel/one-bot.go

@@ -0,0 +1,85 @@
+package channel
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"message-pusher/model"
+	"net/http"
+	"strconv"
+	"strings"
+)
+
+type oneBotMessageRequest struct {
+	MessageType string `json:"message_type"`
+	UserId      int64  `json:"user_id"`
+	GroupId     int64  `json:"group_id"`
+	Message     string `json:"message"`
+	AutoEscape  bool   `json:"auto_escape"`
+}
+
+type oneBotMessageResponse struct {
+	Message string `json:"message"`
+	Status  string `json:"status"`
+	RetCode int    `json:"retcode"`
+}
+
+func SendOneBotMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
+	url := fmt.Sprintf("%s/send_msg", channel_.URL)
+	req := oneBotMessageRequest{
+		Message: message.Content,
+	}
+	if message.Content == "" {
+		req.Message = message.Description
+	}
+	target := channel_.AccountId
+	if message.To != "" {
+		target = message.To
+	}
+	parts := strings.Split(target, "_")
+	var idStr string
+	var type_ string
+	if len(parts) == 1 {
+		type_ = "user"
+		idStr = parts[0]
+	} else if len(parts) == 2 {
+		type_ = parts[0]
+		idStr = parts[1]
+	} else {
+		return errors.New("无效的 OneBot 配置")
+	}
+	id, _ := strconv.ParseInt(idStr, 10, 64)
+	if type_ == "user" {
+		req.UserId = id
+		req.MessageType = "private"
+	} else if type_ == "group" {
+		req.GroupId = id
+		req.MessageType = "group"
+	} else {
+		return errors.New("无效的 OneBot 配置")
+	}
+	reqBody, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+	request, _ := http.NewRequest("POST", url, bytes.NewReader(reqBody))
+	request.Header.Set("Authorization", "Bearer "+channel_.Secret)
+	request.Header.Set("Content-Type", "application/json")
+	resp, err := http.DefaultClient.Do(request)
+	if err != nil {
+		return err
+	}
+	if resp.StatusCode != 200 {
+		return errors.New(resp.Status)
+	}
+	var res oneBotMessageResponse
+	err = json.NewDecoder(resp.Body).Decode(&res)
+	if err != nil {
+		return err
+	}
+	if res.RetCode != 0 {
+		return errors.New(res.Message)
+	}
+	return nil
+}

+ 1 - 0
model/channel.go

@@ -16,6 +16,7 @@ const (
 	TypeBark              = "bark"
 	TypeClient            = "client"
 	TypeNone              = "none"
+	TypeOneBot            = "one_bot"
 )
 
 type Channel struct {

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

@@ -29,6 +29,12 @@ export const CHANNEL_OPTIONS = [
     value: 'discord',
     color: '#404eed',
   },
+  {
+    key: 'one_bot',
+    text: 'OneBot 协议',
+    value: 'one_bot',
+    color: '#616cff',
+  },
   {
     key: 'none',
     text: '不推送',

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

@@ -61,7 +61,15 @@ const EditChannel = () => {
         localInputs.app_id = `${inputs.corp_id}|${inputs.agent_id}`;
         break;
       case 'bark':
-        localInputs.url = 'https://api.day.app';
+        if (localInputs.url === '') {
+          localInputs.url = 'https://api.day.app';
+        }
+        break;
+      case 'one_bot':
+        if (localInputs.url.endsWith('/')) {
+          localInputs.url = localInputs.url.slice(0, -1);
+        }
+        break;
     }
     if (isEditing) {
       res = await API.put(`/api/channel/`, {
@@ -453,6 +461,42 @@ const EditChannel = () => {
             </Form.Group>
           </>
         );
+      case 'one_bot':
+        return (
+          <>
+            <Message>
+              通过 OneBot 协议进行推送,可以使用 <a href='https://github.com/Mrs4s/go-cqhttp' target='_blank'>cqhttp</a> 等实现。
+              利用 OneBot 协议可以实现推送 QQ 消息。
+            </Message>
+            <Form.Group widths={2}>
+              <Form.Input
+                label='服务器地址'
+                name='url'
+                onChange={handleInputChange}
+                autoComplete='new-password'
+                value={inputs.url}
+                placeholder='在此填写服务器地址'
+              />
+              <Form.Input
+                label='推送 key'
+                name='secret'
+                type='password'
+                onChange={handleInputChange}
+                autoComplete='new-password'
+                value={inputs.secret}
+                placeholder='在此填写服务器的 access token'
+              />
+              <Form.Input
+                label='默认推送目标'
+                name='account_id'
+                onChange={handleInputChange}
+                autoComplete='new-password'
+                value={inputs.account_id}
+                placeholder='在此填写默认推送目标,例如 QQ 号,如果是群号则前面必须加上群号前缀,例如 group_123456789'
+              />
+            </Form.Group>
+          </>
+        );
       case 'none':
         return (
           <>