1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-06-30 03:43:25 +00:00

Compare commits

...

40 Commits

Author SHA1 Message Date
00d80d5dfc fix #107. 2020-08-16 19:35:38 +08:00
22d9ddb4ea try to fix #115. 2020-08-16 19:31:40 +08:00
fa33dbdd4e Merge branch 'master' of https://github.com/Mrs4s/go-cqhttp 2020-08-16 19:28:34 +08:00
be8b68c8e9 update MiraiGo & async loading supported. close #111 2020-08-16 19:27:43 +08:00
cef129eff7 Merge pull request #109 from SJTU-Plus/master
fix #103
2020-08-16 02:26:42 +08:00
1fac06f58a fix #103 2020-08-15 23:47:29 +08:00
1a1f860dbe fix #108. 2020-08-15 16:07:56 +08:00
f325b26e1a doc update. close #102 2020-08-14 13:41:48 +08:00
b545e05a9d Merge branch 'master' of https://github.com/Mrs4s/go-cqhttp 2020-08-14 08:28:55 +08:00
b290a0a596 try to fix group image upload failed. #65 2020-08-14 08:28:37 +08:00
774a1e32da Merge pull request #96 from mirai-qq/master
remove os.ModePerm
2020-08-13 21:24:15 +08:00
50eee15a67 remove os.ModePerm 2020-08-13 20:53:42 +08:00
edf6180e1c fix ci failed. 2020-08-13 20:09:57 +08:00
3c04573e82 fix #89. 2020-08-13 20:07:21 +08:00
5e49820319 update README.md 2020-08-13 17:21:54 +08:00
1418e36bab cq code 'video' supported. 2020-08-13 17:18:16 +08:00
611d16d79e update README.md 2020-08-13 11:14:21 +08:00
c6f701e8c8 Merge pull request #84 from Shigma/master
adjust unescape order
2020-08-12 12:32:06 +08:00
a49a57b964 adjust unescape order 2020-08-12 12:23:48 +08:00
bb03315930 try to support escape again. #9 2020-08-12 09:52:32 +08:00
5683db6324 fix #80. 2020-08-12 09:33:25 +08:00
f1ffa17d20 update MiraiGo. 2020-08-12 09:19:00 +08:00
69675aa0f9 fix #75. 2020-08-12 05:01:16 +08:00
782a3e2a39 add default 'http_config.post_message_format' value. 2020-08-11 17:47:16 +08:00
1967e687af fix perm error. 2020-08-11 17:38:33 +08:00
23d436972c save captcha to local file. close #71 2020-08-11 17:35:03 +08:00
129622dd24 fix reverse push issue. 2020-08-11 17:32:09 +08:00
19104d00a3 Merge pull request #78 from yyuueexxiinngg/patch-1
移除上报消息为array时的文本转义
2020-08-11 15:32:45 +08:00
6c0e78b60c Remove content escape when using array post message format. 2020-08-11 15:22:26 +08:00
a075f41a51 http post timeout setting supported. close #77 2020-08-11 15:19:54 +08:00
cdbf903361 Merge pull request #74 from Shigma/api-type-check
sendXXX api type check
2020-08-11 11:20:17 +08:00
906247fcf0 Merge pull request #70 from XYenon/master
Add support for post_message_format: array
2020-08-11 04:54:37 +08:00
72a7430841 add api type check 2020-08-10 20:43:31 +08:00
5c4f586c36 Use string format in db 2020-08-10 19:44:54 +08:00
5d2e202b0c Merge pull request #47 from purerosefallen/patch-1
Add Dockerfile
2020-08-10 16:57:34 +08:00
c27ebadbc4 Add support for post_message_format: array 2020-08-10 13:59:46 +08:00
d5a8f3ead2 Merge pull request #69 from Shigma/master
& escape should come first
2020-08-10 12:51:26 +08:00
03bfd9dd3d & escape should come first 2020-08-10 12:48:50 +08:00
fdd517ee8c dockerignore 2020-08-07 14:36:00 +08:00
2d010326c7 Add a Dockerfile and more configurable environment variables 2020-08-07 14:34:47 +08:00
15 changed files with 285 additions and 63 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
.gitlab-ci.yml
.dockerignore
Dockerfile
README.md
LICENSE

21
Dockerfile Normal file
View File

@ -0,0 +1,21 @@
FROM golang:1.14.2-alpine AS builder
RUN go env -w GO111MODULE=auto \
&& go env -w CGO_ENABLED=0 \
&& mkdir /build
WORKDIR /build
COPY ./ .
RUN cd /build \
&& go build -ldflags "-s -w -extldflags '-static'" -o cqhttp
FROM alpine:latest
COPY --from=builder /build/cqhttp /usr/bin/cqhttp
RUN chmod +x /usr/bin/cqhttp
WORKDIR /data
ENTRYPOINT [ "/usr/bin/cqhttp" ]

View File

@ -28,6 +28,7 @@
- [CQ:image]
- [CQ:record]
- [CQ:video]
- [CQ:face]
- [CQ:at]
- [CQ:share]
@ -91,6 +92,16 @@
</details>
# 关于ISSUE
以下ISSUE会被直接关闭
- 提交BUG不使用Template
- 询问已知问题
- 提问找不到重点
- 重复提问
> 请注意, 开发者并没有义务回复您的问题. 您应该具备基本的提问技巧。
# 性能
在关闭数据库的情况下, 加载25个好友128个群运行24小时后内存使用为10MB左右. 开启数据库后内存使用将根据消息量增加10-20MB, 如果系统内存小于128M建议关闭数据库使用.

View File

@ -124,7 +124,13 @@ func (bot *CQBot) CQSendGroupMessage(groupId int64, i interface{}) MSG {
// fix at display
for _, e := range elem {
if at, ok := e.(*message.AtElement); ok && at.Target != 0 {
at.Display = "@" + bot.Client.FindGroup(groupId).FindMember(at.Target).DisplayName()
at.Display = "@" + func() string {
mem := bot.Client.FindGroup(groupId).FindMember(at.Target)
if mem != nil {
return mem.DisplayName()
}
return strconv.FormatInt(at.Target, 10)
}()
}
}
mid := bot.SendGroupMessage(groupId, &message.SendingMessage{Elements: elem})
@ -404,12 +410,12 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) MSG {
}
case "request":
reqType := context.Get("request_type").Str
if context.Get("approve").Bool() {
if operation.Get("approve").Exists() {
if reqType == "friend" {
bot.CQProcessFriendRequest(context.Get("flag").Str, true)
bot.CQProcessFriendRequest(context.Get("flag").Str, operation.Get("approve").Bool())
}
if reqType == "group" {
bot.CQProcessGroupRequest(context.Get("flag").Str, context.Get("sub_type").Str, true)
bot.CQProcessGroupRequest(context.Get("flag").Str, context.Get("sub_type").Str, operation.Get("approve").Bool())
}
}
}
@ -439,14 +445,14 @@ func (bot *CQBot) CQGetForwardMessage(resId string) MSG {
}
var r []MSG
for _, n := range m.Nodes {
checkMedia(n.Message)
bot.checkMedia(n.Message)
r = append(r, MSG{
"sender": MSG{
"user_id": n.SenderId,
"nickname": n.SenderName,
},
"time": n.Time,
"content": ToStringMessage(n.Message, 0, false),
"content": ToFormattedMessage(n.Message, 0, false),
})
}
return OK(MSG{

View File

@ -12,6 +12,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/xujiajun/nutsdb"
"hash/crc32"
"math/rand"
"path"
"sync"
"time"
@ -85,7 +86,7 @@ func (bot *CQBot) GetGroupMessage(mid int32) MSG {
if err == nil {
return m
}
log.Warnf("获取信息时出现错误: %v", err)
log.Warnf("获取信息时出现错误: %v id: %v", err, mid)
}
return nil
}
@ -94,6 +95,7 @@ func (bot *CQBot) SendGroupMessage(groupId int64, m *message.SendingMessage) int
var newElem []message.IMessageElement
for _, elem := range m.Elements {
if i, ok := elem.(*message.ImageElement); ok {
_, _ = bot.Client.UploadGroupImage(int64(rand.Intn(11451419)), i.Data)
gm, err := bot.Client.UploadGroupImage(groupId, i.Data)
if err != nil {
log.Warnf("警告: 群 %v 消息图片上传失败: %v", groupId, err)
@ -191,7 +193,7 @@ func (bot *CQBot) dispatchEventMessage(m MSG) {
fn(m)
end := time.Now()
if end.Sub(start) > time.Second*5 {
log.Debugf("警告: 事件处理耗时超过 5 秒 (%v), 请检查应用是否有堵塞.", end.Sub(start)/time.Second)
log.Debugf("警告: 事件处理耗时超过 5 秒 (%v), 请检查应用是否有堵塞.", end.Sub(start))
}
}()
}

View File

@ -23,6 +23,88 @@ var matchReg = regexp.MustCompile(`\[CQ:\w+?.*?]`)
var typeReg = regexp.MustCompile(`\[CQ:(\w+)`)
var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`)
func ToArrayMessage(e []message.IMessageElement, code int64, raw ...bool) (r []MSG) {
ur := false
if len(raw) != 0 {
ur = raw[0]
}
for _, elem := range e {
m := MSG{}
switch o := elem.(type) {
case *message.TextElement:
m = MSG{
"type": "text",
"data": map[string]string{"text": o.Content},
}
case *message.AtElement:
if o.Target == 0 {
m = MSG{
"type": "at",
"data": map[string]string{"qq": "all"},
}
} else {
m = MSG{
"type": "at",
"data": map[string]string{"qq": fmt.Sprint(o.Target)},
}
}
case *message.ReplyElement:
m = MSG{
"type": "reply",
"data": map[string]string{"id": fmt.Sprint(ToGlobalId(code, o.ReplySeq))},
}
case *message.ForwardElement:
m = MSG{
"type": "forward",
"data": map[string]string{"id": o.ResId},
}
case *message.FaceElement:
m = MSG{
"type": "face",
"data": map[string]string{"id": fmt.Sprint(o.Index)},
}
case *message.VoiceElement:
if ur {
m = MSG{
"type": "record",
"data": map[string]string{"file": o.Name},
}
} else {
m = MSG{
"type": "record",
"data": map[string]string{"file": o.Name, "url": o.Url},
}
}
case *message.ShortVideoElement:
if ur {
m = MSG{
"type": "video",
"data": map[string]string{"file": o.Name},
}
} else {
m = MSG{
"type": "video",
"data": map[string]string{"file": o.Name, "url": o.Url},
}
}
case *message.ImageElement:
if ur {
m = MSG{
"type": "image",
"data": map[string]string{"file": o.Filename},
}
} else {
m = MSG{
"type": "image",
"data": map[string]string{"file": o.Filename, "url": o.Url},
}
}
}
r = append(r, m)
}
return
}
func ToStringMessage(e []message.IMessageElement, code int64, raw ...bool) (r string) {
ur := false
if len(raw) != 0 {
@ -48,13 +130,19 @@ func ToStringMessage(e []message.IMessageElement, code int64, raw ...bool) (r st
if ur {
r += fmt.Sprintf(`[CQ:record,file=%s]`, o.Name)
} else {
r += fmt.Sprintf(`[CQ:record,file=%s,url=%s]`, o.Name, o.Url)
r += fmt.Sprintf(`[CQ:record,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url))
}
case *message.ShortVideoElement:
if ur {
r += fmt.Sprintf(`[CQ:video,file=%s]`, o.Name)
} else {
r += fmt.Sprintf(`[CQ:video,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url))
}
case *message.ImageElement:
if ur {
r += fmt.Sprintf(`[CQ:image,file=%s]`, o.Filename)
} else {
r += fmt.Sprintf(`[CQ:image,file=%s,url=%s]`, o.Filename, o.Url)
r += fmt.Sprintf(`[CQ:image,file=%s,url=%s]`, o.Filename, CQCodeEscapeValue(o.Url))
}
}
}
@ -75,7 +163,7 @@ func (bot *CQBot) ConvertStringMessage(m string, group bool) (r []message.IMessa
ps := paramReg.FindAllStringSubmatch(code, -1)
d := make(map[string]string)
for _, p := range ps {
d[p[1]] = p[2]
d[p[1]] = CQCodeUnescapeValue(p[2])
}
if t == "reply" && group {
if len(r) > 0 {
@ -323,9 +411,15 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (message.
func CQCodeEscapeText(raw string) string {
ret := raw
ret = strings.ReplaceAll(ret, "&", "&amp;")
ret = strings.ReplaceAll(ret, "[", "&#91;")
ret = strings.ReplaceAll(ret, "]", "&#93;")
ret = strings.ReplaceAll(ret, "&", "&amp;")
return ret
}
func CQCodeEscapeValue(value string) string {
ret := CQCodeEscapeText(value)
ret = strings.ReplaceAll(ret, ",", "&#44;")
return ret
}
@ -336,3 +430,9 @@ func CQCodeUnescapeText(content string) string {
ret = strings.ReplaceAll(ret, "&amp;", "&")
return ret
}
func CQCodeUnescapeValue(content string) string {
ret := strings.ReplaceAll(content, "&#44;", ",")
ret = CQCodeUnescapeText(ret)
return ret
}

View File

@ -14,8 +14,23 @@ import (
"time"
)
var format = "string"
func SetMessageFormat(f string) {
format = f
}
func ToFormattedMessage(e []message.IMessageElement, code int64, raw ...bool) (r interface{}) {
if format == "string" {
r = ToStringMessage(e, code, raw...)
} else if format == "array" {
r = ToArrayMessage(e, code, raw...)
}
return
}
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
checkMedia(m.Elements)
bot.checkMedia(m.Elements)
cqm := ToStringMessage(m.Elements, 0, true)
log.Infof("收到好友 %v(%v) 的消息: %v", m.Sender.DisplayName(), m.Sender.Uin, cqm)
fm := MSG{
@ -24,7 +39,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
"sub_type": "friend",
"message_id": ToGlobalId(m.Sender.Uin, m.Id),
"user_id": m.Sender.Uin,
"message": ToStringMessage(m.Elements, 0, false),
"message": ToFormattedMessage(m.Elements, 0, false),
"raw_message": cqm,
"font": 0,
"self_id": c.Uin,
@ -40,7 +55,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
}
func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) {
checkMedia(m.Elements)
bot.checkMedia(m.Elements)
for _, elem := range m.Elements {
if file, ok := elem.(*message.GroupFileElement); ok {
log.Infof("群 %v(%v) 内 %v(%v) 上传了文件: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, file.Name)
@ -72,7 +87,7 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
"anonymous": nil,
"font": 0,
"group_id": m.GroupCode,
"message": ToStringMessage(m.Elements, m.GroupCode, false),
"message": ToFormattedMessage(m.Elements, m.GroupCode, false),
"message_id": id,
"message_type": "group",
"post_type": "message",
@ -118,7 +133,7 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
}
func (bot *CQBot) tempMessageEvent(c *client.QQClient, m *message.TempMessage) {
checkMedia(m.Elements)
bot.checkMedia(m.Elements)
cqm := ToStringMessage(m.Elements, 0, true)
bot.tempMsgCache.Store(m.Sender.Uin, m.GroupCode)
log.Infof("收到来自群 %v(%v) 内 %v(%v) 的临时会话消息: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm)
@ -128,7 +143,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, m *message.TempMessage) {
"sub_type": "group",
"message_id": m.Id,
"user_id": m.Sender.Uin,
"message": ToStringMessage(m.Elements, 0, false),
"message": ToFormattedMessage(m.Elements, 0, false),
"raw_message": cqm,
"font": 0,
"self_id": c.Uin,
@ -301,7 +316,7 @@ func (bot *CQBot) groupJoinReqEvent(c *client.QQClient, e *client.UserJoinGroupR
"sub_type": "add",
"group_id": e.GroupCode,
"user_id": e.RequesterUin,
"comment": "",
"comment": e.Message,
"flag": flag,
"time": time.Now().Unix(),
"self_id": c.Uin,
@ -347,7 +362,7 @@ func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.Group
}
}
func checkMedia(e []message.IMessageElement) {
func (bot *CQBot) checkMedia(e []message.IMessageElement) {
for _, elem := range e {
switch i := elem.(type) {
case *message.ImageElement:
@ -358,7 +373,7 @@ func checkMedia(e []message.IMessageElement) {
w.WriteUInt32(uint32(i.Size))
w.WriteString(i.Filename)
w.WriteString(i.Url)
}), 0777)
}), 0644)
}
i.Filename = filename
case *message.VoiceElement:
@ -370,8 +385,20 @@ func checkMedia(e []message.IMessageElement) {
log.Warnf("语音文件 %v 下载失败: %v", i.Name, err)
continue
}
_ = ioutil.WriteFile(path.Join(global.VOICE_PATH, i.Name), b, 0777)
_ = ioutil.WriteFile(path.Join(global.VOICE_PATH, i.Name), b, 0644)
}
case *message.ShortVideoElement:
filename := hex.EncodeToString(i.Md5) + ".video"
if !global.PathExists(path.Join(global.VIDEO_PATH, filename)) {
_ = ioutil.WriteFile(path.Join(global.VIDEO_PATH, filename), binary.NewWriterF(func(w *binary.Writer) {
w.Write(i.Md5)
w.WriteUInt32(uint32(i.Size))
w.WriteString(i.Name)
w.Write(i.Uuid)
}), 0644)
}
i.Name = filename
i.Url = bot.Client.GetShortVideoUrl(i.Uuid, i.Md5)
}
}
}

View File

@ -24,11 +24,13 @@ go-cqhttp 支持导入CQHTTP的配置文件, 具体步骤为:
"access_token": "",
"relogin": false,
"relogin_delay": 0,
"post_message_format": "string",
"http_config": {
"enabled": true,
"host": "0.0.0.0",
"port": 5700,
"post_urls": {"url:port": "secret"}
"timeout": 5,
"post_urls": {"url:port": "secret"},
},
"ws_config": {
"enabled": true,

View File

@ -14,9 +14,11 @@ type JsonConfig struct {
AccessToken string `json:"access_token"`
ReLogin bool `json:"relogin"`
ReLoginDelay int `json:"relogin_delay"`
AsyncLoad bool `json:"async_load"`
HttpConfig *GoCQHttpConfig `json:"http_config"`
WSConfig *GoCQWebsocketConfig `json:"ws_config"`
ReverseServers []*GoCQReverseWebsocketConfig `json:"ws_reverse_servers"`
PostMessageFormat string `json:"post_message_format"`
Debug bool `json:"debug"`
}
@ -43,6 +45,7 @@ type GoCQHttpConfig struct {
Enabled bool `json:"enabled"`
Host string `json:"host"`
Port uint16 `json:"port"`
Timeout int32 `json:"timeout"`
PostUrls map[string]string `json:"post_urls"`
}
@ -62,9 +65,10 @@ type GoCQReverseWebsocketConfig struct {
func DefaultConfig() *JsonConfig {
return &JsonConfig{
EnableDB: true,
ReLogin: true,
ReLoginDelay: 3,
EnableDB: true,
ReLogin: true,
ReLoginDelay: 3,
PostMessageFormat: "string",
HttpConfig: &GoCQHttpConfig{
Enabled: true,
Host: "0.0.0.0",

View File

@ -11,6 +11,8 @@ var IMAGE_PATH = path.Join("data", "images")
var VOICE_PATH = path.Join("data", "voices")
var VIDEO_PATH = path.Join("data", "videos")
func PathExists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
@ -25,7 +27,7 @@ func ReadAllText(path string) string {
}
func WriteAllText(path, text string) {
_ = ioutil.WriteFile(path, []byte(text), 0777)
_ = ioutil.WriteFile(path, []byte(text), 0644)
}
func Check(err error) {

2
go.mod
View File

@ -3,7 +3,7 @@ module github.com/Mrs4s/go-cqhttp
go 1.14
require (
github.com/Mrs4s/MiraiGo v0.0.0-20200810032556-a425f9d1b98e
github.com/Mrs4s/MiraiGo v0.0.0-20200816111850-988a766ae224
github.com/gin-gonic/gin v1.6.3
github.com/gorilla/websocket v1.4.2
github.com/guonaihong/gout v0.1.1

8
go.sum
View File

@ -1,9 +1,9 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Mrs4s/MiraiGo v0.0.0-20200809221224-7a84cfae6795 h1:Bu4k9ZS/IIy9Shwd9lS/C2P/2I8fYUwg1OpRF91hr1w=
github.com/Mrs4s/MiraiGo v0.0.0-20200809221224-7a84cfae6795/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM=
github.com/Mrs4s/MiraiGo v0.0.0-20200810032556-a425f9d1b98e h1:5LYDouOL9ZgTL5PwZuuSlFYSfboRQjnXqRIlhviRcGE=
github.com/Mrs4s/MiraiGo v0.0.0-20200810032556-a425f9d1b98e/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM=
github.com/Mrs4s/MiraiGo v0.0.0-20200813091456-988a010b51df h1:ERLrnv7bONrg4NqvC8AWhtEgCZk97uCZdRpQS4gF8UE=
github.com/Mrs4s/MiraiGo v0.0.0-20200813091456-988a010b51df/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM=
github.com/Mrs4s/MiraiGo v0.0.0-20200816111850-988a766ae224 h1:tlWc7RpBCh5VhT0H6wzm/knxj2PpAV3J7wQyieF0nkk=
github.com/Mrs4s/MiraiGo v0.0.0-20200816111850-988a766ae224/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM=
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=

27
main.go
View File

@ -37,15 +37,20 @@ func init() {
log.SetOutput(io.MultiWriter(os.Stderr, w))
}
if !global.PathExists(global.IMAGE_PATH) {
if err := os.MkdirAll(global.IMAGE_PATH, 0677); err != nil {
if err := os.MkdirAll(global.IMAGE_PATH, 0755); err != nil {
log.Fatalf("创建图片缓存文件夹失败: %v", err)
}
}
if !global.PathExists(global.VOICE_PATH) {
if err := os.MkdirAll(global.VOICE_PATH, 06777); err != nil {
if err := os.MkdirAll(global.VOICE_PATH, 0755); err != nil {
log.Fatalf("创建语音缓存文件夹失败: %v", err)
}
}
if !global.PathExists(global.VIDEO_PATH) {
if err := os.MkdirAll(global.VIDEO_PATH, 0755); err != nil {
log.Fatalf("创建视频缓存文件夹失败: %v", err)
}
}
if global.PathExists("cqhttp.json") {
log.Info("发现 cqhttp.json 将在五秒后尝试导入配置,按 Ctrl+C 取消.")
log.Warn("警告: 该操作会删除 cqhttp.json 并覆盖 config.json 文件.")
@ -101,7 +106,8 @@ func main() {
Host: "0.0.0.0",
Port: 6700,
},
Debug: os.Getenv("DEBUG") == "true",
PostMessageFormat: "string",
Debug: os.Getenv("DEBUG") == "true",
}
if post != "" {
conf.HttpConfig.PostUrls[post] = os.Getenv("HTTP_SECRET")
@ -129,7 +135,7 @@ func main() {
if !global.PathExists("device.json") {
log.Warn("虚拟设备信息不存在, 将自动生成随机设备.")
client.GenRandomDevice()
_ = ioutil.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0777)
_ = ioutil.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0644)
log.Info("已生成设备信息并保存到 device.json 文件.")
} else {
log.Info("将使用 device.json 内的设备信息运行Bot.")
@ -165,9 +171,10 @@ func main() {
if !rsp.Success {
switch rsp.Error {
case client.NeedCaptcha:
_ = ioutil.WriteFile("captcha.jpg", rsp.CaptchaImage, 0644)
img, _, _ := image.Decode(bytes.NewReader(rsp.CaptchaImage))
fmt.Println(asciiart.New("image", img).Art)
log.Warn("请输入验证码: (Enter 提交)")
log.Warn("请输入验证码 (captcha.jpg) (Enter 提交)")
text, _ := console.ReadString('\n')
rsp, err = cli.SubmitCaptcha(strings.ReplaceAll(text, "\n", ""), rsp.CaptchaSign)
continue
@ -188,13 +195,19 @@ func main() {
global.Check(cli.ReloadFriendList())
log.Infof("共加载 %v 个好友.", len(cli.FriendList))
log.Infof("开始加载群列表...")
global.Check(cli.ReloadGroupList())
global.Check(cli.ReloadGroupList(conf.AsyncLoad))
log.Infof("共加载 %v 个群.", len(cli.GroupList))
b := 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.HttpConfig != nil && conf.HttpConfig.Enabled {
server.HttpServer.Run(fmt.Sprintf("%s:%d", conf.HttpConfig.Host, conf.HttpConfig.Port), conf.AccessToken, b)
for k, v := range conf.HttpConfig.PostUrls {
server.NewHttpClient().Run(k, v, b)
server.NewHttpClient().Run(k, v, conf.HttpConfig.Timeout, b)
}
}
if conf.WSConfig != nil && conf.WSConfig.Enabled {

View File

@ -21,9 +21,10 @@ type httpServer struct {
}
type httpClient struct {
bot *coolq.CQBot
secret string
addr string
bot *coolq.CQBot
secret string
addr string
timeout int32
}
var HttpServer = &httpServer{}
@ -163,10 +164,14 @@ func NewHttpClient() *httpClient {
return &httpClient{}
}
func (c *httpClient) Run(addr, secret string, bot *coolq.CQBot) {
func (c *httpClient) Run(addr, secret string, timeout int32, bot *coolq.CQBot) {
c.bot = bot
c.secret = secret
c.addr = addr
c.timeout = timeout
if c.timeout < 5 {
c.timeout = 5
}
bot.OnEventPush(c.onBotPushEvent)
log.Infof("HTTP POST上报器已启动: %v", addr)
}
@ -184,7 +189,7 @@ func (c *httpClient) onBotPushEvent(m coolq.MSG) {
h["X-Signature"] = "sha1=" + hex.EncodeToString(mac.Sum(nil))
}
return h
}()).SetTimeout(time.Second * 5).Do()
}()).SetTimeout(time.Second * time.Duration(c.timeout)).Do()
if err != nil {
log.Warnf("上报Event数据到 %v 失败: %v", c.addr, err)
return
@ -224,6 +229,14 @@ func (s *httpServer) GetGroupMemberInfo(c *gin.Context) {
}
func (s *httpServer) SendMessage(c *gin.Context) {
if getParam(c, "message_type") == "private" {
s.SendPrivateMessage(c)
return
}
if getParam(c, "message_type") == "group" {
s.SendGroupMessage(c)
return
}
if getParam(c, "group_id") != "" {
s.SendGroupMessage(c)
return
@ -235,8 +248,8 @@ func (s *httpServer) SendMessage(c *gin.Context) {
func (s *httpServer) SendPrivateMessage(c *gin.Context) {
uid, _ := strconv.ParseInt(getParam(c, "user_id"), 10, 64)
msg := getParam(c, "message")
if gjson.Valid(msg) {
msg, t := getParamWithType(c, "message")
if t == gjson.JSON {
c.JSON(200, s.bot.CQSendPrivateMessage(uid, gjson.Parse(msg)))
return
}
@ -245,8 +258,8 @@ func (s *httpServer) SendPrivateMessage(c *gin.Context) {
func (s *httpServer) SendGroupMessage(c *gin.Context) {
gid, _ := strconv.ParseInt(getParam(c, "group_id"), 10, 64)
msg := getParam(c, "message")
if gjson.Valid(msg) {
msg, t := getParamWithType(c, "message")
if t == gjson.JSON {
c.JSON(200, s.bot.CQSendGroupMessage(gid, gjson.Parse(msg)))
return
}
@ -372,14 +385,19 @@ func getParamOrDefault(c *gin.Context, k, def string) string {
}
func getParam(c *gin.Context, k string) string {
p, _ := getParamWithType(c, k)
return p
}
func getParamWithType(c *gin.Context, k string) (string, gjson.Type) {
if q := c.Query(k); q != "" {
return q
return q, gjson.Null
}
if c.Request.Method == "POST" {
if h := c.Request.Header.Get("Content-Type"); h != "" {
if h == "application/x-www-form-urlencoded" {
if p, ok := c.GetPostForm(k); ok {
return p
return p, gjson.Null
}
}
if h == "application/json" {
@ -388,20 +406,20 @@ func getParam(c *gin.Context, k string) string {
if res.Exists() {
switch res.Type {
case gjson.JSON:
return res.Raw
return res.Raw, gjson.JSON
case gjson.String:
return res.Str
return res.Str, gjson.String
case gjson.Number:
return strconv.FormatInt(res.Int(), 10) // 似乎没有需要接受 float 类型的api
return strconv.FormatInt(res.Int(), 10), gjson.Number // 似乎没有需要接受 float 类型的api
case gjson.True:
return "true"
return "true", gjson.True
case gjson.False:
return "false"
return "false", gjson.False
}
}
}
}
}
}
return ""
return "", gjson.Null
}

View File

@ -172,17 +172,16 @@ func (c *websocketClient) listenApi(conn *wsc.Conn, u bool) {
ret["echo"] = j.Get("echo").Value()
}
c.pushLock.Lock()
log.Debugf("准备发送API %v 处理结果: %v", t, ret.ToJson())
_, _ = conn.Write([]byte(ret.ToJson()))
c.pushLock.Unlock()
}
}
if c.conf.ReverseReconnectInterval != 0 {
time.Sleep(time.Millisecond * time.Duration(c.conf.ReverseReconnectInterval))
if u {
c.connectUniversal()
return
if !u {
c.connectApi()
}
c.connectApi()
}
}
@ -191,7 +190,6 @@ func (c *websocketClient) onBotPushEvent(m coolq.MSG) {
defer c.pushLock.Unlock()
if c.eventConn != nil {
log.Debugf("向WS服务器 %v 推送Event: %v", c.eventConn.RemoteAddr().String(), m.ToJson())
_ = c.eventConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
if _, err := c.eventConn.Write([]byte(m.ToJson())); err != nil {
_ = c.eventConn.Close()
if c.conf.ReverseReconnectInterval != 0 {
@ -204,8 +202,15 @@ func (c *websocketClient) onBotPushEvent(m coolq.MSG) {
}
if c.universalConn != nil {
log.Debugf("向WS服务器 %v 推送Event: %v", c.universalConn.RemoteAddr().String(), m.ToJson())
_ = c.universalConn.SetWriteDeadline(time.Now().Add(time.Second * 3))
_, _ = c.universalConn.Write([]byte(m.ToJson()))
if _, err := c.universalConn.Write([]byte(m.ToJson())); err != nil {
_ = c.universalConn.Close()
if c.conf.ReverseReconnectInterval != 0 {
go func() {
time.Sleep(time.Millisecond * time.Duration(c.conf.ReverseReconnectInterval))
c.connectUniversal()
}()
}
}
}
}
@ -332,6 +337,12 @@ var wsApi = map[string]func(*coolq.CQBot, gjson.Result) coolq.MSG{
)
},
"send_msg": func(bot *coolq.CQBot, p gjson.Result) coolq.MSG {
if p.Get("message_type").Str == "private" {
return bot.CQSendPrivateMessage(p.Get("user_id").Int(), p.Get("message"))
}
if p.Get("message_type").Str == "group" {
return bot.CQSendGroupMessage(p.Get("group_id").Int(), p.Get("message"))
}
if p.Get("group_id").Int() != 0 {
return bot.CQSendGroupMessage(p.Get("group_id").Int(), p.Get("message"))
}