diff --git a/Dockerfile b/Dockerfile index c5eff4d..d5ff7e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,8 @@ RUN set -ex \ FROM alpine:latest +RUN apk add --no-cache ffmpeg + COPY --from=builder /build/cqhttp /usr/bin/cqhttp RUN chmod +x /usr/bin/cqhttp diff --git a/internal/base/feature.go b/internal/base/feature.go index f27c0a0..810e189 100644 --- a/internal/base/feature.go +++ b/internal/base/feature.go @@ -29,3 +29,9 @@ var ( func nocheck(_ io.ReadSeeker) (bool, string) { return true, "" } + +// todo: enable in v1.1.0 +// onebot v12 feature +const ( + AcceptOneBotV12HTTPEndPoint = false +) diff --git a/internal/base/flag.go b/internal/base/flag.go index 85c5ef1..05f38e4 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -3,8 +3,12 @@ package base import ( "flag" + "fmt" "os" + "os/exec" "path" + "path/filepath" + "strings" "time" log "github.com/sirupsen/logrus" @@ -17,7 +21,7 @@ import ( var ( LittleC string // config file LittleD bool // daemon - LittleH bool // help + LittleH bool // Help LittleWD string // working directory ) @@ -55,7 +59,7 @@ func Parse() { dc := path.Join(wd, "config.yml") flag.StringVar(&LittleC, "c", dc, "configuration filename") flag.BoolVar(&LittleD, "d", false, "running as a daemon") - flag.BoolVar(&LittleH, "h", false, "this help") + flag.BoolVar(&LittleH, "h", false, "this Help") flag.StringVar(&LittleWD, "w", "", "cover the working directory") d := flag.Bool("D", false, "debug mode") flag.Parse() @@ -105,3 +109,40 @@ func Init() { } } } + +// Help cli命令行-h的帮助提示 +func Help() { + fmt.Printf(`go-cqhttp service +version: %s +Usage: +server [OPTIONS] +Options: +`, Version) + + flag.PrintDefaults() + os.Exit(0) +} + +// ResetWorkingDir 重设工作路径 +func ResetWorkingDir() { + wd := LittleWD + args := make([]string, 0, len(os.Args)) + for i := 1; i < len(os.Args); i++ { + if os.Args[i] == "-w" { + i++ // skip value field + } else if !strings.HasPrefix(os.Args[i], "-w") { + args = append(args, os.Args[i]) + } + } + p, _ := filepath.Abs(os.Args[0]) + proc := exec.Command(p, args...) + proc.Stdin = os.Stdin + proc.Stdout = os.Stdout + proc.Stderr = os.Stderr + proc.Dir = wd + err := proc.Run() + if err != nil { + panic(err) + } + os.Exit(0) +} diff --git a/main.go b/main.go index 5d086d4..a2648dc 100644 --- a/main.go +++ b/main.go @@ -5,13 +5,8 @@ import ( "crypto/md5" "crypto/sha1" "encoding/hex" - "flag" - "fmt" "os" - "os/exec" "path" - "path/filepath" - "strings" "sync" "time" @@ -29,7 +24,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/selfupdate" - "github.com/Mrs4s/go-cqhttp/modules/config" + "github.com/Mrs4s/go-cqhttp/modules/servers" "github.com/Mrs4s/go-cqhttp/server" _ "github.com/Mrs4s/go-cqhttp/modules/mime" // mime检查模块 @@ -49,11 +44,11 @@ func main() { base.Parse() switch { case base.LittleH: - help() + base.Help() case base.LittleD: server.Daemon() case base.LittleWD != "": - resetWorkDir() + base.ResetWorkingDir() } base.Init() @@ -332,49 +327,8 @@ func main() { base.Account.Status = 0 } cli.SetOnlineStatus(allowStatus[base.Account.Status]) - bot := coolq.NewQQBot(cli) - for _, m := range base.Servers { - if h, ok := m["http"]; ok { - hc := new(config.HTTPServer) - if err := h.Decode(hc); err != nil { - log.Warn("读取http配置失败 :", err) - } else if !hc.Disabled { - go server.RunHTTPServerAndClients(bot, hc) - } - } - if s, ok := m["ws"]; ok { - sc := new(config.WebsocketServer) - if err := s.Decode(sc); err != nil { - log.Warn("读取正向Websocket配置失败 :", err) - } else if !sc.Disabled { - go server.RunWebSocketServer(bot, sc) - } - } - if c, ok := m["ws-reverse"]; ok { - rc := new(config.WebsocketReverse) - if err := c.Decode(rc); err != nil { - log.Warn("读取反向Websocket配置失败 :", err) - } else if !rc.Disabled { - go server.RunWebSocketClient(bot, rc) - } - } - if p, ok := m["pprof"]; ok { - pc := new(config.PprofServer) - if err := p.Decode(pc); err != nil { - log.Warn("读取pprof配置失败 :", err) - } else if !pc.Disabled { - go server.RunPprofServer(pc) - } - } - if p, ok := m["lambda"]; ok { - lc := new(config.LambdaServer) - if err := p.Decode(lc); err != nil { - log.Warn("读取pprof配置失败 :", err) - } else if !lc.Disabled { - go server.RunLambdaClient(bot, lc) - } - } - } + + servers.Run(coolq.NewQQBot(cli)) log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") @@ -414,45 +368,6 @@ func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, erro return result, nil } -// help cli命令行-h的帮助提示 -func help() { - fmt.Printf(`go-cqhttp service -version: %s - -Usage: - -server [OPTIONS] - -Options: -`, base.Version) - - flag.PrintDefaults() - os.Exit(0) -} - -func resetWorkDir() { - wd := base.LittleWD - args := make([]string, 0, len(os.Args)) - for i := 1; i < len(os.Args); i++ { - if os.Args[i] == "-w" { - i++ // skip value field - } else if !strings.HasPrefix(os.Args[i], "-w") { - args = append(args, os.Args[i]) - } - } - p, _ := filepath.Abs(os.Args[0]) - proc := exec.Command(p, args...) - proc.Stdin = os.Stdin - proc.Stdout = os.Stdout - proc.Stderr = os.Stderr - proc.Dir = wd - err := proc.Run() - if err != nil { - panic(err) - } - os.Exit(0) -} - func newClient() *client.QQClient { c := client.NewClientEmpty() c.OnServerUpdated(func(bot *client.QQClient, e *client.ServerUpdatedEvent) bool { diff --git a/modules/servers/servers.go b/modules/servers/servers.go new file mode 100644 index 0000000..579f21b --- /dev/null +++ b/modules/servers/servers.go @@ -0,0 +1,31 @@ +// Package servers provide servers register +package servers + +import ( + "gopkg.in/yaml.v3" + + "github.com/Mrs4s/go-cqhttp/coolq" + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +var svr = make(map[string]func(*coolq.CQBot, yaml.Node)) + +// Register 注册 Server +func Register(name string, proc func(*coolq.CQBot, yaml.Node)) { + _, ok := svr[name] + if ok { + panic(name + " server has existed") + } + svr[name] = proc +} + +// Run 运行所有svr +func Run(bot *coolq.CQBot) { + for _, l := range base.Servers { + for name, conf := range l { + if fn, ok := svr[name]; ok { + go fn(bot, conf) + } + } + } +} diff --git a/server/doc.go b/server/doc.go index 3d4b43d..f0a6bdd 100644 --- a/server/doc.go +++ b/server/doc.go @@ -1,2 +1,13 @@ // Package server 包含HTTP,WebSocket,反向WebSocket请求处理的相关函数与结构体 package server + +import "github.com/Mrs4s/go-cqhttp/modules/servers" + +// 注册 +func init() { + servers.Register("http", runHTTP) + servers.Register("ws", runWSServer) + servers.Register("ws-reverse", runWSClient) + servers.Register("pprof", runPprof) + servers.Register("lambda", runLambda) +} diff --git a/server/http.go b/server/http.go index 767c02c..5f7be02 100644 --- a/server/http.go +++ b/server/http.go @@ -19,8 +19,11 @@ import ( "github.com/Mrs4s/MiraiGo/utils" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" + "gopkg.in/yaml.v3" "github.com/Mrs4s/go-cqhttp/coolq" + "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/modules/config" ) @@ -114,14 +117,21 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request return } - action := strings.TrimPrefix(request.URL.Path, "/") - action = strings.TrimSuffix(action, "_async") - log.Debugf("HTTPServer接收到API调用: %v", action) - ret := s.api.callAPI(action, &ctx) + var response global.MSG + if base.AcceptOneBotV12HTTPEndPoint && request.URL.Path == "/" { + action := strings.TrimSuffix(ctx.Get("action").Str, "_async") + log.Debugf("HTTPServer接收到API调用: %v", action) + response = s.api.callAPI(action, ctx.Get("params")) + } else { + action := strings.TrimPrefix(request.URL.Path, "/") + action = strings.TrimSuffix(action, "_async") + log.Debugf("HTTPServer接收到API调用: %v", action) + response = s.api.callAPI(action, &ctx) + } writer.Header().Set("Content-Type", "application/json; charset=utf-8") writer.WriteHeader(http.StatusOK) - _ = json.NewEncoder(writer).Encode(ret) + _ = json.NewEncoder(writer).Encode(response) } func checkAuth(req *http.Request, token string) int { @@ -149,13 +159,19 @@ func checkAuth(req *http.Request, token string) int { } } -// RunHTTPServerAndClients 启动HTTP服务器与HTTP上报客户端 -func RunHTTPServerAndClients(bot *coolq.CQBot, conf *config.HTTPServer) { - var ( - s = new(httpServer) - addr string - ) - s.accessToken = conf.AccessToken +// runHTTP 启动HTTP服务器与HTTP上报客户端 +func runHTTP(bot *coolq.CQBot, node yaml.Node) { + var conf config.HTTPServer + switch err := node.Decode(&conf); { + case err != nil: + log.Warn("读取http配置失败 :", err) + fallthrough + case conf.Disabled: + return + } + + var addr string + s := &httpServer{accessToken: conf.AccessToken} if conf.Host == "" || conf.Port == 0 { goto client } diff --git a/server/middlewares.go b/server/middlewares.go index 98949c3..6241ca2 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -5,7 +5,6 @@ import ( "context" "os" "sync" - "sync/atomic" "time" "github.com/Mrs4s/go-cqhttp/coolq" @@ -76,7 +75,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) handler { return nil } var ( - ok int32 + once sync.Once ch = make(chan []interface{}, 1) timeout = time.Duration(p.Get("timeout").Int()) * time.Second ) @@ -87,7 +86,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) handler { if queue.Len() == 0 { cond.Wait() } - if atomic.CompareAndSwapInt32(&ok, 0, 1) { + once.Do(func() { limit := int(p.Get("limit").Int()) if limit <= 0 || queue.Len() < limit { limit = queue.Len() @@ -97,12 +96,12 @@ func longPolling(bot *coolq.CQBot, maxSize int) handler { ret[i] = queue.Remove(queue.Front()) } ch <- ret - } + }) }() if timeout != 0 { select { case <-time.After(timeout): - atomic.StoreInt32(&ok, 1) + once.Do(func() {}) return coolq.OK([]interface{}{}) case ret := <-ch: return coolq.OK(ret) diff --git a/server/pprof.go b/server/pprof.go index 5ea0640..00b4988 100644 --- a/server/pprof.go +++ b/server/pprof.go @@ -8,12 +8,23 @@ import ( "time" log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/modules/config" ) -// RunPprofServer 启动 pprof 性能分析服务器 -func RunPprofServer(conf *config.PprofServer) { +// runPprof 启动 pprof 性能分析服务器 +func runPprof(_ *coolq.CQBot, node yaml.Node) { + var conf config.PprofServer + switch err := node.Decode(&conf); { + case err != nil: + log.Warn("读取pprof配置失败 :", err) + fallthrough + case conf.Disabled: + return + } + addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port) mux := http.NewServeMux() mux.HandleFunc("/debug/pprof/", pprof.Index) diff --git a/server/scf.go b/server/scf.go index 68f6da5..6a0ef8e 100644 --- a/server/scf.go +++ b/server/scf.go @@ -13,6 +13,7 @@ import ( "github.com/Mrs4s/MiraiGo/utils" log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" @@ -77,8 +78,17 @@ func (l *lambdaResponseWriter) WriteHeader(statusCode int) { var cli *lambdaClient -// RunLambdaClient type: [scf,aws] -func RunLambdaClient(bot *coolq.CQBot, conf *config.LambdaServer) { +// runLambda type: [scf,aws] +func runLambda(bot *coolq.CQBot, node yaml.Node) { + var conf config.LambdaServer + switch err := node.Decode(&conf); { + case err != nil: + log.Warn("读取lambda配置失败 :", err) + fallthrough + case conf.Disabled: + return + } + cli = &lambdaClient{ lambdaType: conf.Type, client: http.Client{Timeout: 0}, diff --git a/server/websocket.go b/server/websocket.go index a7e3f1c..5200aed 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -10,6 +10,8 @@ import ( "sync" "time" + "gopkg.in/yaml.v3" + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/modules/config" @@ -54,11 +56,20 @@ var upgrader = websocket.Upgrader{ }, } -// RunWebSocketServer 运行一个正向WS server -func RunWebSocketServer(b *coolq.CQBot, conf *config.WebsocketServer) { +// runWSServer 运行一个正向WS server +func runWSServer(b *coolq.CQBot, node yaml.Node) { + var conf config.WebsocketServer + switch err := node.Decode(&conf); { + case err != nil: + log.Warn("读取正向Websocket配置失败 :", err) + fallthrough + case conf.Disabled: + return + } + s := &webSocketServer{ bot: b, - conf: conf, + conf: &conf, token: conf.AccessToken, filter: conf.Filter, } @@ -77,11 +88,20 @@ func RunWebSocketServer(b *coolq.CQBot, conf *config.WebsocketServer) { }() } -// RunWebSocketClient 运行一个正向WS client -func RunWebSocketClient(b *coolq.CQBot, conf *config.WebsocketReverse) { +// runWSClient 运行一个反向向WS client +func runWSClient(b *coolq.CQBot, node yaml.Node) { + var conf config.WebsocketReverse + switch err := node.Decode(&conf); { + case err != nil: + log.Warn("读取反向Websocket配置失败 :", err) + fallthrough + case conf.Disabled: + return + } + c := &websocketClient{ bot: b, - conf: conf, + conf: &conf, token: conf.AccessToken, filter: conf.Filter, }