|
|
@@ -12,6 +12,11 @@ const DC_CONFIGS: Record<number, DCConfig> = {
|
|
|
5: { id: 5, host: '149.154.171.5', port: 80 }
|
|
|
}
|
|
|
|
|
|
+const AUTH_KEY_BYTE_LENGTH = 256
|
|
|
+const SESSION_VERSION_PREFIX = '1'
|
|
|
+const MIN_DC_ID = 1
|
|
|
+const MAX_DC_ID = 5
|
|
|
+
|
|
|
export interface TelegramAccountSession {
|
|
|
dc1_auth_key?: string
|
|
|
dc1_server_salt?: string
|
|
|
@@ -42,20 +47,21 @@ export interface DecodedSessionData {
|
|
|
}
|
|
|
|
|
|
export function decodeSession(sessionStr: string): DecodedSessionData {
|
|
|
- let decodedSessionData: DecodedSessionData
|
|
|
-
|
|
|
try {
|
|
|
const decodedString = Buffer.from(sessionStr, 'base64').toString('utf-8')
|
|
|
- decodedSessionData = JSON.parse(decodedString) as DecodedSessionData
|
|
|
- } catch (e) {
|
|
|
- throw new Error('数据格式无效')
|
|
|
- }
|
|
|
+ const decodedSessionData = JSON.parse(decodedString) as DecodedSessionData
|
|
|
|
|
|
- if (!decodedSessionData || typeof decodedSessionData !== 'object') {
|
|
|
- throw new Error('解析数据失败')
|
|
|
- }
|
|
|
+ if (!decodedSessionData || typeof decodedSessionData !== 'object') {
|
|
|
+ throw new Error('解析数据失败:数据不是有效的对象')
|
|
|
+ }
|
|
|
|
|
|
- return decodedSessionData
|
|
|
+ return decodedSessionData
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof SyntaxError) {
|
|
|
+ throw new Error('数据格式无效:JSON 解析失败')
|
|
|
+ }
|
|
|
+ throw new Error(`数据格式无效:${(error as Error).message}`)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
export function buildStringSession(sessionStr: string): string {
|
|
|
@@ -68,83 +74,96 @@ export function buildStringSession(sessionStr: string): string {
|
|
|
}
|
|
|
|
|
|
const { account, dcId } = accountInfo
|
|
|
+ const authKeyHex = getAuthKeyByDcId(account, dcId)
|
|
|
+ const authKeyBuffer = validateAndConvertAuthKey(authKeyHex)
|
|
|
+ const sessionBuffer = buildSessionBuffer(dcId, authKeyBuffer)
|
|
|
+ const encodedSession = sessionBuffer.toString('base64')
|
|
|
+
|
|
|
+ return SESSION_VERSION_PREFIX + encodedSession
|
|
|
+ } catch (error) {
|
|
|
+ throw new Error(`构建 StringSession 失败: ${(error as Error).message}`)
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- let authKeyHex: string | undefined
|
|
|
-
|
|
|
- switch (dcId) {
|
|
|
- case 1:
|
|
|
- authKeyHex = account.dc1_auth_key
|
|
|
- break
|
|
|
- case 2:
|
|
|
- authKeyHex = account.dc2_auth_key
|
|
|
- break
|
|
|
- case 3:
|
|
|
- authKeyHex = account.dc3_auth_key
|
|
|
- break
|
|
|
- case 4:
|
|
|
- authKeyHex = account.dc4_auth_key
|
|
|
- break
|
|
|
- case 5:
|
|
|
- authKeyHex = account.dc5_auth_key
|
|
|
- break
|
|
|
- default:
|
|
|
- throw new Error(`不支持的 DC ID: ${dcId}`)
|
|
|
+export function buildStringSessionByDcIdAndAuthKey(dcId: number, authKey: string): string {
|
|
|
+ try {
|
|
|
+ if (dcId < MIN_DC_ID || dcId > MAX_DC_ID) {
|
|
|
+ throw new Error(`不支持的 DC ID: ${dcId},必须是 ${MIN_DC_ID}-${MAX_DC_ID} 之间的值`)
|
|
|
}
|
|
|
|
|
|
- if (!authKeyHex) {
|
|
|
- throw new Error(`DC ${dcId} 的 auth_key 不存在`)
|
|
|
+ if (!authKey || typeof authKey !== 'string' || authKey.trim().length === 0) {
|
|
|
+ throw new Error('auth_key 不能为空')
|
|
|
}
|
|
|
|
|
|
- const authKeyBuffer = Buffer.from(authKeyHex, 'hex')
|
|
|
+ const authKeyBuffer = validateAndConvertAuthKey(authKey)
|
|
|
+ const sessionBuffer = buildSessionBuffer(dcId, authKeyBuffer)
|
|
|
+ const encodedSession = sessionBuffer.toString('base64')
|
|
|
|
|
|
- if (authKeyBuffer.length !== 256) {
|
|
|
- throw new Error(`Auth Key 长度不正确,期望 256 字节,实际 ${authKeyBuffer.length} 字节`)
|
|
|
- }
|
|
|
+ return SESSION_VERSION_PREFIX + encodedSession
|
|
|
+ } catch (error) {
|
|
|
+ throw new Error(`构建 StringSession 失败: ${(error as Error).message}`)
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- const dcConfig = DC_CONFIGS[dcId]
|
|
|
- if (!dcConfig) {
|
|
|
- throw new Error(`不支持的 DC ID: ${dcId}`)
|
|
|
- }
|
|
|
+function getAuthKeyByDcId(account: TelegramAccountSession, dcId: number): string {
|
|
|
+ const authKeyMap: Record<number, keyof TelegramAccountSession> = {
|
|
|
+ 1: 'dc1_auth_key',
|
|
|
+ 2: 'dc2_auth_key',
|
|
|
+ 3: 'dc3_auth_key',
|
|
|
+ 4: 'dc4_auth_key',
|
|
|
+ 5: 'dc5_auth_key'
|
|
|
+ }
|
|
|
|
|
|
- const buffers: Buffer[] = []
|
|
|
+ const authKeyField = authKeyMap[dcId]
|
|
|
+ if (!authKeyField) {
|
|
|
+ throw new Error(`不支持的 DC ID: ${dcId}`)
|
|
|
+ }
|
|
|
|
|
|
- buffers.push(Buffer.from([dcId]))
|
|
|
+ const authKeyHex = account[authKeyField] as string | undefined
|
|
|
+ if (!authKeyHex) {
|
|
|
+ throw new Error(`DC ${dcId} 的 auth_key 不存在`)
|
|
|
+ }
|
|
|
|
|
|
- const serverAddressBuffer = Buffer.from(dcConfig.host)
|
|
|
- const addressLengthBuffer = Buffer.allocUnsafe(2)
|
|
|
- addressLengthBuffer.writeInt16BE(serverAddressBuffer.length, 0)
|
|
|
- buffers.push(addressLengthBuffer)
|
|
|
+ return authKeyHex
|
|
|
+}
|
|
|
|
|
|
- buffers.push(serverAddressBuffer)
|
|
|
+function validateAndConvertAuthKey(authKeyHex: string): Buffer {
|
|
|
+ const authKeyBuffer = Buffer.from(authKeyHex, 'hex')
|
|
|
|
|
|
- const portBuffer = Buffer.allocUnsafe(2)
|
|
|
- portBuffer.writeInt16BE(dcConfig.port, 0)
|
|
|
- buffers.push(portBuffer)
|
|
|
+ if (authKeyBuffer.length !== AUTH_KEY_BYTE_LENGTH) {
|
|
|
+ throw new Error(`Auth Key 长度不正确,期望 ${AUTH_KEY_BYTE_LENGTH} 字节,实际 ${authKeyBuffer.length} 字节`)
|
|
|
+ }
|
|
|
|
|
|
- buffers.push(authKeyBuffer)
|
|
|
+ return authKeyBuffer
|
|
|
+}
|
|
|
|
|
|
- const sessionBuffer = Buffer.concat(buffers)
|
|
|
+function buildSessionBuffer(dcId: number, authKeyBuffer: Buffer): Buffer {
|
|
|
+ const dcConfig = DC_CONFIGS[dcId]
|
|
|
+ if (!dcConfig) {
|
|
|
+ throw new Error(`不支持的 DC ID: ${dcId}`)
|
|
|
+ }
|
|
|
|
|
|
- const encodedSession = sessionBuffer.toString('base64')
|
|
|
+ const buffers: Buffer[] = [
|
|
|
+ Buffer.from([dcId]),
|
|
|
+ Buffer.allocUnsafe(2),
|
|
|
+ Buffer.from(dcConfig.host),
|
|
|
+ Buffer.allocUnsafe(2),
|
|
|
+ authKeyBuffer
|
|
|
+ ]
|
|
|
|
|
|
- const sessionString = '1' + encodedSession
|
|
|
+ buffers[1].writeInt16BE(buffers[2].length, 0)
|
|
|
+ buffers[3].writeInt16BE(dcConfig.port, 0)
|
|
|
|
|
|
- return sessionString
|
|
|
- } catch (error) {
|
|
|
- throw new Error(`构建 StringSession 失败: ${(error as Error).message}`)
|
|
|
- }
|
|
|
+ return Buffer.concat(buffers)
|
|
|
}
|
|
|
|
|
|
function extractAccountData(decodedData: DecodedSessionData): { account: TelegramAccountSession; dcId: number } | null {
|
|
|
- const specialKeys = ['auth_key_fingerprint', 'user_auth', 'dc']
|
|
|
+ const specialKeys: (keyof DecodedSessionData)[] = ['auth_key_fingerprint', 'user_auth', 'dc']
|
|
|
|
|
|
- let dcId: number | undefined = decodedData.dc as number
|
|
|
+ let dcId: number | undefined = decodedData.dc as number | undefined
|
|
|
if (!dcId && decodedData.user_auth) {
|
|
|
- const userAuth = decodedData.user_auth as any
|
|
|
- dcId = userAuth.dcID
|
|
|
- if (dcId === 0) {
|
|
|
- dcId = undefined
|
|
|
- }
|
|
|
+ const userAuth = decodedData.user_auth as UserAuth
|
|
|
+ dcId = userAuth.dcID && userAuth.dcID !== 0 ? userAuth.dcID : undefined
|
|
|
}
|
|
|
|
|
|
for (const key in decodedData) {
|
|
|
@@ -153,30 +172,18 @@ function extractAccountData(decodedData: DecodedSessionData): { account: Telegra
|
|
|
}
|
|
|
|
|
|
const value = decodedData[key]
|
|
|
- if (value && typeof value === 'object') {
|
|
|
- const account = value as any
|
|
|
-
|
|
|
- if (account.userId || account.dc1_auth_key || account.dc2_auth_key || account.dc5_auth_key) {
|
|
|
- const accountDcId = account.dcId || dcId
|
|
|
-
|
|
|
- if (!accountDcId) {
|
|
|
- if (account.dc5_auth_key) {
|
|
|
- dcId = 5
|
|
|
- } else if (account.dc2_auth_key) {
|
|
|
- dcId = 2
|
|
|
- } else if (account.dc1_auth_key) {
|
|
|
- dcId = 1
|
|
|
- } else if (account.dc3_auth_key) {
|
|
|
- dcId = 3
|
|
|
- } else if (account.dc4_auth_key) {
|
|
|
- dcId = 4
|
|
|
- }
|
|
|
- } else {
|
|
|
- dcId = accountDcId
|
|
|
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
|
+ const account = value as Partial<TelegramAccountSession>
|
|
|
+
|
|
|
+ if (isValidAccountObject(account)) {
|
|
|
+ let finalDcId = (account.dcId as number | undefined) || dcId
|
|
|
+
|
|
|
+ if (!finalDcId) {
|
|
|
+ finalDcId = inferDcIdFromAuthKeys(account)
|
|
|
}
|
|
|
|
|
|
- if (dcId) {
|
|
|
- return { account: account as TelegramAccountSession, dcId }
|
|
|
+ if (finalDcId && finalDcId >= MIN_DC_ID && finalDcId <= MAX_DC_ID) {
|
|
|
+ return { account: account as TelegramAccountSession, dcId: finalDcId }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -184,3 +191,23 @@ function extractAccountData(decodedData: DecodedSessionData): { account: Telegra
|
|
|
|
|
|
return null
|
|
|
}
|
|
|
+
|
|
|
+function isValidAccountObject(obj: Partial<TelegramAccountSession>): boolean {
|
|
|
+ return !!(
|
|
|
+ obj.userId ||
|
|
|
+ obj.dc1_auth_key ||
|
|
|
+ obj.dc2_auth_key ||
|
|
|
+ obj.dc3_auth_key ||
|
|
|
+ obj.dc4_auth_key ||
|
|
|
+ obj.dc5_auth_key
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function inferDcIdFromAuthKeys(account: Partial<TelegramAccountSession>): number | undefined {
|
|
|
+ if (account.dc5_auth_key) return 5
|
|
|
+ if (account.dc2_auth_key) return 2
|
|
|
+ if (account.dc1_auth_key) return 1
|
|
|
+ if (account.dc3_auth_key) return 3
|
|
|
+ if (account.dc4_auth_key) return 4
|
|
|
+ return undefined
|
|
|
+}
|