Skip to content

Commit c671fc2

Browse files
committed
plugin handler
1 parent 173f3b6 commit c671fc2

File tree

5 files changed

+2676
-588
lines changed

5 files changed

+2676
-588
lines changed

dto_proto/http_dto.proto

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,6 @@ message ListBotResp{
4646
repeated Bot bot_list = 1;
4747
}
4848

49-
//// 机器人登陆 /bot/login/v1/
50-
//message BotLoginAsyncReq{
51-
// int64 bot_id = 1;
52-
//}
53-
//message BotLoginAsyncResp{
54-
//}
55-
5649
// 处理验证码 /captcha/solve/v1/
5750
message SolveCaptchaReq{
5851
int64 bot_id = 1;
@@ -88,12 +81,50 @@ message QRCodeLoginResp{
8881
}
8982

9083

84+
message Plugin{
85+
string name = 1;
86+
bool disabled = 2;
87+
bool json = 3;
88+
repeated string urls = 4;
89+
repeated int32 event_filter = 5;
90+
repeated int32 api_filter = 6;
91+
string regex_filter = 7;
92+
string regex_replace = 8;
93+
repeated Header extra_header = 9;
94+
message Header{
95+
string key = 1;
96+
repeated string value = 2;
97+
}
98+
}
99+
100+
message ListPluginReq{
101+
}
102+
message ListPluginResp{
103+
repeated Plugin plugins = 1;
104+
}
105+
106+
message SavePluginReq{
107+
Plugin plugin = 1;
108+
}
109+
message SavePluginResp{
110+
}
111+
112+
message DeletePluginReq{
113+
string name = 1;
114+
}
115+
message DeletePluginResp{
116+
}
117+
118+
91119
service HttpService{
92120
rpc CreateBot(CreateBotReq)returns (CreateBotResp);
93121
rpc DeleteBot(DeleteBotReq)returns (DeleteBotResp);
94122
rpc ListBot(ListBotReq)returns (ListBotResp);
95-
// rpc BotLoginAsync(BotLoginAsyncReq)returns (BotLoginAsyncResp);
96123
rpc SolveCaptcha(SolveCaptchaReq)returns (SolveCaptchaResp);
97124
rpc FetchQRCode(FetchQRCodeReq)returns (QRCodeLoginResp);
98125
rpc QueryQRCodeStatus(QueryQRCodeStatusReq)returns (QRCodeLoginResp);
126+
127+
rpc ListPlugin(ListPluginReq)returns (ListPluginResp);
128+
rpc SavePlugin(SavePluginReq)returns (SavePluginResp);
129+
rpc DeletePlugin(DeletePluginReq)returns (DeletePluginResp);
99130
}

pkg/config/config.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
package config
22

3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/ProtobufBot/Go-Mirai-Client/pkg/util"
8+
log "github.com/sirupsen/logrus"
9+
"io/ioutil"
10+
"os"
11+
"path"
12+
"strings"
13+
)
14+
315
//go:generate go run github.com/a8m/syncmap -o "gen_plugin_map.go" -pkg config -name PluginMap "map[string]*Plugin"
416
var (
517
Fragment = false // 是否分片
@@ -45,3 +57,83 @@ type Plugin struct {
4557
ExtraHeader map[string][]string `json:"extra_header"` // 自定义请求头
4658
// TODO event filter, msg filter, regex filter, prefix filter, suffix filter
4759
}
60+
61+
const pluginPath = "plugins"
62+
63+
func LoadPlugins() {
64+
if !util.PathExists(pluginPath) {
65+
return
66+
}
67+
files, err := ioutil.ReadDir(pluginPath)
68+
if err != nil {
69+
log.Warnf("failed to read plugin dir: %s", err)
70+
return
71+
}
72+
73+
if len(files) == 0 {
74+
log.Warnf("plugin dir is empty")
75+
return
76+
}
77+
78+
ClearPlugins(Plugins)
79+
for _, file := range files {
80+
if !strings.HasSuffix(file.Name(), ".json") {
81+
continue
82+
}
83+
pluginName := strings.TrimSuffix(file.Name(), ".json")
84+
filepath := path.Join(pluginPath, file.Name())
85+
b, err := os.ReadFile(filepath)
86+
if err != nil {
87+
log.Warnf("failed to read plugin file: %s %s", filepath, err)
88+
continue
89+
}
90+
plugin := &Plugin{}
91+
if err := json.NewDecoder(bytes.NewReader(b)).Decode(plugin); err != nil {
92+
log.Warnf("failed to decode plugin file: %s %s", filepath, err)
93+
continue
94+
}
95+
plugin.Name = pluginName
96+
Plugins.Store(plugin.Name, plugin)
97+
}
98+
}
99+
100+
func WritePlugins() {
101+
if !util.PathExists(pluginPath) {
102+
if err := os.MkdirAll(pluginPath, 0777); err != nil {
103+
log.Warnf("failed to mkdir")
104+
return
105+
}
106+
}
107+
DeletePluginFiles()
108+
Plugins.Range(func(key string, plugin *Plugin) bool {
109+
pluginFilename := fmt.Sprintf("%s.json", plugin.Name)
110+
filepath := path.Join(pluginPath, pluginFilename)
111+
b, err := json.MarshalIndent(plugin, "", " ")
112+
if err != nil {
113+
log.Warnf("failed to marshal plugin, %s", plugin.Name)
114+
return true
115+
}
116+
if err := os.WriteFile(filepath, b, 0777); err != nil {
117+
log.Warnf("failed to write file, %s", pluginFilename)
118+
return true
119+
}
120+
return true
121+
})
122+
}
123+
124+
func DeletePluginFiles() {
125+
files, err := ioutil.ReadDir(pluginPath)
126+
if err != nil {
127+
log.Warnf("failed to read plugin dir: %s", err)
128+
}
129+
for _, file := range files {
130+
if !strings.HasSuffix(file.Name(), ".json") {
131+
continue
132+
}
133+
filepath := path.Join(pluginPath, file.Name())
134+
if err := os.Remove(filepath); err != nil {
135+
log.Warnf("failed to remove plugin file: %s", filepath)
136+
continue
137+
}
138+
}
139+
}

pkg/gmc/gmc.go

Lines changed: 6 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package gmc
22

33
import (
4-
"bytes"
5-
"encoding/json"
64
"flag"
75
"fmt"
8-
"io/ioutil"
96
"net"
107
"net/http"
118
"os"
@@ -92,10 +89,9 @@ func Start() {
9289
os.Exit(0)
9390
}
9491

95-
pluginPath := "plugins"
96-
LoadPlugins(pluginPath) // 如果文件存在,从文件读取gmc config
97-
LoadParamConfig() // 如果参数存在,从参数读取gmc config,并覆盖
98-
WritePlugins(pluginPath) // 内存中的gmc config写到文件
92+
config.LoadPlugins() // 如果文件存在,从文件读取gmc config
93+
LoadParamConfig() // 如果参数存在,从参数读取gmc config,并覆盖
94+
config.WritePlugins() // 内存中的gmc config写到文件
9995
config.Plugins.Range(func(key string, value *config.Plugin) bool {
10096
log.Infof("Plugin(%s): %s", value.Name, util.MustMarshal(value))
10197
return true
@@ -105,66 +101,6 @@ func Start() {
105101
InitGin() // 初始化GIN HTTP管理
106102
}
107103

108-
func LoadPlugins(pluginPath string) {
109-
if !util.PathExists(pluginPath) {
110-
return
111-
}
112-
files, err := ioutil.ReadDir(pluginPath)
113-
if err != nil {
114-
log.Warnf("failed to read plugin dir: %s", err)
115-
return
116-
}
117-
118-
if len(files) == 0 {
119-
log.Warnf("plugin dir is empty")
120-
return
121-
}
122-
123-
config.ClearPlugins(config.Plugins)
124-
for _, file := range files {
125-
if !strings.HasSuffix(file.Name(), ".json") {
126-
continue
127-
}
128-
pluginName := strings.TrimSuffix(file.Name(), ".json")
129-
filepath := path.Join(pluginPath, file.Name())
130-
b, err := os.ReadFile(filepath)
131-
if err != nil {
132-
log.Warnf("failed to read plugin file: %s %s", filepath, err)
133-
continue
134-
}
135-
plugin := &config.Plugin{}
136-
if err := json.NewDecoder(bytes.NewReader(b)).Decode(plugin); err != nil {
137-
log.Warnf("failed to decode plugin file: %s %s", filepath, err)
138-
continue
139-
}
140-
plugin.Name = pluginName
141-
config.Plugins.Store(plugin.Name, plugin)
142-
}
143-
}
144-
145-
func WritePlugins(pluginPath string) {
146-
if !util.PathExists(pluginPath) {
147-
if err := os.MkdirAll(pluginPath, 0777); err != nil {
148-
log.Warnf("failed to mkdir")
149-
return
150-
}
151-
}
152-
config.Plugins.Range(func(key string, plugin *config.Plugin) bool {
153-
pluginFilename := fmt.Sprintf("%s.json", plugin.Name)
154-
filepath := path.Join(pluginPath, pluginFilename)
155-
b, err := json.MarshalIndent(plugin, "", " ")
156-
if err != nil {
157-
log.Warnf("failed to marshal plugin, %s", plugin.Name)
158-
return true
159-
}
160-
if err := os.WriteFile(filepath, b, 0777); err != nil {
161-
log.Warnf("failed to write file, %s", pluginFilename)
162-
return true
163-
}
164-
return true
165-
})
166-
}
167-
168104
func LoadParamConfig() {
169105
// sms是true,如果本来是true,不变。如果本来是false,变true
170106
if sms {
@@ -223,6 +159,9 @@ func InitGin() {
223159
router.POST("/captcha/solve/v1", handler.SolveCaptcha)
224160
router.POST("/qrcode/fetch/v1", handler.FetchQrCode)
225161
router.POST("/qrcode/query/v1", handler.QueryQRCodeStatus)
162+
router.POST("/plugin/list/v1", handler.ListPlugin)
163+
router.POST("/plugin/save/v1", handler.SavePlugin)
164+
router.POST("/plugin/delete/v1", handler.DeletePlugin)
226165
realPort, err := RunGin(router, ":"+config.Port)
227166
if err != nil {
228167
util.FatalError(fmt.Errorf("failed to run gin, err: %+v", err))

pkg/gmc/handler/bot.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/ProtobufBot/Go-Mirai-Client/pkg/bot"
12+
"github.com/ProtobufBot/Go-Mirai-Client/pkg/config"
1213
"github.com/ProtobufBot/Go-Mirai-Client/pkg/device"
1314
"github.com/ProtobufBot/Go-Mirai-Client/pkg/gmc/plugins"
1415
"github.com/ProtobufBot/Go-Mirai-Client/pkg/plugin"
@@ -229,6 +230,98 @@ func QueryQRCodeStatus(c *gin.Context) {
229230
Return(c, resp)
230231
}
231232

233+
func ListPlugin(c *gin.Context) {
234+
req := &dto.ListPluginReq{}
235+
err := c.Bind(req)
236+
if err != nil {
237+
c.String(http.StatusBadRequest, "bad request")
238+
return
239+
}
240+
var resp = &dto.ListPluginResp{
241+
Plugins: []*dto.Plugin{},
242+
}
243+
config.Plugins.Range(func(key string, p *config.Plugin) bool {
244+
resp.Plugins = append(resp.Plugins, &dto.Plugin{
245+
Name: p.Name,
246+
Disabled: p.Disabled,
247+
Json: p.Json,
248+
Urls: p.Urls,
249+
EventFilter: p.EventFilter,
250+
ApiFilter: p.ApiFilter,
251+
RegexFilter: p.RegexFilter,
252+
RegexReplace: p.RegexReplace,
253+
ExtraHeader: func() []*dto.Plugin_Header {
254+
headers := make([]*dto.Plugin_Header, 0)
255+
for k, v := range p.ExtraHeader {
256+
headers = append(headers, &dto.Plugin_Header{
257+
Key: k,
258+
Value: v,
259+
})
260+
}
261+
return headers
262+
}(),
263+
})
264+
return true
265+
})
266+
Return(c, resp)
267+
}
268+
269+
func SavePlugin(c *gin.Context) {
270+
req := &dto.SavePluginReq{}
271+
err := c.Bind(req)
272+
if err != nil {
273+
c.String(http.StatusBadRequest, "bad request")
274+
return
275+
}
276+
if req.Plugin == nil {
277+
c.String(http.StatusBadRequest, "plugin is nil")
278+
return
279+
}
280+
p := req.Plugin
281+
if p.ApiFilter == nil {
282+
p.ApiFilter = []int32{}
283+
}
284+
if p.EventFilter == nil {
285+
p.EventFilter = []int32{}
286+
}
287+
if p.Urls == nil {
288+
p.Urls = []string{}
289+
}
290+
config.Plugins.Store(p.Name, &config.Plugin{
291+
Name: p.Name,
292+
Disabled: p.Disabled,
293+
Json: p.Json,
294+
Urls: p.Urls,
295+
EventFilter: p.EventFilter,
296+
ApiFilter: p.ApiFilter,
297+
RegexFilter: p.RegexFilter,
298+
RegexReplace: p.RegexReplace,
299+
ExtraHeader: func() map[string][]string {
300+
headers := map[string][]string{}
301+
for _, h := range p.ExtraHeader {
302+
headers[h.Key] = h.Value
303+
}
304+
return headers
305+
}(),
306+
})
307+
config.WritePlugins()
308+
resp := &dto.SavePluginResp{}
309+
Return(c, resp)
310+
}
311+
312+
func DeletePlugin(c *gin.Context) {
313+
req := &dto.DeletePluginReq{}
314+
err := c.Bind(req)
315+
if err != nil {
316+
c.String(http.StatusBadRequest, "bad request")
317+
return
318+
}
319+
config.Plugins.Delete(req.Name)
320+
config.WritePlugins()
321+
resp := &dto.DeletePluginResp{}
322+
Return(c, resp)
323+
}
324+
232325
func Return(c *gin.Context, resp proto.Message) {
233326
var (
234327
data []byte

0 commit comments

Comments
 (0)