Skip to content

Commit 7937b05

Browse files
committed
feat: try support team account
#220
1 parent aef813c commit 7937b05

File tree

2 files changed

+101
-6
lines changed

2 files changed

+101
-6
lines changed

src/api.ts

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@ import urlcat from 'urlcat'
22
import { apiUrl, baseUrl } from './constants'
33
import { getChatIdFromUrl, getConversationFromSharePage, isSharePage } from './page'
44
import { blobToDataURL } from './utils/dom'
5+
import { memorize } from './utils/memorize'
56

67
interface ApiSession {
78
accessToken: string
9+
authProvider: string
810
expires: string
911
user: {
1012
email: string
1113
groups: string[]
14+
// token's issued_at timestamp
15+
iat: number
1216
id: string
17+
// token's expiration timestamp
18+
idp: string
1319
image: string
20+
intercom_hash: string
21+
// whether the user has multi-factor authentication enabled
22+
mfa: boolean
1423
name: string
1524
picture: string
1625
}
@@ -192,6 +201,46 @@ export interface ApiConversations {
192201
total: number
193202
}
194203

204+
interface ApiAccountsCheckAccountDetail {
205+
account_user_role: 'account-owner' | string
206+
account_user_id: string | null
207+
processor: Record<string, boolean>
208+
account_id: string | null
209+
organization_id?: string | null
210+
is_most_recent_expired_subscription_gratis: boolean
211+
has_previously_paid_subscription: boolean
212+
name?: string | null
213+
profile_picture_id?: string | null
214+
profile_picture_url?: string | null
215+
structure: 'workspace' | 'personal'
216+
plan_type: 'team' | 'free'
217+
is_deactivated: boolean
218+
promo_data: Record<string, unknown>
219+
}
220+
221+
interface ApiAccountsCheckEntitlement {
222+
subscription_id?: string | null
223+
has_active_subscription?: boolean
224+
subscription_plan?: 'chatgptteamplan' | 'chatgptplusplan'
225+
expires_at?: string | null
226+
billing_period?: 'monthly' | string | null
227+
}
228+
229+
interface ApiAccountsCheckAccount {
230+
account: ApiAccountsCheckAccountDetail
231+
features: string[]
232+
entitlement: ApiAccountsCheckEntitlement
233+
last_active_subscription?: Record<string, unknown> | null
234+
is_eligible_for_yearly_plus_subscription: boolean
235+
}
236+
237+
interface ApiAccountsCheck {
238+
accounts: {
239+
[key: string]: ApiAccountsCheckAccount
240+
}
241+
account_ordering: string[]
242+
}
243+
195244
type ApiFileDownload = {
196245
status: 'success'
197246
/** signed download url */
@@ -210,6 +259,7 @@ const sessionApi = urlcat(baseUrl, '/api/auth/session')
210259
const conversationApi = (id: string) => urlcat(apiUrl, '/conversation/:id', { id })
211260
const conversationsApi = (offset: number, limit: number) => urlcat(apiUrl, '/conversations', { offset, limit })
212261
const fileDownloadApi = (id: string) => urlcat(apiUrl, '/files/:id/download', { id })
262+
const accountsCheckApi = urlcat(apiUrl, '/accounts/check/v4-2023-04-27')
213263

214264
export async function getCurrentChatId(): Promise<string> {
215265
if (isSharePage()) {
@@ -349,12 +399,14 @@ export async function deleteConversation(chatId: string): Promise<boolean> {
349399

350400
async function fetchApi<T>(url: string, options?: RequestInit): Promise<T> {
351401
const accessToken = await getAccessToken()
402+
const accountId = await getTeamAccountId()
352403

353404
const response = await fetch(url, {
354405
...options,
355406
headers: {
356407
'Authorization': `Bearer ${accessToken}`,
357408
'X-Authorization': `Bearer ${accessToken}`,
409+
...(accountId ? { 'Chatgpt-Account-Id': accountId } : {}),
358410
...options?.headers,
359411
},
360412
})
@@ -364,20 +416,46 @@ async function fetchApi<T>(url: string, options?: RequestInit): Promise<T> {
364416
return response.json()
365417
}
366418

419+
async function _fetchSession(): Promise<ApiSession> {
420+
const response = await fetch(sessionApi)
421+
if (!response.ok) {
422+
throw new Error(response.statusText)
423+
}
424+
return response.json()
425+
}
426+
427+
const fetchSession = memorize(_fetchSession)
428+
367429
async function getAccessToken(): Promise<string> {
368430
const session = await fetchSession()
369431
return session.accessToken
370432
}
371433

372-
let session: ApiSession | null = null
373-
async function fetchSession(): Promise<ApiSession> {
374-
if (session) return session
375-
const response = await fetch(sessionApi)
434+
async function _fetchAccountsCheck(): Promise<ApiAccountsCheck> {
435+
const accessToken = await getAccessToken()
436+
437+
const response = await fetch(accountsCheckApi, {
438+
headers: {
439+
'Authorization': `Bearer ${accessToken}`,
440+
'X-Authorization': `Bearer ${accessToken}`,
441+
},
442+
})
376443
if (!response.ok) {
377444
throw new Error(response.statusText)
378445
}
379-
session = await response.json()
380-
return session!
446+
return response.json()
447+
}
448+
449+
const fetchAccountsCheck = memorize(_fetchAccountsCheck)
450+
451+
export async function getTeamAccountId(): Promise<string | null> {
452+
const accountsCheck = await fetchAccountsCheck()
453+
const accountKey = accountsCheck.account_ordering?.[0] || 'default'
454+
const account = accountsCheck.accounts[accountKey]
455+
if (!account) return null
456+
if (account.account.plan_type !== 'team') return null
457+
458+
return account.account.account_id
381459
}
382460

383461
export interface ConversationResult {

src/utils/memorize.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const generateKey = (args: any[]) => JSON.stringify(args)
2+
3+
export function memorize<T extends (...args: any[]) => any>(fn: T): T {
4+
const cache = new Map<string, any>()
5+
6+
const memorized = (...args: Parameters<T>): ReturnType<T> => {
7+
const key = generateKey(args)
8+
if (cache.has(key)) {
9+
return cache.get(key)
10+
}
11+
const result = fn(...args)
12+
cache.set(key, result)
13+
return result
14+
}
15+
16+
return memorized as T
17+
}

0 commit comments

Comments
 (0)