95 lines
3.2 KiB
Go
95 lines
3.2 KiB
Go
package douyin
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// PhoneNumberURL Douyin get phone number interface address
|
|
PhoneNumberURL = "https://developer.toutiao.com/api/apps/v1/number_get/bind/"
|
|
)
|
|
|
|
// PhoneNumberRequest request parameters
|
|
type PhoneNumberRequest struct {
|
|
AppID string `json:"app_id"` // Note: specific parameter name might vary, but usually it's app_id or appid. Docs say `app_id` for some v1 interfaces.
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
// PhoneNumberResponse response
|
|
type PhoneNumberResponse struct {
|
|
ErrNo int `json:"err_no"`
|
|
ErrTips string `json:"err_tips"`
|
|
Data struct {
|
|
PhoneNumber string `json:"phone_number"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
// GetPhoneNumber calls Douyin interface to get user phone number
|
|
func GetPhoneNumber(ctx context.Context, accessToken, appID, code string) (string, error) {
|
|
if accessToken == "" || code == "" {
|
|
return "", fmt.Errorf("missing parameters")
|
|
}
|
|
|
|
// Note: Verify the correct endpoint and parameters.
|
|
// Commonly for `v1/number_get/bind/`, it might expect `encryptedData`?
|
|
// But `tt.getPhoneNumber` provides `code`.
|
|
// Let's assume the modern Code-based API which is often similar to `number_get/bind` but with `code`.
|
|
// Check if we need to call `https://developer.toutiao.com/api/apps/v1/user/get_phone_number_v1` instead?
|
|
// Or `https://developer.open-douyin.com/api/apps/v1/img/get_phone_number_v1`?
|
|
// Since I cannot verify the exact URL visually, I will stick to the one I hypothesized or the most "standard" one found in similar Go SDKs.
|
|
// Actually, let's try to assume it's `POST /api/apps/v2/jscode2session` style but for phone.
|
|
|
|
// A common variation for "get phone number by code" in Douyin is sending `code` and `app_id` to an endpoint.
|
|
// Let's go with the one stated in many online resources for "Douyin Mini Program Get Phone Number".
|
|
|
|
reqBody := map[string]string{
|
|
"app_id": appID,
|
|
"code": code,
|
|
}
|
|
|
|
jsonBody, err := json.Marshal(reqBody)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to marshal request: %w", err)
|
|
}
|
|
|
|
httpClient := &http.Client{Timeout: 10 * time.Second}
|
|
// The URL might need `access_token` in query param or header?
|
|
// Most `v1` Douyin APIs require `access_token` in header or query. We will put in header `access-token`.
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", PhoneNumberURL, strings.NewReader(string(jsonBody)))
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
// Some docs say `access-token` in header
|
|
req.Header.Set("access-token", accessToken)
|
|
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to request Douyin interface: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to read response: %w", err)
|
|
}
|
|
|
|
var result PhoneNumberResponse
|
|
if err := json.Unmarshal(body, &result); err != nil {
|
|
return "", fmt.Errorf("failed to parse response: %w, body: %s", err, string(body))
|
|
}
|
|
|
|
if result.ErrNo != 0 {
|
|
return "", fmt.Errorf("failed to get phone number: %s (code: %d)", result.ErrTips, result.ErrNo)
|
|
}
|
|
|
|
return result.Data.PhoneNumber, nil
|
|
}
|