// Package abogus computes the a_bogus signature required by 抖店/抖音 risk-controlled endpoints. // // Implementation: embeds a JavaScript file with the a_bogus algorithm and runs it via goja // (pure-Go JS engine, no CGO). The JS itself does not depend on any browser globals — it // takes the URL query string and User-Agent as inputs and returns the signature string. package abogus import ( _ "embed" "fmt" "sync" "github.com/dop251/goja" ) //go:embed a_bogus.js var aBogusJS string // Generator is reusable. NewGenerator compiles the JS once; Sign() is goroutine-safe. type Generator struct { program *goja.Program mu sync.Mutex pool []*goja.Runtime } // NewGenerator compiles the embedded a_bogus.js and returns a ready-to-use generator. func NewGenerator() (*Generator, error) { // strict=false: a_bogus.js 原文有 `n = ...`(未声明)等浏览器宽松模式特性,goja 严格模式会拒绝 prog, err := goja.Compile("a_bogus.js", aBogusJS, false) if err != nil { return nil, fmt.Errorf("compile a_bogus.js: %w", err) } return &Generator{program: prog}, nil } // Sign computes the a_bogus signature for a given URL query string and User-Agent. // urlSearchParams is the raw query string WITHOUT a_bogus itself (everything else included). func (g *Generator) Sign(urlSearchParams, userAgent string) (string, error) { rt := g.acquire() defer g.release(rt) if _, err := rt.RunProgram(g.program); err != nil { return "", fmt.Errorf("run a_bogus.js: %w", err) } fn, ok := goja.AssertFunction(rt.Get("generate_a_bogus")) if !ok { return "", fmt.Errorf("generate_a_bogus is not a function") } v, err := fn(goja.Undefined(), rt.ToValue(urlSearchParams), rt.ToValue(userAgent)) if err != nil { return "", fmt.Errorf("call generate_a_bogus: %w", err) } return v.String(), nil } func (g *Generator) acquire() *goja.Runtime { g.mu.Lock() defer g.mu.Unlock() if n := len(g.pool); n > 0 { rt := g.pool[n-1] g.pool = g.pool[:n-1] return rt } return goja.New() } func (g *Generator) release(rt *goja.Runtime) { g.mu.Lock() defer g.mu.Unlock() g.pool = append(g.pool, rt) }