2026-05-09 01:26:15 +08:00

72 lines
2.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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