package zhanlu import ( "bytes" "context" "crypto/rand" "fmt" "net/http" "time" "git.misaka.ren/M1saka/zhanlu_proxy/internal/auth" "git.misaka.ren/M1saka/zhanlu_proxy/internal/sign" ) type Client struct { BaseURL string Path string Creds auth.Credentials Signer sign.Signer HTTPClient *http.Client } func NewClient(baseURL, path string, creds auth.Credentials, signer sign.Signer, timeout time.Duration) *Client { return &Client{ BaseURL: baseURL, Path: path, Creds: creds, Signer: signer, HTTPClient: &http.Client{ Timeout: timeout, }, } } func (c *Client) ChatCompletions(ctx context.Context, body []byte) (*http.Response, error) { baseURL := c.BaseURL if c.Creds.BaseURL != "" { baseURL = c.Creds.BaseURL } signedURL, err := c.Signer.BuildOpURL(c.Path, c.Creds, baseURL, http.MethodPost) if err != nil { return nil, err } encryptedBody, err := auth.EncryptCredential(string(body), c.Creds.Token) if err != nil { return nil, err } wrappedBody := []byte(fmt.Sprintf(`{"data":%q}`, encryptedBody)) req, err := http.NewRequestWithContext(ctx, http.MethodPost, signedURL, bytes.NewReader(wrappedBody)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "text/event-stream, application/json") req.Header.Set("plugin_type", "vscode") req.Header.Set("plugin_version", "2.8.0") req.Header.Set("service_type", "code") req.Header.Set("request", randomRequestID()) return c.HTTPClient.Do(req) } func randomRequestID() string { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return fmt.Sprintf("%d", time.Now().UnixNano()) } b[6] = (b[6] & 0x0f) | 0x40 b[8] = (b[8] & 0x3f) | 0x80 return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) }