|
|
@@ -423,7 +423,9 @@ export class TaskService {
|
|
|
try {
|
|
|
await this.senderService.incrementUsageCount(senderId)
|
|
|
} catch (error) {
|
|
|
- // 更新 usageCount 失败不影响主流程,静默处理
|
|
|
+ this.app.log.warn(
|
|
|
+ `更新 sender usageCount 失败 [${senderId}]: ${error instanceof Error ? error.message : '未知错误'}`
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
totalSentCount++
|
|
|
@@ -478,6 +480,539 @@ export class TaskService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ async testCreateChatGroup(
|
|
|
+ senderId: string,
|
|
|
+ groupName: string,
|
|
|
+ groupDescription: string,
|
|
|
+ groupType: string,
|
|
|
+ session?: string,
|
|
|
+ dcId?: number,
|
|
|
+ authKey?: string
|
|
|
+ ): Promise<any> {
|
|
|
+ let client: TelegramClient | null = null
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sessionResult = await this.resolveSenderSession(senderId, session, dcId, authKey)
|
|
|
+ if ('error' in sessionResult) {
|
|
|
+ return sessionResult.error
|
|
|
+ }
|
|
|
+ const { sessionString } = sessionResult
|
|
|
+
|
|
|
+ this.app.log.info('正在连接 TelegramClient...')
|
|
|
+ client = await this.tgClientService.connect(sessionString)
|
|
|
+ this.app.log.info('TelegramClient 连接完成')
|
|
|
+
|
|
|
+ // 新 Session 登录后等待 20-40 秒,避免新账号或首次使用触发限制
|
|
|
+ const waitTime = Math.floor(Math.random() * 21) + 20
|
|
|
+ this.app.log.info(`连接成功后等待 ${waitTime} 秒,避免新 Session 被限制`)
|
|
|
+ await new Promise(resolve => setTimeout(resolve, waitTime * 1000))
|
|
|
+
|
|
|
+ this.app.log.info(`等待完成,开始创建群组`)
|
|
|
+
|
|
|
+ // 群类型校验
|
|
|
+ if (groupType !== 'channel' && groupType !== 'megagroup') {
|
|
|
+ return {
|
|
|
+ success: false,
|
|
|
+ message: `不支持的群组类型: ${groupType},仅支持: megagroup(超级群组), channel(频道)`
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.app.log.info(`正在创建${groupType === 'channel' ? '频道' : '超级群组'}: ${groupName}`)
|
|
|
+
|
|
|
+ // 创建群
|
|
|
+ const result = await client.invoke(
|
|
|
+ new Api.channels.CreateChannel({
|
|
|
+ title: groupName,
|
|
|
+ about: groupDescription || '',
|
|
|
+ megagroup: groupType === 'megagroup'
|
|
|
+ })
|
|
|
+ )
|
|
|
+
|
|
|
+ const updates = result as any
|
|
|
+ const createdChat = updates.chats?.[0]
|
|
|
+
|
|
|
+ if (!createdChat) {
|
|
|
+ return {
|
|
|
+ success: false,
|
|
|
+ message: '创建群组失败,未返回群组信息'
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const chatId = createdChat.id
|
|
|
+ const accessHash = createdChat.accessHash
|
|
|
+
|
|
|
+ this.app.log.info(`群组创建成功,ID: ${chatId}, accessHash: ${accessHash}`)
|
|
|
+
|
|
|
+ // 必须构造 InputChannel(否则无法发送消息 / 获取 full info / 邀请)
|
|
|
+ const inputChannel = new Api.InputChannel({
|
|
|
+ channelId: chatId,
|
|
|
+ accessHash: accessHash
|
|
|
+ })
|
|
|
+
|
|
|
+ // ⭐ 必须发送一条消息,让群出现在会话列表中
|
|
|
+ try {
|
|
|
+ await client.sendMessage(inputChannel, {
|
|
|
+ message: groupDescription || `Welcome to ${groupName}!`
|
|
|
+ })
|
|
|
+ this.app.log.info('已向群组发送欢迎消息')
|
|
|
+ } catch (e) {
|
|
|
+ this.app.log.warn('发送欢迎消息失败: ' + (e instanceof Error ? e.message : '未知错误'))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取链接
|
|
|
+ let groupLink: string | null = null
|
|
|
+ try {
|
|
|
+ const fullChannel = await client.invoke(
|
|
|
+ new Api.channels.GetFullChannel({
|
|
|
+ channel: inputChannel
|
|
|
+ })
|
|
|
+ )
|
|
|
+
|
|
|
+ const channel = fullChannel.chats?.[0] as any
|
|
|
+
|
|
|
+ // 若存在 username(公开群)
|
|
|
+ if (channel?.username) {
|
|
|
+ groupLink = `https://t.me/${channel.username}`
|
|
|
+ this.app.log.info(`群组公开链接(永久): ${groupLink}`)
|
|
|
+ } else {
|
|
|
+ // 创建私有邀请链接(有效期 1 天)
|
|
|
+ const expireDate = Math.floor(Date.now() / 1000) + 24 * 60 * 60
|
|
|
+
|
|
|
+ const inviteLink = await client.invoke(
|
|
|
+ new Api.messages.ExportChatInvite({
|
|
|
+ peer: inputChannel,
|
|
|
+ expireDate: expireDate
|
|
|
+ })
|
|
|
+ )
|
|
|
+
|
|
|
+ const invite = inviteLink as any
|
|
|
+ if (invite?.link) {
|
|
|
+ groupLink = invite.link
|
|
|
+ this.app.log.info(`群组邀请链接(1 天有效): ${groupLink}`)
|
|
|
+ } else {
|
|
|
+ this.app.log.warn('未能获取群组邀请链接')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ this.app.log.warn(`获取群组链接失败: ${errorMessage}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ success: true,
|
|
|
+ message: '群组创建成功',
|
|
|
+ data: {
|
|
|
+ chatId: chatId.toString(),
|
|
|
+ chatTitle: groupName,
|
|
|
+ chatType: groupType,
|
|
|
+ groupLink: groupLink || null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ this.app.log.error(`创建聊天群失败: ${errorMessage}`)
|
|
|
+ return {
|
|
|
+ success: false,
|
|
|
+ message: `创建聊天群失败: ${errorMessage}`
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ if (client) {
|
|
|
+ try {
|
|
|
+ await this.tgClientService.disconnect()
|
|
|
+ this.app.log.info('TelegramClient 已断开连接')
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ this.app.log.error(`断开连接失败: ${errorMessage}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async testInviteMembersToChat(
|
|
|
+ senderId: string,
|
|
|
+ chatId: string,
|
|
|
+ members: string[],
|
|
|
+ accessHash?: string
|
|
|
+ ): Promise<{
|
|
|
+ success: boolean
|
|
|
+ message: string
|
|
|
+ data?: { chatId: string; inviteResults: { total: number; success: number; failed: number; errors: string[] } }
|
|
|
+ }> {
|
|
|
+ let client: TelegramClient | null = null
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (!members || members.length === 0) {
|
|
|
+ return { success: false, message: '成员列表不能为空' }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!chatId || !chatId.trim()) {
|
|
|
+ return { success: false, message: 'chatId 不能为空' }
|
|
|
+ }
|
|
|
+
|
|
|
+ const sessionResult = await this.resolveSenderSession(senderId)
|
|
|
+ if ('error' in sessionResult) {
|
|
|
+ return sessionResult.error
|
|
|
+ }
|
|
|
+ const { sessionString } = sessionResult
|
|
|
+
|
|
|
+ this.app.log.info('正在连接 TelegramClient...')
|
|
|
+ client = await this.tgClientService.connect(sessionString)
|
|
|
+ this.app.log.info('TelegramClient 连接完成')
|
|
|
+
|
|
|
+ const waitTime = Math.floor(Math.random() * 21) + 20
|
|
|
+ this.app.log.info(`连接成功后等待 ${waitTime} 秒,避免新 Session 被限制`)
|
|
|
+ await new Promise(resolve => setTimeout(resolve, waitTime * 1000))
|
|
|
+ this.app.log.info('等待完成,开始邀请成员')
|
|
|
+
|
|
|
+ let successCount = 0
|
|
|
+ let failedCount = 0
|
|
|
+ const errors: string[] = []
|
|
|
+
|
|
|
+ // 获取群组/频道实体(兼容 channel、megagroup、普通群)
|
|
|
+ const chatTarget = chatId.trim()
|
|
|
+ const accessHashTrimmed = accessHash?.trim()
|
|
|
+ const channelIdWithHashMatch = chatTarget.match(/^(-?\d+)[,:#](\d+)$/)
|
|
|
+ let chatEntity: any
|
|
|
+ try {
|
|
|
+ if (channelIdWithHashMatch) {
|
|
|
+ // 直接使用已知 id + accessHash,避免 getEntity 失败
|
|
|
+ const [, idStr, accessHashStr] = channelIdWithHashMatch
|
|
|
+ chatEntity = {
|
|
|
+ id: BigInt(idStr),
|
|
|
+ accessHash: BigInt(accessHashStr),
|
|
|
+ className: 'Channel',
|
|
|
+ title: chatTarget
|
|
|
+ }
|
|
|
+ this.app.log.info('已使用显式 channelId + accessHash 初始化频道实体')
|
|
|
+ } else if (accessHashTrimmed && /^-?\d+$/.test(chatTarget)) {
|
|
|
+ // 单独提供 chatId + accessHash
|
|
|
+ chatEntity = {
|
|
|
+ id: BigInt(chatTarget),
|
|
|
+ accessHash: BigInt(accessHashTrimmed),
|
|
|
+ className: 'Channel',
|
|
|
+ title: chatTarget
|
|
|
+ }
|
|
|
+ this.app.log.info('已使用 chatId + accessHash 初始化频道实体')
|
|
|
+ } else {
|
|
|
+ const parsedChatId = /^-?\d+$/.test(chatTarget) ? Number(chatTarget) : chatTarget
|
|
|
+ chatEntity = await client.getEntity(parsedChatId)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ // 如果是纯数字且未带 -100 前缀,尝试补全后再次获取(频道/超级群常见)
|
|
|
+ if (/^\d+$/.test(chatTarget)) {
|
|
|
+ try {
|
|
|
+ const prefixedChannelId = BigInt(`-100${chatTarget}`)
|
|
|
+ chatEntity = await client.getEntity(prefixedChannelId.toString())
|
|
|
+ this.app.log.info(`通过 -100 前缀重试获取群组成功: ${prefixedChannelId.toString()}`)
|
|
|
+ } catch (error2) {
|
|
|
+ const errorMessage2 = error2 instanceof Error ? error2.message : '未知错误'
|
|
|
+ this.app.log.error(`获取群组信息失败(二次尝试): ${errorMessage2}`)
|
|
|
+ return { success: false, message: `获取群组信息失败: ${errorMessage2}` }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ this.app.log.error(`获取群组信息失败: ${errorMessage}`)
|
|
|
+ return { success: false, message: `获取群组信息失败: ${errorMessage}` }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.app.log.info(
|
|
|
+ `已获取群组信息: ${chatEntity.title || chatEntity.username || chatTarget} (class: ${chatEntity.className})`
|
|
|
+ )
|
|
|
+
|
|
|
+ const isChannel = chatEntity?.className === 'Channel'
|
|
|
+ const isChat = chatEntity?.className === 'Chat'
|
|
|
+ if (!isChannel && !isChat) {
|
|
|
+ return { success: false, message: '目标并非群组或频道,无法邀请成员' }
|
|
|
+ }
|
|
|
+
|
|
|
+ const inviteToGroup = async (inputUser: Api.InputUser) => {
|
|
|
+ if (isChannel) {
|
|
|
+ if (!chatEntity?.accessHash) {
|
|
|
+ throw new Error('缺少 accessHash,无法邀请到频道/超级群组')
|
|
|
+ }
|
|
|
+ const inputChannel = new Api.InputChannel({
|
|
|
+ channelId: chatEntity.id,
|
|
|
+ accessHash: chatEntity.accessHash
|
|
|
+ })
|
|
|
+ await client!.invoke(
|
|
|
+ new Api.channels.InviteToChannel({
|
|
|
+ channel: inputChannel,
|
|
|
+ users: [inputUser]
|
|
|
+ })
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ await client!.invoke(
|
|
|
+ new Api.messages.AddChatUser({
|
|
|
+ chatId: chatEntity.id,
|
|
|
+ userId: inputUser,
|
|
|
+ fwdLimit: 0
|
|
|
+ })
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历成员列表,逐个邀请
|
|
|
+ for (let i = 0; i < members.length; i++) {
|
|
|
+ const member = members[i]
|
|
|
+ let lastError: string | null = null
|
|
|
+
|
|
|
+ this.app.log.info(`正在邀请第 ${i + 1}/${members.length} 个成员: ${member}`)
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 解析成员 target
|
|
|
+ const parsedTarget = this.parseTarget(member)
|
|
|
+ if (!parsedTarget) {
|
|
|
+ throw new Error(`成员格式错误: ${member}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取目标用户实体
|
|
|
+ let targetUser: any
|
|
|
+ try {
|
|
|
+ targetUser = await this.tgClientService.getTargetPeer(client, parsedTarget)
|
|
|
+ if (!targetUser) {
|
|
|
+ throw new Error('无法获取用户信息')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ throw new Error(`获取用户信息失败: ${error instanceof Error ? error.message : '未知错误'}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建 InputUser
|
|
|
+ const inputUser = new Api.InputUser({
|
|
|
+ userId: targetUser.id,
|
|
|
+ accessHash: targetUser.accessHash || BigInt(0)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 邀请用户到群组/频道
|
|
|
+ try {
|
|
|
+ await inviteToGroup(inputUser)
|
|
|
+ this.app.log.info(`成功邀请成员: ${member}`)
|
|
|
+ successCount++
|
|
|
+ } catch (inviteError) {
|
|
|
+ const inviteErrorMessage = inviteError instanceof Error ? inviteError.message : '未知错误'
|
|
|
+
|
|
|
+ // 处理特定的错误情况
|
|
|
+ if (inviteErrorMessage.includes('USER_ALREADY_PARTICIPANT')) {
|
|
|
+ this.app.log.info(`成员已在群组中: ${member}`)
|
|
|
+ successCount++
|
|
|
+ } else if (inviteErrorMessage.includes('PEER_FLOOD')) {
|
|
|
+ throw new Error('邀请频率过快,触发 PEER_FLOOD 限制,请降低邀请频率')
|
|
|
+ } else if (inviteErrorMessage.includes('USER_PRIVACY_RESTRICTED')) {
|
|
|
+ throw new Error('用户隐私设置不允许被邀请')
|
|
|
+ } else if (inviteErrorMessage.includes('USER_NOT_MUTUAL_CONTACT')) {
|
|
|
+ throw new Error('用户不是双向联系人,无法邀请')
|
|
|
+ } else if (inviteErrorMessage.includes('USER_CHANNELS_TOO_MUCH')) {
|
|
|
+ throw new Error('用户加入的群组/频道数量已达上限')
|
|
|
+ } else if (inviteErrorMessage.includes('INVITE_REQUEST_SENT')) {
|
|
|
+ this.app.log.info(`已向用户发送邀请请求: ${member}`)
|
|
|
+ successCount++
|
|
|
+ } else {
|
|
|
+ throw inviteError
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ const errorDetail = `${member}: ${errorMessage}`
|
|
|
+ this.app.log.error(`邀请成员失败: ${errorDetail}`)
|
|
|
+ errors.push(errorDetail)
|
|
|
+ failedCount++
|
|
|
+ lastError = errorMessage
|
|
|
+ } finally {
|
|
|
+ // 统一处理延迟逻辑
|
|
|
+ if (i < members.length - 1) {
|
|
|
+ let delay: number
|
|
|
+ let delayReason: string
|
|
|
+
|
|
|
+ // 如果有错误,根据错误类型决定延迟时间
|
|
|
+ if (lastError) {
|
|
|
+ if (
|
|
|
+ lastError.includes('FLOOD_WAIT') ||
|
|
|
+ lastError.includes('SLOWMODE_WAIT') ||
|
|
|
+ lastError.includes('PEER_FLOOD') ||
|
|
|
+ lastError.includes('Too Many Requests')
|
|
|
+ ) {
|
|
|
+ delay = 120
|
|
|
+ delayReason = `检测到限制(${lastError.includes('PEER_FLOOD') ? 'PEER_FLOOD' : 'FLOOD_WAIT'})`
|
|
|
+ } else {
|
|
|
+ // 普通错误也需要遵守 30 秒以上的间隔
|
|
|
+ delay = Math.floor(Math.random() * 11) + 30
|
|
|
+ delayReason = '普通错误后等待'
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 成功邀请后的正常延迟
|
|
|
+ delay = Math.floor(Math.random() * 11) + 30
|
|
|
+ delayReason = '避免 Peer Flood'
|
|
|
+ }
|
|
|
+
|
|
|
+ this.app.log.info(`${delayReason},等待 ${delay} 秒后继续邀请下一个成员`)
|
|
|
+ await new Promise(resolve => setTimeout(resolve, delay * 1000))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.app.log.info(`成员邀请完成: 总计=${members.length}, 成功=${successCount}, 失败=${failedCount}`)
|
|
|
+
|
|
|
+ return {
|
|
|
+ success: true,
|
|
|
+ message: `成员邀请完成,共邀请 ${members.length} 个成员,成功 ${successCount} 个,失败 ${failedCount} 个`,
|
|
|
+ data: {
|
|
|
+ chatId: chatId.toString(),
|
|
|
+ inviteResults: {
|
|
|
+ total: members.length,
|
|
|
+ success: successCount,
|
|
|
+ failed: failedCount,
|
|
|
+ errors: errors
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ this.app.log.error(`邀请成员失败: ${errorMessage}`)
|
|
|
+ return {
|
|
|
+ success: false,
|
|
|
+ message: `邀请成员失败: ${errorMessage}`
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ if (client) {
|
|
|
+ try {
|
|
|
+ await this.tgClientService.disconnect()
|
|
|
+ this.app.log.info('TelegramClient 已断开连接')
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ this.app.log.error(`断开连接失败: ${errorMessage}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async resolveSenderSession(
|
|
|
+ senderId: string,
|
|
|
+ session?: string,
|
|
|
+ dcId?: number,
|
|
|
+ authKey?: string
|
|
|
+ ): Promise<{ sender: Sender; sessionString: string } | { error: { success: false; message: string } }> {
|
|
|
+ if ((dcId !== undefined && authKey === undefined) || (dcId === undefined && authKey !== undefined)) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: 'dcId 和 authKey 必须同时传参'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let sender: Sender | null = null
|
|
|
+ let sessionString: string | null = null
|
|
|
+
|
|
|
+ if (session) {
|
|
|
+ try {
|
|
|
+ sessionString = buildStringSession(session)
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: `解析 session 失败: ${errorMessage}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const existingSender = await this.senderRepository.findOne({ where: { id: senderId } })
|
|
|
+ if (existingSender) {
|
|
|
+ await this.senderRepository.update(senderId, { sessionStr: sessionString })
|
|
|
+ sender = await this.senderRepository.findOne({ where: { id: senderId } })
|
|
|
+ } else {
|
|
|
+ sender = await this.senderService.create(senderId, undefined, undefined, sessionString)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sender) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: '创建或更新 sender 失败'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (dcId !== undefined && authKey !== undefined) {
|
|
|
+ try {
|
|
|
+ sessionString = buildStringSessionByDcIdAndAuthKey(dcId, authKey)
|
|
|
+ } catch (error) {
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误'
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: `解析 session 失败: ${errorMessage}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const existingSender = await this.senderRepository.findOne({ where: { id: senderId } })
|
|
|
+ if (existingSender) {
|
|
|
+ await this.senderRepository.update(senderId, { dcId, authKey, sessionStr: sessionString })
|
|
|
+ sender = await this.senderRepository.findOne({ where: { id: senderId } })
|
|
|
+ } else {
|
|
|
+ sender = await this.senderService.create(senderId, dcId, authKey, sessionString)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sender) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: '创建或更新 sender 失败'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ sender = await this.senderRepository.findOne({ where: { id: senderId, delFlag: false } })
|
|
|
+ if (!sender) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: '发送账号不存在或已被删除'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sender) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: '发送账号不存在或已被删除'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sessionString) {
|
|
|
+ sessionString = sender.sessionStr
|
|
|
+ if (!sessionString) {
|
|
|
+ if (!sender.dcId || !sender.authKey) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: 'Sender 缺少 dcId 或 authKey 信息'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sessionString = buildStringSessionByDcIdAndAuthKey(sender.dcId, sender.authKey)
|
|
|
+ await this.senderRepository.update(sender.id, { sessionStr: sessionString })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sessionString) {
|
|
|
+ return {
|
|
|
+ error: {
|
|
|
+ success: false,
|
|
|
+ message: '无法生成 session 信息'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ sender,
|
|
|
+ sessionString
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private parseTarget(targetId: string): string | number | null {
|
|
|
const trimmed = targetId.trim()
|
|
|
|
|
|
@@ -501,9 +1036,6 @@ export class TaskService {
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 检查是否可以向目标发送消息
|
|
|
- */
|
|
|
private async checkCanSendMessage(client: TelegramClient, targetPeer: any): Promise<boolean> {
|
|
|
try {
|
|
|
// 检查目标用户的完整信息
|