Files
codexautopool/backend/internal/invite/team.go

196 lines
4.7 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 invite
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"codex-pool/internal/client"
)
// DefaultProxy 默认代理
const DefaultProxy = "http://127.0.0.1:7890"
// TeamInviter Team 邀请器
type TeamInviter struct {
client *client.TLSClient
accessToken string
accountID string
}
// InviteRequest 邀请请求
type InviteRequest struct {
EmailAddresses []string `json:"email_addresses"`
Role string `json:"role"`
ResendEmails bool `json:"resend_emails"`
}
// AccountCheckResponse 账号检查响应
type AccountCheckResponse struct {
Accounts map[string]struct {
Account struct {
PlanType string `json:"plan_type"`
} `json:"account"`
} `json:"accounts"`
}
// New 创建邀请器 (使用默认代理)
func New(accessToken string) *TeamInviter {
c, _ := client.New(DefaultProxy)
return &TeamInviter{
client: c,
accessToken: accessToken,
}
}
// NewWithProxy 创建邀请器 (指定代理)
func NewWithProxy(accessToken, proxy string) *TeamInviter {
c, _ := client.New(proxy)
return &TeamInviter{
client: c,
accessToken: accessToken,
}
}
// SetAccountID 手动设置 account_id当已有存储值时使用
func (t *TeamInviter) SetAccountID(accountID string) {
t.accountID = accountID
}
// GetAccountID 获取 Team 的 account_id (workspace_id)
func (t *TeamInviter) GetAccountID() (string, error) {
req, _ := http.NewRequest("GET", "https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27", nil)
req.Header.Set("Authorization", "Bearer "+t.accessToken)
req.Header.Set("Accept", "application/json")
resp, err := t.client.Do(req)
if err != nil {
return "", fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)[:min(200, len(body))])
}
var result AccountCheckResponse
if err := json.Unmarshal(body, &result); err != nil {
return "", fmt.Errorf("解析失败: %v", err)
}
// 查找 team plan 的 account_id
for accountID, info := range result.Accounts {
if accountID != "default" && info.Account.PlanType == "team" {
t.accountID = accountID
return accountID, nil
}
}
// 如果没找到 team返回第一个非 default 的
for accountID := range result.Accounts {
if accountID != "default" {
t.accountID = accountID
return accountID, nil
}
}
return "", fmt.Errorf("未找到 account_id")
}
// SendInvites 发送邀请
func (t *TeamInviter) SendInvites(emails []string) error {
if t.accountID == "" {
return fmt.Errorf("未设置 account_id请先调用 GetAccountID()")
}
url := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/invites", t.accountID)
payload := InviteRequest{
EmailAddresses: emails,
Role: "standard-user",
ResendEmails: true,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+t.accessToken)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Chatgpt-Account-Id", t.accountID)
resp, err := t.client.Do(req)
if err != nil {
return fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 && resp.StatusCode != 201 {
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(respBody)[:min(200, len(respBody))])
}
return nil
}
// GetPendingInvites 获取待处理的邀请列表
func (t *TeamInviter) GetPendingInvites() ([]string, error) {
if t.accountID == "" {
return nil, fmt.Errorf("未设置 account_id")
}
url := fmt.Sprintf("https://chatgpt.com/backend-api/accounts/%s/invites?offset=0&limit=100&query=", t.accountID)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+t.accessToken)
resp, err := t.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result struct {
Invites []struct {
Email string `json:"email"`
} `json:"invites"`
}
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
var emails []string
for _, inv := range result.Invites {
emails = append(emails, inv.Email)
}
return emails, nil
}
// AcceptInvite 接受邀请 (使用被邀请账号的 token)
func AcceptInvite(inviteLink string, accessToken string) error {
c, _ := client.New(DefaultProxy)
req, _ := http.NewRequest("GET", inviteLink, nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
resp, err := c.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 && resp.StatusCode != 302 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)[:min(100, len(body))])
}
return nil
}