Skip to content

Commit a5b2df7

Browse files
committed
update
1 parent f62e4a3 commit a5b2df7

File tree

9 files changed

+325
-140
lines changed

9 files changed

+325
-140
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
# Go-Mirai-Client
22

3+
## session.token 登录说明
4+
5+
只支持单账号登录,如需多账号登录,请放不同文件夹内,例如
6+
7+
```shell
8+
File
9+
|- File one
10+
| |- gmc.exe
11+
| |- session.token
12+
| |- deviceInfo.toml
13+
| |- logs
14+
| | |- 2023-06-30.log
15+
| | |- latest.log
16+
| |- plugins
17+
| | |- default.json
18+
| |- device
19+
| | |- device-1688014725597.json
20+
|- File two
21+
| |- gmc.exe
22+
| |- session.token
23+
| |- deviceInfo.toml
24+
| |- logs
25+
| | |- 2023-06-30.log
26+
| | |- latest.log
27+
| |- plugins
28+
| | |- default.json
29+
| |- device
30+
| | |- device-1688014725597.json
31+
```
32+
33+
334
[![QQ群](https://img.shields.io/static/v1?label=QQ%E7%BE%A4&message=335783090&color=blue)](https://jq.qq.com/?_wv=1027&k=B7Of3GMZ)
435

536
用于收发QQ消息,并通过 websocket + protobuf 上报给 server 进行处理。

dto_proto/http_dto.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ message CreateBotReq{
2929
int64 device_seed = 3; // 设备信息随机种子
3030
int32 client_protocol = 4; // 协议类型
3131
string sign_server = 5;
32+
string sign_server_auth = 6;
3233
}
3334
message CreateBotResp{
3435
}

pkg/bot/api_handler.s

Whitespace-only changes.

pkg/bot/bot.go

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package bot
33
import (
44
"bytes"
55
"encoding/hex"
6+
"encoding/json"
67
"errors"
78
"fmt"
89
"net/http"
@@ -32,15 +33,57 @@ type GMCLogin struct {
3233
DeviceSeed int64
3334
ClientProtocol int32
3435
SignServer string
36+
SignServerKey string
3537
}
3638

39+
type SignRegister struct {
40+
Uin uint64
41+
AndroidId string
42+
Guid string
43+
Qimei36 string
44+
Key string
45+
}
46+
47+
type RequestCallback struct {
48+
Cmd string `json:"cmd,omitempty"` // trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish
49+
Body string `json:"body,omitempty"`
50+
CallBackId int `json:"callbackId,omitempty"`
51+
}
52+
53+
type RequestSignData struct {
54+
Token string `json:"token,omitempty"`
55+
Extra string `json:"extra,omitempty"`
56+
Sign string `json:"sign,omitempty"`
57+
O3dId string `json:"o3did,omitempty"`
58+
RequestCallback []*RequestCallback
59+
}
60+
61+
type RequestSignResult struct {
62+
Code int `json:"code,omitempty"`
63+
Msg string `json:"msg,omitempty"`
64+
Data *RequestSignData
65+
}
66+
67+
var RSR RequestSignResult
68+
3769
var GTL *GMCLogin
3870

71+
var SR SignRegister
72+
73+
var IsRequestTokenAgain bool = false
74+
75+
var TTI_i = 30
76+
3977
func GmcTokenLogin() (g GMCLogin, err error) {
4078
_, err = toml.DecodeFile("deviceInfo.toml", &GTL)
4179
return *GTL, err
4280
}
4381

82+
func SRI() (sr SignRegister, err error) {
83+
_, err = toml.DecodeFile("signRegisterInfo.toml", &SR)
84+
return SR, err
85+
}
86+
4487
func PathExists(path string) bool {
4588
_, err := os.Stat(path)
4689
return err == nil || errors.Is(err, os.ErrExist)
@@ -73,6 +116,7 @@ func InitLog(cli *client.QQClient) {
73116
func Login(cli *client.QQClient) (bool, error) {
74117
cli.AllowSlider = true
75118
if GTL.ClientProtocol == 1 && GTL.SignServer != "" {
119+
wrapper.RegisterSign = RegisterSign
76120
wrapper.DandelionEnergy = Energy
77121
wrapper.FekitGetSign = Sign
78122
} else if GTL.SignServer != "" {
@@ -178,7 +222,7 @@ func Energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, erro
178222
}
179223
response, err := download.Request{
180224
Method: http.MethodGet,
181-
URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v", id, hex.EncodeToString(salt)),
225+
URL: signServer + "custom_energy" + fmt.Sprintf("?uin=%v&data=%v&salt=%v", uin, id, hex.EncodeToString(salt)),
182226
}.Bytes()
183227
if err != nil {
184228
log.Warnf("获取T544 sign时出现错误: %v server: %v", err, signServer)
@@ -214,5 +258,87 @@ func Sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b
214258
sign, _ = hex.DecodeString(gjson.GetBytes(response, "data.sign").String())
215259
extra, _ = hex.DecodeString(gjson.GetBytes(response, "data.extra").String())
216260
token, _ = hex.DecodeString(gjson.GetBytes(response, "data.token").String())
261+
262+
json.Unmarshal(response, &RSR)
263+
fmt.Println(RSR.Data.RequestCallback[0], RSR.Data.RequestCallback[1])
217264
return sign, extra, token, nil
218265
}
266+
267+
func RegisterSign(uin uint64, androidId []byte, guid []byte, Qimei36 string, signServerAuth string) {
268+
signServer := GTL.SignServer
269+
if !strings.HasSuffix(signServer, "/") {
270+
signServer += "/"
271+
}
272+
SR.Uin = uin
273+
SR.AndroidId = string(androidId)
274+
SR.Guid = string(guid)
275+
SR.Guid = string(guid)
276+
SR.Key = signServerAuth
277+
// http://your.host:port/register?uin=[QQ]&android_id=[ANDROID_ID]&guid=[GUID]&qimei36=[QIMEI36]&key=[KEY]
278+
_ = os.WriteFile("signRegisterInfo.toml", []byte(fmt.Sprintf("uin= %v \nandroidId= \"%s\" \nguid= \"%s\" \nqimei36= \"%s\" \nkey= \"%s\"", uin, hex.EncodeToString(androidId), hex.EncodeToString(guid), Qimei36, signServerAuth)), 0o644)
279+
280+
fmt.Println(uin, hex.EncodeToString(androidId), hex.EncodeToString(guid), Qimei36, signServerAuth)
281+
fmt.Println(fmt.Sprintf("?uin=%v&android_id=%s&guid=%s&qimei36=%s&key=%s", uin, hex.EncodeToString(androidId), hex.EncodeToString(guid), Qimei36, signServerAuth))
282+
response, err := download.Request{
283+
Method: http.MethodGet,
284+
URL: signServer + "register" + fmt.Sprintf("?uin=%v&android_id=%s&guid=%s&qimei36=%s&key=%s", uin, hex.EncodeToString(androidId), hex.EncodeToString(guid), Qimei36, signServerAuth),
285+
}.Bytes()
286+
if err != nil {
287+
log.Warnf("初始化 Sign 失败\n", err)
288+
} else {
289+
log.Info("初始化 Sign 成功")
290+
fmt.Println(gjson.GetBytes(response, "msg").String())
291+
}
292+
}
293+
294+
// http://your.host:port/submit?uin=[QQ]&cmd=[CMD]&callback_id=[CALLBACK_ID]&buffer=[BUFFER]
295+
func SubmitRequestCallback(uin uint64, cmd string, callbackId int, buffer []byte) {
296+
signServer := GTL.SignServer
297+
if !strings.HasSuffix(signServer, "/") {
298+
signServer += "/"
299+
}
300+
response, err := download.Request{
301+
Method: http.MethodGet,
302+
URL: signServer + "submit" + fmt.Sprintf("?uin=%v&cmd=%s&callback_id=%v&buffer=%s", uin, cmd, callbackId, buffer),
303+
}.Bytes()
304+
if err != nil {
305+
log.Warnf(cmd, " ", callbackId, "提交失败\n", err)
306+
} else {
307+
log.Info(cmd, " ", callbackId, "提交成功")
308+
fmt.Println(string(response))
309+
fmt.Println(gjson.GetBytes(response, "msg").String())
310+
}
311+
}
312+
313+
func RequestToken(uin uint64) {
314+
signServer := GTL.SignServer
315+
if !strings.HasSuffix(signServer, "/") {
316+
signServer += "/"
317+
}
318+
response, err := download.Request{
319+
Method: http.MethodGet,
320+
URL: signServer + "request_token" + fmt.Sprintf("?uin=%v", uin),
321+
}.Bytes()
322+
if err != nil || strings.HasPrefix(gjson.GetBytes(response, "msg").String(), "Uin") { // QSign
323+
log.Warnf("请求 Token 失败\n", gjson.GetBytes(response, "msg").String(), err)
324+
log.Info("正在重新注册 ", uin)
325+
RegisterSign(SR.Uin, []byte(SR.AndroidId), []byte(SR.Guid), SR.Qimei36, SR.Key)
326+
IsRequestTokenAgain = true
327+
} else if strings.HasPrefix(gjson.GetBytes(response, "msg").String(), "QSign") {
328+
log.Warn("QSign not initialized, unable to request_ Token, please submit the initialization package first.")
329+
} else {
330+
log.Info("请求 Token 成功")
331+
fmt.Println(string(response))
332+
fmt.Println(gjson.GetBytes(response, "msg").String())
333+
}
334+
}
335+
336+
func TTIR(uin uint64) {
337+
for TTI_i >= 0 {
338+
time.Sleep(time.Minute)
339+
if TTI_i == 0 {
340+
TTI_i = 30
341+
RequestToken(uin)
342+
}
343+
}
344+
}

pkg/bot/captcha.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,11 @@ func ProcessLoginRsp(cli *client.QQClient, rsp *client.LoginResponse) (bool, err
5555
})
5656
defer WaitingCaptchas.Delete(cli.Uin)
5757

58-
for count := 120; count > 0; count-- {
59-
str := fetchCaptcha(id)
60-
if str != "" {
61-
rsp, err := cli.SubmitTicket(str)
62-
if err != nil {
63-
return false, err
64-
}
65-
return ProcessLoginRsp(cli, rsp)
66-
}
67-
time.Sleep(time.Second)
58+
ticket := getTicket(id)
59+
rsp, err := cli.SubmitTicket(ticket)
60+
if err != nil {
61+
return false, err
6862
}
69-
log.Warnf("验证超时")
7063
return ProcessLoginRsp(cli, rsp)
7164
case client.NeedCaptcha:
7265
log.Infof("遇到图形验证码,根据README提示操作 https://github.com/protobufbot/Go-Mirai-Client (顺便star)")
@@ -160,3 +153,15 @@ func fetchCaptcha(id string) string {
160153
}
161154
return ""
162155
}
156+
157+
func getTicket(id string) string {
158+
for count := 120; count > 0; count-- {
159+
str := fetchCaptcha(id)
160+
if str != "" {
161+
return str
162+
}
163+
time.Sleep(time.Second)
164+
}
165+
log.Warnf("验证超时")
166+
return ""
167+
}

pkg/gmc/gmc.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"time"
1313

14+
"github.com/ProtobufBot/Go-Mirai-Client/pkg/bot"
1415
"github.com/ProtobufBot/Go-Mirai-Client/pkg/config"
1516
"github.com/ProtobufBot/Go-Mirai-Client/pkg/gmc/handler"
1617
"github.com/ProtobufBot/Go-Mirai-Client/pkg/static"
@@ -98,6 +99,11 @@ func Start() {
9899

99100
CreateBotIfParamExist() // 如果环境变量存在,使用环境变量创建机器人 UIN PASSWORD
100101
InitGin() // 初始化GIN HTTP管理
102+
sr, err := bot.SRI()
103+
if err != nil {
104+
log.Warn("signRegisterInfo.toml 不存在,应该是首次登录")
105+
}
106+
fmt.Println(sr)
101107
handler.TokenLogin()
102108
}
103109

@@ -138,7 +144,7 @@ func CreateBotIfParamExist() {
138144
if uin != 0 && pass != "" {
139145
log.Infof("使用参数创建机器人 %d", uin)
140146
go func() {
141-
handler.CreateBotImpl(uin, pass, 0, 0)
147+
handler.CreateBotImpl(uin, pass, 0, 0, "")
142148
}()
143149
}
144150
}

pkg/gmc/handler/bot.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,22 @@ func CreateBot(c *gin.Context) {
106106
DeviceSeed: req.DeviceSeed,
107107
ClientProtocol: 6,
108108
SignServer: req.SignServer,
109+
SignServerKey: req.SignServerAuth,
109110
}
110-
_ = os.WriteFile("deviceInfo.toml", []byte(fmt.Sprintf("DeviceSeed = %d \nClientProtocol= %d \nSignServer= \"%s\"", req.DeviceSeed, 6, req.SignServer)), 0o644)
111+
_ = os.WriteFile("deviceInfo.toml", []byte(fmt.Sprintf("DeviceSeed = %d \nClientProtocol= %d \nSignServer= \"%s\" \nSignServerkey= \"%s\"", req.DeviceSeed, 6, req.SignServer, req.SignServerAuth)), 0o644)
111112
go func() {
112-
CreateBotImpl(req.BotId, req.Password, req.DeviceSeed, 6)
113+
CreateBotImpl(req.BotId, req.Password, req.DeviceSeed, 6, req.SignServerAuth)
113114
}()
114115
} else {
115116
bot.GTL = &bot.GMCLogin{
116117
DeviceSeed: req.DeviceSeed,
117118
ClientProtocol: req.ClientProtocol,
118119
SignServer: req.SignServer,
120+
SignServerKey: req.SignServerAuth,
119121
}
120-
_ = os.WriteFile("deviceInfo.toml", []byte(fmt.Sprintf("DeviceSeed = %d \nClientProtocol= %d \nSignServer= \"%s\"", req.DeviceSeed, req.ClientProtocol, req.SignServer)), 0o644)
122+
_ = os.WriteFile("deviceInfo.toml", []byte(fmt.Sprintf("DeviceSeed = %d \nClientProtocol= %d \nSignServer= \"%s\" \nSignServerkey= \"%s\"", req.DeviceSeed, req.ClientProtocol, req.SignServer, req.SignServerAuth)), 0o644)
121123
go func() {
122-
CreateBotImpl(req.BotId, req.Password, req.DeviceSeed, req.ClientProtocol)
124+
CreateBotImpl(req.BotId, req.Password, req.DeviceSeed, req.ClientProtocol, req.SignServerAuth)
123125
}()
124126
}
125127
resp := &dto.CreateBotResp{}
@@ -396,23 +398,27 @@ func Return(c *gin.Context, resp proto.Message) {
396398
c.Data(http.StatusOK, c.ContentType(), data)
397399
}
398400

399-
func CreateBotImpl(uin int64, password string, deviceRandSeed int64, clientProtocol int32) {
400-
CreateBotImplMd5(uin, md5.Sum([]byte(password)), deviceRandSeed, clientProtocol)
401+
func CreateBotImpl(uin int64, password string, deviceRandSeed int64, clientProtocol int32, signServerKey string) {
402+
CreateBotImplMd5(uin, md5.Sum([]byte(password)), deviceRandSeed, clientProtocol, signServerKey)
401403
}
402404

403-
func CreateBotImplMd5(uin int64, passwordMd5 [16]byte, deviceRandSeed int64, clientProtocol int32) {
405+
func CreateBotImplMd5(uin int64, passwordMd5 [16]byte, deviceRandSeed int64, clientProtocol int32, signServerKey string) {
404406
var deviceInfo *client.DeviceInfo
405407
log.Infof("开始初始化设备信息")
406408
if deviceRandSeed != 0 {
407409
deviceInfo = device.GetDevice(deviceRandSeed, clientProtocol)
408410
} else {
409411
deviceInfo = device.GetDevice(uin, clientProtocol)
410412
}
413+
414+
fmt.Println(signServerKey)
415+
411416
log.Infof("设备信息 %+v", string(deviceInfo.ToJson()))
412417

413418
log.Infof("创建机器人 %+v", uin)
414419

415420
cli := client.NewClientMd5(uin, passwordMd5)
421+
cli.SetSSK(signServerKey)
416422
cli.UseDevice(deviceInfo)
417423
bot.Clients.Store(uin, cli)
418424

@@ -428,25 +434,24 @@ func CreateBotImplMd5(uin int64, passwordMd5 [16]byte, deviceRandSeed int64, cli
428434
}
429435
if ok {
430436
log.Infof("登录成功")
431-
bot.GTL = &bot.GMCLogin{
432-
DeviceSeed: bot.GTL.DeviceSeed,
433-
ClientProtocol: bot.GTL.ClientProtocol,
434-
SignServer: "",
437+
for _, data := range bot.RSR.Data.RequestCallback {
438+
go bot.SubmitRequestCallback(uint64(uin), data.Cmd, data.CallBackId, []byte(data.Body))
435439
}
440+
time.Sleep(time.Second * 3)
436441
AfterLogin(cli)
437442
accountToken := cli.GenToken()
438443
_ = os.WriteFile("session.token", accountToken, 0o644)
439444
} else {
440445
log.Infof("登录失败")
441-
bot.GTL = &bot.GMCLogin{
442-
DeviceSeed: bot.GTL.DeviceSeed,
443-
ClientProtocol: bot.GTL.ClientProtocol,
444-
SignServer: "",
445-
}
446446
}
447447
}
448448

449449
func AfterLogin(cli *client.QQClient) {
450+
bot.RequestToken(uint64(cli.Uin))
451+
if bot.IsRequestTokenAgain {
452+
bot.RequestToken(uint64(cli.Uin))
453+
}
454+
go bot.TTIR(uint64(cli.Uin))
450455
for {
451456
time.Sleep(5 * time.Second)
452457
if cli.Online.Load() {

0 commit comments

Comments
 (0)