mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-05-04 19:17:37 +08:00
feat: qrcode login & remove admin api.
This commit is contained in:
parent
c51e603337
commit
e0a551cea2
@ -1,14 +1,9 @@
|
|||||||
package global
|
package global
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hjson/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
@ -324,13 +319,14 @@ func (c *JSONConfig) Save(path string) error {
|
|||||||
|
|
||||||
// getCurrentPath 获取当前文件的路径,直接返回string
|
// getCurrentPath 获取当前文件的路径,直接返回string
|
||||||
func getCurrentPath() string {
|
func getCurrentPath() string {
|
||||||
cwd, e := GetCurrentPath()
|
cwd, e := os.Getwd()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
panic(e)
|
panic(e)
|
||||||
}
|
}
|
||||||
return cwd
|
return cwd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// GetCurrentPath 预留,获取当前目录地址
|
// GetCurrentPath 预留,获取当前目录地址
|
||||||
func GetCurrentPath() (string, error) {
|
func GetCurrentPath() (string, error) {
|
||||||
file, err := exec.LookPath(os.Args[0])
|
file, err := exec.LookPath(os.Args[0])
|
||||||
@ -351,3 +347,4 @@ func GetCurrentPath() (string, error) {
|
|||||||
}
|
}
|
||||||
return fpath[0 : i+1], nil
|
return fpath[0 : i+1], nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
9
go.mod
9
go.mod
@ -3,9 +3,9 @@ module github.com/Mrs4s/go-cqhttp
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210315005315-55aef1b0ffd0
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f // indirect
|
||||||
|
github.com/Mrs4s/MiraiGo v0.0.0-20210321041821-505aae133db3
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/gin-contrib/pprof v1.3.0
|
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-gonic/gin v1.6.3
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/guonaihong/gout v0.1.5
|
github.com/guonaihong/gout v0.1.5
|
||||||
@ -15,14 +15,17 @@ require (
|
|||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||||
github.com/lestrrat-go/strftime v1.0.4 // indirect
|
github.com/lestrrat-go/strftime v1.0.4 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
||||||
github.com/tidwall/gjson v1.6.8
|
github.com/tidwall/gjson v1.6.8
|
||||||
|
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 // indirect
|
||||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
||||||
github.com/yinghau76/go-ascii-art v0.0.0-20190517192627-e7f465a30189
|
|
||||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b
|
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b
|
||||||
|
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||||
)
|
)
|
||||||
|
18
go.sum
18
go.sum
@ -1,7 +1,9 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
|
||||||
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210315005315-55aef1b0ffd0 h1:wCQr8EabWHuAuS6tgGOB4+RhvAA/s4AdMox7TeX2tQQ=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210321041821-505aae133db3 h1:PEv43+PPI9JmCrXsK1N0oAwCgnjs4q3yrshugK6TpZo=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20210315005315-55aef1b0ffd0/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw=
|
github.com/Mrs4s/MiraiGo v0.0.0-20210321041821-505aae133db3/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -50,6 +52,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
@ -76,6 +79,10 @@ github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkL
|
|||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
||||||
github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8=
|
github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8=
|
||||||
github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
|
github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
|
||||||
|
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA=
|
||||||
|
github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
@ -101,6 +108,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
@ -118,6 +127,8 @@ github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
|||||||
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
||||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 h1:BWVtt2VBY+lmVDu9MGKqLGKl04B+iRHcrW1Ptyi/8tg=
|
||||||
|
github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2/go.mod h1:lPnW9HVS0vJdeYyQtOvIvlXgZPNhUAhwz+z5r8AJk0Y=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
@ -130,6 +141,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
|
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
|
||||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
|
||||||
|
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
@ -151,6 +164,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
|
golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
|
||||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
164
login.go
Normal file
164
login.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/tuotoo/qrcode"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
var console = bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
var readLine = func() (str string) {
|
||||||
|
str, _ = console.ReadString('\n')
|
||||||
|
str = strings.TrimSpace(str)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cli *client.QQClient
|
||||||
|
|
||||||
|
// ErrSMSRequestError SMS请求出错
|
||||||
|
var ErrSMSRequestError = errors.New("sms request error")
|
||||||
|
|
||||||
|
func commonLogin() error {
|
||||||
|
res, err := cli.Login()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return loginResponseProcessor(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func qrcodeLogin() error {
|
||||||
|
rsp, err := cli.FetchQRCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fi, err := qrcode.Decode(bytes.NewReader(rsp.ImageData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = ioutil.WriteFile("qrcode.png", rsp.ImageData, 0644)
|
||||||
|
log.Infof("请使用手机QQ扫描二维码 (qrcode.png) : ")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
qrcodeTerminal.New().Get(fi.Content).Print()
|
||||||
|
s, err := cli.QueryQRCodeStatus(rsp.Sig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prevState := s.State
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
s, _ = cli.QueryQRCodeStatus(rsp.Sig)
|
||||||
|
if s == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if prevState == s.State {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prevState = s.State
|
||||||
|
if s.State == client.QRCodeCanceled {
|
||||||
|
log.Fatalf("扫码被用户取消.")
|
||||||
|
}
|
||||||
|
if s.State == client.QRCodeTimeout {
|
||||||
|
log.Fatalf("二维码过期")
|
||||||
|
}
|
||||||
|
if s.State == client.QRCodeWaitingForConfirm {
|
||||||
|
log.Infof("扫码成功, 请在手机端确认登录.")
|
||||||
|
}
|
||||||
|
if s.State == client.QRCodeConfirmed {
|
||||||
|
res, err := cli.QRCodeLogin(s.LoginInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return loginResponseProcessor(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginResponseProcessor(res *client.LoginResponse) error {
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Success {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var text string
|
||||||
|
switch res.Error {
|
||||||
|
case client.SliderNeededError:
|
||||||
|
log.Warnf("登录需要滑条验证码. ")
|
||||||
|
log.Warnf("请参考文档 -> https://github.com/Mrs4s/go-cqhttp/blob/master/docs/slider.md <- 抓包获取 Ticket")
|
||||||
|
println()
|
||||||
|
log.Warnf("请用浏览器打开 -> %v <- 并获取Ticket.", res.VerifyUrl)
|
||||||
|
println()
|
||||||
|
log.Warn("请输入Ticket: (Enter 提交)")
|
||||||
|
text = readLine()
|
||||||
|
res, err = cli.SubmitTicket(text)
|
||||||
|
continue
|
||||||
|
case client.NeedCaptcha:
|
||||||
|
log.Warnf("登录需要滑条验证码.")
|
||||||
|
_ = ioutil.WriteFile("captcha.jpg", res.CaptchaImage, 0644)
|
||||||
|
log.Warnf("请输入验证码 (captcha.jpg): (Enter 提交)")
|
||||||
|
text = readLine()
|
||||||
|
global.DelFile("captcha.jpg")
|
||||||
|
res, err = cli.SubmitCaptcha(text, res.CaptchaSign)
|
||||||
|
continue
|
||||||
|
case client.SMSNeededError:
|
||||||
|
log.Warnf("账号已开启设备锁, 按 Enter 向手机 %v 发送短信验证码.", res.SMSPhone)
|
||||||
|
readLine()
|
||||||
|
if !cli.RequestSMS() {
|
||||||
|
log.Warnf("发送验证码失败,可能是请求过于频繁.")
|
||||||
|
return errors.WithStack(ErrSMSRequestError)
|
||||||
|
}
|
||||||
|
log.Warn("请输入短信验证码: (Enter 提交)")
|
||||||
|
text = readLine()
|
||||||
|
res, err = cli.SubmitSMS(text)
|
||||||
|
continue
|
||||||
|
case client.SMSOrVerifyNeededError:
|
||||||
|
log.Warnf("账号已开启设备锁,请选择验证方式:")
|
||||||
|
log.Warnf("1. 向手机 %v 发送短信验证码", res.SMSPhone)
|
||||||
|
log.Warnf("2. 使用手机QQ扫码验证.")
|
||||||
|
log.Warn("请输入(1 - 2):")
|
||||||
|
text = readLine()
|
||||||
|
if strings.Contains(text, "1") {
|
||||||
|
if !cli.RequestSMS() {
|
||||||
|
log.Warnf("发送验证码失败,可能是请求过于频繁.")
|
||||||
|
return errors.WithStack(ErrSMSRequestError)
|
||||||
|
}
|
||||||
|
log.Warn("请输入短信验证码: (Enter 提交)")
|
||||||
|
text = readLine()
|
||||||
|
res, err = cli.SubmitSMS(text)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case client.UnsafeDeviceError:
|
||||||
|
log.Warnf("账号已开启设备锁,请前往 -> %v <- 验证后重启Bot.", res.VerifyUrl)
|
||||||
|
log.Infof("按 Enter 继续....")
|
||||||
|
readLine()
|
||||||
|
os.Exit(0)
|
||||||
|
case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError:
|
||||||
|
msg := res.ErrorMessage
|
||||||
|
if strings.Contains(msg, "版本") {
|
||||||
|
msg = "密码错误或账号被冻结"
|
||||||
|
}
|
||||||
|
if strings.Contains(msg, "冻结") {
|
||||||
|
log.Fatalf("账号被冻结")
|
||||||
|
}
|
||||||
|
log.Warnf("登录失败: %v", msg)
|
||||||
|
log.Infof("按 Enter 继续....")
|
||||||
|
readLine()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
131
main.go
131
main.go
@ -13,13 +13,12 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -168,11 +167,11 @@ func main() {
|
|||||||
time.Sleep(time.Second * 10)
|
time.Sleep(time.Second * 10)
|
||||||
}
|
}
|
||||||
if conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "") {
|
if conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "") {
|
||||||
log.Warnf("请修改 %s 以添加账号密码.", global.DefaultConfFile)
|
log.Warn("账号密码未配置, 将使用二维码登录.")
|
||||||
if !isFastStart {
|
if !isFastStart {
|
||||||
|
log.Warn("将在 5秒 后继续.")
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("当前版本:", coolq.Version)
|
log.Info("当前版本:", coolq.Version)
|
||||||
@ -241,7 +240,7 @@ func main() {
|
|||||||
log.Fatalf("加密存储的密码损坏,请尝试重新配置密码")
|
log.Fatalf("加密存储的密码损坏,请尝试重新配置密码")
|
||||||
}
|
}
|
||||||
copy(global.PasswordHash[:], ph)
|
copy(global.PasswordHash[:], ph)
|
||||||
} else {
|
} else if conf.Password != "" {
|
||||||
global.PasswordHash = md5.Sum([]byte(conf.Password))
|
global.PasswordHash = md5.Sum([]byte(conf.Password))
|
||||||
}
|
}
|
||||||
if !isFastStart {
|
if !isFastStart {
|
||||||
@ -262,7 +261,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
return "未知"
|
return "未知"
|
||||||
}())
|
}())
|
||||||
cli := client.NewClientMd5(conf.Uin, global.PasswordHash)
|
cli = client.NewClientEmpty()
|
||||||
|
if conf.Uin != 0 && global.PasswordHash != [16]byte{} {
|
||||||
|
cli.Uin = conf.Uin
|
||||||
|
cli.PasswordMd5 = global.PasswordHash
|
||||||
|
}
|
||||||
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case "INFO":
|
case "INFO":
|
||||||
@ -289,34 +292,102 @@ func main() {
|
|||||||
log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ")
|
log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ")
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
if conf.WebUI == nil {
|
/*
|
||||||
conf.WebUI = &global.GoCQWebUI{
|
if conf.WebUI == nil {
|
||||||
Enabled: true,
|
conf.WebUI = &global.GoCQWebUI{
|
||||||
WebInput: false,
|
Enabled: true,
|
||||||
Host: "0.0.0.0",
|
WebInput: false,
|
||||||
WebUIPort: 9999,
|
Host: "0.0.0.0",
|
||||||
|
WebUIPort: 9999,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if conf.WebUI.WebUIPort <= 0 {
|
||||||
|
conf.WebUI.WebUIPort = 9999
|
||||||
|
}
|
||||||
|
if conf.WebUI.Host == "" {
|
||||||
|
conf.WebUI.Host = "127.0.0.1"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
global.Proxy = conf.ProxyRewrite
|
||||||
|
// b := server.WebServer.Run(fmt.Sprintf("%s:%d", conf.WebUI.Host, conf.WebUI.WebUIPort), cli)
|
||||||
|
// c := server.Console
|
||||||
|
isQRCodeLogin := conf.Uin == 0 || len(conf.Password) == 0
|
||||||
|
if !isQRCodeLogin {
|
||||||
|
if err := commonLogin(); err != nil {
|
||||||
|
log.Fatalf("登录时发生致命错误: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := qrcodeLogin(); err != nil {
|
||||||
|
log.Fatalf("登录时发生致命错误: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if conf.WebUI.WebUIPort <= 0 {
|
var times uint = 1 // 重试次数
|
||||||
conf.WebUI.WebUIPort = 9999
|
var reLoginLock sync.Mutex
|
||||||
|
cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) {
|
||||||
|
reLoginLock.Lock()
|
||||||
|
defer reLoginLock.Unlock()
|
||||||
|
log.Warnf("Bot已离线: %v", e.Message)
|
||||||
|
if !conf.ReLogin.Enabled {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if isQRCodeLogin {
|
||||||
|
log.Fatalf("二维码登录暂不支持重连.")
|
||||||
|
}
|
||||||
|
if times > conf.ReLogin.MaxReloginTimes && conf.ReLogin.MaxReloginTimes != 0 {
|
||||||
|
log.Fatalf("Bot重连次数超过限制, 停止")
|
||||||
|
}
|
||||||
|
if conf.ReLogin.ReLoginDelay > 0 {
|
||||||
|
log.Warnf("将在 %v 秒后尝试重连. 重连次数:%v/%v", conf.ReLogin.ReLoginDelay, times, conf.ReLogin.MaxReloginTimes)
|
||||||
|
}
|
||||||
|
log.Warnf("尝试重连...")
|
||||||
|
if cli.Online {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := commonLogin(); err != nil {
|
||||||
|
log.Fatalf("登录时发生致命错误: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cli.AllowSlider = true
|
||||||
|
log.Infof("登录成功 欢迎使用: %v", cli.Nickname)
|
||||||
|
log.Info("开始加载好友列表...")
|
||||||
|
global.Check(cli.ReloadFriendList())
|
||||||
|
log.Infof("共加载 %v 个好友.", len(cli.FriendList))
|
||||||
|
log.Infof("开始加载群列表...")
|
||||||
|
global.Check(cli.ReloadGroupList())
|
||||||
|
log.Infof("共加载 %v 个群.", len(cli.GroupList))
|
||||||
|
bot := coolq.NewQQBot(cli, conf)
|
||||||
|
if conf.PostMessageFormat != "string" && conf.PostMessageFormat != "array" {
|
||||||
|
log.Warnf("post_message_format 配置错误, 将自动使用 string")
|
||||||
|
coolq.SetMessageFormat("string")
|
||||||
|
} else {
|
||||||
|
coolq.SetMessageFormat(conf.PostMessageFormat)
|
||||||
}
|
}
|
||||||
if conf.WebUI.Host == "" {
|
if conf.RateLimit.Enabled {
|
||||||
conf.WebUI.Host = "127.0.0.1"
|
global.InitLimiter(conf.RateLimit.Frequency, conf.RateLimit.BucketSize)
|
||||||
}
|
}
|
||||||
global.Proxy = conf.ProxyRewrite
|
log.Info("正在加载事件过滤器.")
|
||||||
b := server.WebServer.Run(fmt.Sprintf("%s:%d", conf.WebUI.Host, conf.WebUI.WebUIPort), cli)
|
global.BootFilter()
|
||||||
c := server.Console
|
coolq.IgnoreInvalidCQCode = conf.IgnoreInvalidCQCode
|
||||||
r := server.Restart
|
coolq.SplitURL = conf.FixURL
|
||||||
|
coolq.ForceFragmented = conf.ForceFragmented
|
||||||
|
if conf.HTTPConfig != nil && conf.HTTPConfig.Enabled {
|
||||||
|
go server.CQHTTPApiServer.Run(fmt.Sprintf("%s:%d", conf.HTTPConfig.Host, conf.HTTPConfig.Port), conf.AccessToken, bot)
|
||||||
|
for k, v := range conf.HTTPConfig.PostUrls {
|
||||||
|
server.NewHTTPClient().Run(k, v, conf.HTTPConfig.Timeout, bot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if conf.WSConfig != nil && conf.WSConfig.Enabled {
|
||||||
|
go server.WebSocketServer.Run(fmt.Sprintf("%s:%d", conf.WSConfig.Host, conf.WSConfig.Port), conf.AccessToken, bot)
|
||||||
|
}
|
||||||
|
for _, rc := range conf.ReverseServers {
|
||||||
|
go server.NewWebSocketClient(rc, conf.AccessToken, bot).Run()
|
||||||
|
}
|
||||||
|
log.Info("资源初始化完成, 开始处理信息.")
|
||||||
|
log.Info("アトリは、高性能ですから!")
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
go checkUpdate()
|
go checkUpdate()
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
select {
|
<-c
|
||||||
case <-c:
|
|
||||||
b.Release()
|
|
||||||
case <-r:
|
|
||||||
log.Info("正在重启中...")
|
|
||||||
defer b.Release()
|
|
||||||
restart(arg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordHashEncrypt 使用key加密给定passwordHash
|
// PasswordHashEncrypt 使用key加密给定passwordHash
|
||||||
@ -439,7 +510,7 @@ func selfUpdate(imageURL string) {
|
|||||||
log.Error("更新失败: ", err)
|
log.Error("更新失败: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
wc := global.WriteCounter{}
|
wc := global.WriteCounter{}
|
||||||
err, _ = global.UpdateFromStream(io.TeeReader(resp.Body, &wc))
|
err, _ = global.UpdateFromStream(io.TeeReader(resp.Body, &wc))
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
@ -463,6 +534,7 @@ func selfUpdate(imageURL string) {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func restart(args []string) {
|
func restart(args []string) {
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
@ -493,6 +565,7 @@ func restart(args []string) {
|
|||||||
}
|
}
|
||||||
_ = cmd.Start()
|
_ = cmd.Start()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func getConfig() *global.JSONConfig {
|
func getConfig() *global.JSONConfig {
|
||||||
var conf *global.JSONConfig
|
var conf *global.JSONConfig
|
||||||
|
@ -2,7 +2,6 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
@ -192,17 +191,19 @@ func getGroupHonorInfo(bot *coolq.CQBot, p resultGetter) coolq.MSG {
|
|||||||
return bot.CQGetGroupHonorInfo(p.Get("group_id").Int(), p.Get("type").Str)
|
return bot.CQGetGroupHonorInfo(p.Get("group_id").Int(), p.Get("type").Str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRestart(_ *coolq.CQBot, p resultGetter) coolq.MSG {
|
func setRestart(_ *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||||
var delay int64
|
/*
|
||||||
delay = p.Get("delay").Int()
|
var delay int64
|
||||||
if delay < 0 {
|
delay = p.Get("delay").Int()
|
||||||
delay = 0
|
if delay < 0 {
|
||||||
}
|
delay = 0
|
||||||
defer func(delay int64) {
|
}
|
||||||
time.Sleep(time.Duration(delay) * time.Millisecond)
|
defer func(delay int64) {
|
||||||
Restart <- struct{}{}
|
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||||
}(delay)
|
Restart <- struct{}{}
|
||||||
return coolq.MSG{"data": nil, "retcode": 0, "status": "async"}
|
}(delay)
|
||||||
|
*/
|
||||||
|
return coolq.MSG{"data": nil, "retcode": 99, "msg": "restart un-supported now", "wording": "restart函数暂不兼容", "status": "failed"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func canSendImage(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
func canSendImage(bot *coolq.CQBot, _ resultGetter) coolq.MSG {
|
||||||
|
@ -1,661 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
|
||||||
"github.com/gin-contrib/pprof"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
asciiart "github.com/yinghau76/go-ascii-art"
|
|
||||||
)
|
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
|
|
||||||
// WebInput 网页输入channel
|
|
||||||
var WebInput = make(chan string, 1) // 长度1,用于阻塞
|
|
||||||
|
|
||||||
// Console 控制台channel
|
|
||||||
var Console = make(chan os.Signal, 1)
|
|
||||||
|
|
||||||
// Restart 重启信号监听channel
|
|
||||||
var Restart = make(chan struct{}, 1)
|
|
||||||
|
|
||||||
// JSONConfig go-cqhttp配置
|
|
||||||
var JSONConfig *global.JSONConfig
|
|
||||||
|
|
||||||
type webServer struct {
|
|
||||||
engine *gin.Engine
|
|
||||||
bot *coolq.CQBot
|
|
||||||
Cli *client.QQClient
|
|
||||||
Conf *global.JSONConfig // old config
|
|
||||||
Console *bufio.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebServer Admin子站的Server
|
|
||||||
var WebServer = &webServer{}
|
|
||||||
|
|
||||||
// APIAdminRoutingTable Admin子站的路由映射
|
|
||||||
var APIAdminRoutingTable = map[string]func(s *webServer, c *gin.Context){
|
|
||||||
"do_restart": AdminDoRestart, // 热重启
|
|
||||||
"do_process_restart": AdminProcessRestart, // 进程重启
|
|
||||||
"get_web_write": AdminWebWrite, // 获取是否验证码输入
|
|
||||||
"do_web_write": AdminDoWebWrite, // web上进行输入操作
|
|
||||||
"do_restart_docker": AdminDoRestartDocker, // 直接停止(依赖supervisord/docker)重新拉起
|
|
||||||
"do_config_base": AdminDoConfigBase, // 修改config.json中的基础部分
|
|
||||||
"do_config_http": AdminDoConfigHTTP, // 修改config.json的http部分
|
|
||||||
"do_config_ws": AdminDoConfigWS, // 修改config.json的正向ws部分
|
|
||||||
"do_config_reverse": AdminDoConfigReverseWS, // 修改config.json 中的反向ws部分
|
|
||||||
"do_config_json": AdminDoConfigJSON, // 直接修改 config.json配置
|
|
||||||
"get_config_json": AdminGetConfigJSON, // 拉取 当前的config.json配置
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed 构建失败返回MSG
|
|
||||||
func Failed(code int, msg string) coolq.MSG {
|
|
||||||
return coolq.MSG{"data": nil, "retcode": code, "status": "failed", "msg": msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *webServer) Run(addr string, cli *client.QQClient) *coolq.CQBot {
|
|
||||||
s.Cli = cli
|
|
||||||
s.Conf = GetConf()
|
|
||||||
JSONConfig = s.Conf
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
s.engine = gin.New()
|
|
||||||
|
|
||||||
s.engine.Use(AuthMiddleWare())
|
|
||||||
|
|
||||||
// 通用路由
|
|
||||||
s.engine.Any("/admin/:action", s.admin)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// 开启端口监听
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.Enabled {
|
|
||||||
if Debug {
|
|
||||||
pprof.Register(s.engine)
|
|
||||||
log.Debugf("pprof 性能分析服务已启动在 http://%v/debug/pprof, 如果有任何性能问题请下载报告并提交给开发者", addr)
|
|
||||||
time.Sleep(time.Second * 3)
|
|
||||||
}
|
|
||||||
log.Infof("Admin API 服务器已启动: %v", addr)
|
|
||||||
err := s.engine.Run(addr)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
log.Infof("请检查端口是否被占用.")
|
|
||||||
c := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
||||||
<-c
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 关闭端口监听
|
|
||||||
c := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
||||||
<-c
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
s.Dologin()
|
|
||||||
s.UpServer()
|
|
||||||
b := s.bot // 外部引入 bot对象,用于操作bot
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// logincore 登录核心实现
|
|
||||||
func (s *webServer) logincore(relogin bool) {
|
|
||||||
s.Console = bufio.NewReader(os.Stdin)
|
|
||||||
readLine := func() (str string) {
|
|
||||||
str, _ = s.Console.ReadString('\n')
|
|
||||||
str = strings.TrimSpace(str)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Cli.Online {
|
|
||||||
log.Warn("Bot已登录")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var times uint = 1 // 重试次数
|
|
||||||
for res, err := s.Cli.Login(); ; res, err = s.Cli.Login() {
|
|
||||||
var text string
|
|
||||||
count := 0
|
|
||||||
|
|
||||||
if res == nil {
|
|
||||||
goto Relogin
|
|
||||||
}
|
|
||||||
|
|
||||||
Again: // 不执行 s.Cli.Login() 的循环,适用输入验证码等更新 res 的操作
|
|
||||||
if err == nil && res.Success { // 登录成功
|
|
||||||
break
|
|
||||||
} else if err == client.ErrAlreadyOnline {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch res.Error {
|
|
||||||
case client.SliderNeededError:
|
|
||||||
log.Warnf("登录需要滑条验证码, 请选择解决方案: ")
|
|
||||||
log.Warnf("1. 自行抓包. (推荐)")
|
|
||||||
log.Warnf("2. 使用Cef自动处理.")
|
|
||||||
log.Warnf("3. 不提交滑块并继续.(可能会导致上网环境异常错误)")
|
|
||||||
log.Warnf("详细信息请参考文档 -> https://github.com/Mrs4s/go-cqhttp/blob/master/docs/slider.md <-")
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("请输入(1 - 3): (http://%s:%d/admin/do_web_write 输入)", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Warn("请输入(1 - 3):")
|
|
||||||
text = readLine()
|
|
||||||
}
|
|
||||||
if strings.Contains(text, "1") {
|
|
||||||
log.Warnf("请用浏览器打开 -> %v <- 并获取Ticket.", res.VerifyUrl)
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("请输入Ticket: (http://%s:%d/admin/do_web_write 输入)", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Warn("请输入Ticket: (Enter 提交)")
|
|
||||||
text = readLine()
|
|
||||||
}
|
|
||||||
res, err = s.Cli.SubmitTicket(strings.TrimSpace(text))
|
|
||||||
goto Again
|
|
||||||
}
|
|
||||||
if strings.Contains(text, "3") {
|
|
||||||
s.Cli.AllowSlider = false
|
|
||||||
s.Cli.Disconnect()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
id := utils.RandomStringRange(6, "0123456789")
|
|
||||||
log.Warnf("滑块ID为 %v 请在30S内处理.", id)
|
|
||||||
ticket, err := global.GetSliderTicket(res.VerifyUrl, id)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("错误: " + err.Error())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
res, err = s.Cli.SubmitTicket(ticket)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("错误: " + err.Error())
|
|
||||||
continue // 尝试重新登录
|
|
||||||
}
|
|
||||||
goto Again
|
|
||||||
case client.NeedCaptcha:
|
|
||||||
_ = ioutil.WriteFile("captcha.jpg", res.CaptchaImage, 0644)
|
|
||||||
img, _, _ := image.Decode(bytes.NewReader(res.CaptchaImage))
|
|
||||||
fmt.Println(asciiart.New("image", img).Art)
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("请输入验证码 (captcha.jpg): (http://%s:%d/admin/do_web_write 输入)", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Warn("请输入验证码 (captcha.jpg): (Enter 提交)")
|
|
||||||
text = readLine()
|
|
||||||
}
|
|
||||||
global.DelFile("captcha.jpg")
|
|
||||||
res, err = s.Cli.SubmitCaptcha(strings.ReplaceAll(text, "\n", ""), res.CaptchaSign)
|
|
||||||
goto Again
|
|
||||||
case client.SMSNeededError:
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("账号已开启设备锁, 已向手机 %v 发送短信验证码.", res.SMSPhone)
|
|
||||||
} else {
|
|
||||||
log.Warnf("账号已开启设备锁, 按下 Enter 向手机 %v 发送短信验证码.", res.SMSPhone)
|
|
||||||
readLine()
|
|
||||||
}
|
|
||||||
if !s.Cli.RequestSMS() {
|
|
||||||
log.Warnf("发送验证码失败,可能是请求过于频繁.")
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("请输入短信验证码: (http://%s:%d/admin/do_web_write 输入)", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Warn("请输入短信验证码: (Enter 提交)")
|
|
||||||
text = readLine()
|
|
||||||
}
|
|
||||||
res, err = s.Cli.SubmitSMS(strings.ReplaceAll(strings.ReplaceAll(text, "\n", ""), "\r", ""))
|
|
||||||
goto Again
|
|
||||||
case client.SMSOrVerifyNeededError:
|
|
||||||
log.Warnf("账号已开启设备锁,请选择验证方式:")
|
|
||||||
log.Warnf("1. 向手机 %v 发送短信验证码", res.SMSPhone)
|
|
||||||
log.Warnf("2. 使用手机QQ扫码验证.")
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("请输入(1 - 2): (http://%s:%d/admin/do_web_write 输入)", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Warn("请输入(1 - 2):")
|
|
||||||
text = readLine()
|
|
||||||
}
|
|
||||||
if strings.Contains(text, "1") {
|
|
||||||
if !s.Cli.RequestSMS() {
|
|
||||||
log.Warnf("发送验证码失败,可能是请求过于频繁.")
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Warnf("请输入短信验证码: (http://%s:%d/admin/do_web_write 输入)....", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Warn("请输入短信验证码: (Enter 提交)")
|
|
||||||
text = readLine()
|
|
||||||
}
|
|
||||||
res, err = s.Cli.SubmitSMS(strings.ReplaceAll(strings.ReplaceAll(text, "\n", ""), "\r", ""))
|
|
||||||
goto Again
|
|
||||||
}
|
|
||||||
log.Warnf("请前往 -> %v <- 验证.", res.VerifyUrl)
|
|
||||||
log.Infof("按 Enter 继续....")
|
|
||||||
readLine()
|
|
||||||
continue
|
|
||||||
case client.UnsafeDeviceError:
|
|
||||||
log.Warnf("账号已开启设备锁,请前往 -> %v <- 验证.", res.VerifyUrl)
|
|
||||||
if s.Conf.WebUI != nil && s.Conf.WebUI.WebInput {
|
|
||||||
log.Infof(" (http://%s:%d/admin/do_web_write 确认后继续)....", s.Conf.WebUI.Host, s.Conf.WebUI.WebUIPort)
|
|
||||||
text = <-WebInput
|
|
||||||
} else {
|
|
||||||
log.Infof("按 Enter 继续....")
|
|
||||||
readLine()
|
|
||||||
}
|
|
||||||
log.Info(text)
|
|
||||||
continue
|
|
||||||
case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError:
|
|
||||||
msg := res.ErrorMessage
|
|
||||||
if strings.Contains(msg, "版本") {
|
|
||||||
msg = "密码错误或账号被冻结"
|
|
||||||
}
|
|
||||||
if strings.Contains(msg, "上网环境") && count < 5 {
|
|
||||||
s.Cli.Disconnect()
|
|
||||||
log.Warnf("错误: 当前上网环境异常. 将更换服务器并重试.")
|
|
||||||
count++
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(msg, "冻结") {
|
|
||||||
log.Fatalf("账号被冻结, 放弃重连")
|
|
||||||
}
|
|
||||||
log.Warnf("登录失败: %v", msg)
|
|
||||||
log.Infof("按 Enter 继续....")
|
|
||||||
readLine()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
Relogin:
|
|
||||||
if relogin {
|
|
||||||
if times > s.Conf.ReLogin.MaxReloginTimes && s.Conf.ReLogin.MaxReloginTimes != 0 {
|
|
||||||
log.Fatal("重连失败: 重连次数达到设置的上限值")
|
|
||||||
s.bot.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Warnf("将在 %v 秒后尝试重连. 重连次数:%v", s.Conf.ReLogin.ReLoginDelay, times)
|
|
||||||
times++
|
|
||||||
time.Sleep(time.Second * time.Duration(s.Conf.ReLogin.ReLoginDelay))
|
|
||||||
s.Cli.Disconnect()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if relogin {
|
|
||||||
log.Info("重连成功")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dologin 主程序登录
|
|
||||||
func (s *webServer) Dologin() {
|
|
||||||
s.Cli.AllowSlider = true
|
|
||||||
s.logincore(false)
|
|
||||||
log.Infof("登录成功 欢迎使用: %v", s.Cli.Nickname)
|
|
||||||
log.Info("开始加载好友列表...")
|
|
||||||
global.Check(s.Cli.ReloadFriendList())
|
|
||||||
log.Infof("共加载 %v 个好友.", len(s.Cli.FriendList))
|
|
||||||
log.Infof("开始加载群列表...")
|
|
||||||
global.Check(s.Cli.ReloadGroupList())
|
|
||||||
log.Infof("共加载 %v 个群.", len(s.Cli.GroupList))
|
|
||||||
s.bot = coolq.NewQQBot(s.Cli, s.Conf)
|
|
||||||
if s.Conf.PostMessageFormat != "string" && s.Conf.PostMessageFormat != "array" {
|
|
||||||
log.Warnf("post_message_format 配置错误, 将自动使用 string")
|
|
||||||
coolq.SetMessageFormat("string")
|
|
||||||
} else {
|
|
||||||
coolq.SetMessageFormat(s.Conf.PostMessageFormat)
|
|
||||||
}
|
|
||||||
if s.Conf.RateLimit.Enabled {
|
|
||||||
global.InitLimiter(s.Conf.RateLimit.Frequency, s.Conf.RateLimit.BucketSize)
|
|
||||||
}
|
|
||||||
log.Info("正在加载事件过滤器.")
|
|
||||||
global.BootFilter()
|
|
||||||
coolq.IgnoreInvalidCQCode = s.Conf.IgnoreInvalidCQCode
|
|
||||||
coolq.SplitURL = s.Conf.FixURL
|
|
||||||
coolq.ForceFragmented = s.Conf.ForceFragmented
|
|
||||||
log.Info("资源初始化完成, 开始处理信息.")
|
|
||||||
log.Info("アトリは、高性能ですから!")
|
|
||||||
|
|
||||||
s.Cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) {
|
|
||||||
if !s.Conf.ReLogin.Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Warnf("Bot已离线 (%v),尝试重连", e.Message)
|
|
||||||
s.logincore(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *webServer) admin(c *gin.Context) {
|
|
||||||
action := c.Param("action")
|
|
||||||
log.Debugf("WebServer接收到cgi调用: %v", action)
|
|
||||||
if f, ok := APIAdminRoutingTable[action]; ok {
|
|
||||||
f(s, c)
|
|
||||||
} else {
|
|
||||||
c.JSON(200, coolq.Failed(404))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConf 获取当前配置文件信息
|
|
||||||
func GetConf() *global.JSONConfig {
|
|
||||||
if JSONConfig != nil {
|
|
||||||
return JSONConfig
|
|
||||||
}
|
|
||||||
conf := global.LoadConfig(global.DefaultConfFile)
|
|
||||||
return conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthMiddleWare Admin控制器登录验证
|
|
||||||
func AuthMiddleWare() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
// 处理跨域问题
|
|
||||||
c.Header("Access-Control-Allow-Origin", "*")
|
|
||||||
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
|
|
||||||
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH, DELETE")
|
|
||||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
|
|
||||||
c.Header("Access-Control-Allow-Credentials", "true")
|
|
||||||
// 放行所有OPTIONS方法,因为有的模板是要请求两次的
|
|
||||||
if c.Request.Method == "OPTIONS" {
|
|
||||||
c.AbortWithStatus(http.StatusNoContent)
|
|
||||||
}
|
|
||||||
if strings.Contains(c.Request.URL.Path, "debug") {
|
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 处理请求
|
|
||||||
if c.Request.Method != "GET" && c.Request.Method != "POST" {
|
|
||||||
log.Warnf("已拒绝客户端 %v 的请求: 方法错误", c.Request.RemoteAddr)
|
|
||||||
c.Status(404)
|
|
||||||
c.Abort()
|
|
||||||
}
|
|
||||||
if c.Request.Method == "POST" && strings.Contains(c.Request.Header.Get("Content-Type"), "application/json") {
|
|
||||||
d, err := c.GetRawData()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("获取请求 %v 的Body时出现错误: %v", c.Request.RequestURI, err)
|
|
||||||
c.Status(400)
|
|
||||||
c.Abort()
|
|
||||||
}
|
|
||||||
if !gjson.ValidBytes(d) {
|
|
||||||
log.Warnf("已拒绝客户端 %v 的请求: 非法Json", c.Request.RemoteAddr)
|
|
||||||
c.Status(400)
|
|
||||||
c.Abort()
|
|
||||||
}
|
|
||||||
c.Set("json_body", gjson.ParseBytes(d))
|
|
||||||
}
|
|
||||||
authToken := conf.AccessToken
|
|
||||||
auth := c.Request.Header.Get("Authorization")
|
|
||||||
switch {
|
|
||||||
case auth != "":
|
|
||||||
if strings.SplitN(auth, " ", 2)[1] != authToken {
|
|
||||||
c.AbortWithStatus(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Next()
|
|
||||||
case c.Query("access_token") != authToken:
|
|
||||||
c.AbortWithStatus(401)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *webServer) DoReLogin() { // TODO: 协议层的 ReLogin
|
|
||||||
JSONConfig = nil
|
|
||||||
conf := GetConf()
|
|
||||||
OldConf := s.Conf
|
|
||||||
cli := client.NewClient(conf.Uin, conf.Password)
|
|
||||||
log.Info("开始尝试登录并同步消息...")
|
|
||||||
log.Infof("使用协议: %v", func() string {
|
|
||||||
switch client.SystemDeviceInfo.Protocol {
|
|
||||||
case client.IPad:
|
|
||||||
return "iPad"
|
|
||||||
case client.AndroidPhone:
|
|
||||||
return "Android Phone"
|
|
||||||
case client.AndroidWatch:
|
|
||||||
return "Android Watch"
|
|
||||||
case client.MacOS:
|
|
||||||
return "MacOS"
|
|
||||||
}
|
|
||||||
return "未知"
|
|
||||||
}())
|
|
||||||
cli.OnLog(func(c *client.QQClient, e *client.LogEvent) {
|
|
||||||
switch e.Type {
|
|
||||||
case "INFO":
|
|
||||||
log.Info("Protocol -> " + e.Message)
|
|
||||||
case "ERROR":
|
|
||||||
log.Error("Protocol -> " + e.Message)
|
|
||||||
case "DEBUG":
|
|
||||||
log.Debug("Protocol -> " + e.Message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
cli.OnServerUpdated(func(bot *client.QQClient, e *client.ServerUpdatedEvent) bool {
|
|
||||||
if !conf.UseSSOAddress {
|
|
||||||
log.Infof("收到服务器地址更新通知, 根据配置文件已忽略.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ")
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
s.Cli = cli
|
|
||||||
s.Dologin()
|
|
||||||
// 关闭之前的 server
|
|
||||||
if OldConf.HTTPConfig != nil && OldConf.HTTPConfig.Enabled {
|
|
||||||
cqHTTPServer.ShutDown()
|
|
||||||
}
|
|
||||||
// if OldConf.WSConfig != nil && OldConf.WSConfig.Enabled {
|
|
||||||
// server.WsShutdown()
|
|
||||||
// }
|
|
||||||
// s.UpServer()
|
|
||||||
|
|
||||||
s.ReloadServer()
|
|
||||||
s.Conf = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *webServer) UpServer() {
|
|
||||||
conf := GetConf()
|
|
||||||
if conf.HTTPConfig != nil && conf.HTTPConfig.Enabled {
|
|
||||||
go cqHTTPServer.Run(fmt.Sprintf("%s:%d", conf.HTTPConfig.Host, conf.HTTPConfig.Port), conf.AccessToken, s.bot)
|
|
||||||
for k, v := range conf.HTTPConfig.PostUrls {
|
|
||||||
newHTTPClient().Run(k, v, conf.HTTPConfig.Timeout, s.bot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if conf.WSConfig != nil && conf.WSConfig.Enabled {
|
|
||||||
go WebSocketServer.Run(fmt.Sprintf("%s:%d", conf.WSConfig.Host, conf.WSConfig.Port), conf.AccessToken, s.bot)
|
|
||||||
}
|
|
||||||
for _, rc := range conf.ReverseServers {
|
|
||||||
go NewWebSocketClient(rc, conf.AccessToken, s.bot).Run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 暂不支持ws服务的重启
|
|
||||||
func (s *webServer) ReloadServer() {
|
|
||||||
conf := GetConf()
|
|
||||||
if conf.HTTPConfig != nil && conf.HTTPConfig.Enabled {
|
|
||||||
go cqHTTPServer.Run(fmt.Sprintf("%s:%d", conf.HTTPConfig.Host, conf.HTTPConfig.Port), conf.AccessToken, s.bot)
|
|
||||||
for k, v := range conf.HTTPConfig.PostUrls {
|
|
||||||
newHTTPClient().Run(k, v, conf.HTTPConfig.Timeout, s.bot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, rc := range conf.ReverseServers {
|
|
||||||
go NewWebSocketClient(rc, conf.AccessToken, s.bot).Run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoRestart 热重启
|
|
||||||
func AdminDoRestart(s *webServer, c *gin.Context) {
|
|
||||||
s.bot.Release()
|
|
||||||
s.bot = nil
|
|
||||||
s.Cli = nil
|
|
||||||
s.DoReLogin()
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminProcessRestart 进程重启
|
|
||||||
func AdminProcessRestart(s *webServer, c *gin.Context) {
|
|
||||||
Restart <- struct{}{}
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoRestartDocker 冷重启
|
|
||||||
func AdminDoRestartDocker(s *webServer, c *gin.Context) {
|
|
||||||
Console <- os.Kill
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminWebWrite web输入html页面
|
|
||||||
func AdminWebWrite(s *webServer, c *gin.Context) {
|
|
||||||
pic := global.ReadAllText("captcha.jpg")
|
|
||||||
var picbase64 string
|
|
||||||
var ispic = false
|
|
||||||
if pic != "" {
|
|
||||||
input := []byte(pic)
|
|
||||||
// base64编码
|
|
||||||
picbase64 = base64.StdEncoding.EncodeToString(input)
|
|
||||||
ispic = true
|
|
||||||
}
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{
|
|
||||||
"ispic": ispic, // 为空则为 设备锁 或者没有需要输入
|
|
||||||
"picbase64": picbase64, // web上显示图片
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoWebWrite web输入处理
|
|
||||||
func AdminDoWebWrite(s *webServer, c *gin.Context) {
|
|
||||||
input := c.PostForm("input")
|
|
||||||
WebInput <- input
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoConfigBase 普通配置修改
|
|
||||||
func AdminDoConfigBase(s *webServer, c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
conf.Uin, _ = strconv.ParseInt(c.PostForm("uin"), 10, 64)
|
|
||||||
conf.Password = c.PostForm("password")
|
|
||||||
if c.PostForm("enable_db") == "true" {
|
|
||||||
conf.EnableDB = true
|
|
||||||
} else {
|
|
||||||
conf.EnableDB = false
|
|
||||||
}
|
|
||||||
conf.AccessToken = c.PostForm("access_token")
|
|
||||||
if err := conf.Save(global.DefaultConfFile); err != nil {
|
|
||||||
log.Fatalf("保存 %s 时出现错误: %v", global.DefaultConfFile, err)
|
|
||||||
c.JSON(200, Failed(502, "保存 "+global.DefaultConfFile+" 时出现错误:"+fmt.Sprintf("%v", err)))
|
|
||||||
} else {
|
|
||||||
JSONConfig = nil
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoConfigHTTP HTTP配置修改
|
|
||||||
func AdminDoConfigHTTP(s *webServer, c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
p, _ := strconv.ParseUint(c.PostForm("port"), 10, 16)
|
|
||||||
conf.HTTPConfig.Port = uint16(p)
|
|
||||||
conf.HTTPConfig.Host = c.PostForm("host")
|
|
||||||
if c.PostForm("enable") == "true" {
|
|
||||||
conf.HTTPConfig.Enabled = true
|
|
||||||
} else {
|
|
||||||
conf.HTTPConfig.Enabled = false
|
|
||||||
}
|
|
||||||
t, _ := strconv.ParseInt(c.PostForm("timeout"), 10, 32)
|
|
||||||
conf.HTTPConfig.Timeout = int32(t)
|
|
||||||
if c.PostForm("post_url") != "" {
|
|
||||||
conf.HTTPConfig.PostUrls[c.PostForm("post_url")] = c.PostForm("post_secret")
|
|
||||||
}
|
|
||||||
if err := conf.Save(global.DefaultConfFile); err != nil {
|
|
||||||
log.Fatalf("保存 %s 时出现错误: %v", global.DefaultConfFile, err)
|
|
||||||
c.JSON(200, Failed(502, "保存 "+global.DefaultConfFile+" 时出现错误:"+fmt.Sprintf("%v", err)))
|
|
||||||
} else {
|
|
||||||
JSONConfig = nil
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoConfigWS ws配置修改
|
|
||||||
func AdminDoConfigWS(s *webServer, c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
p, _ := strconv.ParseUint(c.PostForm("port"), 10, 16)
|
|
||||||
conf.WSConfig.Port = uint16(p)
|
|
||||||
conf.WSConfig.Host = c.PostForm("host")
|
|
||||||
if c.PostForm("enable") == "true" {
|
|
||||||
conf.WSConfig.Enabled = true
|
|
||||||
} else {
|
|
||||||
conf.WSConfig.Enabled = false
|
|
||||||
}
|
|
||||||
if err := conf.Save(global.DefaultConfFile); err != nil {
|
|
||||||
log.Fatalf("保存 %s 时出现错误: %v", global.DefaultConfFile, err)
|
|
||||||
c.JSON(200, Failed(502, "保存 "+global.DefaultConfFile+" 时出现错误:"+fmt.Sprintf("%v", err)))
|
|
||||||
} else {
|
|
||||||
JSONConfig = nil
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoConfigReverseWS 反向ws配置修改
|
|
||||||
func AdminDoConfigReverseWS(s *webServer, c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
conf.ReverseServers[0].ReverseAPIURL = c.PostForm("reverse_api_url")
|
|
||||||
conf.ReverseServers[0].ReverseURL = c.PostForm("reverse_url")
|
|
||||||
conf.ReverseServers[0].ReverseEventURL = c.PostForm("reverse_event_url")
|
|
||||||
t, _ := strconv.ParseUint(c.PostForm("reverse_reconnect_interval"), 10, 16)
|
|
||||||
conf.ReverseServers[0].ReverseReconnectInterval = uint16(t)
|
|
||||||
if c.PostForm("enable") == "true" {
|
|
||||||
conf.ReverseServers[0].Enabled = true
|
|
||||||
} else {
|
|
||||||
conf.ReverseServers[0].Enabled = false
|
|
||||||
}
|
|
||||||
if err := conf.Save(global.DefaultConfFile); err != nil {
|
|
||||||
log.Fatalf("保存 %s 时出现错误: %v", global.DefaultConfFile, err)
|
|
||||||
c.JSON(200, Failed(502, "保存 "+global.DefaultConfFile+" 时出现错误:"+fmt.Sprintf("%v", err)))
|
|
||||||
} else {
|
|
||||||
JSONConfig = nil
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminDoConfigJSON config.hjson配置修改
|
|
||||||
func AdminDoConfigJSON(s *webServer, c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
JSON := c.PostForm("json")
|
|
||||||
err := json.Unmarshal([]byte(JSON), &conf)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("尝试加载配置文件 %v 时出现错误: %v", global.DefaultConfFile, err)
|
|
||||||
c.JSON(200, Failed(502, "保存 "+global.DefaultConfFile+" 时出现错误:"+fmt.Sprintf("%v", err)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := conf.Save(global.DefaultConfFile); err != nil {
|
|
||||||
log.Fatalf("保存 %s 时出现错误: %v", global.DefaultConfFile, err)
|
|
||||||
c.JSON(200, Failed(502, "保存 "+global.DefaultConfFile+" 时出现错误:"+fmt.Sprintf("%v", err)))
|
|
||||||
} else {
|
|
||||||
JSONConfig = nil
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdminGetConfigJSON 拉取config.hjson配置
|
|
||||||
func AdminGetConfigJSON(s *webServer, c *gin.Context) {
|
|
||||||
conf := GetConf()
|
|
||||||
c.JSON(200, coolq.OK(coolq.MSG{"config": conf}))
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
// daemon 功能写在这,目前仅支持了-d 作为后台运行参数,stop,start,restart这些功能目前看起来并不需要,可以通过api控制,后续需要的话再补全。
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
|
// daemon 功能写在这,目前仅支持了-d 作为后台运行参数,stop,start,restart这些功能目前看起来并不需要,可以通过api控制,后续需要的话再补全。
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -29,7 +29,8 @@ type httpServer struct {
|
|||||||
api apiCaller
|
api apiCaller
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpClient struct {
|
// HTTPClient 反向HTTP上报客户端
|
||||||
|
type HTTPClient struct {
|
||||||
bot *coolq.CQBot
|
bot *coolq.CQBot
|
||||||
secret string
|
secret string
|
||||||
addr string
|
addr string
|
||||||
@ -40,7 +41,8 @@ type httpContext struct {
|
|||||||
ctx *gin.Context
|
ctx *gin.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
var cqHTTPServer = &httpServer{}
|
// CQHTTPApiServer CQHTTPApiServer实例
|
||||||
|
var CQHTTPApiServer = &httpServer{}
|
||||||
|
|
||||||
// Debug 是否启用Debug模式
|
// Debug 是否启用Debug模式
|
||||||
var Debug = false
|
var Debug = false
|
||||||
@ -107,11 +109,13 @@ func (s *httpServer) Run(addr, authToken string, bot *coolq.CQBot) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHTTPClient() *httpClient {
|
// NewHTTPClient 返回反向HTTP客户端
|
||||||
return &httpClient{}
|
func NewHTTPClient() *HTTPClient {
|
||||||
|
return &HTTPClient{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) Run(addr, secret string, timeout int32, bot *coolq.CQBot) {
|
// Run 运行反向HTTP服务
|
||||||
|
func (c *HTTPClient) Run(addr, secret string, timeout int32, bot *coolq.CQBot) {
|
||||||
c.bot = bot
|
c.bot = bot
|
||||||
c.secret = secret
|
c.secret = secret
|
||||||
c.addr = addr
|
c.addr = addr
|
||||||
@ -123,7 +127,7 @@ func (c *httpClient) Run(addr, secret string, timeout int32, bot *coolq.CQBot) {
|
|||||||
log.Infof("HTTP POST上报器已启动: %v", addr)
|
log.Infof("HTTP POST上报器已启动: %v", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) onBotPushEvent(m *bytes.Buffer) {
|
func (c *HTTPClient) onBotPushEvent(m *bytes.Buffer) {
|
||||||
var res string
|
var res string
|
||||||
err := gout.POST(c.addr).SetJSON(m.Bytes()).BindBody(&res).SetHeader(func() gout.H {
|
err := gout.POST(c.addr).SetJSON(m.Bytes()).BindBody(&res).SetHeader(func() gout.H {
|
||||||
h := gout.H{
|
h := gout.H{
|
||||||
|
@ -177,7 +177,7 @@ func (c *WebSocketClient) connectUniversal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *WebSocketClient) listenAPI(conn *webSocketConn, u bool) {
|
func (c *WebSocketClient) listenAPI(conn *webSocketConn, u bool) {
|
||||||
defer conn.Close()
|
defer func() { _ = conn.Close() }()
|
||||||
for {
|
for {
|
||||||
buffer := global.NewBuffer()
|
buffer := global.NewBuffer()
|
||||||
t, reader, err := conn.NextReader()
|
t, reader, err := conn.NextReader()
|
||||||
@ -258,7 +258,7 @@ func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) {
|
|||||||
err = c.WriteMessage(websocket.TextMessage, []byte(s.handshake))
|
err = c.WriteMessage(websocket.TextMessage, []byte(s.handshake))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("WebSocket 握手时出现错误: %v", err)
|
log.Warnf("WebSocket 握手时出现错误: %v", err)
|
||||||
c.Close()
|
_ = c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) {
|
|||||||
err = c.WriteMessage(websocket.TextMessage, []byte(s.handshake))
|
err = c.WriteMessage(websocket.TextMessage, []byte(s.handshake))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("WebSocket 握手时出现错误: %v", err)
|
log.Warnf("WebSocket 握手时出现错误: %v", err)
|
||||||
c.Close()
|
_ = c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("接受 WebSocket 连接: %v (/)", r.RemoteAddr)
|
log.Infof("接受 WebSocket 连接: %v (/)", r.RemoteAddr)
|
||||||
@ -321,7 +321,7 @@ func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *webSocketServer) listenAPI(c *webSocketConn) {
|
func (s *webSocketServer) listenAPI(c *webSocketConn) {
|
||||||
defer c.Close()
|
defer func() { _ = c.Close() }()
|
||||||
for {
|
for {
|
||||||
buffer := global.NewBuffer()
|
buffer := global.NewBuffer()
|
||||||
t, reader, err := c.NextReader()
|
t, reader, err := c.NextReader()
|
||||||
@ -348,7 +348,7 @@ func (c *webSocketConn) handleRequest(_ *coolq.CQBot, payload []byte) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.Printf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack())
|
log.Printf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack())
|
||||||
c.Close()
|
_ = c.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
global.RateLimit(context.Background())
|
global.RateLimit(context.Background())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user