init
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.idea
|
||||
.cscode
|
||||
*.iml
|
||||
@@ -0,0 +1,27 @@
|
||||
cvl_csrf_token=62083f188904477e9bc1c5a70537145c;
|
||||
acf_ccn=8b025ffcbf403d5e0d880ad6327bb3cf;
|
||||
PHPSESSID=86k3denvoc1j16rl1fsv5l2mc2;acf_auth=5a8eUzBoDc8uyLasCP7LXaO9a2vUK%2FDaNFwYi0MVdUFMpUcMBfsoZfifJZKCQC8WgJuKq7WB78VZrRlRXnZUSt87TZM0tLNdnplqtuL87c7idAqo%2Fal57ls;acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk0MjQxNjQ1MSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo2OTE2MTY4NSwic3RrIjoiNGZkNDY5ZDE2OWY3NzM0YiIsImV4cCI6MTc4MjMyMjc2OSwiaWF0IjoxNzgxNzE3OTcwLCJrZXkiOiJkeS1qd3QtbWQ1In0.NDc0YzMyNjQ0OGIzNTkxYzkxNGQ1NDk0Y2U0NTk1NTU;
|
||||
acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQeyJ1aWQiOjk0MjQxNjQ1MSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo2OTE2MTY4NSwic3RrIjoiNGZkNDY5ZDE2OWY3NzM0YiIsImV4cCI6MTc4MjMyMjc2OwiaWF0IjoxNzgxNzE3OTcwLCJrZXkiOiJkeS1qd3QtbWQ1In0.NWM1ZDgwZjE1N2ZhNTE3NDUxNmQ4ZjljMzI5ODQ0ZjM;dy_auth=29bfEdmmHp50M9Zr2gxQ3%2F2AJD2DAdCK6I%2FoX92U5n13i64R%2B%2FywJZf%2FqG4%2BCJvbUQmFaUSu3dFbJj4%2Be1gU1DpW6ligJYaJLnNuVbmKMeZCtayBmGtns6A;
|
||||
wan_auth37wan=cf514c616a0fzsvhClbvRbju0sSZb4aDqHQQabpd66aLoVky4bvRRXYDfYMX3iSUNiDuh%2BHt%2B20QXW0f5JLZ6lBtOW8LIIwb31iX8OiZurGB0aztSKA;
|
||||
acf_uid=942416451;
|
||||
acf_username=942416451;
|
||||
acf_nickname=%E7%94%A8%E6%88%B74512197651;
|
||||
acf_own_room=0;
|
||||
acf_groupid=1;
|
||||
acf_phonestatus=1;
|
||||
acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F18_;
|
||||
acf_ct=0;
|
||||
acf_ltkid=69161685;
|
||||
acf_biz=1;
|
||||
acf_stk=4fd469d169f7734b;
|
||||
acf_devid=9869cce23d84da0fd8b1cd20162b3def--用户4512197651--
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
acf_auth = a81bCldfVSQZYKOqZrnBkKFEjwUMqnbQ0Bgo % 2FlPrLZY9Fs % 2Fg4e9GXx4twrNdZ38vIxfEHDv26xk0KoVcxNHl5nkI4FCpTQO % 2FHQIUMxrQ55Ooft4FmULM4lc;
|
||||
acf_jwt_token = eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg5ODQyNDM5OSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo0MDExMDcyOSwic3RrIjoiZjc3ODg2ZmFiOWFjZWFmNiIsImV4cCI6MTc4MjM3MDk0NSwiaWF0IjoxNzgxNzY2MTQ1LCJrZXkiOiJkeS1qd3QtbWQ1In0.YjNhN2EwMTUxMjc5ODI3YzQ2YWQ0OWVlZmRiYzFlNDE
|
||||
@@ -0,0 +1,7 @@
|
||||
module dygou_go
|
||||
|
||||
go 1.26.3
|
||||
|
||||
require github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
|
||||
require golang.org/x/image v0.42.0 // indirect
|
||||
@@ -0,0 +1,4 @@
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
golang.org/x/image v0.42.0 h1:1gSs6ehNWXLbkHBIPcWztk3D/6aIA/8hauiAYtlodVY=
|
||||
golang.org/x/image v0.42.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
@@ -0,0 +1,676 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MANUAL_ID = "G4KA4Qnz4LDp7"
|
||||
ROOM_ID = "9263298"
|
||||
EXCHANGE_URL = "https://www.douyu.com/wgapi/ordnc/activity/peace/exchange"
|
||||
CSRF_URL = "https://www.douyu.com/japi/carnival/nc/common/generateCsrf"
|
||||
BIND_INFO = "https://www.douyu.com/japi/carnivalApi/v2/tencent/bindInfo?actAlias=20260120QYOOB"
|
||||
JIFEN_HOST = "https://www.douyu.com/japi/oms/web/peace/credit/balance"
|
||||
|
||||
CONCURRENCY = 3
|
||||
GLOBAL_CONCURRENT = 300
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
TimeApiUrl string `json:"timeApiUrl"`
|
||||
}
|
||||
|
||||
var config Config
|
||||
|
||||
type Account struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Cookie string `json:"cookie"`
|
||||
}
|
||||
|
||||
type Product struct {
|
||||
CommodityID string `json:"commodityId"`
|
||||
CommodityName string `json:"commodityName"`
|
||||
Score int `json:"score"`
|
||||
Count int `json:"count"`
|
||||
Pic string `json:"pic"`
|
||||
}
|
||||
|
||||
type SeckillRequest struct {
|
||||
AccountProducts map[string]string `json:"accountProducts"`
|
||||
TargetTime string `json:"targetTime"`
|
||||
Advance int `json:"advance"`
|
||||
}
|
||||
|
||||
var (
|
||||
accounts []Account
|
||||
accountsMu sync.RWMutex
|
||||
|
||||
products []Product
|
||||
productsMu sync.RWMutex
|
||||
|
||||
results = make(map[string]string)
|
||||
resultsMu sync.RWMutex
|
||||
|
||||
httpClient = &http.Client{
|
||||
Timeout: 10 * time.Second, // ← 加这一行,防止请求卡死
|
||||
Transport: &http.Transport{
|
||||
MaxConnsPerHost: 100,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 50,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
seckillTimer *time.Timer
|
||||
timerMu sync.Mutex
|
||||
|
||||
cachedFont *truetype.Font
|
||||
fontMu sync.Mutex
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 设置日志格式,显示毫秒
|
||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds) // ← 加这行
|
||||
|
||||
loadConfig() // ← 新增,放在最前面
|
||||
loadAccounts()
|
||||
os.MkdirAll("success_images", 0755)
|
||||
|
||||
initFont() // ← 加这一行,预加载字体
|
||||
|
||||
// 测试一下 currentTime 能不能通
|
||||
if t := fetchCurrentTime(); !t.IsZero() {
|
||||
log.Printf("✅ currentTime 获取成功: %s", t.In(time.FixedZone("CST", 8*3600)).Format("2006-01-02 15:04:05"))
|
||||
} else {
|
||||
log.Printf("⚠️ currentTime 获取失败,请检查网络")
|
||||
}
|
||||
|
||||
log.Printf("服务启动,账号数: %d", len(accounts))
|
||||
|
||||
http.HandleFunc("/api/products", handleProducts)
|
||||
http.HandleFunc("/api/load-products", handleLoadProducts)
|
||||
http.HandleFunc("/api/accounts", handleAccounts)
|
||||
http.HandleFunc("/api/reload-accounts", handleReloadAccounts)
|
||||
http.HandleFunc("/api/seckill", handleSeckill)
|
||||
http.HandleFunc("/api/schedule", handleSchedule)
|
||||
http.HandleFunc("/api/results", handleResults)
|
||||
http.HandleFunc("/api/time", handleTime)
|
||||
http.HandleFunc("/api/clear-results", handleClearResults)
|
||||
http.HandleFunc("/api/query-score", handleQueryScore)
|
||||
http.Handle("/", http.FileServer(http.Dir("./static")))
|
||||
|
||||
log.Printf("✅ 服务启动: http://localhost:2169 | 账号:%d", len(accounts))
|
||||
log.Fatal(http.ListenAndServe(":2169", nil))
|
||||
}
|
||||
|
||||
func initFont() {
|
||||
fontMu.Lock()
|
||||
defer fontMu.Unlock()
|
||||
if cachedFont != nil {
|
||||
return
|
||||
}
|
||||
fontBytes, err := os.ReadFile("Microsoft Yahei.ttf")
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] 字体文件加载失败: %v", err)
|
||||
return
|
||||
}
|
||||
f, err := freetype.ParseFont(fontBytes)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] 字体解析失败: %v", err)
|
||||
return
|
||||
}
|
||||
cachedFont = f
|
||||
log.Printf("[OK] 字体加载成功")
|
||||
}
|
||||
|
||||
// 新增加载配置函数
|
||||
func loadConfig() {
|
||||
f, err := os.Open("config.json")
|
||||
if err != nil {
|
||||
log.Printf("⚠️ 读取 config.json 失败,使用默认URL")
|
||||
config.TimeApiUrl = "https://www.douyu.com/wgapi/ordnc/activity/peace/storedetail?manualID=G4KA4Qnz4LDp7&commodityID=20260120QYOOB_bag274&rid=9263298"
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
decoder := json.NewDecoder(f)
|
||||
if err := decoder.Decode(&config); err != nil {
|
||||
log.Printf("⚠️ 解析 config.json 失败,使用默认URL")
|
||||
config.TimeApiUrl = "https://www.douyu.com/wgapi/ordnc/activity/peace/storedetail?manualID=G4KA4Qnz4LDp7&commodityID=20260120QYOOB_bag274&rid=9263298"
|
||||
return
|
||||
}
|
||||
log.Printf("✅ 加载配置成功: %s", config.TimeApiUrl)
|
||||
}
|
||||
|
||||
// ==================== 核心:从商品详情接口获取服务端当前时间 ====================
|
||||
|
||||
// fetchCurrentTime 从商品详情接口获取服务端当前时间
|
||||
func fetchCurrentTime() time.Time {
|
||||
url := config.TimeApiUrl + "&_=" + fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0) Gecko/20100101 Firefox/152.0")
|
||||
req.Header.Set("Accept", "application/json, text/plain, */*")
|
||||
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5")
|
||||
req.Header.Set("Referer", "https://www.douyu.com/pages/live-peace-handbook/web/shop?ditchname=pass0&roomId="+ROOM_ID)
|
||||
req.Header.Set("Connection", "keep-alive")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("fetchCurrentTime 请求失败: %v", err)
|
||||
return time.Time{}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Printf("fetchCurrentTime HTTP状态码: %d", resp.StatusCode)
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result struct {
|
||||
Data struct {
|
||||
PreExchange struct {
|
||||
CurrentTime string `json:"currentTime"`
|
||||
} `json:"preExchange"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
log.Printf("fetchCurrentTime 解析JSON失败: %v", err)
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
ts, err := strconv.ParseInt(result.Data.PreExchange.CurrentTime, 10, 64)
|
||||
if err != nil || ts == 0 {
|
||||
log.Printf("fetchCurrentTime 解析时间戳失败: %s", result.Data.PreExchange.CurrentTime)
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
return time.Unix(ts, 0).UTC()
|
||||
}
|
||||
|
||||
// getServerTime 获取服务端当前时间(带重试)
|
||||
func getServerTime() time.Time {
|
||||
for i := 0; i < 3; i++ {
|
||||
t := fetchCurrentTime()
|
||||
if !t.IsZero() {
|
||||
return t
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
// 全部失败,降级到本地时间
|
||||
log.Printf("⚠️ getServerTime 全部失败,降级到本地时间")
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
// ==================== API ====================
|
||||
|
||||
func handleClearResults(w http.ResponseWriter, _ *http.Request) {
|
||||
resultsMu.Lock()
|
||||
results = make(map[string]string)
|
||||
resultsMu.Unlock()
|
||||
json.NewEncoder(w).Encode(map[string]string{"ok": "true"})
|
||||
}
|
||||
|
||||
// 查询积分
|
||||
func getScore(cookie string) string {
|
||||
body := strings.NewReader(`{}`)
|
||||
req, _ := http.NewRequest("POST", JIFEN_HOST, body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
req.Header.Set("Cookie", cookie)
|
||||
req.Header.Set("Referer", "https://www.douyu.com/")
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "查询失败"
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result struct {
|
||||
Data struct {
|
||||
Balance int `json:"balance"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.Unmarshal(b, &result)
|
||||
return fmt.Sprintf("%d", result.Data.Balance)
|
||||
}
|
||||
|
||||
func handleQueryScore(w http.ResponseWriter, r *http.Request) {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
cookie := string(body)
|
||||
if cookie == "" {
|
||||
http.Error(w, "缺少cookie", 400)
|
||||
return
|
||||
}
|
||||
score := getScore(cookie)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"score": score})
|
||||
}
|
||||
|
||||
func loadAccounts() {
|
||||
accountsMu.Lock()
|
||||
defer accountsMu.Unlock()
|
||||
accounts = nil
|
||||
f, err := os.Open("user.txt")
|
||||
if err != nil {
|
||||
log.Printf("读取user.txt失败: %v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "----", 3)
|
||||
if len(parts) == 3 {
|
||||
accounts = append(accounts, Account{Name: parts[0], Password: parts[1], Cookie: parts[2]})
|
||||
}
|
||||
}
|
||||
log.Printf("加载 %d 个账号", len(accounts))
|
||||
}
|
||||
|
||||
func handleTime(w http.ResponseWriter, _ *http.Request) {
|
||||
loc := time.FixedZone("CST", 8*3600)
|
||||
serverTime := getServerTime()
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"serverTime": serverTime.In(loc).Format("2006-01-02 15:04:05.000"),
|
||||
"localTime": time.Now().In(loc).Format("2006-01-02 15:04:05.000"),
|
||||
"source": "currentTime",
|
||||
})
|
||||
}
|
||||
|
||||
func handleProducts(w http.ResponseWriter, _ *http.Request) {
|
||||
productsMu.RLock()
|
||||
defer productsMu.RUnlock()
|
||||
json.NewEncoder(w).Encode(products)
|
||||
}
|
||||
|
||||
func handleLoadProducts(w http.ResponseWriter, _ *http.Request) {
|
||||
url := fmt.Sprintf("https://www.douyu.com/wgapi/ordnc/activity/peace/storehome?manualID=%s&rid=%s", MANUAL_ID, ROOM_ID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
http.Error(w, "请求失败", 500)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var result struct {
|
||||
Data struct {
|
||||
List []struct {
|
||||
CommodityID string `json:"commodityId"`
|
||||
CommodityName string `json:"commodityName"`
|
||||
Score int `json:"score"`
|
||||
Count int `json:"count"`
|
||||
Pic string `json:"pic"`
|
||||
} `json:"list"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.Unmarshal(body, &result)
|
||||
|
||||
productsMu.Lock()
|
||||
products = nil
|
||||
for _, p := range result.Data.List {
|
||||
products = append(products, Product{p.CommodityID, p.CommodityName, p.Score, p.Count, p.Pic})
|
||||
}
|
||||
productsMu.Unlock()
|
||||
log.Printf("加载商品: %d个", len(products))
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"success": true, "count": len(products)})
|
||||
}
|
||||
|
||||
func handleAccounts(w http.ResponseWriter, _ *http.Request) {
|
||||
accountsMu.RLock()
|
||||
defer accountsMu.RUnlock()
|
||||
type R struct {
|
||||
Index int `json:"index"`
|
||||
Name string `json:"name"`
|
||||
Cookie string `json:"cookie"`
|
||||
}
|
||||
list := []R{}
|
||||
for i, a := range accounts {
|
||||
list = append(list, R{i, a.Name, a.Cookie})
|
||||
}
|
||||
json.NewEncoder(w).Encode(list)
|
||||
}
|
||||
|
||||
func handleReloadAccounts(w http.ResponseWriter, _ *http.Request) {
|
||||
loadAccounts()
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"success": true, "count": len(accounts)})
|
||||
}
|
||||
|
||||
func handleSeckill(w http.ResponseWriter, r *http.Request) {
|
||||
var req SeckillRequest
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if len(req.AccountProducts) == 0 {
|
||||
http.Error(w, "请选择账号和商品", 400)
|
||||
return
|
||||
}
|
||||
go executeSeckill(req.AccountProducts)
|
||||
json.NewEncoder(w).Encode(map[string]string{"success": "true", "msg": "抢购已启动"})
|
||||
}
|
||||
|
||||
func handleSchedule(w http.ResponseWriter, r *http.Request) {
|
||||
var req SeckillRequest
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if req.TargetTime == "" {
|
||||
http.Error(w, "请设置目标时间", 400)
|
||||
return
|
||||
}
|
||||
targetTime, err := time.ParseInLocation("2006-01-02 15:04:05", req.TargetTime, time.Local)
|
||||
if err != nil {
|
||||
http.Error(w, "时间格式错误", 400)
|
||||
return
|
||||
}
|
||||
targetTime = targetTime.UTC()
|
||||
|
||||
// ✅ 用 currentTime 获取服务端当前时间
|
||||
serverTime := getServerTime()
|
||||
if serverTime.IsZero() {
|
||||
http.Error(w, "获取服务端时间失败", 500)
|
||||
return
|
||||
}
|
||||
|
||||
advance := time.Duration(req.Advance) * time.Millisecond
|
||||
triggerTime := targetTime.Add(-advance)
|
||||
|
||||
remain := triggerTime.Sub(serverTime)
|
||||
log.Printf("📍 目标时间: %s, 服务端时间: %s, 剩余: %v, 提前: %dms",
|
||||
targetTime.In(time.FixedZone("CST", 8*3600)).Format("15:04:05.000"),
|
||||
serverTime.In(time.FixedZone("CST", 8*3600)).Format("15:04:05.000"),
|
||||
remain,
|
||||
req.Advance,
|
||||
)
|
||||
|
||||
if remain < 0 {
|
||||
http.Error(w, "目标时间已过", 400)
|
||||
return
|
||||
}
|
||||
|
||||
timerMu.Lock()
|
||||
if seckillTimer != nil {
|
||||
seckillTimer.Stop()
|
||||
}
|
||||
timerMu.Unlock()
|
||||
|
||||
ap := req.AccountProducts
|
||||
loc := time.FixedZone("CST", 8*3600)
|
||||
|
||||
timerMu.Lock()
|
||||
seckillTimer = time.AfterFunc(remain, func() {
|
||||
//// ✅ 到点后再次获取最新的服务端时间,确保准时
|
||||
//now := getServerTime()
|
||||
//
|
||||
//log.Printf("🚀 触发! 目标:%s 实际:%s 偏差:%v",
|
||||
// targetTime.In(loc).Format("15:04:05.000"),
|
||||
// now.In(loc).Format("15:04:05.000"),
|
||||
// now.Sub(targetTime),
|
||||
//)
|
||||
log.Printf("🚀 触发! 本地时间: %s", time.Now().In(loc).Format("15:04:05.000"))
|
||||
executeSeckill(ap)
|
||||
})
|
||||
timerMu.Unlock()
|
||||
|
||||
log.Printf("⏰ 定时: %s(北京时间) 提前%dms", targetTime.In(loc).Format("15:04:05"), req.Advance)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"targetTime": targetTime.In(loc).Format("2006-01-02 15:04:05"),
|
||||
"triggerTime": triggerTime.In(loc).Format("2006-01-02 15:04:05.000"),
|
||||
"advance": req.Advance,
|
||||
"remainSecond": int(remain.Seconds()),
|
||||
"serverTime": serverTime.In(loc).Format("2006-01-02 15:04:05.000"),
|
||||
})
|
||||
}
|
||||
|
||||
func handleResults(w http.ResponseWriter, _ *http.Request) {
|
||||
resultsMu.RLock()
|
||||
defer resultsMu.RUnlock()
|
||||
json.NewEncoder(w).Encode(results)
|
||||
}
|
||||
|
||||
// ==================== 抢购执行 ====================
|
||||
func executeSeckill(ap map[string]string) {
|
||||
var wg sync.WaitGroup
|
||||
var ok, fail int32
|
||||
limit := make(chan struct{}, GLOBAL_CONCURRENT)
|
||||
|
||||
accountsMu.RLock()
|
||||
accs := make([]Account, len(accounts))
|
||||
copy(accs, accounts)
|
||||
accountsMu.RUnlock()
|
||||
|
||||
for _, a := range accs {
|
||||
cid, ex := ap[a.Name]
|
||||
if !ex || cid == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// ✅ 每个账号只获取一次 CSRF(移到这里,在循环内部)
|
||||
csrf := getCsrf(a.Cookie)
|
||||
if csrf == "" {
|
||||
log.Printf("❌ %s 获取 CSRF 失败,跳过", a.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i < CONCURRENCY; i++ {
|
||||
wg.Add(1)
|
||||
go func(a Account, cid string, csrf string, idx int) {
|
||||
defer wg.Done()
|
||||
limit <- struct{}{}
|
||||
defer func() { <-limit }()
|
||||
res := doExchange(a, cid, csrf)
|
||||
resultsMu.Lock()
|
||||
results[fmt.Sprintf("%s_第%d包", a.Name, idx+1)] = res
|
||||
resultsMu.Unlock()
|
||||
if strings.Contains(res, "成功") {
|
||||
atomic.AddInt32(&ok, 1)
|
||||
log.Printf("✅ %s 第%d包 成功: %s", a.Name, idx+1, res)
|
||||
rn := ""
|
||||
if strings.Contains(res, "|") {
|
||||
rn = strings.SplitN(res, "|", 2)[1]
|
||||
}
|
||||
go saveImg(a, cid, rn) // ← 异步保存,不阻塞
|
||||
} else {
|
||||
atomic.AddInt32(&fail, 1)
|
||||
log.Printf("❌ %s 第%d包 失败: %s", a.Name, idx+1, res)
|
||||
}
|
||||
}(a, cid, csrf, i) // ← 传入 csrf
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
log.Printf("抢购完成: 成功%d 失败%d", ok, fail)
|
||||
}
|
||||
|
||||
func doExchange(a Account, cid string, csrf string) string {
|
||||
//csrf := getCsrf(a.Cookie)
|
||||
if csrf == "" {
|
||||
return "CSRF失败"
|
||||
}
|
||||
|
||||
ccn := extractMid(a.Cookie, "acf_ccn=", ";")
|
||||
ck := a.Cookie
|
||||
if ccn != "" {
|
||||
ck += "; acf_ccn=" + ccn
|
||||
}
|
||||
ck += "; cvl_csrf_token=" + csrf
|
||||
ck = strings.ReplaceAll(ck, ";;", ";")
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := multipart.NewWriter(&buf)
|
||||
w.WriteField("manualID", MANUAL_ID)
|
||||
w.WriteField("commodityID", cid)
|
||||
w.WriteField("rid", ROOM_ID)
|
||||
w.WriteField("csrfToken", csrf)
|
||||
w.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", EXCHANGE_URL, &buf)
|
||||
req.Header.Set("Content-Type", w.FormDataContentType())
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
req.Header.Set("Origin", "https://www.douyu.com")
|
||||
req.Header.Set("Referer", "https://www.douyu.com/pages/live-peace-handbook/web/shop?roomId=9263298")
|
||||
req.Header.Set("Cookie", ck)
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "请求失败"
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
s := string(b)
|
||||
if strings.Contains(s, `"error":0`) {
|
||||
rn := getRoleName(a.Cookie)
|
||||
if rn != "" {
|
||||
return "成功|" + rn
|
||||
}
|
||||
return "成功"
|
||||
}
|
||||
return "失败|" + trunc(s, 80)
|
||||
}
|
||||
|
||||
// ==================== 辅助函数 ====================
|
||||
|
||||
func getCsrf(cookie string) string {
|
||||
req, _ := http.NewRequest("POST", CSRF_URL, nil)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
req.Header.Set("Cookie", cookie)
|
||||
req.Header.Set("Referer", "https://www.douyu.com/")
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
for _, c := range resp.Cookies() {
|
||||
if c.Name == "cvl_csrf_token" {
|
||||
return c.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getRoleName(cookie string) string {
|
||||
req, _ := http.NewRequest("GET", BIND_INFO, nil)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
req.Header.Set("Cookie", cookie)
|
||||
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||
req.Header.Set("Referer", "https://www.douyu.com/")
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
return extractMid(string(b), `"roleName":"`, `"`)
|
||||
}
|
||||
|
||||
func saveImg(a Account, cid, roleName string) {
|
||||
productsMu.RLock()
|
||||
var pname string
|
||||
for _, p := range products {
|
||||
if p.CommodityID == cid {
|
||||
pname = p.CommodityName
|
||||
break
|
||||
}
|
||||
}
|
||||
productsMu.RUnlock()
|
||||
|
||||
if cachedFont == nil {
|
||||
log.Printf("字体未加载,无法生成图片")
|
||||
return
|
||||
}
|
||||
|
||||
width, height := 600, 280
|
||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
bgColor := color.RGBA{213, 239, 253, 255}
|
||||
greenColor := color.RGBA{76, 175, 80, 255}
|
||||
whiteColor := color.RGBA{255, 255, 255, 255}
|
||||
blackColor := color.RGBA{0, 0, 0, 255}
|
||||
|
||||
// 画背景
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
if y < 60 {
|
||||
img.Set(x, y, greenColor)
|
||||
} else {
|
||||
img.Set(x, y, bgColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c := freetype.NewContext()
|
||||
c.SetDPI(72)
|
||||
c.SetFont(cachedFont) // ← 用缓存的字体,不再读取文件
|
||||
c.SetClip(img.Bounds())
|
||||
c.SetDst(img)
|
||||
|
||||
loc := time.FixedZone("CST", 8*3600)
|
||||
now := time.Now().In(loc)
|
||||
|
||||
texts := []struct {
|
||||
text string
|
||||
size float64
|
||||
x, y int
|
||||
color color.Color
|
||||
}{
|
||||
{pname, 20, 25, 42, whiteColor},
|
||||
{"兑换时间:" + now.Format("2006年01月02日 15:04:05"), 16, 25, 110, blackColor},
|
||||
{"游戏昵称:" + roleName, 16, 25, 150, blackColor},
|
||||
{"兑换账号:" + a.Name, 16, 25, 190, blackColor},
|
||||
}
|
||||
|
||||
for _, t := range texts {
|
||||
c.SetFontSize(t.size)
|
||||
c.SetSrc(image.NewUniform(t.color))
|
||||
pt := freetype.Pt(t.x, t.y)
|
||||
c.DrawString(t.text, pt)
|
||||
}
|
||||
|
||||
fn := fmt.Sprintf("success_images/%s_%s_%d.png", a.Name, pname, time.Now().Unix())
|
||||
f2, _ := os.Create(fn)
|
||||
defer f2.Close()
|
||||
png.Encode(f2, img)
|
||||
log.Printf("✅ 图片已保存: %s", fn)
|
||||
}
|
||||
|
||||
func extractMid(s, start, end string) string {
|
||||
i := strings.Index(s, start)
|
||||
if i == -1 {
|
||||
return ""
|
||||
}
|
||||
i += len(start)
|
||||
j := strings.Index(s[i:], end)
|
||||
if j == -1 {
|
||||
return s[i:]
|
||||
}
|
||||
return s[i : i+j]
|
||||
}
|
||||
|
||||
func trunc(s string, n int) string {
|
||||
if len(s) > n {
|
||||
return s[:n] + "..."
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>斗鱼抢购系统</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:'Microsoft YaHei',sans-serif;background:#0d1117;color:#c9d1d9;display:flex;height:100vh}
|
||||
|
||||
/* ========== 左侧商品列表 ========== */
|
||||
.left{width:280px;min-width:280px;background:#161b22;border-right:1px solid#30363d;display:flex;flex-direction:column}
|
||||
.left-header{padding:10px;border-bottom:1px solid#30363d}
|
||||
.left-list{flex:1;overflow-y:auto;padding:8px}
|
||||
.left-list .prod-item{display:flex;align-items:center;gap:8px;padding:8px;margin:4px 0;background:#21262d;border-radius:6px;cursor:pointer;border:2px solid transparent;transition:.2s}
|
||||
.left-list .prod-item:hover{border-color:#58a6ff}
|
||||
.left-list .prod-item.active{border-color:#f78166;background:#1a1f2b}
|
||||
.left-list .prod-item img{width:40px;height:40px;border-radius:4px;object-fit:cover;background:#0d1117}
|
||||
.left-list .prod-item .pinfo{flex:1;min-width:0}
|
||||
.left-list .prod-item .pname{font-size:13px;font-weight:bold;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.left-list .prod-item .pmeta{font-size:11px;color:#8b949e;display:flex;gap:8px}
|
||||
.pmeta .pscore{color:#d2991d}
|
||||
.pmeta .pstock{color:#7ee787}
|
||||
.pmeta .pstock.low{color:#f78166}
|
||||
|
||||
/* ========== 右侧主区域 ========== */
|
||||
.right{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative}
|
||||
|
||||
/* 工具栏 */
|
||||
.toolbar{padding:8px 14px;background:#161b22;border-bottom:1px solid#30363d;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
||||
.btn{padding:6px 12px;border:1px solid#30363d;border-radius:5px;background:#21262d;color:#c9d1d9;cursor:pointer;font-size:13px;white-space:nowrap;transition:.2s}
|
||||
.btn:hover{background:#30363d}
|
||||
.btn-red{background:#da3633;border-color:#da3633;color:#fff;font-size:14px;padding:8px 16px}
|
||||
.btn-red:hover{background:#c62828}
|
||||
.btn-green{background:#238636;border-color:#238636;color:#fff;font-size:14px;padding:8px 16px}
|
||||
.btn-green:hover{background:#1b6b2a}
|
||||
.btn-blue{background:#1f6feb;border-color:#1f6feb;color:#fff;font-size:14px;padding:8px 16px}
|
||||
.btn-blue:hover{background:#1158c7}
|
||||
.btn-cancel{background:#6e7681;border-color:#6e7681;color:#fff;font-size:14px;padding:8px 16px}
|
||||
.btn-cancel:hover{background:#555}
|
||||
|
||||
/* 定时信息栏 */
|
||||
.schedule-bar{padding:10px 16px;background:#161b22;border-bottom:1px solid#30363d;display:flex;gap:16px;align-items:center;flex-wrap:wrap;font-size:14px}
|
||||
.schedule-bar label{color:#8b949e;font-size:14px;font-weight:bold;white-space:nowrap}
|
||||
.schedule-bar .time-box{background:#0d1117;padding:6px 14px;border-radius:4px;font-family:'Consolas',monospace;font-size:16px;font-weight:bold}
|
||||
.schedule-bar .time-box.server{color:#58a6ff}
|
||||
.schedule-bar .time-box.offset{color:#f0883e}
|
||||
.schedule-bar input{padding:6px 12px;background:#0d1117;color:#fff;border:2px solid#30363d;border-radius:4px;font-size:16px;font-weight:bold}
|
||||
.schedule-bar input#targetTime{width:150px;text-align:center}
|
||||
.schedule-bar input#advance{width:80px;text-align:center}
|
||||
.schedule-bar .hint{color:#8b949e;font-size:12px}
|
||||
.countdown-badge{background:#da3633;color:#fff;padding:6px 16px;border-radius:4px;font-weight:bold;font-size:16px}
|
||||
|
||||
/* 账号表格 */
|
||||
.table-wrap{flex:1;overflow-y:auto;padding:8px 14px;min-height:150px}
|
||||
table{width:100%;border-collapse:collapse;font-size:13px}
|
||||
th{background:#21262d;padding:8px;text-align:left;border-bottom:2px solid#30363d;position:sticky;top:0;z-index:1;color:#8b949e;font-size:13px}
|
||||
td{padding:6px 8px;border-bottom:1px solid#21262d;vertical-align:middle}
|
||||
tr:hover{background:#1a1f2b}
|
||||
.cookie-td{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#6e7681;font-size:11px}
|
||||
.prod-tag{display:inline-block;background:#238636;color:#fff;padding:3px 10px;border-radius:3px;font-size:12px}
|
||||
.status-td{font-size:16px}
|
||||
|
||||
/* 日志区 */
|
||||
.log-wrap{border-top:2px solid#30363d;background:#0d1117;display:flex;flex-direction:column;height:200px}
|
||||
.log-header{padding:8px 16px;background:#161b22;border-bottom:1px solid#30363d;display:flex;justify-content:space-between;align-items:center;font-size:13px}
|
||||
.log-body{flex:1;overflow-y:auto;padding:10px 16px;font-family:'Consolas','Courier New',monospace;font-size:13px;line-height:1.8}
|
||||
.log-s{color:#7ee787}
|
||||
.log-e{color:#f78166}
|
||||
.log-i{color:#58a6ff}
|
||||
.log-w{color:#f0883e}
|
||||
.log-result{background:#1a1f2b;padding:2px 8px;margin:2px 0;border-radius:3px;word-break:break-all}
|
||||
|
||||
/* ========== 右侧滑入提示 ========== */
|
||||
.toast-container{position:fixed;top:20px;right:-420px;z-index:9999;transition:right .4s ease}
|
||||
.toast-container.show{right:20px}
|
||||
.toast{background:#161b22;border:2px solid#30363d;border-radius:10px;padding:16px 20px;margin-bottom:10px;min-width:350px;box-shadow:0 8px 30px rgba(0,0,0,.5)}
|
||||
.toast-title{font-size:16px;font-weight:bold;margin-bottom:6px}
|
||||
.toast-body{font-size:13px;color:#8b949e;line-height:1.6}
|
||||
.toast-success{border-color:#238636}.toast-success .toast-title{color:#7ee787}
|
||||
.toast-info{border-color:#1f6feb}.toast-info .toast-title{color:#58a6ff}
|
||||
.toast-warn{border-color:#d2991d}.toast-warn .toast-title{color:#d2991d}
|
||||
.toast-error{border-color:#da3633}.toast-error .toast-title{color:#f78166}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 右侧滑入提示容器 -->
|
||||
<div class="toast-container" id="toastContainer"></div>
|
||||
|
||||
<!-- ==================== 左侧商品列表 ==================== -->
|
||||
<div class="left">
|
||||
<div class="left-header">
|
||||
<button class="btn btn-blue" onclick="loadProducts()" style="width:100%">🔄 加载商品</button>
|
||||
</div>
|
||||
<div class="left-list" id="prodList">
|
||||
<div style="text-align:center;color:#8b949e;padding:40px 10px;font-size:13px">点击"加载商品"获取列表</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== 右侧 ==================== -->
|
||||
<div class="right">
|
||||
<div class="toolbar">
|
||||
<button class="btn btn-red" onclick="startNow()">⚡ 立即抢购</button>
|
||||
<button class="btn btn-green" id="btnSchedule" onclick="startSchedule()">⏰ 开启定时</button>
|
||||
<button class="btn btn-cancel" id="btnCancel" onclick="cancelSchedule()" style="display:none">⏹ 停止定时</button>
|
||||
<span style="color:#8b949e;font-size:14px;margin-left:8px">账号:<b id="accCnt" style="color:#58a6ff">0</b></span>
|
||||
<span style="color:#8b949e;font-size:14px"> 选中商品:<b id="selProd" style="color:#f78166"> 未选</b></span>
|
||||
<button class="btn btn-blue" onclick="reloadAccounts()" style="font-size:12px;padding:4px 10px">🔄 加载账号</button>
|
||||
<button class="btn btn-blue" onclick="queryAllScore()" style="font-size:12px;padding:4px 10px">💰 查询积分</button>
|
||||
<span id="cdBadge" class="countdown-badge" style="display:none"></span>
|
||||
</div>
|
||||
|
||||
<div class="schedule-bar">
|
||||
<label>服务器时间:</label>
|
||||
<span class="time-box server" id="svrTime">--</span>
|
||||
<label>目标时间:</label>
|
||||
<input type="time" id="targetTime" value="18:00:00" step="1">
|
||||
<label>提前(ms):</label>
|
||||
<input type="number" id="advance" value="300" min="0" max="5000" step="100" style="width:90px;text-align:center">
|
||||
<span class="hint">(定时触发=目标-提前量)</span>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:36px"><input type="checkbox" id="chkAll" onchange="toggleAll()"></th>
|
||||
<th>账号名</th>
|
||||
<th>Cookie</th>
|
||||
<th>商品</th>
|
||||
<th>积分</th>
|
||||
<th style="width:80px">状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="accTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="log-wrap">
|
||||
<div class="log-header">
|
||||
<span>📋 运行日志(自动刷新)</span>
|
||||
<button class="btn" onclick="clearLog()" style="font-size:12px;padding:4px 10px">清空</button>
|
||||
</div>
|
||||
<div class="log-body" id="logBody">
|
||||
<div class="log-i">[系统] 就绪,等待操作...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let accounts=[], products=[], selProdId=null, selProdName='', selAccs=new Set();
|
||||
let scheduleActive=false, countdownTimer=null, lastResults={};
|
||||
let statusMap={}; // ← 加这行
|
||||
|
||||
async function init(){
|
||||
// 恢复缓存的选择
|
||||
let savedAccs=localStorage.getItem('selAccs');
|
||||
let savedProd=localStorage.getItem('selProd');
|
||||
let savedProdName=localStorage.getItem('selProdName');
|
||||
let savedTime=localStorage.getItem('targetTime');
|
||||
let savedAdv=localStorage.getItem('advance');
|
||||
let savedStatus=localStorage.getItem('statusMap'); // ← 提前读取
|
||||
|
||||
if(savedAccs) selAccs=new Set(JSON.parse(savedAccs));
|
||||
if(savedProd){ selProdId=savedProd; selProdName=savedProdName||''; document.getElementById('selProd').textContent=selProdName; }
|
||||
if(savedTime) document.getElementById('targetTime').value=savedTime;
|
||||
if(savedAdv) document.getElementById('advance').value=savedAdv;
|
||||
if(savedStatus) statusMap=JSON.parse(savedStatus); // ← 提前恢复
|
||||
|
||||
|
||||
await loadAccounts();
|
||||
|
||||
await loadProducts(); // ← 加这行,启动自动加载商品
|
||||
|
||||
updateTime();
|
||||
|
||||
clearLog(); // ← 加这行,启动时清空日志区
|
||||
|
||||
|
||||
//localStorage.removeItem('statusMap'); // ← 加这行
|
||||
// statusMap={}; // ← 加这行 这两行删除就保存成功失败得状态
|
||||
|
||||
// 清空后端旧结果
|
||||
await fetch('/api/clear-results', {method:'POST'});
|
||||
|
||||
setInterval(updateTime,1000);
|
||||
setInterval(checkResults,2000);
|
||||
}
|
||||
|
||||
function updateTime(){
|
||||
fetch('/api/time').then(r=>r.json()).then(d=>{
|
||||
document.getElementById('svrTime').textContent=d.serverTime||'--';
|
||||
}).catch(()=>{});
|
||||
}
|
||||
|
||||
//加载账号
|
||||
async function reloadAccounts(){
|
||||
let r=await fetch('/api/reload-accounts');
|
||||
let d=await r.json();
|
||||
await loadAccounts();
|
||||
toast('✅ 账号已刷新',`共 ${d.count} 个账号`,'success');
|
||||
}
|
||||
|
||||
//并发查询函数
|
||||
async function queryAllScore() {
|
||||
addLog('正在并发查询积分...', 'i');
|
||||
let tasks = [];
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
tasks.push((async (idx) => {
|
||||
let a = accounts[idx];
|
||||
try {
|
||||
console.log('账号对象:', JSON.stringify(a));
|
||||
console.log('Cookie长度:', a.cookie?.length, '前50字符:', a.cookie?.substring(0, 50));
|
||||
|
||||
let r = await fetch('/api/query-score', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
body: a.cookie
|
||||
});
|
||||
let d = await r.json();
|
||||
let score = (d.score !== undefined && d.score !== null && d.score !== '') ? d.score : 'N/A';
|
||||
// ✅ 用账号名存积分
|
||||
localStorage.setItem('accScore_' + a.name, score); // ← 改成 a.name
|
||||
let el = document.getElementById('score_' + idx);
|
||||
if (el) el.textContent = score;
|
||||
} catch (e) { }
|
||||
})(i));
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
addLog('积分查询完成', 's');
|
||||
toast('✅ 积分查询完成', '', 'success');
|
||||
}
|
||||
|
||||
|
||||
// ==================== Toast提示 ====================
|
||||
function toast(title, body, type, duration){
|
||||
type=type||'info'; duration=duration||4000;
|
||||
let container=document.getElementById('toastContainer');
|
||||
let div=document.createElement('div');
|
||||
div.className='toast toast-'+type;
|
||||
div.innerHTML=`<div class="toast-title">${title}</div><div class="toast-body">${body}</div>`;
|
||||
container.appendChild(div);
|
||||
// 触发滑入
|
||||
setTimeout(()=>container.classList.add('show'),10);
|
||||
// 自动移除
|
||||
setTimeout(()=>{
|
||||
div.style.opacity='0';
|
||||
div.style.transition='opacity .3s';
|
||||
setTimeout(()=>{div.remove();if(container.children.length===0)container.classList.remove('show');},300);
|
||||
},duration);
|
||||
}
|
||||
|
||||
// ==================== 商品 ====================
|
||||
async function loadProducts(){
|
||||
addLog('正在从斗鱼加载商品...','i');
|
||||
try{
|
||||
await fetch('/api/load-products');
|
||||
let r=await fetch('/api/products'); products=await r.json();
|
||||
renderProducts();
|
||||
addLog(`✅ 加载完成,共 ${products.length} 个商品`,'s');
|
||||
toast('✅ 加载成功',`共加载 ${products.length} 个商品`,'success');
|
||||
}catch(e){
|
||||
addLog('加载失败,请检查网络','e');
|
||||
toast('❌ 加载失败','请检查网络连接','error');
|
||||
}
|
||||
}
|
||||
function renderProducts(){
|
||||
let h='';
|
||||
products.forEach(p=>{
|
||||
h+=`<div class="prod-item${selProdId==p.commodityId?' active':''}" onclick="selProduct('${p.commodityId}','${escHtml(p.commodityName)}')">
|
||||
<img src="${p.pic}" onerror="this.style.display='none'">
|
||||
<div class="pinfo"><div class="pname">${p.commodityName}</div>
|
||||
<div class="pmeta"><span class="pscore">💰${p.score}</span><span class="pstock${p.count<500?' low':''}">📦${p.count}</span></div></div>
|
||||
</div>`;
|
||||
});
|
||||
document.getElementById('prodList').innerHTML=h||'<div style="text-align:center;color:#8b949e;padding:40px">暂无商品</div>';
|
||||
}
|
||||
|
||||
function selProduct(id, name) {
|
||||
selProdId = id;
|
||||
selProdName = name;
|
||||
document.getElementById('selProd').textContent = name;
|
||||
// ✅ 用账号名存商品绑定
|
||||
selAccs.forEach(i => {
|
||||
let accountName = accounts[i].name; // 用账号名作为 key
|
||||
localStorage.setItem('accProd_' + accountName, id);
|
||||
localStorage.setItem('accProdName_' + accountName, name);
|
||||
});
|
||||
|
||||
// selAccs.clear();
|
||||
// localStorage.setItem('selAccs', JSON.stringify([...selAccs]));
|
||||
renderProducts();
|
||||
renderAccounts();
|
||||
}
|
||||
|
||||
// ==================== 账号 ====================
|
||||
// async function loadAccounts(){
|
||||
// let r=await fetch('/api/accounts'); accounts=await r.json();
|
||||
// renderAccounts();
|
||||
// }
|
||||
|
||||
async function loadAccounts() {
|
||||
let r = await fetch('/api/accounts');
|
||||
accounts = await r.json();
|
||||
|
||||
let validNames = new Set(accounts.map(a => a.name));
|
||||
|
||||
for (let key in localStorage) {
|
||||
// 只清理商品和积分缓存,保留 selAccs、statusMap、targetTime、advance
|
||||
if (key.startsWith('accProd_') || key.startsWith('accProdName_') || key.startsWith('accScore_')) {
|
||||
let name = key.substring(key.indexOf('_') + 1);
|
||||
if (!validNames.has(name)) {
|
||||
localStorage.removeItem(key);
|
||||
console.log('清理无效缓存:', key);
|
||||
}
|
||||
}
|
||||
}
|
||||
renderAccounts();
|
||||
}
|
||||
|
||||
|
||||
function renderAccounts() {
|
||||
document.getElementById('accCnt').textContent = accounts.length;
|
||||
let h = accounts.map((a, i) => {
|
||||
let isSel = selAccs.has(i);
|
||||
// ✅ 用账号名读取商品、积分、状态
|
||||
let prodName = localStorage.getItem('accProdName_' + a.name) || '-';
|
||||
let prodTag = prodName !== '-' ? `<span class="prod-tag">${prodName}</span>` : '-';
|
||||
let scoreVal = localStorage.getItem('accScore_' + a.name) || '-'; // ← 改成 a.name
|
||||
let st = statusMap[a.name]; // ← 已经是 a.name,保持
|
||||
let statusStr = '-';
|
||||
if (st === true) statusStr = '✅';
|
||||
else if (st === false) statusStr = '❌';
|
||||
return `<tr>
|
||||
<td>
|
||||
<label style="display:flex;align-items:center;cursor:pointer" onclick="togAcc(${i})">
|
||||
<div style="width:18px;height:18px;border-radius:4px;border:2px solid ${isSel ? '#238636' : '#30363d'};background:${isSel ? '#238636' : 'transparent'};display:flex;align-items:center;justify-content:center;transition:.2s">
|
||||
${isSel ? '<span style="color:#fff;font-size:12px">✓</span>' : ''}
|
||||
</div>
|
||||
</label>
|
||||
</td>
|
||||
<td style="font-weight:bold">${a.name}</td>
|
||||
<td class="cookie-td" title="${a.cookie}">${a.cookie}</td>
|
||||
<td>${prodTag}</td>
|
||||
<td id="score_${i}">${scoreVal}</td>
|
||||
<td class="status-td" id="st_${i}">${statusStr}</td>
|
||||
</tr>`;
|
||||
});
|
||||
document.getElementById('accTable').innerHTML = h.join('');
|
||||
}
|
||||
|
||||
|
||||
function toggleAll(){
|
||||
let ck=document.getElementById('chkAll').checked;
|
||||
if(ck) accounts.forEach((_,i)=>selAccs.add(i));
|
||||
else selAccs.clear();
|
||||
|
||||
localStorage.setItem('selAccs',JSON.stringify([...selAccs]));
|
||||
|
||||
renderAccounts();
|
||||
}
|
||||
function togAcc(i){
|
||||
if(selAccs.has(i)) selAccs.delete(i); else selAccs.add(i);
|
||||
localStorage.setItem('selAccs',JSON.stringify([...selAccs]));
|
||||
renderAccounts();
|
||||
}
|
||||
|
||||
// ==================== 立即抢购(不弹窗) ====================
|
||||
async function startNow(){
|
||||
let ap=buildAP();
|
||||
if(!ap) return;
|
||||
addLog(`⚡ 立即抢购: ${Object.keys(ap).length}个账号 → ${selProdName}`,'i');
|
||||
toast('⚡ 抢购中...',`${Object.keys(ap).length} 个账号正在抢购 ${selProdName}`,'info',3000);
|
||||
let r=await fetch('/api/seckill',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({accountProducts:ap})});
|
||||
let d=await r.json();
|
||||
addLog(d.msg||'已提交','i');
|
||||
}
|
||||
|
||||
// ==================== 定时抢购 ====================
|
||||
async function startSchedule(){
|
||||
let ap=buildAP();
|
||||
if(!ap) return;
|
||||
let timeStr=document.getElementById('targetTime').value.trim();
|
||||
if(!timeStr){ toast('⚠️ 提示','请选择目标时间','warn'); return; }
|
||||
if(timeStr.split(':').length===2) timeStr+=':00';
|
||||
let today=new Intl.DateTimeFormat('zh-CN',{year:'numeric',month:'2-digit',day:'2-digit'})
|
||||
.format(new Date()).replace(/\//g,'-');
|
||||
let fullTime=today+' '+timeStr;
|
||||
let adv=parseInt(document.getElementById('advance').value)||300;
|
||||
|
||||
addLog(`⏰ 设置定时: ${fullTime}, 提前=${adv}ms`,'i');
|
||||
let r=await fetch('/api/schedule',{method:'POST',headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({accountProducts:ap,targetTime:fullTime,advance:adv})});
|
||||
let d=await r.json();
|
||||
if(d.success){
|
||||
scheduleActive=true;
|
||||
// 保存定时信息
|
||||
localStorage.setItem('targetTime',timeStr);
|
||||
localStorage.setItem('advance',adv);
|
||||
|
||||
// 锁定:关闭自动,固定当前值
|
||||
document.getElementById('btnSchedule').style.display='none';
|
||||
document.getElementById('btnCancel').style.display='inline-block';
|
||||
addLog(`✅ 定时已开启! 触发: ${d.triggerTime} (剩余${d.remainSecond}秒)`,'s');
|
||||
toast('⏰ 定时已开启',`触发时刻: ${d.triggerTime}<br>剩余时间: <b>${formatRemain(d.remainSecond)}</b><br>商品: ${selProdName}<br>账号: ${Object.keys(ap).length} 个`,'success',5000);
|
||||
startCountdown(d.remainSecond);
|
||||
} else {
|
||||
addLog('❌ 设置失败: '+(d.error||'未知'),'e');
|
||||
toast('❌ 定时失败',d.error||'未知错误','error');
|
||||
}
|
||||
}
|
||||
async function cancelSchedule(){
|
||||
scheduleActive=false;
|
||||
// 清除缓存的定时
|
||||
localStorage.removeItem('targetTime');
|
||||
localStorage.removeItem('advance');
|
||||
|
||||
// 恢复自动
|
||||
document.getElementById('btnSchedule').style.display='inline-block';
|
||||
document.getElementById('btnCancel').style.display='none';
|
||||
stopCountdown();
|
||||
addLog('⚠️ 定时已手动停止','w');
|
||||
toast('⏹ 定时已停止','抢购任务已取消','warn');
|
||||
}
|
||||
|
||||
function startCountdown(sec) {
|
||||
stopCountdown();
|
||||
let badge = document.getElementById('cdBadge');
|
||||
badge.style.display = 'inline-block';
|
||||
let startTime = Date.now();
|
||||
let endTime = startTime + sec * 1000;
|
||||
|
||||
countdownTimer = setInterval(() => {
|
||||
let now = Date.now();
|
||||
let remain = Math.max(0, Math.ceil((endTime - now) / 1000));
|
||||
if (remain <= 0) {
|
||||
badge.textContent = '🚀 触发中...';
|
||||
stopCountdown();
|
||||
scheduleActive = false;
|
||||
document.getElementById('btnSchedule').style.display = 'inline-block';
|
||||
document.getElementById('btnCancel').style.display = 'none';
|
||||
setTimeout(() => badge.style.display = 'none', 5000);
|
||||
} else {
|
||||
badge.textContent = `⏱ ${formatRemain(remain)}`;
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
||||
function stopCountdown(){
|
||||
clearInterval(countdownTimer);
|
||||
document.getElementById('cdBadge').style.display='none';
|
||||
}
|
||||
function formatRemain(sec){
|
||||
let h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60),s=sec%60;
|
||||
return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
|
||||
}
|
||||
|
||||
// ==================== 结果刷新 ====================
|
||||
async function checkResults() {
|
||||
try {
|
||||
let r = await fetch('/api/results');
|
||||
let d = await r.json();
|
||||
if (JSON.stringify(d) !== JSON.stringify(lastResults) && Object.keys(d).length > 0) {
|
||||
lastResults = d;
|
||||
let newStatusMap = {};
|
||||
let h = '<div class="log-i">=== 最新结果 ===</div>';
|
||||
for (let [k, v] of Object.entries(d)) {
|
||||
let cls = v.includes('成功') ? 'log-s' : 'log-e';
|
||||
h += `<div class="log-result ${cls}"><b>${k}</b>: ${v}</div>`;
|
||||
let name = k.split('_')[0];
|
||||
// ✅ 直接用账号名作为 key,不再用 findIndex
|
||||
if (!(name in newStatusMap)) newStatusMap[name] = false;
|
||||
if (v.includes('成功')) newStatusMap[name] = true;
|
||||
}
|
||||
statusMap = newStatusMap;
|
||||
localStorage.setItem('statusMap', JSON.stringify(statusMap));
|
||||
renderAccounts();
|
||||
let el = document.getElementById('logBody');
|
||||
el.innerHTML += h;
|
||||
el.scrollTop = el.scrollHeight;
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
function buildAP() {
|
||||
if (selAccs.size === 0) {
|
||||
toast('⚠️ 提示', '请勾选要抢购的账号', 'warn');
|
||||
return null;
|
||||
}
|
||||
let ap = {};
|
||||
selAccs.forEach(i => {
|
||||
let accountName = accounts[i].name; // ← 先取账号名
|
||||
let cid = localStorage.getItem('accProd_' + accountName); // ← 用账号名读
|
||||
if (cid) {
|
||||
ap[accountName] = cid;
|
||||
}
|
||||
});
|
||||
if (Object.keys(ap).length === 0) {
|
||||
toast('⚠️ 提示', '勾选的账号未绑定商品', 'warn');
|
||||
return null;
|
||||
}
|
||||
return ap;
|
||||
}
|
||||
|
||||
|
||||
function hasAnyAccProd(){
|
||||
for(let i=0; i<accounts.length; i++){
|
||||
if (localStorage.getItem('accProd_' + accounts[i].name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function addLog(msg, type = 'i') {
|
||||
let el = document.getElementById('logBody');
|
||||
let now = new Date();
|
||||
let timeStr = now.toLocaleTimeString() + '.' + String(now.getMilliseconds()).padStart(3, '0');
|
||||
el.innerHTML += `<div class="log-${type}">[${timeStr}] ${msg}</div>`;
|
||||
el.scrollTop = el.scrollHeight;
|
||||
}
|
||||
|
||||
|
||||
function clearLog(){
|
||||
document.getElementById('logBody').innerHTML='<div class="log-i">[系统] 就绪,等待操作...</div>';
|
||||
lastResults={};
|
||||
}
|
||||
|
||||
function escHtml(s){ return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
cvl_csrf_token=62083f188904477e9bc1c5a70537145c; acf_ccn=8b025ffcbf403d5e0d880ad6327bb3cf; PHPSESSID=86k3denvoc1j16rl1fsv5l2mc2; acf_auth=5a8eUzBoDc8uyLasCP7LXaO9a2vUK%2FDaNFwYi0MVdUFMpUcMBfsoZfifJZKCQC8WgJuKq7WB78VZrRlRXnZUSt87TZM0tLNdnplqtuL87c7idAqo%2Fal57ls; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk0MjQxNjQ1MSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo2OTE2MTY4NSwic3RrIjoiNGZkNDY5ZDE2OWY3NzM0YiIsImV4cCI6MTc4MjMyMjc2OSwiaWF0IjoxNzgxNzE3OTcwLCJrZXkiOiJkeS1qd3QtbWQ1In0.NDc0YzMyNjQ0OGIzNTkxYzkxNGQ1NDk0Y2U0NTk1NTU; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk0MjQxNjQ1MSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo2OTE2MTY4NSwic3RrIjoiNGZkNDY5ZDE2OWY3NzM0YiIsImV4cCI6MTc4MjMyMjc2OSwiaWF0IjoxNzgxNzE3OTcwLCJrZXkiOiJkeS1qd3QtbWQ1In0.NWM1ZDgwZjE1N2ZhNTE3NDUxNmQ4ZjljMzI5ODQ0ZjM; dy_auth=29bfEdmmHp50M9Zr2gxQ3%2F2AJD2DAdCK6I%2FoX92U5n13i64R%2B%2FywJZf%2FqG4%2BCJvbUQmFaUSu3dFbJj4%2Be1gU1DpW6ligJYaJLnNuVbmKMeZCtayBmGtns6A; wan_auth37wan=cf514c616a0fzsvhClbvRbju0sSZb4aDqHQQabpd66aLoVky4bvRRXYDfYMX3iSUNiDuh%2BHt%2B20QXW0f5JLZ6lBtOW8LIIwb31iX8OiZurGB0aztSKA; acf_uid=942416451; acf_username=942416451; acf_nickname=%E7%94%A8%E6%88%B74512197651; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F18_; acf_ct=0; acf_ltkid=69161685; acf_biz=1; acf_stk=4fd469d169f7734b; acf_devid=9869cce23d84da0fd8b1cd20162b3def--用户4512197651--
|
||||
cvl_csrf_token=72766ff5bc3046fcad8b75d35d66c5ee; acf_ccn=69e155a4a5a9584f54237171d64ea2f6; PHPSESSID=e93n0eqo5euha5o8id1n9rf8t0; acf_auth=0e6fV%2BISmSosvoQYyB3%2FE7qoi%2BzJLJuAyOBcuHFEkUVNlXYtaTVE6GcEbe%2BQrVjS3YyihRsigIP6ICk5osYbiTjxtZ2FQXveQWipgjKLjSP5XcqoHVAwiOo; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjcyMjQ2NDQ3MywiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjoxOTY4NzcwNywic3RrIjoiMWZmOWFmYjE0MDg4NTFkNyIsImV4cCI6MTc4MjMyMjgxNCwiaWF0IjoxNzgxNzE4MDE2LCJrZXkiOiJkeS1qd3QtbWQ1In0.ZTI5NzE1ZWFlODBhMDNmYmI4ODVhNTEwYzhlZjYxYTE; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjcyMjQ2NDQ3MywiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjoxOTY4NzcwNywic3RrIjoiMWZmOWFmYjE0MDg4NTFkNyIsImV4cCI6MTc4MjMyMjgxNCwiaWF0IjoxNzgxNzE4MDE2LCJrZXkiOiJkeS1qd3QtbWQ1In0.MzQ0MjcyMzg0ZjlmYzMxN2EwYWNhZTc3ZjAyNWNiZGE; dy_auth=0ac1Yv943lmZyRHUxS3Tfi8NVFJ6l2bivH0FrHNLfzlyqTxGEewNsmL1W0g7wBisNMpl3NH8GnhSdoh6GD1zYPDhqyJAqK0n6B%2FXpVjTpXu4eoVX4hFVvm8; wan_auth37wan=c8781c56af25SCHFffglLu3%2FVizZeEOeUSmBhRY1evJgKirLUlhnB9%2F3MR3FNy2GOc9KCMnCmaX%2BVMLgPizMdGBDRyWThJDZdol552WLTEXOkawhA0w; acf_uid=722464473; acf_username=722464473; acf_nickname=%E7%94%A8%E6%88%B70906888647; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F14_; acf_ct=0; acf_ltkid=19687707; acf_biz=1; acf_stk=1ff9afb1408851d7; acf_devid=8f6099cda2021f0c5d1b8aed2e2fd0f2--用户0906888647--
|
||||
cvl_csrf_token=391cc241cf5543d2ba0dcafb18b6d5b6; acf_ccn=3894304ac2c336406e182b305e04fb48; PHPSESSID=mtl16kuu1evvuu64mg0r0m6lt7; acf_auth=4f0cDameLsYc%2Fc6nxZAWo9hd0UUQYYkM68%2FaxWjK9TnHTS1%2FqfHV6ZkoufDzoVo3%2F%2BeoXCQmJRxJn4Nw7ceHLT%2BckHUXWviohTIe4Z7equjK8mj8zn%2Fv6%2Bc; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg1NDQ1NDMwMSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo1MDU4NzU0NCwic3RrIjoiYTU0N2QxMGZlMTI1MWQxNSIsImV4cCI6MTc4MjMyMjg1OCwiaWF0IjoxNzgxNzE4MDU5LCJrZXkiOiJkeS1qd3QtbWQ1In0.YzBhNTEwYTIyMzVmNTNkMWRlZGVhMzZkNjM4ZDRlMTQ; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg1NDQ1NDMwMSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo1MDU4NzU0NCwic3RrIjoiYTU0N2QxMGZlMTI1MWQxNSIsImV4cCI6MTc4MjMyMjg1OCwiaWF0IjoxNzgxNzE4MDU5LCJrZXkiOiJkeS1qd3QtbWQ1In0.N2QwOTc5ZDNkY2YwMzkxMDM1ZDJiZjYxZjdkY2M1MGU; dy_auth=83e8R3RGjxZmnQUs7%2BAG6r%2FYU7VgPsLocn062izEn4CR9ugImCzojRbuf5C0rctz2%2BiE1325VM1ZTahjI5DfhTijiI8OEZt11i4EFDwzCa6yaIeLjFRALw8; wan_auth37wan=168cf0a37b1a9LmX4D048ZczXpR7PHCmclfd5Yumy48A8Cw07jJ%2Fwi2ve0LJsblBUxlKNvrEDMra3oew57UtA2h%2F0a%2Fnh2jMfoALi81jYf0pNhUym4w; acf_uid=854454301; acf_username=854454301; acf_nickname=%E7%94%A8%E6%88%B71111696940; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F15_; acf_ct=0; acf_ltkid=50587544; acf_biz=1; acf_stk=a547d10fe1251d15; acf_devid=b01f91d99a2ff57e6135cfd56952e78e--用户1111696940--
|
||||
cvl_csrf_token=94967a859ff94114afe73b8244627b31; acf_ccn=6f8688d7044082fb900c5dc6353ffed5; PHPSESSID=udo8qhr1ccbs82sck0d4p97507; acf_auth=681aIxgzeI2HfUfcndJJinqJZ%2FdJkgVIFRdV05x97k1dZeQ78oMO5ef%2F467GJzRz25h5PNlfxEefjIxATinhkspquvuw8%2BO8QYh4K0BDWRY8FOQDIJP2DEs; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg1NDQ0NDkwMywiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MTEzMDI1MCwic3RrIjoiNDFlZmUzMzk2YjUyNWU4YiIsImV4cCI6MTc4MjMyMjg5MSwiaWF0IjoxNzgxNzE4MDkyLCJrZXkiOiJkeS1qd3QtbWQ1In0.NjM3Njk1OGZlNjRiMzI0M2NhN2RkNzQ3OGRiZjVmYjU; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg1NDQ0NDkwMywiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MTEzMDI1MCwic3RrIjoiNDFlZmUzMzk2YjUyNWU4YiIsImV4cCI6MTc4MjMyMjg5MSwiaWF0IjoxNzgxNzE4MDkyLCJrZXkiOiJkeS1qd3QtbWQ1In0.NWIzYTUwZDEyYWVhOWZlMmExMmY1YjhlMzZiOTYzZjY; dy_auth=8149Y2QeUJnbKHvoBfNtNSeN844E6Po1q6OfuPetsTtDGs0Y7zwENLgo2klr7dj3NLp%2F1ZLQRa5OmrGi43VgjeQTt9m2JIbCfx34sQ320iNupgI1LixeXH4; wan_auth37wan=0ec27ed609bfl1Kl%2FrjX4Ciy83J1UhOio%2B731iT2O4%2Fcwf%2B9Qq52D5XsDR2H5WEfa39YN51ZZPgVyyn4UrlDSOGMgFoOwgu%2BG2pvvhEzZPUl0aJSLew; acf_uid=854444903; acf_username=854444903; acf_nickname=%E7%94%A8%E6%88%B73034406534; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F19_; acf_ct=0; acf_ltkid=81130250; acf_biz=1; acf_stk=41efe3396b525e8b; acf_devid=94ad16bdc3a22933f2bb47450750a800--用户3034406534--
|
||||
cvl_csrf_token=d0c5f2b3b8654fa68c2962ef70dabde5; acf_ccn=d3ce0c987c0f1d9c869b762670016a84; PHPSESSID=e0eqtiisapckc6t94ef36kpcq4; acf_auth=7fce6Pt%2FqjLiQ88mbK21GMy%2B4s9q7Qd7MPvBH4mEIAomwHhl%2BuJgGLzxgqmFeYpqls3aiK0soTlB8zOP5vTp4lO6wPCsxtR%2FDqog%2BQgbg9zj6W9au6bDjNA; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM5NzU5MSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo1MTg3MDYyMiwic3RrIjoiMTk4ZTg5NWI2NWM5MDQwNiIsImV4cCI6MTc4MjMyMzAxMCwiaWF0IjoxNzgxNzE4MjExLCJrZXkiOiJkeS1qd3QtbWQ1In0.OTFhZDg4MDk0YzdlNjMzNWIzNDVlODQ4ZGE0MTVjZGQ; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM5NzU5MSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo1MTg3MDYyMiwic3RrIjoiMTk4ZTg5NWI2NWM5MDQwNiIsImV4cCI6MTc4MjMyMzAxMCwiaWF0IjoxNzgxNzE4MjExLCJrZXkiOiJkeS1qd3QtbWQ1In0.OWZhZTYzM2I0ZGEzN2M5ZDRjOTM3ZTgxYmQ5YmQ5Y2Q; dy_auth=f057PKDpqAMBbuAG7b6YMwZEQ6m9RN7h8ZCxo5E%2FvNdDtYvCAWXkqpBn93tLFSZ8ANi1qWInAAp%2FnhJKFmQv3OjLcDQLcS6e4MyiMO8TH9zlbCTi%2BDOuW9k; wan_auth37wan=7687a2a5b0b8xCCgkvtGL8X8B2UKQnLDUxUG0kla2Gff1rQy9yrUGCDgH0roFAckD5Pv3edF7DdB43cY3%2Bt34R14IOKDPvm79hxtrjmqW5%2FrN5w%2BEWg; acf_uid=986397591; acf_username=986397591; acf_nickname=%E7%94%A8%E6%88%B77136807143; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F16_; acf_ct=0; acf_ltkid=51870622; acf_biz=1; acf_stk=198e895b65c90406; acf_devid=76c9b447bae44002cdd3bb4f3dbca9c7--用户7136807143--
|
||||
cvl_csrf_token=a64de78995e043eaa830959207822ab0; acf_ccn=d35d2474f4da2b930d5060fa85a00f4f; PHPSESSID=rd5vt773thk0t5983rsupoiod4; acf_auth=0133SXDnMXS7rij62w4zYZwJ7noiq5lTGwLjSF4Z836DGyl38030TsZC8jHbj4TdKakrqbn7%2BU2aTN7UzDbPEZRW4%2FE1Goh1gTtgD7t8plR2rFmANGZhy6Q; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjc2NjQ2MTcxMiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo2NDk4ODk0NSwic3RrIjoiZGU3NTNkM2M0ZGM0YTNhMyIsImV4cCI6MTc4MjMyNDQ5MiwiaWF0IjoxNzgxNzE5Njk0LCJrZXkiOiJkeS1qd3QtbWQ1In0.ZDMwNzYzM2UzNmZlYTIwYmM5ZjJhOThjY2NmZmFkOTc; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjc2NjQ2MTcxMiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo2NDk4ODk0NSwic3RrIjoiZGU3NTNkM2M0ZGM0YTNhMyIsImV4cCI6MTc4MjMyNDQ5MiwiaWF0IjoxNzgxNzE5Njk0LCJrZXkiOiJkeS1qd3QtbWQ1In0.YjBhMzRmZGJmZjViZTJlMWI2ZGFiYTZjZGZiZGE5MTY; dy_auth=ddaef6nGWUg62nGb1DhnH%2FJNud5Tkxtb1AKDxL3K3Yo726lNwNeZzNqNfdfCUUL%2F6%2B%2Fwn4F8%2BCqw2iLslzQyV0XbqGUu6NhiaBU0mB3a8LNgh08zN%2B9Cp4c; wan_auth37wan=55248d5881b6sHDSbqFXywM8KrzLhkSVUUImmKC3fwsrM%2BmELESTdzIRGIxRJBRXm%2BK5WYQVFuDm%2F8UduXPfj0o2F6AUJaHsNfAs2RJZYPN%2FUzGfe9o; acf_uid=766461712; acf_username=766461712; acf_nickname=%E7%94%A8%E6%88%B72908179850; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F14_; acf_ct=0; acf_ltkid=64988945; acf_biz=1; acf_stk=de753d3c4dc4a3a3; acf_devid=8cf50b795c0b6f75fa1ade228d818ad1--用户2908179850--
|
||||
cvl_csrf_token=7be65f59f54f4041ba738cbc8229d126; acf_ccn=2c3ee0b1a40ed05832c732f3ae0e819c; PHPSESSID=61hqdklp43vbvenkrvj384amf6; acf_auth=8289zzvfeHFkqFjaRWvoQFOjUZmhLsqbqC6D70uJRXyAxLOoWxcleVTS81AGGpwVnj6c%2BXv7BQd5MFl1X5zbirY5dVBWfw6GJfPqXCBNMZWLRPSkCiTpjYEzZw; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjEwMzAzOTMzNDQsImN0IjowLCJzdWIiOiJzdCIsImF1ZCI6WyJkeSIsIm5vbmUiXSwiYml6IjoxLCJsdGtpZCI6OTM4Mzc4NDMsInN0ayI6IjMzNjg1Nzg0ZDVjMjExN2UiLCJleHAiOjE3ODIzMjQ1NDIsImlhdCI6MTc4MTcxOTc0Mywia2V5IjoiZHktand0LW1kNSJ9.NjRlODc4MGY2NDA1YzAwNGIwMGIwNWVjYzQ4ZjUxMzk; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjEwMzAzOTMzNDQsImN0IjowLCJzdWIiOiJzdCIsImF1ZCI6WyJkbSIsIm5vbmUiXSwiYml6IjoxLCJsdGtpZCI6OTM4Mzc4NDMsInN0ayI6IjMzNjg1Nzg0ZDVjMjExN2UiLCJleHAiOjE3ODIzMjQ1NDIsImlhdCI6MTc4MTcxOTc0Mywia2V5IjoiZHktand0LW1kNSJ9.ZDY5YzRmYzYxZWEzZDZiY2QyMTM2OTgwNmEzMzM4ZjY; dy_auth=a39f2uRm7dyoUvJpdm8TlAOjliArDDLT9GjkTHb%2BLi8fI48p7mnphGhnaRRdw1Lb6cj5qednR8BAMMrpzzrWYDlFgIxlRmsKi3CB62sPgs5UWoy8I6jT7jKA4w; wan_auth37wan=ad346eae38aeA%2FBFo8X7Cir5zfykMYTafaPOPMSUdQbzWg5LZ24upGBd0%2Bz%2FrLiTXiCYp%2FU5QGkuUFtYnbbz3LqQYwl1TvZyGMzpush6%2FaI%2Bkr6er%2FBc; acf_uid=1030393344; acf_username=1030393344; acf_nickname=%E7%94%A8%E6%88%B74464278627; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F22_; acf_ct=0; acf_ltkid=93837843; acf_biz=1; acf_stk=33685784d5c2117e; acf_devid=abc81a30b6e43b13a21b4dbd4320e028--用户4464278627--
|
||||
cvl_csrf_token=2564c48c4e88456591c6e5bbb3ff2a0c; acf_ccn=b47feb7fc0108f8f64e59a726c2e0234; PHPSESSID=54bb670kai0rdvv5cfiqi2tc17; acf_auth=10d6AnYgb7ZtBEzAXRCE9RX47fvhzny%2FBZDEgZRmdSMqbz7y0D0h9gtraRs%2BsCO5dsYB0ze%2FWtYvPh0LpBYdS4izFrjVvppJuSCb%2BUWjoVQsEkmoUh5njno; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjU0NjUyNzY5MCwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MjcyNjg2NCwic3RrIjoiZTMxYjgwOWZiMmE1YThkZCIsImV4cCI6MTc4MjMyNDU3MywiaWF0IjoxNzgxNzE5Nzc0LCJrZXkiOiJkeS1qd3QtbWQ1In0.ZmM5MGZkNjFiNzgxOGQ3NWVjZTI3YzU2NmI1MjgyNTE; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjU0NjUyNzY5MCwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MjcyNjg2NCwic3RrIjoiZTMxYjgwOWZiMmE1YThkZCIsImV4cCI6MTc4MjMyNDU3MywiaWF0IjoxNzgxNzE5Nzc0LCJrZXkiOiJkeS1qd3QtbWQ1In0.ODQwMjZhNjViMjM3NmU3ZDZjOTc5MGNiMjRkMmU5MDg; dy_auth=f9b3NRR9d%2FdCxO8Zpu7fuzfOEHlbFVZY2x4GYeENmbAloi7OVF1%2FE2GJ%2Bp6up9GjiTZbgWNtBC6eHWrfJ4UHJ85dxcU5e77uAfh4JEwJxMFhJNBRtgIfnCs; wan_auth37wan=9dec96017a26hzrc8cCr%2BAZfI2z3Pm812XDa%2FVQX8mlDE7sQ60I%2BV1zwLIwkHCErpMNR5hg4kNUSJHmBMf%2FiQedJF6k2z2Q6cIC9y3II4ohVgfZWuMg; acf_uid=546527690; acf_username=546527690; acf_nickname=%E7%94%A8%E6%88%B79193471674; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F19_; acf_ct=0; acf_ltkid=82726864; acf_biz=1; acf_stk=e31b809fb2a5a8dd; acf_devid=cc83f49c9739aa6c7761a3067e95b27d--用户9193471674--
|
||||
cvl_csrf_token=3b1908893b61491888ae8e75d21a80e1; acf_ccn=1fc50cb6bdffaea36027651f0ab0fbab; PHPSESSID=qo7ahf33b2g8jl5u64kj9qlmg3; acf_auth=ad3bTOqnX5bgLJq0eY3nwL9a2ZrvUhqqtqJdoDtCMmXFdDEyzJFXdUxY76pxwW7CDW%2F9zukgJ%2Fn0VN5Pc%2FTSovtSUlO9%2BNe6MLkZk9ugI8Cu0nY679Fe6ME; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk0MjQxNDI1NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo5MDE1OTAzOCwic3RrIjoiYmNmNWQ4YTViNWMxMjM0YSIsImV4cCI6MTc4MjMyNDY3OCwiaWF0IjoxNzgxNzE5ODc5LCJrZXkiOiJkeS1qd3QtbWQ1In0.ZGM3ZTE2Y2IzMWQ2ZGZiYzkyMGU2YTg5ZGVkOGE4OTE; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk0MjQxNDI1NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo5MDE1OTAzOCwic3RrIjoiYmNmNWQ4YTViNWMxMjM0YSIsImV4cCI6MTc4MjMyNDY3OCwiaWF0IjoxNzgxNzE5ODc5LCJrZXkiOiJkeS1qd3QtbWQ1In0.MGY1ZTY2NDRlMThkZTlhZmExYTU5ZTY5OTM2M2NhNjg; dy_auth=88d2FeG2a694j3WC7n8N0YAyRG8dO9rL5vFeqMn9FZVysTSEYkdUgOjD1WqiU4UEfIjo6GJpdkVjtC5aOdShpPWbEvKSHTapzGmnWMG%2FSsPcBBph2oXlrc8; wan_auth37wan=eff704456fbd6LLPutdpoC7gMoV8onVHipFFowpMqv5OR9ZXH1Xij%2BqUs59nIA41wBnWlRQYc%2Fa1Z8QzjMta%2F3owH3U%2Fi16yAqmaEAeOlSJ8b%2Fp%2BybU; acf_uid=942414256; acf_username=942414256; acf_nickname=%E7%94%A8%E6%88%B75367847434; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F18_; acf_ct=0; acf_ltkid=90159038; acf_biz=1; acf_stk=bcf5d8a5b5c1234a; acf_devid=0273876555742d63b52de49abd11d35d--用户5367847434--
|
||||
cvl_csrf_token=a7531207a91442e3a65fa8dbc02724e4; acf_ccn=c61707ad405ad94a104f5628b83f9f28; PHPSESSID=el8ag5nuroga3cfdb425rr6ot7; acf_auth=c4b5sRHkqG4GRXNzKMciPCXOMXCvEBscJraWAkDtQx1IjrCZcZOEBRXUori3KFJyg7FmamAHZWuPFkasvC7y66aEhw7BR7ZXgMnxAhCZyNUX8ByDhrSWTvo; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg1NDQ0NDk3NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo5NjIyNjM0Miwic3RrIjoiYThkNTIxNmViMDVhMGIwOCIsImV4cCI6MTc4MjMyNDcwOSwiaWF0IjoxNzgxNzE5OTExLCJrZXkiOiJkeS1qd3QtbWQ1In0.ODVhNzY5NGMyYTgzYzAzNWQzMzQ2OTY4ZWQzNzkxNGI; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjg1NDQ0NDk3NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo5NjIyNjM0Miwic3RrIjoiYThkNTIxNmViMDVhMGIwOCIsImV4cCI6MTc4MjMyNDcwOSwiaWF0IjoxNzgxNzE5OTExLCJrZXkiOiJkeS1qd3QtbWQ1In0.MWY0YjNkNGFjODZlNTk4MDQ0ZThmNTYyZmNmNWQ2NWM; dy_auth=853f%2B01%2FM3XkkAyNNOhzsXDyfwPyaknBLOmqMojhwANeCTkr2ZOWPP%2FbBLnM9AShVXAGafENZNkH3yQ5H%2FcpWuZ%2Bx1%2B%2Betbkh5y1p%2BkfrtdirhNOFc0%2FJDo; wan_auth37wan=0316f2b159eb4voYsGPjXS7tVzFE0PuCY6qWJnjpyRflGGsR0aFTQmORCyUM58Sb2ZzHldidJ%2BgmmnI5N94Bh0jgvWQq0MryQDbrYaDlPtKsbARhb8A; acf_uid=854444976; acf_username=854444976; acf_nickname=%E7%94%A8%E6%88%B71744287786; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F22_; acf_ct=0; acf_ltkid=96226342; acf_biz=1; acf_stk=a8d5216eb05a0b08; acf_devid=189e7ecf5f25567d733111468f9005d3--用户1744287786--
|
||||
Reference in New Issue
Block a user