|
|
@@ -25,6 +25,12 @@ export class TgClientService {
|
|
|
|
|
|
constructor() {}
|
|
|
|
|
|
+ // ==================== 初始化相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化应用配置,获取 API_ID 和 API_HASH
|
|
|
+ * @private
|
|
|
+ */
|
|
|
private async initializeApp(): Promise<void> {
|
|
|
if (this.initPromise) {
|
|
|
return this.initPromise
|
|
|
@@ -50,6 +56,13 @@ export class TgClientService {
|
|
|
return this.initPromise
|
|
|
}
|
|
|
|
|
|
+ // ==================== 连接管理相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接到 Telegram,支持 WSS 和 TCP 两种连接策略
|
|
|
+ * @param sessionString - Telegram Session 字符串
|
|
|
+ * @throws 当连接数达到上限或连接失败时抛出错误
|
|
|
+ */
|
|
|
async connect(sessionString: string): Promise<void> {
|
|
|
if (TgClientService.activeClientsCount >= TgClientService.maxActiveClients) {
|
|
|
throw new Error('TelegramClient 并发连接数已达上限')
|
|
|
@@ -112,6 +125,9 @@ export class TgClientService {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 断开 Telegram 连接并清理资源
|
|
|
+ */
|
|
|
async disconnect(): Promise<void> {
|
|
|
if (!this.client) {
|
|
|
return
|
|
|
@@ -121,14 +137,32 @@ export class TgClientService {
|
|
|
this.client = null
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取当前的 TelegramClient 实例
|
|
|
+ * @returns TelegramClient 实例,如果未连接则返回 null
|
|
|
+ */
|
|
|
getClient(): TelegramClient | null {
|
|
|
return this.client
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 检查客户端是否已连接
|
|
|
+ * @returns 如果客户端存在且已连接返回 true,否则返回 false
|
|
|
+ */
|
|
|
isConnected(): boolean {
|
|
|
return this.client !== null && (this.client.connected ?? false)
|
|
|
}
|
|
|
|
|
|
+ // ==================== 频道/群组管理相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建频道或超级群组
|
|
|
+ * @param groupName - 群组名称
|
|
|
+ * @param groupDescription - 群组描述
|
|
|
+ * @param groupType - 群组类型:'megagroup' 或 'channel'
|
|
|
+ * @returns 返回创建的群组信息,包含 chatId 和 accessHash
|
|
|
+ * @throws 当创建失败时抛出错误
|
|
|
+ */
|
|
|
async createChannelGroup(
|
|
|
groupName: string,
|
|
|
groupDescription: string,
|
|
|
@@ -172,6 +206,12 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 根据 chatId 和 accessHash 创建 InputChannel 对象
|
|
|
+ * @param chatId - 频道/群组 ID
|
|
|
+ * @param accessHash - 访问哈希值
|
|
|
+ * @returns InputChannel 对象
|
|
|
+ */
|
|
|
async getInputChannel(chatId: string | number, accessHash: string | number): Promise<Api.InputChannel> {
|
|
|
return new Api.InputChannel({
|
|
|
channelId: bigInt(chatId.toString()),
|
|
|
@@ -179,39 +219,155 @@ export class TgClientService {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- async sendMessageToChannelGroup(inputChannel: Api.InputChannel, message: string): Promise<void> {
|
|
|
+ /**
|
|
|
+ * 通过邀请链接加入群组
|
|
|
+ * @param inviteLink - Telegram 邀请链接
|
|
|
+ * @returns 返回加入的群组信息,包含 chatId 和 title
|
|
|
+ * @throws 当邀请链接无效、过期或加入失败时抛出错误
|
|
|
+ */
|
|
|
+ async joinGroupByInviteLink(inviteLink: string): Promise<{ chatId: string; title: string }> {
|
|
|
this.ensureConnected()
|
|
|
- this.app.log.info('正在发送群组消息...')
|
|
|
+ this.app.log.info(`正在通过邀请链接加入群组: ${inviteLink}`)
|
|
|
|
|
|
try {
|
|
|
- await this.client!.sendMessage(inputChannel, {
|
|
|
- message: message.trim()
|
|
|
- })
|
|
|
- this.app.log.info('已向群组发送消息')
|
|
|
+ // 解析邀请链接,提取 hash
|
|
|
+ const hash = this.extractInviteHash(inviteLink)
|
|
|
+ if (!hash) {
|
|
|
+ throw new Error('无效的邀请链接格式')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查邀请链接信息
|
|
|
+ await this.client!.invoke(
|
|
|
+ new Api.messages.CheckChatInvite({
|
|
|
+ hash
|
|
|
+ })
|
|
|
+ )
|
|
|
+
|
|
|
+ // 使用邀请链接加入群组
|
|
|
+ const result = await this.client!.invoke(
|
|
|
+ new Api.messages.ImportChatInvite({
|
|
|
+ hash
|
|
|
+ })
|
|
|
+ )
|
|
|
+
|
|
|
+ const updates = result as any
|
|
|
+ let chat = updates.chats?.[0]
|
|
|
+
|
|
|
+ if (!chat) {
|
|
|
+ // 如果是 Updates 类型,尝试从 updates 中获取
|
|
|
+ if (updates.updates) {
|
|
|
+ for (const update of updates.updates) {
|
|
|
+ if (update.className === 'UpdateNewMessage' && update.message?.peerId) {
|
|
|
+ const chatId = update.message.peerId.channelId || update.message.peerId.chatId
|
|
|
+ if (chatId && updates.chats) {
|
|
|
+ chat = updates.chats.find((c: any) => c.id?.toString() === chatId?.toString())
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!chat) {
|
|
|
+ throw new Error('加入群组失败,未返回群组信息')
|
|
|
+ }
|
|
|
+
|
|
|
+ const chatId = chat.id?.toString() || chat.id
|
|
|
+ const title = chat.title || '未知群组'
|
|
|
+
|
|
|
+ this.app.log.info(`成功加入群组: ${title} (ID: ${chatId})`)
|
|
|
+
|
|
|
+ return {
|
|
|
+ chatId: String(chatId),
|
|
|
+ title
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
- const errorMessage = this.extractErrorMessage(error)
|
|
|
- throw new Error(`发送群组消息失败: ${errorMessage}`)
|
|
|
+ let errorMessage = this.extractErrorMessage(error)
|
|
|
+
|
|
|
+ if (errorMessage.includes('INVITE_HASH_EXPIRED')) {
|
|
|
+ errorMessage = '邀请链接已过期'
|
|
|
+ } else if (errorMessage.includes('INVITE_HASH_INVALID')) {
|
|
|
+ errorMessage = '邀请链接无效'
|
|
|
+ } else if (errorMessage.includes('USER_ALREADY_PARTICIPANT')) {
|
|
|
+ errorMessage = '您已经是该群组的成员'
|
|
|
+ } else if (errorMessage.includes('CHANNELS_TOO_MUCH')) {
|
|
|
+ errorMessage = '加入的频道/群组数量已达上限'
|
|
|
+ } else if (errorMessage.includes('INVITE_REQUEST_SENT')) {
|
|
|
+ errorMessage = '已发送加入请求,等待管理员批准'
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new Error(`通过邀请链接加入群组失败: ${errorMessage}`)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async inviteMembersToChannelGroup(inputChannel: Api.InputChannel, inputUsers: Api.InputUser[]): Promise<void> {
|
|
|
+ /**
|
|
|
+ * 退出指定的频道或群组
|
|
|
+ * @param target - 群组标识,可以是 chatId、username 或 InputChannel 对象
|
|
|
+ * @returns 返回操作结果,包含 success 状态和群组标题(如果可用)
|
|
|
+ * @throws 当退出失败时抛出错误
|
|
|
+ */
|
|
|
+ async leaveGroup(target: string | number | Api.InputChannel): Promise<{ success: boolean; title?: string }> {
|
|
|
this.ensureConnected()
|
|
|
- this.app.log.info('正在邀请成员到群组...')
|
|
|
+ this.app.log.info(`正在退出群组: ${target}`)
|
|
|
|
|
|
try {
|
|
|
+ let inputChannel: Api.InputChannel
|
|
|
+ let chatTitle: string | undefined
|
|
|
+
|
|
|
+ // 如果已经是 InputChannel,直接使用
|
|
|
+ if (target instanceof Api.InputChannel) {
|
|
|
+ inputChannel = target
|
|
|
+ } else {
|
|
|
+ // 获取群组实体
|
|
|
+ const entity = await this.client!.getEntity(target)
|
|
|
+
|
|
|
+ if (!entity) {
|
|
|
+ throw new Error('未找到该群组')
|
|
|
+ }
|
|
|
+
|
|
|
+ chatTitle = (entity as any).title
|
|
|
+ const channel = entity as any
|
|
|
+
|
|
|
+ inputChannel = new Api.InputChannel({
|
|
|
+ channelId: bigInt(channel.id.toString()),
|
|
|
+ accessHash: bigInt(channel.accessHash.toString())
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退出频道/超级群组
|
|
|
await this.client!.invoke(
|
|
|
- new Api.channels.InviteToChannel({
|
|
|
- channel: inputChannel,
|
|
|
- users: inputUsers
|
|
|
+ new Api.channels.LeaveChannel({
|
|
|
+ channel: inputChannel
|
|
|
})
|
|
|
)
|
|
|
- this.app.log.info('已邀请成员到群组')
|
|
|
+
|
|
|
+ this.app.log.info(`成功退出群组${chatTitle ? `: ${chatTitle}` : ''}`)
|
|
|
+
|
|
|
+ return {
|
|
|
+ success: true,
|
|
|
+ title: chatTitle
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
const errorMessage = this.extractErrorMessage(error)
|
|
|
- throw new Error(`邀请成员到群组失败: ${errorMessage}`)
|
|
|
+ let errorMsg = `退出群组失败: ${errorMessage}`
|
|
|
+
|
|
|
+ if (errorMessage.includes('CHANNEL_PRIVATE')) {
|
|
|
+ errorMsg = '该群组不存在或您不是成员'
|
|
|
+ } else if (errorMessage.includes('USER_NOT_PARTICIPANT')) {
|
|
|
+ errorMsg = '您不是该群组的成员'
|
|
|
+ } else if (errorMessage.includes('CHAT_ADMIN_REQUIRED')) {
|
|
|
+ errorMsg = '需要管理员权限才能执行此操作'
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new Error(errorMsg)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取群组的邀请链接
|
|
|
+ * @param inputChannel - InputChannel 对象
|
|
|
+ * @returns 返回邀请链接,如果获取失败则返回 null
|
|
|
+ */
|
|
|
async getInviteLink(inputChannel: Api.InputChannel): Promise<string | null> {
|
|
|
this.ensureConnected()
|
|
|
|
|
|
@@ -228,6 +384,12 @@ export class TgClientService {
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 为群组设置公开用户名并返回公开链接
|
|
|
+ * @param inputChannel - InputChannel 对象
|
|
|
+ * @param groupName - 群组名称(可选,用于生成用户名)
|
|
|
+ * @returns 返回公开链接,如果设置失败则返回 null
|
|
|
+ */
|
|
|
async getPublicLink(inputChannel: Api.InputChannel, groupName?: string): Promise<string | null> {
|
|
|
this.ensureConnected()
|
|
|
|
|
|
@@ -247,6 +409,85 @@ export class TgClientService {
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
+ // ==================== 消息发送相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 向频道或群组发送消息
|
|
|
+ * @param inputChannel - InputChannel 对象
|
|
|
+ * @param message - 要发送的消息内容
|
|
|
+ * @throws 当发送失败时抛出错误
|
|
|
+ */
|
|
|
+ async sendMessageToChannelGroup(inputChannel: Api.InputChannel, message: string): Promise<void> {
|
|
|
+ this.ensureConnected()
|
|
|
+ this.app.log.info('正在发送群组消息...')
|
|
|
+
|
|
|
+ try {
|
|
|
+ await this.client!.sendMessage(inputChannel, {
|
|
|
+ message: message.trim()
|
|
|
+ })
|
|
|
+ this.app.log.info('已向群组发送消息')
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = this.extractErrorMessage(error)
|
|
|
+ throw new Error(`发送群组消息失败: ${errorMessage}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 向指定的用户或群组发送消息
|
|
|
+ * @param targetPeer - 目标实体(用户或群组)
|
|
|
+ * @param message - 要发送的消息内容
|
|
|
+ * @returns 返回发送结果
|
|
|
+ * @throws 当发送失败时抛出错误
|
|
|
+ */
|
|
|
+ async sendMessageToPeer(targetPeer: any, message: string): Promise<any> {
|
|
|
+ this.ensureConnected()
|
|
|
+ this.app.log.info('正在发送消息...')
|
|
|
+
|
|
|
+ try {
|
|
|
+ const result = await this.client!.sendMessage(targetPeer, {
|
|
|
+ message: message
|
|
|
+ })
|
|
|
+ return result
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = this.extractErrorMessage(error)
|
|
|
+ throw new Error(`发送消息失败: ${errorMessage}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 成员管理相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 邀请成员加入频道或群组
|
|
|
+ * @param inputChannel - InputChannel 对象
|
|
|
+ * @param inputUsers - 要邀请的用户列表
|
|
|
+ * @throws 当邀请失败时抛出错误
|
|
|
+ */
|
|
|
+ async inviteMembersToChannelGroup(inputChannel: Api.InputChannel, inputUsers: Api.InputUser[]): Promise<void> {
|
|
|
+ this.ensureConnected()
|
|
|
+ this.app.log.info('正在邀请成员到群组...')
|
|
|
+
|
|
|
+ try {
|
|
|
+ await this.client!.invoke(
|
|
|
+ new Api.channels.InviteToChannel({
|
|
|
+ channel: inputChannel,
|
|
|
+ users: inputUsers
|
|
|
+ })
|
|
|
+ )
|
|
|
+ this.app.log.info('已邀请成员到群组')
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = this.extractErrorMessage(error)
|
|
|
+ throw new Error(`邀请成员到群组失败: ${errorMessage}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 联系人管理相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取目标实体信息,支持通过手机号或 ID 获取
|
|
|
+ * @param parsedTarget - 目标标识,可以是手机号(以+开头)或用户ID/用户名
|
|
|
+ * @returns 返回目标实体对象
|
|
|
+ * @throws 当无法获取目标信息时抛出错误
|
|
|
+ */
|
|
|
async getTargetPeer(parsedTarget: string | number): Promise<any> {
|
|
|
this.ensureConnected()
|
|
|
this.app.log.info('正在获取目标实体信息...')
|
|
|
@@ -289,21 +530,11 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async sendMessageToPeer(targetPeer: any, message: string): Promise<any> {
|
|
|
- this.ensureConnected()
|
|
|
- this.app.log.info('正在发送消息...')
|
|
|
-
|
|
|
- try {
|
|
|
- const result = await this.client!.sendMessage(targetPeer, {
|
|
|
- message: message
|
|
|
- })
|
|
|
- return result
|
|
|
- } catch (error) {
|
|
|
- const errorMessage = this.extractErrorMessage(error)
|
|
|
- throw new Error(`发送消息失败: ${errorMessage}`)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 清除与指定实体的会话历史记录
|
|
|
+ * @param targetPeer - 目标实体(用户或群组)
|
|
|
+ * @throws 当清除失败时抛出错误
|
|
|
+ */
|
|
|
async clearConversation(targetPeer: any): Promise<void> {
|
|
|
this.ensureConnected()
|
|
|
this.app.log.info('正在清除会话...')
|
|
|
@@ -321,6 +552,11 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 删除临时联系人
|
|
|
+ * @param userId - 要删除的用户 ID
|
|
|
+ * @throws 当删除失败时抛出错误
|
|
|
+ */
|
|
|
async deleteTempContact(userId: number): Promise<void> {
|
|
|
this.ensureConnected()
|
|
|
this.app.log.info('正在删除临时联系人...')
|
|
|
@@ -337,12 +573,26 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ==================== 私有辅助方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确保客户端已连接,如果未连接则抛出错误
|
|
|
+ * @private
|
|
|
+ * @throws 当客户端未连接时抛出错误
|
|
|
+ */
|
|
|
private ensureConnected(): void {
|
|
|
if (!this.client) {
|
|
|
throw new Error('TelegramClient 未连接,请先调用 connect() 方法')
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 销毁客户端并清理资源
|
|
|
+ * @private
|
|
|
+ * @param client - 要销毁的 TelegramClient 实例
|
|
|
+ * @param context - 上下文信息,用于日志记录
|
|
|
+ * @param decrementCount - 是否减少活跃客户端计数
|
|
|
+ */
|
|
|
private async disposeClient(client: TelegramClient | null, context: string, decrementCount: boolean): Promise<void> {
|
|
|
if (!client) {
|
|
|
return
|
|
|
@@ -376,6 +626,12 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 从错误对象中提取错误消息
|
|
|
+ * @private
|
|
|
+ * @param error - 错误对象
|
|
|
+ * @returns 错误消息字符串
|
|
|
+ */
|
|
|
private extractErrorMessage(error: unknown): string {
|
|
|
if (error instanceof Error) {
|
|
|
return error.message
|
|
|
@@ -386,6 +642,40 @@ export class TgClientService {
|
|
|
return '未知错误'
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 从邀请链接中提取 hash 值
|
|
|
+ * @private
|
|
|
+ * @param inviteLink - Telegram 邀请链接
|
|
|
+ * @returns 提取的 hash 值,如果解析失败则返回 null
|
|
|
+ */
|
|
|
+ private extractInviteHash(inviteLink: string): string | null {
|
|
|
+ try {
|
|
|
+ // 支持的格式:
|
|
|
+ // https://t.me/+ojthGOSrE4czNzhk
|
|
|
+ // https://t.me/joinchat/ojthGOSrE4czNzhk
|
|
|
+ // t.me/+ojthGOSrE4czNzhk
|
|
|
+ const patterns = [/t\.me\/\+([A-Za-z0-9_-]+)/, /t\.me\/joinchat\/([A-Za-z0-9_-]+)/, /^([A-Za-z0-9_-]+)$/]
|
|
|
+
|
|
|
+ for (const pattern of patterns) {
|
|
|
+ const match = inviteLink.match(pattern)
|
|
|
+ if (match && match[1]) {
|
|
|
+ return match[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null
|
|
|
+ } catch (error) {
|
|
|
+ this.app.log.error(`解析邀请链接失败: ${this.extractErrorMessage(error)}`)
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成群组用户名
|
|
|
+ * @private
|
|
|
+ * @param groupName - 群组名称(可选)
|
|
|
+ * @returns 生成的用户名
|
|
|
+ */
|
|
|
private generateGroupUsername(groupName?: string): string {
|
|
|
const random = Math.random().toString(36).slice(2, 8)
|
|
|
const maxLength = 32
|
|
|
@@ -395,6 +685,12 @@ export class TgClientService {
|
|
|
return `group_${safeName}_${random}`
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 构建英文名称,如果名称不是纯英文则使用随机单词
|
|
|
+ * @private
|
|
|
+ * @param name - 原始名称(可选)
|
|
|
+ * @returns 处理后的英文名称
|
|
|
+ */
|
|
|
private buildEnglishName(name?: string): string {
|
|
|
if (name && this.isPureEnglish(name)) {
|
|
|
const sanitized = this.sanitizeName(name)
|
|
|
@@ -405,10 +701,22 @@ export class TgClientService {
|
|
|
return this.getRandomEnglishWord()
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 检查字符串是否为纯英文(包含字母、数字、空格、下划线和连字符)
|
|
|
+ * @private
|
|
|
+ * @param value - 要检查的字符串
|
|
|
+ * @returns 如果是纯英文返回 true,否则返回 false
|
|
|
+ */
|
|
|
private isPureEnglish(value: string): boolean {
|
|
|
return /^[A-Za-z0-9 _-]+$/.test(value)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 清理名称字符串,转换为小写并替换特殊字符为下划线
|
|
|
+ * @private
|
|
|
+ * @param name - 要清理的名称
|
|
|
+ * @returns 清理后的名称
|
|
|
+ */
|
|
|
private sanitizeName(name: string): string {
|
|
|
return name
|
|
|
.trim()
|
|
|
@@ -418,6 +726,11 @@ export class TgClientService {
|
|
|
.replace(/_+/g, '_')
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取随机英文单词
|
|
|
+ * @private
|
|
|
+ * @returns 随机选择的英文单词
|
|
|
+ */
|
|
|
private getRandomEnglishWord(): string {
|
|
|
const words = [
|
|
|
'apple',
|
|
|
@@ -439,11 +752,23 @@ export class TgClientService {
|
|
|
return words[Math.floor(Math.random() * words.length)]
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 检查错误是否为 Session 被吊销的错误
|
|
|
+ * @private
|
|
|
+ * @param error - 错误对象
|
|
|
+ * @returns 如果是 Session 吊销错误返回 true,否则返回 false
|
|
|
+ */
|
|
|
private isSessionRevokedError(error: unknown): boolean {
|
|
|
const msg = this.extractErrorMessage(error)
|
|
|
return msg.includes('SESSION_REVOKED') || msg.includes('AUTH_KEY_UNREGISTERED') || msg.includes('AUTH_KEY_INVALID')
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 带超时的连接方法
|
|
|
+ * @private
|
|
|
+ * @param client - TelegramClient 实例
|
|
|
+ * @throws 当连接超时或失败时抛出错误
|
|
|
+ */
|
|
|
private async connectWithTimeout(client: TelegramClient): Promise<void> {
|
|
|
let timer: NodeJS.Timeout | null = null
|
|
|
|
|
|
@@ -465,6 +790,13 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 验证 Session 是否有效,并获取账号信息
|
|
|
+ * @private
|
|
|
+ * @param client - TelegramClient 实例
|
|
|
+ * @returns 返回当前登录账号信息
|
|
|
+ * @throws 当 Session 无效时抛出错误
|
|
|
+ */
|
|
|
private async ensureValidSession(client: TelegramClient): Promise<any> {
|
|
|
try {
|
|
|
this.app.log.info('TelegramClient 连接成功,正在获取账号信息...')
|
|
|
@@ -477,6 +809,10 @@ export class TgClientService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 记录当前活跃的 Telegram 客户端数量
|
|
|
+ * @private
|
|
|
+ */
|
|
|
private logActiveClientCount(): void {
|
|
|
this.app?.log?.info?.(`当前活跃 Telegram 客户端数量: ${TgClientService.activeClientsCount}`)
|
|
|
}
|