Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
G
go-cqhttp
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
go-cqhttp
Commits
e5be47e7
Commit
e5be47e7
authored
Aug 12, 2020
by
nanahira
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of github.com:Mrs4s/go-cqhttp
parents
f10e5e3b
bb033159
Pipeline
#529
failed with stages
in 2 minutes and 21 seconds
Changes
15
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
462 additions
and
98 deletions
+462
-98
.github/ISSUE_TEMPLATE/bug--.md
.github/ISSUE_TEMPLATE/bug--.md
+20
-0
Dockerfile
Dockerfile
+0
-1
README.md
README.md
+1
-0
coolq/api.go
coolq/api.go
+3
-3
coolq/bot.go
coolq/bot.go
+33
-4
coolq/cqcode.go
coolq/cqcode.go
+171
-9
coolq/event.go
coolq/event.go
+51
-10
docs/config.md
docs/config.md
+9
-2
global/config.go
global/config.go
+26
-19
global/fs.go
global/fs.go
+9
-0
go.mod
go.mod
+1
-1
go.sum
go.sum
+6
-2
main.go
main.go
+79
-22
server/http.go
server/http.go
+35
-17
server/websocket.go
server/websocket.go
+18
-8
No files found.
.github/ISSUE_TEMPLATE/bug--.md
0 → 100644
View file @
e5be47e7
---
name
:
Bug汇报
about
:
Create a report to help us improve
title
:
'
'
labels
:
'
'
assignees
:
'
'
---
**环境信息**
请根据实际使用环境修改以下信息
go-cqhttp版本: v0.9.10
运行环境: windows_amd64
连接方式: 反向WS
**bug内容**
请在这里详细描述bug的内容
**复现方法**
请在这里分步骤的描述如何复现这个bug
Dockerfile
View file @
e5be47e7
...
...
@@ -2,7 +2,6 @@ FROM golang:1.14.2-alpine AS builder
RUN
go
env
-w
GO111MODULE
=
auto
\
&&
go
env
-w
CGO_ENABLED
=
0
\
&&
go
env
-w
GOPROXY
=
https://goproxy.cn,https://gocenter.io,https://goproxy.io,direct
\
&&
mkdir
/build
WORKDIR
/build
...
...
README.md
View file @
e5be47e7
...
...
@@ -27,6 +27,7 @@
<summary>
已实现CQ码
</summary>
-
[CQ:image]
-
[CQ:record]
-
[CQ:face]
-
[CQ:at]
-
[CQ:share]
...
...
coolq/api.go
View file @
e5be47e7
...
...
@@ -439,14 +439,14 @@ func (bot *CQBot) CQGetForwardMessage(resId string) MSG {
}
var
r
[]
MSG
for
_
,
n
:=
range
m
.
Nodes
{
check
Image
(
n
.
Message
)
check
Media
(
n
.
Message
)
r
=
append
(
r
,
MSG
{
"sender"
:
MSG
{
"user_id"
:
n
.
SenderId
,
"nickname"
:
n
.
SenderName
,
},
"time"
:
n
.
Time
,
"content"
:
To
String
Message
(
n
.
Message
,
0
,
false
),
"content"
:
To
Formatted
Message
(
n
.
Message
,
0
,
false
),
})
}
return
OK
(
MSG
{
...
...
@@ -477,7 +477,7 @@ func (bot *CQBot) CQCanSendImage() MSG {
}
func
(
bot
*
CQBot
)
CQCanSendRecord
()
MSG
{
return
OK
(
MSG
{
"yes"
:
fals
e
})
return
OK
(
MSG
{
"yes"
:
tru
e
})
}
func
(
bot
*
CQBot
)
CQGetStatus
()
MSG
{
...
...
coolq/bot.go
View file @
e5be47e7
...
...
@@ -14,6 +14,7 @@ import (
"hash/crc32"
"path"
"sync"
"time"
)
type
CQBot
struct
{
...
...
@@ -24,6 +25,7 @@ type CQBot struct {
friendReqCache
sync
.
Map
invitedReqCache
sync
.
Map
joinReqCache
sync
.
Map
tempMsgCache
sync
.
Map
}
type
MSG
map
[
string
]
interface
{}
...
...
@@ -58,6 +60,7 @@ func NewQQBot(cli *client.QQClient, conf *global.JsonConfig) *CQBot {
bot
.
Client
.
OnGroupMemberLeaved
(
bot
.
memberLeaveEvent
)
bot
.
Client
.
OnGroupMemberPermissionChanged
(
bot
.
memberPermissionChangedEvent
)
bot
.
Client
.
OnNewFriendRequest
(
bot
.
friendRequestEvent
)
bot
.
Client
.
OnNewFriendAdded
(
bot
.
friendAddedEvent
)
bot
.
Client
.
OnGroupInvited
(
bot
.
groupInvitedEvent
)
bot
.
Client
.
OnUserWantJoinGroup
(
bot
.
groupJoinReqEvent
)
return
bot
...
...
@@ -99,6 +102,15 @@ func (bot *CQBot) SendGroupMessage(groupId int64, m *message.SendingMessage) int
newElem
=
append
(
newElem
,
gm
)
continue
}
if
i
,
ok
:=
elem
.
(
*
message
.
VoiceElement
);
ok
{
gv
,
err
:=
bot
.
Client
.
UploadGroupPtt
(
groupId
,
i
.
Data
)
if
err
!=
nil
{
log
.
Warnf
(
"警告: 群 %v 消息语音上传失败: %v"
,
groupId
,
err
)
continue
}
newElem
=
append
(
newElem
,
gv
)
continue
}
newElem
=
append
(
newElem
,
elem
)
}
m
.
Elements
=
newElem
...
...
@@ -112,7 +124,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, m *message.SendingMessage) in
if
i
,
ok
:=
elem
.
(
*
message
.
ImageElement
);
ok
{
fm
,
err
:=
bot
.
Client
.
UploadPrivateImage
(
target
,
i
.
Data
)
if
err
!=
nil
{
log
.
Warnf
(
"警告:
好友
%v 消息图片上传失败."
,
target
)
log
.
Warnf
(
"警告:
私聊
%v 消息图片上传失败."
,
target
)
continue
}
newElem
=
append
(
newElem
,
fm
)
...
...
@@ -121,8 +133,17 @@ func (bot *CQBot) SendPrivateMessage(target int64, m *message.SendingMessage) in
newElem
=
append
(
newElem
,
elem
)
}
m
.
Elements
=
newElem
ret
:=
bot
.
Client
.
SendPrivateMessage
(
target
,
m
)
return
ToGlobalId
(
target
,
ret
.
Id
)
var
id
int32
if
bot
.
Client
.
FindFriend
(
target
)
!=
nil
{
id
=
bot
.
Client
.
SendPrivateMessage
(
target
,
m
)
.
Id
}
else
{
if
code
,
ok
:=
bot
.
tempMsgCache
.
Load
(
target
);
ok
{
id
=
bot
.
Client
.
SendTempMessage
(
code
.
(
int64
),
target
,
m
)
.
Id
}
else
{
return
-
1
}
}
return
ToGlobalId
(
target
,
id
)
}
func
(
bot
*
CQBot
)
InsertGroupMessage
(
m
*
message
.
GroupMessage
)
int32
{
...
...
@@ -164,7 +185,15 @@ func (bot *CQBot) Release() {
func
(
bot
*
CQBot
)
dispatchEventMessage
(
m
MSG
)
{
for
_
,
f
:=
range
bot
.
events
{
f
(
m
)
fn
:=
f
go
func
()
{
start
:=
time
.
Now
()
fn
(
m
)
end
:=
time
.
Now
()
if
end
.
Sub
(
start
)
>
time
.
Second
*
5
{
log
.
Debugf
(
"警告: 事件处理耗时超过 5 秒 (%v秒), 请检查应用是否有堵塞."
,
end
.
Sub
(
start
)
/
time
.
Second
)
}
}()
}
}
...
...
coolq/cqcode.go
View file @
e5be47e7
...
...
@@ -23,6 +23,76 @@ 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
.
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
{
...
...
@@ -31,7 +101,7 @@ func ToStringMessage(e []message.IMessageElement, code int64, raw ...bool) (r st
for
_
,
elem
:=
range
e
{
switch
o
:=
elem
.
(
type
)
{
case
*
message
.
TextElement
:
r
+=
o
.
Content
r
+=
CQCodeEscapeText
(
o
.
Content
)
case
*
message
.
AtElement
:
if
o
.
Target
==
0
{
r
+=
"[CQ:at,qq=all]"
...
...
@@ -44,11 +114,17 @@ func ToStringMessage(e []message.IMessageElement, code int64, raw ...bool) (r st
r
+=
fmt
.
Sprintf
(
"[CQ:forward,id=%s]"
,
o
.
ResId
)
case
*
message
.
FaceElement
:
r
+=
fmt
.
Sprintf
(
`[CQ:face,id=%d]`
,
o
.
Index
)
case
*
message
.
VoiceElement
:
if
ur
{
r
+=
fmt
.
Sprintf
(
`[CQ:record,file=%s]`
,
o
.
Name
)
}
else
{
r
+=
fmt
.
Sprintf
(
`[CQ:record,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
)
)
}
}
}
...
...
@@ -61,7 +137,7 @@ func (bot *CQBot) ConvertStringMessage(m string, group bool) (r []message.IMessa
for
_
,
idx
:=
range
i
{
if
idx
[
0
]
>
si
{
text
:=
m
[
si
:
idx
[
0
]]
r
=
append
(
r
,
message
.
NewText
(
text
))
r
=
append
(
r
,
message
.
NewText
(
CQCodeUnescapeText
(
text
)
))
}
code
:=
m
[
idx
[
0
]
:
idx
[
1
]]
si
=
idx
[
1
]
...
...
@@ -69,7 +145,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
{
...
...
@@ -96,13 +172,14 @@ func (bot *CQBot) ConvertStringMessage(m string, group bool) (r []message.IMessa
}
elem
,
err
:=
bot
.
ToElement
(
t
,
d
,
group
)
if
err
!=
nil
{
log
.
Warnf
(
"转换CQ码到MiraiGo Element时出现错误: %v 将忽略本段CQ码."
,
err
)
log
.
Warnf
(
"转换CQ码到MiraiGo Element时出现错误: %v 将原样发送."
,
err
)
r
=
append
(
r
,
message
.
NewText
(
code
))
continue
}
r
=
append
(
r
,
elem
)
}
if
si
!=
len
(
m
)
{
r
=
append
(
r
,
message
.
NewText
(
m
[
si
:
]
))
r
=
append
(
r
,
message
.
NewText
(
CQCodeUnescapeText
(
m
[
si
:
])
))
}
return
}
...
...
@@ -194,12 +271,18 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (message.
return
message
.
NewImage
(
b
),
nil
}
rawPath
:=
path
.
Join
(
global
.
IMAGE_PATH
,
f
)
if
!
global
.
PathExists
(
rawPath
)
&&
global
.
PathExists
(
rawPath
+
".cqimg"
)
{
rawPath
+=
".cqimg"
}
if
!
global
.
PathExists
(
rawPath
)
&&
d
[
"url"
]
!=
""
{
return
bot
.
ToElement
(
t
,
map
[
string
]
string
{
"file"
:
d
[
"url"
]},
group
)
}
if
global
.
PathExists
(
rawPath
)
{
b
,
err
:=
ioutil
.
ReadFile
(
rawPath
)
if
err
!=
nil
{
return
nil
,
err
}
if
path
.
Ext
(
rawPath
)
!=
".image"
{
if
path
.
Ext
(
rawPath
)
!=
".image"
&&
path
.
Ext
(
rawPath
)
!=
".cqimg"
{
return
message
.
NewImage
(
b
),
nil
}
if
len
(
b
)
<
20
{
...
...
@@ -212,9 +295,9 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (message.
kv
:=
strings
.
SplitN
(
line
,
"="
,
2
)
switch
kv
[
0
]
{
case
"md5"
:
hash
,
_
=
hex
.
DecodeString
(
kv
[
1
]
)
hash
,
_
=
hex
.
DecodeString
(
strings
.
ReplaceAll
(
kv
[
1
],
"
\r
"
,
""
)
)
case
"size"
:
t
,
_
:=
strconv
.
Atoi
(
kv
[
1
]
)
t
,
_
:=
strconv
.
Atoi
(
strings
.
ReplaceAll
(
kv
[
1
],
"
\r
"
,
""
)
)
size
=
int32
(
t
)
}
}
...
...
@@ -223,6 +306,12 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (message.
hash
=
r
.
ReadBytes
(
16
)
size
=
r
.
ReadInt32
()
}
if
size
==
0
{
return
nil
,
errors
.
New
(
"img size is 0"
)
}
if
len
(
hash
)
!=
16
{
return
nil
,
errors
.
New
(
"invalid hash"
)
}
if
group
{
rsp
,
err
:=
bot
.
Client
.
QueryGroupImage
(
1
,
hash
,
size
)
if
err
!=
nil
{
...
...
@@ -237,6 +326,51 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (message.
return
rsp
,
nil
}
return
nil
,
errors
.
New
(
"invalid image"
)
case
"record"
:
if
!
group
{
return
nil
,
errors
.
New
(
"private voice unsupported now"
)
}
f
:=
d
[
"file"
]
var
data
[]
byte
if
strings
.
HasPrefix
(
f
,
"http"
)
||
strings
.
HasPrefix
(
f
,
"https"
)
{
b
,
err
:=
global
.
GetBytes
(
f
)
if
err
!=
nil
{
return
nil
,
err
}
data
=
b
}
if
strings
.
HasPrefix
(
f
,
"base64"
)
{
b
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
strings
.
ReplaceAll
(
f
,
"base64://"
,
""
))
if
err
!=
nil
{
return
nil
,
err
}
data
=
b
}
if
strings
.
HasPrefix
(
f
,
"file"
)
{
fu
,
err
:=
url
.
Parse
(
f
)
if
err
!=
nil
{
return
nil
,
err
}
if
strings
.
HasPrefix
(
fu
.
Path
,
"/"
)
&&
runtime
.
GOOS
==
`windows`
{
fu
.
Path
=
fu
.
Path
[
1
:
]
}
b
,
err
:=
ioutil
.
ReadFile
(
fu
.
Path
)
if
err
!=
nil
{
return
nil
,
err
}
data
=
b
}
if
global
.
PathExists
(
path
.
Join
(
global
.
VOICE_PATH
,
f
))
{
b
,
err
:=
ioutil
.
ReadFile
(
path
.
Join
(
global
.
VOICE_PATH
,
f
))
if
err
!=
nil
{
return
nil
,
err
}
data
=
b
}
if
!
global
.
IsAMR
(
data
)
{
return
nil
,
errors
.
New
(
"unsupported voice file format (please use AMR file for now)"
)
}
return
&
message
.
VoiceElement
{
Data
:
data
},
nil
case
"face"
:
id
,
err
:=
strconv
.
Atoi
(
d
[
"id"
])
if
err
!=
nil
{
...
...
@@ -256,3 +390,31 @@ func (bot *CQBot) ToElement(t string, d map[string]string, group bool) (message.
return
nil
,
errors
.
New
(
"unsupported cq code: "
+
t
)
}
}
func
CQCodeEscapeText
(
raw
string
)
string
{
ret
:=
raw
ret
=
strings
.
ReplaceAll
(
ret
,
"&"
,
"&"
)
ret
=
strings
.
ReplaceAll
(
ret
,
"["
,
"["
)
ret
=
strings
.
ReplaceAll
(
ret
,
"]"
,
"]"
)
return
ret
}
func
CQCodeEscapeValue
(
value
string
)
string
{
ret
:=
CQCodeEscapeText
(
value
)
ret
=
strings
.
ReplaceAll
(
ret
,
","
,
","
)
return
ret
}
func
CQCodeUnescapeText
(
content
string
)
string
{
ret
:=
content
ret
=
strings
.
ReplaceAll
(
ret
,
"["
,
"["
)
ret
=
strings
.
ReplaceAll
(
ret
,
"]"
,
"]"
)
ret
=
strings
.
ReplaceAll
(
ret
,
"&"
,
"&"
)
return
ret
}
func
CQCodeUnescapeValue
(
content
string
)
string
{
ret
:=
CQCodeUnescapeText
(
content
)
ret
=
strings
.
ReplaceAll
(
ret
,
","
,
","
)
return
ret
}
coolq/event.go
View file @
e5be47e7
...
...
@@ -10,11 +10,27 @@ import (
"io/ioutil"
"path"
"strconv"
"strings"
"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
)
{
check
Image
(
m
.
Elements
)
check
Media
(
m
.
Elements
)
cqm
:=
ToStringMessage
(
m
.
Elements
,
0
,
true
)
log
.
Infof
(
"收到好友 %v(%v) 的消息: %v"
,
m
.
Sender
.
DisplayName
(),
m
.
Sender
.
Uin
,
cqm
)
fm
:=
MSG
{
...
...
@@ -23,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"
:
To
String
Message
(
m
.
Elements
,
0
,
false
),
"message"
:
To
Formatted
Message
(
m
.
Elements
,
0
,
false
),
"raw_message"
:
cqm
,
"font"
:
0
,
"self_id"
:
c
.
Uin
,
...
...
@@ -39,7 +55,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
}
func
(
bot
*
CQBot
)
groupMessageEvent
(
c
*
client
.
QQClient
,
m
*
message
.
GroupMessage
)
{
check
Image
(
m
.
Elements
)
check
Media
(
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
)
...
...
@@ -71,7 +87,7 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
"anonymous"
:
nil
,
"font"
:
0
,
"group_id"
:
m
.
GroupCode
,
"message"
:
To
String
Message
(
m
.
Elements
,
m
.
GroupCode
,
false
),
"message"
:
To
Formatted
Message
(
m
.
Elements
,
m
.
GroupCode
,
false
),
"message_id"
:
id
,
"message_type"
:
"group"
,
"post_type"
:
"message"
,
...
...
@@ -117,8 +133,9 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
}
func
(
bot
*
CQBot
)
tempMessageEvent
(
c
*
client
.
QQClient
,
m
*
message
.
TempMessage
)
{
check
Image
(
m
.
Elements
)
check
Media
(
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
)
tm
:=
MSG
{
"post_type"
:
"message"
,
...
...
@@ -126,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"
:
To
String
Message
(
m
.
Elements
,
0
,
false
),
"message"
:
To
Formatted
Message
(
m
.
Elements
,
0
,
false
),
"raw_message"
:
cqm
,
"font"
:
0
,
"self_id"
:
c
.
Uin
,
...
...
@@ -260,6 +277,18 @@ func (bot *CQBot) friendRequestEvent(c *client.QQClient, e *client.NewFriendRequ
})
}
func
(
bot
*
CQBot
)
friendAddedEvent
(
c
*
client
.
QQClient
,
e
*
client
.
NewFriendEvent
)
{
log
.
Infof
(
"添加了新好友: %v(%v)"
,
e
.
Friend
.
Nickname
,
e
.
Friend
.
Uin
)
bot
.
tempMsgCache
.
Delete
(
e
.
Friend
.
Uin
)
bot
.
dispatchEventMessage
(
MSG
{
"post_type"
:
"notice"
,
"notice_type"
:
"friend_add"
,
"self_id"
:
c
.
Uin
,
"user_id"
:
e
.
Friend
.
Uin
,
"time"
:
time
.
Now
()
.
Unix
(),
})
}
func
(
bot
*
CQBot
)
groupInvitedEvent
(
c
*
client
.
QQClient
,
e
*
client
.
GroupInvitedRequest
)
{
log
.
Infof
(
"收到来自群 %v(%v) 内用户 %v(%v) 的加群邀请."
,
e
.
GroupName
,
e
.
GroupCode
,
e
.
InvitorNick
,
e
.
InvitorUin
)
flag
:=
strconv
.
FormatInt
(
e
.
RequestId
,
10
)
...
...
@@ -278,7 +307,7 @@ func (bot *CQBot) groupInvitedEvent(c *client.QQClient, e *client.GroupInvitedRe
}
func
(
bot
*
CQBot
)
groupJoinReqEvent
(
c
*
client
.
QQClient
,
e
*
client
.
UserJoinGroupRequest
)
{
log
.
Infof
(
"群 %v(%v) 收到来自用户 %v(%v) 的加群请求."
,
e
.
GroupName
,
e
.
Group
Nam
e
,
e
.
RequesterNick
,
e
.
RequesterUin
)
log
.
Infof
(
"群 %v(%v) 收到来自用户 %v(%v) 的加群请求."
,
e
.
GroupName
,
e
.
Group
Cod
e
,
e
.
RequesterNick
,
e
.
RequesterUin
)
flag
:=
strconv
.
FormatInt
(
e
.
RequestId
,
10
)
bot
.
joinReqCache
.
Store
(
flag
,
e
)
bot
.
dispatchEventMessage
(
MSG
{
...
...
@@ -287,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
,
...
...
@@ -333,9 +362,10 @@ func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.Group
}
}
func
check
Image
(
e
[]
message
.
IMessageElement
)
{
func
check
Media
(
e
[]
message
.
IMessageElement
)
{
for
_
,
elem
:=
range
e
{
if
i
,
ok
:=
elem
.
(
*
message
.
ImageElement
);
ok
{
switch
i
:=
elem
.
(
type
)
{
case
*
message
.
ImageElement
:
filename
:=
hex
.
EncodeToString
(
i
.
Md5
)
+
".image"
if
!
global
.
PathExists
(
path
.
Join
(
global
.
IMAGE_PATH
,
filename
))
{
_
=
ioutil
.
WriteFile
(
path
.
Join
(
global
.
IMAGE_PATH
,
filename
),
binary
.
NewWriterF
(
func
(
w
*
binary
.
Writer
)
{
...
...
@@ -346,6 +376,17 @@ func checkImage(e []message.IMessageElement) {
}),
0777
)
}
i
.
Filename
=
filename
case
*
message
.
VoiceElement
:
i
.
Name
=
strings
.
ReplaceAll
(
i
.
Name
,
"{"
,
""
)
i
.
Name
=
strings
.
ReplaceAll
(
i
.
Name
,
"}"
,
""
)
if
!
global
.
PathExists
(
path
.
Join
(
global
.
VOICE_PATH
,
i
.
Name
))
{
b
,
err
:=
global
.
GetBytes
(
i
.
Url
)
if
err
!=
nil
{
log
.
Warnf
(
"语音文件 %v 下载失败: %v"
,
i
.
Name
,
err
)
continue
}
_
=
ioutil
.
WriteFile
(
path
.
Join
(
global
.
VOICE_PATH
,
i
.
Name
),
b
,
0777
)
}
}
}
}
docs/config.md
View file @
e5be47e7
...
...
@@ -18,6 +18,8 @@ go-cqhttp 支持导入CQHTTP的配置文件, 具体步骤为:
{
"uin"
:
0
,
"password"
:
""
,
"encrypt_password"
:
false
,
"password_encrypted"
:
""
,
"enable_db"
:
true
,
"access_token"
:
""
,
"relogin"
:
false
,
...
...
@@ -26,7 +28,8 @@ go-cqhttp 支持导入CQHTTP的配置文件, 具体步骤为:
"enabled"
:
true
,
"host"
:
"0.0.0.0"
,
"port"
:
5700
,
"post_urls"
:
[]
"post_urls"
:
{
"url:port"
:
"secret"
},
"post_message_format"
:
"string"
},
"ws_config"
:
{
"enabled"
:
true
,
...
...
@@ -49,6 +52,8 @@ go-cqhttp 支持导入CQHTTP的配置文件, 具体步骤为:
| ------------------ | -------- | ------------------------------------------------------------------- |
| uin | int64 | 登录用QQ号 |
| password | string | 登录用密码 |
| encrypt_password | bool | 是否对密码进行加密. |
| password_encrypted | string | 加密后的密码(请勿修改) |
| enable_db | bool | 是否开启内置数据库, 关闭后将无法使用
**回复/撤回**
等上下文相关接口 |
| access_token | string | 同CQHTTP的
`access_token`
用于身份验证 |
| relogin | bool | 是否自动重新登录 |
...
...
@@ -57,4 +62,6 @@ go-cqhttp 支持导入CQHTTP的配置文件, 具体步骤为:
| ws_config | object | Websocket API 配置 |
| ws_reverse_servers | object[] | 反向 Websocket API 配置 |
> 注: 开启密码加密后程序将在每次启动时要求输入解密密钥, 密钥错误会导致登录时提示密码错误.
> 解密后密码将储存在内存中,用于自动重连等功能. 所以此加密并不能防止内存读取.
> 解密密钥在使用完成后并不会留存在内存中, 所以可用相对简单的字符串作为密钥
global/config.go
View file @
e5be47e7
...
...
@@ -6,16 +6,18 @@ import (
)
type
JsonConfig
struct
{
Uin
int64
`json:"uin"`
Password
string
`json:"password"`
EnableDB
bool
`json:"enable_db"`
AccessToken
string
`json:"access_token"`
ReLogin
bool
`json:"relogin"`
ReLoginDelay
int
`json:"relogin_delay"`
HttpConfig
*
GoCQHttpConfig
`json:"http_config"`
WSConfig
*
GoCQWebsocketConfig
`json:"ws_config"`
ReverseServers
[]
*
GoCQReverseWebsocketConfig
`json:"ws_reverse_servers"`
Debug
bool
`json:"debug"`
Uin
int64
`json:"uin"`
Password
string
`json:"password"`
EncryptPassword
bool
`json:"encrypt_password"`
PasswordEncrypted
string
`json:"password_encrypted"`
EnableDB
bool
`json:"enable_db"`
AccessToken
string
`json:"access_token"`
ReLogin
bool
`json:"relogin"`
ReLoginDelay
int
`json:"relogin_delay"`
HttpConfig
*
GoCQHttpConfig
`json:"http_config"`
WSConfig
*
GoCQWebsocketConfig
`json:"ws_config"`
ReverseServers
[]
*
GoCQReverseWebsocketConfig
`json:"ws_reverse_servers"`
Debug
bool
`json:"debug"`
}
type
CQHttpApiConfig
struct
{
...
...
@@ -38,10 +40,12 @@ type CQHttpApiConfig struct {
}
type
GoCQHttpConfig
struct
{
Enabled
bool
`json:"enabled"`
Host
string
`json:"host"`
Port
uint16
`json:"port"`
PostUrls
map
[
string
]
string
`json:"post_urls"`
Enabled
bool
`json:"enabled"`
Host
string
`json:"host"`
Port
uint16
`json:"port"`
Timeout
int32
`json:"timeout"`
PostUrls
map
[
string
]
string
`json:"post_urls"`
PostMessageFormat
string
`json:"post_message_format"`
}
type
GoCQWebsocketConfig
struct
{
...
...
@@ -60,12 +64,15 @@ type GoCQReverseWebsocketConfig struct {
func
DefaultConfig
()
*
JsonConfig
{
return
&
JsonConfig
{
EnableDB
:
true
,
EnableDB
:
true
,
ReLogin
:
true
,
ReLoginDelay
:
3
,
HttpConfig
:
&
GoCQHttpConfig
{
Enabled
:
true
,
Host
:
"0.0.0.0"
,
Port
:
5700
,
PostUrls
:
map
[
string
]
string
{},
Enabled
:
true
,
Host
:
"0.0.0.0"
,
Port
:
5700
,
PostUrls
:
map
[
string
]
string
{},
PostMessageFormat
:
"string"
,
},
WSConfig
:
&
GoCQWebsocketConfig
{
Enabled
:
true
,
...
...
global/fs.go
View file @
e5be47e7
...
...
@@ -9,6 +9,8 @@ import (
var
IMAGE_PATH
=
path
.
Join
(
"data"
,
"images"
)
var
VOICE_PATH
=
path
.
Join
(
"data"
,
"voices"
)
func
PathExists
(
path
string
)
bool
{
_
,
err
:=
os
.
Stat
(
path
)
return
err
==
nil
||
os
.
IsExist
(
err
)
...
...
@@ -31,3 +33,10 @@ func Check(err error) {
log
.
Fatalf
(
"遇到错误: %v"
,
err
)
}
}
func
IsAMR
(
b
[]
byte
)
bool
{
if
len
(
b
)
<=
6
{
return
false
}
return
b
[
0
]
==
0x23
&&
b
[
1
]
==
0x21
&&
b
[
2
]
==
0x41
&&
b
[
3
]
==
0x4D
&&
b
[
4
]
==
0x52
// amr file header
}
go.mod
View file @
e5be47e7
...
...
@@ -3,7 +3,7 @@ module github.com/Mrs4s/go-cqhttp
go 1.14
require (
github.com/Mrs4s/MiraiGo v0.0.0-202008
07030850-ed30f7ad5934
github.com/Mrs4s/MiraiGo v0.0.0-202008
12011522-ee1117893fad
github.com/gin-gonic/gin v1.6.3
github.com/gorilla/websocket v1.4.2
github.com/guonaihong/gout v0.1.1
...
...
go.sum
View file @
e5be47e7
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-20200807030850-ed30f7ad5934 h1:LoNjIsnyEQFGP9IchIQ65yHRCfNKSru3BAOguRepkCM=
github.com/Mrs4s/MiraiGo v0.0.0-20200807030850-ed30f7ad5934/go.mod h1:0je03wji/tSw4bUH4QCF2Z4/EjyNWjSJTyy5tliX6EM=
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-20200812011522-ee1117893fad h1:mOz8SozY2NEjXivlOrTwGPsbukcpLYpi/rv0/ASM/Hg=
github.com/Mrs4s/MiraiGo v0.0.0-20200812011522-ee1117893fad/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=
...
...
main.go
View file @
e5be47e7
...
...
@@ -3,16 +3,10 @@ package main
import
(
"bufio"
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/Mrs4s/MiraiGo/client"
"github.com/Mrs4s/go-cqhttp/coolq"
"github.com/Mrs4s/go-cqhttp/global"
"github.com/Mrs4s/go-cqhttp/server"
rotatelogs
"github.com/lestrrat-go/file-rotatelogs"
log
"github.com/sirupsen/logrus"
easy
"github.com/t-tomalak/logrus-easy-formatter"
asciiart
"github.com/yinghau76/go-ascii-art"
"image"
"io"
"io/ioutil"
...
...
@@ -22,6 +16,16 @@ import (
"strconv"
"strings"
"time"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client"
"github.com/Mrs4s/go-cqhttp/coolq"
"github.com/Mrs4s/go-cqhttp/global"
"github.com/Mrs4s/go-cqhttp/server"
rotatelogs
"github.com/lestrrat-go/file-rotatelogs"
log
"github.com/sirupsen/logrus"
easy
"github.com/t-tomalak/logrus-easy-formatter"
asciiart
"github.com/yinghau76/go-ascii-art"
)
func
init
()
{
...
...
@@ -33,14 +37,16 @@ func init() {
if
err
==
nil
{
log
.
SetOutput
(
io
.
MultiWriter
(
os
.
Stderr
,
w
))
}
if
!
global
.
PathExists
(
"data"
)
{
if
err
:=
os
.
Mkdir
(
"data"
,
0777
);
err
!=
nil
{
log
.
Fatalf
(
"创建数据文件夹失败: %v"
,
err
)
}
if
err
:=
os
.
Mkdir
(
path
.
Join
(
"data"
,
"images"
),
0777
);
err
!=
nil
{
if
!
global
.
PathExists
(
global
.
IMAGE_PATH
)
{
if
err
:=
os
.
MkdirAll
(
global
.
IMAGE_PATH
,
os
.
ModePerm
);
err
!=
nil
{
log
.
Fatalf
(
"创建图片缓存文件夹失败: %v"
,
err
)
}
}
if
!
global
.
PathExists
(
global
.
VOICE_PATH
)
{
if
err
:=
os
.
MkdirAll
(
global
.
VOICE_PATH
,
os
.
ModePerm
);
err
!=
nil
{
log
.
Fatalf
(
"创建语音缓存文件夹失败: %v"
,
err
)
}
}
if
global
.
PathExists
(
"cqhttp.json"
)
{
log
.
Info
(
"发现 cqhttp.json 将在五秒后尝试导入配置,按 Ctrl+C 取消."
)
log
.
Warn
(
"警告: 该操作会删除 cqhttp.json 并覆盖 config.json 文件."
)
...
...
@@ -86,10 +92,11 @@ func main() {
Uin
:
uin
,
Password
:
pwd
,
HttpConfig
:
&
global
.
GoCQHttpConfig
{
Enabled
:
true
,
Host
:
"0.0.0.0"
,
Port
:
5700
,
PostUrls
:
map
[
string
]
string
{},
Enabled
:
true
,
Host
:
"0.0.0.0"
,
Port
:
5700
,
PostUrls
:
map
[
string
]
string
{},
PostMessageFormat
:
"string"
,
},
WSConfig
:
&
global
.
GoCQWebsocketConfig
{
Enabled
:
true
,
...
...
@@ -112,7 +119,7 @@ func main() {
time
.
Sleep
(
time
.
Second
*
5
)
return
}
if
conf
.
Uin
==
0
||
conf
.
Password
==
""
{
if
conf
.
Uin
==
0
||
(
conf
.
Password
==
""
&&
conf
.
PasswordEncrypted
==
""
)
{
log
.
Warnf
(
"请修改 config.json 以添加账号密码."
)
time
.
Sleep
(
time
.
Second
*
5
)
return
...
...
@@ -124,7 +131,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
(),
os
.
ModePerm
)
log
.
Info
(
"已生成设备信息并保存到 device.json 文件."
)
}
else
{
log
.
Info
(
"将使用 device.json 内的设备信息运行Bot."
)
...
...
@@ -145,7 +152,25 @@ func main() {
conf
.
EnableDB
=
false
log
.
Infof
(
"已覆盖 ENABLE_DB 为 false"
)
}
if
conf
.
EncryptPassword
&&
conf
.
PasswordEncrypted
==
""
{
log
.
Infof
(
"密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)"
)
strKey
,
_
:=
console
.
ReadString
(
'\n'
)
key
:=
md5
.
Sum
([]
byte
(
strKey
))
if
encrypted
:=
EncryptPwd
(
conf
.
Password
,
key
[
:
]);
encrypted
!=
""
{
conf
.
Password
=
""
conf
.
PasswordEncrypted
=
encrypted
_
=
conf
.
Save
(
"config.json"
)
}
else
{
log
.
Warnf
(
"加密时出现问题."
)
}
}
if
conf
.
PasswordEncrypted
!=
""
{
log
.
Infof
(
"密码加密已启用, 请输入Key对密码进行解密以继续: (Enter 提交)"
)
strKey
,
_
:=
console
.
ReadString
(
'\n'
)
key
:=
md5
.
Sum
([]
byte
(
strKey
))
conf
.
Password
=
DecryptPwd
(
conf
.
PasswordEncrypted
,
key
[
:
])
}
log
.
Info
(
"Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消."
)
time
.
Sleep
(
time
.
Second
*
5
)
log
.
Info
(
"开始尝试登录并同步消息..."
)
...
...
@@ -156,9 +181,10 @@ func main() {
if
!
rsp
.
Success
{
switch
rsp
.
Error
{
case
client
.
NeedCaptcha
:
_
=
ioutil
.
WriteFile
(
"captcha.jpg"
,
rsp
.
CaptchaImage
,
os
.
ModePerm
)
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
...
...
@@ -184,8 +210,14 @@ func main() {
b
:=
coolq
.
NewQQBot
(
cli
,
conf
)
if
conf
.
HttpConfig
!=
nil
&&
conf
.
HttpConfig
.
Enabled
{
server
.
HttpServer
.
Run
(
fmt
.
Sprintf
(
"%s:%d"
,
conf
.
HttpConfig
.
Host
,
conf
.
HttpConfig
.
Port
),
conf
.
AccessToken
,
b
)
if
conf
.
HttpConfig
.
PostMessageFormat
!=
"string"
&&
conf
.
HttpConfig
.
PostMessageFormat
!=
"array"
{
log
.
Warnf
(
"http_config.post_message_format 配置错误, 将自动使用 string"
)
coolq
.
SetMessageFormat
(
"string"
)
}
else
{
coolq
.
SetMessageFormat
(
conf
.
HttpConfig
.
PostMessageFormat
)
}
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
{
...
...
@@ -224,3 +256,28 @@ func main() {
<-
c
b
.
Release
()
}
func
EncryptPwd
(
pwd
string
,
key
[]
byte
)
string
{
tea
:=
binary
.
NewTeaCipher
(
key
)
if
tea
==
nil
{
return
""
}
return
base64
.
StdEncoding
.
EncodeToString
(
tea
.
Encrypt
([]
byte
(
pwd
)))
}
func
DecryptPwd
(
ePwd
string
,
key
[]
byte
)
string
{
defer
func
()
{
if
pan
:=
recover
();
pan
!=
nil
{
log
.
Fatalf
(
"密码解密失败: %v"
,
pan
)
}
}()
encrypted
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
ePwd
)
if
err
!=
nil
{
panic
(
err
)
}
tea
:=
binary
.
NewTeaCipher
(
key
)
if
tea
==
nil
{
panic
(
"密钥错误"
)
}
return
string
(
tea
.
Decrypt
(
encrypted
))
}
server/http.go
View file @
e5be47e7
...
...
@@ -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
}
server/websocket.go
View file @
e5be47e7
...
...
@@ -64,14 +64,15 @@ func (c *websocketClient) Run() {
if
!
c
.
conf
.
Enabled
{
return
}
if
c
.
conf
.
ReverseApiUrl
!=
""
{
c
.
connectApi
()
}
if
c
.
conf
.
ReverseEventUrl
!=
""
{
c
.
connectEvent
()
}
if
c
.
conf
.
ReverseUrl
!=
""
{
c
.
connectUniversal
()
}
else
{
if
c
.
conf
.
ReverseApiUrl
!=
""
{
c
.
connectApi
()
}
if
c
.
conf
.
ReverseEventUrl
!=
""
{
c
.
connectEvent
()
}
}
c
.
bot
.
OnEventPush
(
c
.
onBotPushEvent
)
}
...
...
@@ -171,6 +172,7 @@ 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
()
}
...
...
@@ -193,8 +195,10 @@ func (c *websocketClient) onBotPushEvent(m coolq.MSG) {
if
_
,
err
:=
c
.
eventConn
.
Write
([]
byte
(
m
.
ToJson
()));
err
!=
nil
{
_
=
c
.
eventConn
.
Close
()
if
c
.
conf
.
ReverseReconnectInterval
!=
0
{
time
.
Sleep
(
time
.
Millisecond
*
time
.
Duration
(
c
.
conf
.
ReverseReconnectInterval
))
c
.
connectEvent
()
go
func
()
{
time
.
Sleep
(
time
.
Millisecond
*
time
.
Duration
(
c
.
conf
.
ReverseReconnectInterval
))
c
.
connectEvent
()
}()
}
}
}
...
...
@@ -327,6 +331,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"
))
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment