diff --git a/server/apiAdmin.go b/server/apiAdmin.go index 864a79a..0ec1b7f 100644 --- a/server/apiAdmin.go +++ b/server/apiAdmin.go @@ -117,209 +117,207 @@ func (s *webServer) Run(addr string, cli *client.QQClient) *coolq.CQBot { return b } -func (s *webServer) Dologin() { +// 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 } - conf := GetConf() - cli := s.Cli - cli.AllowSlider = true - rsp, err := cli.Login() - count := 0 - for { - global.Check(err) + + 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 - if !rsp.Success { - switch rsp.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): ") + count := 0 + + if res == nil { + goto Relogin + } + + Again: // 不执行 s.Cli.Login() 的循环,适用输入验证码等更新 res 的操作 + if err == nil && res.Success { // 登录成功 + 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() - if strings.Contains(text, "1") { - log.Warnf("请用浏览器打开 -> %v <- 并获取Ticket.", rsp.VerifyUrl) - log.Warn("请输入Ticket: (Enter 提交)") - text = readLine() - rsp, err = cli.SubmitTicket(strings.TrimSpace(text)) - continue - } - 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) - } + res, err = s.Cli.SubmitTicket(strings.TrimSpace(text)) + goto Again + } + if strings.Contains(text, "3") { + s.Cli.AllowSlider = false + s.Cli.Disconnect() continue - case client.NeedCaptcha: - _ = ioutil.WriteFile("captcha.jpg", rsp.CaptchaImage, 0644) - img, _, _ := image.Decode(bytes.NewReader(rsp.CaptchaImage)) - fmt.Println(asciiart.New("image", img).Art) - if conf.WebUI != nil && conf.WebUI.WebInput { - log.Warnf("请输入验证码 (captcha.jpg): (http://%s:%d/admin/do_web_write 输入)", conf.WebUI.Host, conf.WebUI.WebUIPort) - text = <-WebInput - } else { - log.Warn("请输入验证码 (captcha.jpg): (Enter 提交)") - text = readLine() - } - rsp, err = cli.SubmitCaptcha(strings.ReplaceAll(text, "\n", ""), rsp.CaptchaSign) - global.DelFile("captcha.jpg") + } + 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: + log.Warnf("账号已开启设备锁, 按下 Enter 向手机 %v 发送短信验证码.", res.SMSPhone) + readLine() + if !s.Cli.RequestSMS() { + log.Warnf("发送验证码失败,可能是请求过于频繁.") + time.Sleep(time.Second * 5) continue - case client.SMSNeededError: - log.Warnf("账号已开启设备锁, 按下 Enter 向手机 %v 发送短信验证码.", rsp.SMSPhone) - readLine() - if !cli.RequestSMS() { + } + 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扫码验证.") + 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) } log.Warn("请输入短信验证码: (Enter 提交)") 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 - case client.SMSOrVerifyNeededError: - log.Warnf("账号已开启设备锁,请选择验证方式:") - log.Warnf("1. 向手机 %v 发送短信验证码", rsp.SMSPhone) - log.Warnf("2. 使用手机QQ扫码验证.") - log.Warn("请输入(1 - 2): ") - text = readLine() - if strings.Contains(text, "1") { - if !cli.RequestSMS() { - log.Warnf("发送验证码失败,可能是请求过于频繁.") - time.Sleep(time.Second * 5) - os.Exit(0) - } - log.Warn("请输入短信验证码: (Enter 提交)") - text = readLine() - rsp, err = cli.SubmitSMS(strings.ReplaceAll(strings.ReplaceAll(text, "\n", ""), "\r", "")) - 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) + } + 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 } - break } - log.Infof("登录成功 欢迎使用: %v", cli.Nickname) - time.Sleep(time.Second) + 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(cli.ReloadFriendList()) - log.Infof("共加载 %v 个好友.", len(cli.FriendList)) + global.Check(s.Cli.ReloadFriendList()) + log.Infof("共加载 %v 个好友.", len(s.Cli.FriendList)) log.Infof("开始加载群列表...") - global.Check(cli.ReloadGroupList()) - log.Infof("共加载 %v 个群.", len(cli.GroupList)) - s.bot = coolq.NewQQBot(cli, conf) - if conf.PostMessageFormat != "string" && conf.PostMessageFormat != "array" { + 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(conf.PostMessageFormat) + coolq.SetMessageFormat(s.Conf.PostMessageFormat) } - if conf.RateLimit.Enabled { - global.InitLimiter(conf.RateLimit.Frequency, conf.RateLimit.BucketSize) + if s.Conf.RateLimit.Enabled { + global.InitLimiter(s.Conf.RateLimit.Frequency, s.Conf.RateLimit.BucketSize) } log.Info("正在加载事件过滤器.") global.BootFilter() - coolq.IgnoreInvalidCQCode = conf.IgnoreInvalidCQCode - coolq.SplitURL = conf.FixURL - coolq.ForceFragmented = conf.ForceFragmented + coolq.IgnoreInvalidCQCode = s.Conf.IgnoreInvalidCQCode + coolq.SplitURL = s.Conf.FixURL + coolq.ForceFragmented = s.Conf.ForceFragmented log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") - cli.OnDisconnected(func(bot *client.QQClient, e *client.ClientDisconnectedEvent) { - if conf.ReLogin.Enabled { - conf.ReLogin.Enabled = false - defer func() { conf.ReLogin.Enabled = true }() - 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.Cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) { + if !s.Conf.ReLogin.Enabled { + return } - s.bot.Release() - log.Fatalf("Bot已离线:%v", e.Message) + log.Warnf("Bot已离线 (%v),尝试重连", e.Message) + s.logincore(true) }) }