1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-04 19:17:37 +08:00

Merge pull request #624 from qianjunakasumi/dev

修复登录和重连逻辑,支持重连的验证码等处理,Fix #620
This commit is contained in:
Mrs4s 2021-02-16 17:58:15 +08:00 committed by GitHub
commit 02581ff8a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -117,209 +117,207 @@ func (s *webServer) Run(addr string, cli *client.QQClient) *coolq.CQBot {
return b return b
} }
func (s *webServer) Dologin() { // logincore 登录核心实现
func (s *webServer) logincore(relogin bool) {
s.Console = bufio.NewReader(os.Stdin) s.Console = bufio.NewReader(os.Stdin)
readLine := func() (str string) { readLine := func() (str string) {
str, _ = s.Console.ReadString('\n') str, _ = s.Console.ReadString('\n')
str = strings.TrimSpace(str) str = strings.TrimSpace(str)
return return
} }
conf := GetConf()
cli := s.Cli if s.Cli.Online {
cli.AllowSlider = true log.Warn("Bot已登录")
rsp, err := cli.Login() return
count := 0 }
for {
global.Check(err) var times uint = 1 // 重试次数
for res, err := s.Cli.Login(); ; res, err = s.Cli.Login() {
var text string var text string
if !rsp.Success { count := 0
switch rsp.Error {
case client.SliderNeededError: if res == nil {
log.Warnf("登录需要滑条验证码, 请选择解决方案: ") goto Relogin
log.Warnf("1. 自行抓包. (推荐)") }
log.Warnf("2. 使用Cef自动处理.")
log.Warnf("3. 不提交滑块并继续.(可能会导致上网环境异常错误)") Again: // 不执行 s.Cli.Login() 的循环,适用输入验证码等更新 res 的操作
log.Warnf("详细信息请参考文档 -> https://github.com/Mrs4s/go-cqhttp/blob/master/docs/slider.md <-") if err == nil && res.Success { // 登录成功
log.Warn("请输入(1 - 3): ") break
} else if err == client.ErrAlreadyOnline {
break
}
log.Error("登录遇到错误: " + err.Error())
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 <-")
log.Warn("请输入(1 - 3): ")
text = readLine()
if strings.Contains(text, "1") {
log.Warnf("请用浏览器打开 -> %v <- 并获取Ticket.", res.VerifyUrl)
log.Warn("请输入Ticket (Enter 提交)")
text = readLine() text = readLine()
if strings.Contains(text, "1") { res, err = s.Cli.SubmitTicket(strings.TrimSpace(text))
log.Warnf("请用浏览器打开 -> %v <- 并获取Ticket.", rsp.VerifyUrl) goto Again
log.Warn("请输入Ticket (Enter 提交)") }
text = readLine() if strings.Contains(text, "3") {
rsp, err = cli.SubmitTicket(strings.TrimSpace(text)) s.Cli.AllowSlider = false
continue s.Cli.Disconnect()
}
if strings.Contains(text, "3") {
cli.AllowSlider = false
cli.Disconnect()
rsp, err = cli.Login()
continue
}
id := utils.RandomStringRange(6, "0123456789")
log.Warnf("滑块ID为 %v 请在30S内处理.", id)
ticket, err := global.GetSliderTicket(rsp.VerifyUrl, id)
if err != nil {
log.Warnf("错误: " + err.Error())
os.Exit(0)
}
rsp, err = cli.SubmitTicket(ticket)
if err != nil {
log.Warnf("错误: " + err.Error())
os.Exit(0)
}
continue continue
case client.NeedCaptcha: }
_ = ioutil.WriteFile("captcha.jpg", rsp.CaptchaImage, 0644) id := utils.RandomStringRange(6, "0123456789")
img, _, _ := image.Decode(bytes.NewReader(rsp.CaptchaImage)) log.Warnf("滑块ID为 %v 请在30S内处理.", id)
fmt.Println(asciiart.New("image", img).Art) ticket, err := global.GetSliderTicket(res.VerifyUrl, id)
if conf.WebUI != nil && conf.WebUI.WebInput { if err != nil {
log.Warnf("请输入验证码 (captcha.jpg) (http://%s:%d/admin/do_web_write 输入)", conf.WebUI.Host, conf.WebUI.WebUIPort) log.Warnf("错误: " + err.Error())
text = <-WebInput os.Exit(0)
} else { }
log.Warn("请输入验证码 (captcha.jpg) (Enter 提交)") res, err = s.Cli.SubmitTicket(ticket)
text = readLine() if err != nil {
} log.Warnf("错误: " + err.Error())
rsp, err = cli.SubmitCaptcha(strings.ReplaceAll(text, "\n", ""), rsp.CaptchaSign) continue // 尝试重新登录
global.DelFile("captcha.jpg") }
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:
log.Warnf("账号已开启设备锁, 按下 Enter 向手机 %v 发送短信验证码.", res.SMSPhone)
readLine()
if !s.Cli.RequestSMS() {
log.Warnf("发送验证码失败,可能是请求过于频繁.")
time.Sleep(time.Second * 5)
continue continue
case client.SMSNeededError: }
log.Warnf("账号已开启设备锁, 按下 Enter 向手机 %v 发送短信验证码.", rsp.SMSPhone) log.Warn("请输入短信验证码: (Enter 提交)")
readLine() text = readLine()
if !cli.RequestSMS() { 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扫码验证.")
log.Warn("请输入(1 - 2): ")
text = readLine()
if strings.Contains(text, "1") {
if !s.Cli.RequestSMS() {
log.Warnf("发送验证码失败,可能是请求过于频繁.") log.Warnf("发送验证码失败,可能是请求过于频繁.")
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
os.Exit(0) os.Exit(0)
} }
log.Warn("请输入短信验证码: (Enter 提交)") log.Warn("请输入短信验证码: (Enter 提交)")
text = readLine() text = readLine()
rsp, err = cli.SubmitSMS(strings.ReplaceAll(strings.ReplaceAll(text, "\n", ""), "\r", "")) 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:
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 continue
case client.SMSOrVerifyNeededError: }
log.Warnf("账号已开启设备锁,请选择验证方式:") if strings.Contains(msg, "冻结") {
log.Warnf("1. 向手机 %v 发送短信验证码", rsp.SMSPhone) log.Fatalf("账号被冻结, 放弃重连")
log.Warnf("2. 使用手机QQ扫码验证.") }
log.Warn("请输入(1 - 2): ") log.Warnf("登录失败: %v", msg)
text = readLine() log.Infof("按 Enter 继续....")
if strings.Contains(text, "1") { readLine()
if !cli.RequestSMS() { os.Exit(0)
log.Warnf("发送验证码失败,可能是请求过于频繁.") }
time.Sleep(time.Second * 5)
os.Exit(0) Relogin:
} if relogin {
log.Warn("请输入短信验证码: (Enter 提交)") if times > s.Conf.ReLogin.MaxReloginTimes && s.Conf.ReLogin.MaxReloginTimes != 0 {
text = readLine() log.Fatal("重连失败: 重连次数达到设置的上限值")
rsp, err = cli.SubmitSMS(strings.ReplaceAll(strings.ReplaceAll(text, "\n", ""), "\r", "")) s.bot.Release()
continue
}
log.Warnf("请前往 -> %v <- 验证并重启Bot.", rsp.VerifyUrl)
log.Infof("按 Enter 继续....")
readLine()
os.Exit(0)
return
case client.UnsafeDeviceError:
log.Warnf("账号已开启设备锁,请前往 -> %v <- 验证并重启Bot.", rsp.VerifyUrl)
if conf.WebUI != nil && conf.WebUI.WebInput {
log.Infof(" (http://%s:%d/admin/do_web_write 确认后继续)....", conf.WebUI.Host, conf.WebUI.WebUIPort)
text = <-WebInput
} else {
log.Infof("按 Enter 继续....")
readLine()
}
log.Info(text)
os.Exit(0)
return
case client.OtherLoginError, client.UnknownLoginError:
msg := rsp.ErrorMessage
if strings.Contains(msg, "版本") {
msg = "密码错误或账号被冻结"
}
if strings.Contains(msg, "上网环境") && count < 5 {
cli.Disconnect()
rsp, err = cli.Login()
count++
log.Warnf("错误: 当前上网环境异常. 将更换服务器并重试.")
time.Sleep(time.Second)
continue
}
log.Warnf("登录失败: %v", msg)
log.Infof("按 Enter 继续....")
readLine()
os.Exit(0)
return 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
} }
break
} }
log.Infof("登录成功 欢迎使用: %v", cli.Nickname) if relogin {
time.Sleep(time.Second) log.Info("重连成功")
}
}
// Dologin 主程序登录
func (s *webServer) Dologin() {
s.Cli.AllowSlider = true
s.logincore(false)
log.Infof("登录成功 欢迎使用: %v", s.Cli.Nickname)
log.Info("开始加载好友列表...") log.Info("开始加载好友列表...")
global.Check(cli.ReloadFriendList()) global.Check(s.Cli.ReloadFriendList())
log.Infof("共加载 %v 个好友.", len(cli.FriendList)) log.Infof("共加载 %v 个好友.", len(s.Cli.FriendList))
log.Infof("开始加载群列表...") log.Infof("开始加载群列表...")
global.Check(cli.ReloadGroupList()) global.Check(s.Cli.ReloadGroupList())
log.Infof("共加载 %v 个群.", len(cli.GroupList)) log.Infof("共加载 %v 个群.", len(s.Cli.GroupList))
s.bot = coolq.NewQQBot(cli, conf) s.bot = coolq.NewQQBot(s.Cli, s.Conf)
if conf.PostMessageFormat != "string" && conf.PostMessageFormat != "array" { if s.Conf.PostMessageFormat != "string" && s.Conf.PostMessageFormat != "array" {
log.Warnf("post_message_format 配置错误, 将自动使用 string") log.Warnf("post_message_format 配置错误, 将自动使用 string")
coolq.SetMessageFormat("string") coolq.SetMessageFormat("string")
} else { } else {
coolq.SetMessageFormat(conf.PostMessageFormat) coolq.SetMessageFormat(s.Conf.PostMessageFormat)
} }
if conf.RateLimit.Enabled { if s.Conf.RateLimit.Enabled {
global.InitLimiter(conf.RateLimit.Frequency, conf.RateLimit.BucketSize) global.InitLimiter(s.Conf.RateLimit.Frequency, s.Conf.RateLimit.BucketSize)
} }
log.Info("正在加载事件过滤器.") log.Info("正在加载事件过滤器.")
global.BootFilter() global.BootFilter()
coolq.IgnoreInvalidCQCode = conf.IgnoreInvalidCQCode coolq.IgnoreInvalidCQCode = s.Conf.IgnoreInvalidCQCode
coolq.SplitURL = conf.FixURL coolq.SplitURL = s.Conf.FixURL
coolq.ForceFragmented = conf.ForceFragmented coolq.ForceFragmented = s.Conf.ForceFragmented
log.Info("资源初始化完成, 开始处理信息.") log.Info("资源初始化完成, 开始处理信息.")
log.Info("アトリは、高性能ですから!") log.Info("アトリは、高性能ですから!")
cli.OnDisconnected(func(bot *client.QQClient, e *client.ClientDisconnectedEvent) {
if conf.ReLogin.Enabled { s.Cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) {
conf.ReLogin.Enabled = false if !s.Conf.ReLogin.Enabled {
defer func() { conf.ReLogin.Enabled = true }() return
var times uint = 1
for {
if cli.Online {
log.Warn("Bot已登录")
return
}
if times > conf.ReLogin.MaxReloginTimes && conf.ReLogin.MaxReloginTimes != 0 {
break
}
log.Warnf("Bot已离线 (%v),将在 %v 秒后尝试重连. 重连次数:%v",
e.Message, conf.ReLogin.ReLoginDelay, times)
times++
time.Sleep(time.Second * time.Duration(conf.ReLogin.ReLoginDelay))
rsp, err := cli.Login()
if err != nil {
log.Errorf("重连失败: %v", err)
cli.Disconnect()
continue
}
if !rsp.Success {
switch rsp.Error {
case client.NeedCaptcha:
log.Fatalf("重连失败: 需要验证码. (验证码处理正在开发中)")
case client.UnsafeDeviceError:
log.Fatalf("重连失败: 设备锁")
default:
log.Errorf("重连失败: %v", rsp.ErrorMessage)
if strings.Contains(rsp.ErrorMessage, "冻结") {
log.Fatalf("账号被冻结, 放弃重连")
}
cli.Disconnect()
continue
}
}
log.Info("重连成功")
return
}
log.Fatal("重连失败: 重连次数达到设置的上限值")
} }
s.bot.Release() log.Warnf("Bot已离线 (%v),尝试重连", e.Message)
log.Fatalf("Bot已离线%v", e.Message) s.logincore(true)
}) })
} }