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 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 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), }