'use client'; import { Channel, StreamChat } from 'stream-chat'; import { create } from 'zustand'; import { getUserToken, createChannel } from '@/services/editor'; import { parseSSEStream, parseData } from '@/utils/streamParser'; type Message = { key: string; role: string; content: string; }; interface StreamChatStore { client: StreamChat | null; user: { userId: string; userName: string; }; // 连接 StreamChat 客户端 connect: (user: any) => Promise; // 频道 channels: Channel[]; currentChannel: Channel | null; // 创建某个角色的聊天频道, 返回channelId createChannel: (characterId: string) => Promise; switchToChannel: (id: string) => Promise; queryChannels: (filter: any) => Promise; deleteChannel: (id: string) => Promise; clearChannels: () => Promise; getCurrentCharacter: () => any | null; // 消息列表 messages: Message[]; setMessages: (messages: Message[]) => void; // 发送消息 sendMessage: (content: string) => Promise; // 清除通知 clearNotifications: () => Promise; } export const useStreamChatStore = create((set, get) => ({ client: null, user: { userId: '', userName: '', }, channels: [], messages: [], setMessages: (messages: any[]) => set({ messages }), currentChannel: null, // 获取当前聊天频道中的角色id getCurrentCharacter() { const { currentChannel, user } = get(); return ( Object.values(currentChannel?.state?.members || {})?.find((i) => i.user?.id !== user?.userId) ?.user?.id || null ); }, // 创建某个角色的聊天频道 async createChannel(characterId: string) { const { user, client } = get(); const { switchToChannel, queryChannels } = get(); if (!client) { return false; } const { data } = await createChannel({ userId: user.userId, userName: user.userName, characterId, }); if (!data?.channelId) { return false; } await queryChannels({}); switchToChannel(data.channelId); return data.channelId; }, async connect(user) { const { client } = get(); set({ user }); if (client) return; const { data } = await getUserToken(user); const streamClient = new StreamChat(process.env.NEXT_PUBLIC_STREAM_CHAT_API_KEY || ''); const res = await streamClient.connectUser( { id: user.userId, name: user.userName, }, data ); set({ client: streamClient }); }, async switchToChannel(id: string) { const { client, user } = get(); const channel = client!.channel('messaging', id); const result = await channel.query({ messages: { limit: 100 }, }); const messages = result.messages.map((i) => ({ key: i.id, role: i.user?.id === user.userId ? 'user' : 'assistant', content: i.text!, })); set({ currentChannel: channel, messages }); }, async queryChannels() { const { user, client } = get(); if (!client) { console.error('StreamChat client is not connected'); return; } try { const channels = await client.queryChannels( { members: { $in: [user.userId], }, }, { last_message_at: -1, }, { message_limit: 1, // 返回最新的1条消息 } ); set({ channels }); } catch (error) { console.error('Failed to query channels:', error); } }, async deleteChannel(id: string) { const { channels, currentChannel, queryChannels } = get(); const channel = channels.find((ch) => ch.id === id); if (!channel) { console.warn(`Channel with id ${id} not found`); return; } try { await channel.delete(); await queryChannels({}); if (currentChannel?.id === id) { set({ currentChannel: null }); } } catch (error) { console.error(`Failed to delete channel ${id}:`, error); } }, async clearChannels() { const { channels } = get(); try { // 停止监听所有频道 for (const channel of channels) { try { await channel.stopWatching(); } catch (error) { console.warn(`Failed to stop watching channel ${channel.id}:`, error); } } // 清空频道列表和当前频道 set({ channels: [], currentChannel: null, }); } catch (error) { console.error('Failed to clear channels:', error); } }, async clearNotifications() {}, // 发送消息 sendMessage: async (content: any) => { const { user, currentChannel, getCurrentCharacter, setMessages, messages } = get(); // 过滤出用户和助手的消息 const filteredMessages = messages.filter((i) => i.role === 'user' || i.role === 'assistant'); let finalMessages = [ ...filteredMessages, { key: user.userId, role: 'user', content: content }, { key: 'assistant', role: 'assistant', content: '' }, ]; setMessages(finalMessages); // 发送消息到服务器 const response = await fetch( `${process.env.NEXT_PUBLIC_CHAT_API_URL}/chat-api/chat/testPrompt`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ userId: user.userId, channelId: currentChannel?.id || '', message: content, promptTemplateId: 'default', characterId: getCurrentCharacter()?.id, modelName: 'gpt-3.5-turbo', }), } ); // 处理服务器返回的 SSE 流 await parseSSEStream(response, (event: string, data: string) => { if (event === 'chat-message') { const d = parseData(data); const lastMsg = finalMessages[finalMessages.length - 1]; if (lastMsg.role === 'assistant') { lastMsg.content = d.content || ''; } setMessages([...finalMessages]); } }); }, }));