Files
huya-go/internal/sniper/sniper.go
T
2026-07-06 18:08:23 +08:00

131 lines
3.1 KiB
Go

package sniper
import (
"context"
"strings"
"sync"
"time"
"huya-go/internal/huya"
)
type Account struct {
UID int64 `json:"uid"`
Cookie string `json:"-"`
}
type Target struct {
PID int32 `json:"pid"`
Name string `json:"name"`
}
type Config struct {
SID int32
Loops int
Delay time.Duration
Concurrency int
StopAfterSuccess bool
}
type Result struct {
Loop int `json:"loop"`
UID int64 `json:"uid"`
Action string `json:"action"`
Prize *huya.PrizeInfo `json:"prize,omitempty"`
Exchange *huya.ScoreExchangePrizeResp `json:"exchange,omitempty"`
Error string `json:"error,omitempty"`
}
type Runner struct {
Client huya.ActivityClient
}
func (r *Runner) Run(ctx context.Context, accounts []Account, target Target, cfg Config, emit func(Result)) error {
if cfg.Loops <= 0 {
cfg.Loops = 1
}
if cfg.Delay <= 0 {
cfg.Delay = 150 * time.Millisecond
}
if cfg.Concurrency <= 0 {
cfg.Concurrency = 1
}
var client huya.ActivityClient = r.Client
if client == nil {
client = huya.NewHTTPClient(nil)
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
for loop := 1; loop <= cfg.Loops; loop++ {
if ctx.Err() != nil {
return ctx.Err()
}
sem := make(chan struct{}, cfg.Concurrency)
var wg sync.WaitGroup
for _, account := range accounts {
account := account
wg.Add(1)
go func() {
defer wg.Done()
select {
case sem <- struct{}{}:
defer func() { <-sem }()
case <-ctx.Done():
return
}
result := r.tryAccount(ctx, client, account, target, cfg.SID, loop)
if emit != nil {
emit(result)
}
if cfg.StopAfterSuccess && result.Exchange != nil && result.Exchange.Success() {
cancel()
}
}()
}
wg.Wait()
if ctx.Err() != nil {
return ctx.Err()
}
if loop < cfg.Loops {
timer := time.NewTimer(cfg.Delay)
select {
case <-timer.C:
case <-ctx.Done():
timer.Stop()
return ctx.Err()
}
}
}
return nil
}
func (r *Runner) tryAccount(ctx context.Context, client huya.ActivityClient, account Account, target Target, sid int32, loop int) Result {
list, err := client.GetActPrizeList(ctx, account.UID, account.Cookie, sid)
if err != nil {
return Result{Loop: loop, UID: account.UID, Action: "list_error", Error: err.Error()}
}
prize := matchPrize(list.Prizes, target)
if prize == nil {
return Result{Loop: loop, UID: account.UID, Action: "not_found"}
}
exchange, err := client.ScoreExchangePrize(ctx, account.UID, account.Cookie, sid, int32(prize.ID))
if err != nil {
return Result{Loop: loop, UID: account.UID, Action: "exchange_error", Prize: prize, Error: err.Error()}
}
return Result{Loop: loop, UID: account.UID, Action: "exchange", Prize: prize, Exchange: exchange}
}
func matchPrize(prizes []huya.PrizeInfo, target Target) *huya.PrizeInfo {
for i := range prizes {
if target.PID > 0 && int32(prizes[i].ID) == target.PID {
return &prizes[i]
}
if target.PID == 0 && target.Name != "" && strings.Contains(prizes[i].Name, target.Name) {
return &prizes[i]
}
}
return nil
}