211 lines
4.1 KiB
Go
211 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/golang/freetype/truetype"
|
|
)
|
|
|
|
type Config struct {
|
|
TimeAPIURL string `json:"timeApiUrl"`
|
|
}
|
|
|
|
type appPaths struct {
|
|
ConfigFile string
|
|
AccountFile string
|
|
LogDir string
|
|
SuccessDir string
|
|
FontFiles []string
|
|
}
|
|
|
|
type TraceConfig struct {
|
|
Enabled bool
|
|
MaxBodyBytes int
|
|
}
|
|
|
|
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"`
|
|
Status int `json:"status"`
|
|
OpenStatus int `json:"openStatus"`
|
|
StoreStatus int `json:"storeStatus"`
|
|
TimeLimited int `json:"timeLimited"`
|
|
CountLimited int `json:"countLimited"`
|
|
EliteLimited int `json:"eliteLimited"`
|
|
}
|
|
|
|
type ProductPayload struct {
|
|
Success bool `json:"success"`
|
|
Source string `json:"source"`
|
|
Products []Product `json:"products"`
|
|
Count int `json:"count"`
|
|
FetchedAt string `json:"fetchedAt"`
|
|
AgeSecond int `json:"ageSecond"`
|
|
Stale bool `json:"stale"`
|
|
}
|
|
|
|
type AccountView struct {
|
|
Index int `json:"index"`
|
|
Name string `json:"name"`
|
|
Cookie string `json:"cookie"`
|
|
}
|
|
|
|
type SeckillRequest struct {
|
|
AccountProducts map[string]string `json:"accountProducts"`
|
|
TargetTime string `json:"targetTime"`
|
|
Advance int `json:"advance"`
|
|
}
|
|
|
|
type APIResponse struct {
|
|
Error int `json:"error"`
|
|
Msg string `json:"msg"`
|
|
Data json.RawMessage `json:"data"`
|
|
}
|
|
|
|
type LogEntry struct {
|
|
ID int64 `json:"id"`
|
|
Time string `json:"time"`
|
|
Level string `json:"level"`
|
|
Category string `json:"category"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
type appState struct {
|
|
config Config
|
|
paths appPaths
|
|
client *DouyuClient
|
|
logs *memoryLogStore
|
|
trace *traceRecorder
|
|
|
|
accountsMu sync.RWMutex
|
|
accounts []Account
|
|
|
|
productsMu sync.RWMutex
|
|
products []Product
|
|
productsAt time.Time
|
|
|
|
resultsMu sync.RWMutex
|
|
results map[string]string
|
|
|
|
scheduleMu sync.Mutex
|
|
schedule *time.Timer
|
|
warmupSchedule *time.Timer
|
|
resyncSchedule *time.Timer
|
|
|
|
warmupsMu sync.RWMutex
|
|
warmups map[string]WarmupData
|
|
|
|
fontMu sync.Mutex
|
|
font *truetype.Font
|
|
}
|
|
|
|
type DouyuClient struct {
|
|
httpClient *http.Client
|
|
timeAPIURL string
|
|
trace *traceRecorder
|
|
}
|
|
|
|
type traceRecorder struct {
|
|
enabled bool
|
|
maxBodyBytes int
|
|
writer *asyncLogWriter
|
|
filePath string
|
|
seq uint64
|
|
}
|
|
|
|
type asyncLogWriter struct {
|
|
dst io.Writer
|
|
ch chan []byte
|
|
wg sync.WaitGroup
|
|
closed uint32
|
|
dropped uint64
|
|
}
|
|
|
|
type ExchangeAttempt struct {
|
|
Account Account
|
|
Product Product
|
|
Index int
|
|
}
|
|
|
|
type ExchangeResult struct {
|
|
OK bool
|
|
Account string
|
|
Product string
|
|
Attempt int
|
|
RoleName string
|
|
Message string
|
|
}
|
|
|
|
type TimeSample struct {
|
|
ServerTime time.Time
|
|
LocalTime time.Time
|
|
RTT time.Duration
|
|
Offset time.Duration
|
|
ErrorBound time.Duration
|
|
RawCurrentTime int64
|
|
Samples int
|
|
Source string
|
|
Fallback bool
|
|
}
|
|
|
|
type TimeCalibration struct {
|
|
Offset time.Duration
|
|
ErrorBound time.Duration
|
|
RTT time.Duration
|
|
Samples int
|
|
CreatedAt time.Time
|
|
Source string
|
|
Fallback bool
|
|
}
|
|
|
|
func (c TimeCalibration) EstimatedServerNow() time.Time {
|
|
if c.CreatedAt.IsZero() {
|
|
return time.Now().UTC()
|
|
}
|
|
return time.Now().Add(c.Offset).UTC()
|
|
}
|
|
|
|
func (c TimeCalibration) IsZero() bool {
|
|
return c.CreatedAt.IsZero()
|
|
}
|
|
|
|
type WarmupData struct {
|
|
CSRF string
|
|
RoleName string
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// EstimatedServerNow 根据采样偏移估算当前斗鱼时间。
|
|
func (t TimeSample) EstimatedServerNow() time.Time {
|
|
if t.LocalTime.IsZero() {
|
|
return t.ServerTime
|
|
}
|
|
return time.Now().Add(t.Offset).UTC()
|
|
}
|
|
|
|
type memoryLogStore struct {
|
|
mu sync.RWMutex
|
|
nextID int64
|
|
max int
|
|
filePath string
|
|
entries []LogEntry
|
|
}
|
|
|
|
var state = &appState{
|
|
config: Config{TimeAPIURL: defaultTimeAPI},
|
|
results: make(map[string]string),
|
|
}
|