package scheduler import ( "log" "strings" "time" "go-helper/internal/chatgpt" "go-helper/internal/database" ) // StartTokenChecker runs a periodic loop that refreshes access tokens // for all accounts that have a refresh token. func StartTokenChecker(db *database.DB, client *chatgpt.Client, intervalMinutes int) { if intervalMinutes <= 0 { intervalMinutes = 30 } interval := time.Duration(intervalMinutes) * time.Minute log.Printf("[Scheduler] Token 定时检测已启动,间隔 %d 分钟", intervalMinutes) go func() { // Run once immediately at startup. checkAndRefreshAll(db, client) ticker := time.NewTicker(interval) defer ticker.Stop() for range ticker.C { checkAndRefreshAll(db, client) } }() } func checkAndRefreshAll(db *database.DB, client *chatgpt.Client) { accounts, err := db.GetAccountsWithRT() if err != nil { log.Printf("[Scheduler] 获取账号列表失败: %v", err) return } if len(accounts) == 0 { return } log.Printf("[Scheduler] 开始检查 %d 个账号的 Token 状态", len(accounts)) refreshed, failed, banned := 0, 0, 0 for _, acc := range accounts { result, err := client.RefreshAccessToken(acc.RefreshToken) if err != nil { log.Printf("[Scheduler] 刷新失败 [ID=%d %s]: %v", acc.ID, acc.Email, err) failed++ // Check if the error indicates token is completely invalid. errMsg := strings.ToLower(err.Error()) if strings.Contains(errMsg, "invalid_grant") || strings.Contains(errMsg, "token has been revoked") || strings.Contains(errMsg, "unauthorized") { log.Printf("[Scheduler] RT 无效,标记封号 [ID=%d %s]", acc.ID, acc.Email) _ = db.BanAccount(acc.ID) banned++ } continue } if err := db.UpdateAccountTokens(acc.ID, result.AccessToken, result.RefreshToken); err != nil { log.Printf("[Scheduler] 更新 Token 失败 [ID=%d]: %v", acc.ID, err) failed++ continue } // Also try to fetch account info to update subscription expiry. infos, err := client.FetchAccountInfo(result.AccessToken) if err != nil { // Token works but account info fetch failed — might be account_deactivated. errMsg := strings.ToLower(err.Error()) if strings.Contains(errMsg, "account_deactivated") || strings.Contains(errMsg, "已停用") { log.Printf("[Scheduler] 账号已停用,标记封号 [ID=%d %s]", acc.ID, acc.Email) _ = db.BanAccount(acc.ID) banned++ continue } } else if len(infos) > 0 { // Update expire_at from subscription info. for _, info := range infos { if info.AccountID == acc.ChatgptAccountID && info.ExpiresAt != "" { _ = db.UpdateAccountInfo(acc.ID, acc.ChatgptAccountID, info.ExpiresAt) break } } } refreshed++ } log.Printf("[Scheduler] 检查完成: 刷新成功 %d, 失败 %d, 封号 %d", refreshed, failed, banned) }