Skip to content

Commit 1906890

Browse files
committed
feat: add unified WebSocket connection manager and routing
- Implemented UnifiedWebSocketManager for managing WebSocket connections, including subscription handling and message sending. - Created unified WebSocket router to handle client messages, including authentication, subscription, and chat session management. - Added support for logging and plugin progress subscriptions. - Enhanced error handling and response structure for WebSocket operations.
1 parent 7d0d429 commit 1906890

28 files changed

Lines changed: 3815 additions & 1107 deletions
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { unifiedWsClient, type ConnectionStatus } from './unified-ws'
2+
3+
interface ChatSessionOpenPayload {
4+
group_id?: string
5+
group_name?: string
6+
person_id?: string
7+
platform?: string
8+
user_id?: string
9+
user_name?: string
10+
}
11+
12+
type ChatSessionListener = (message: Record<string, unknown>) => void
13+
14+
class ChatWsClient {
15+
private initialized = false
16+
private listeners: Map<string, Set<ChatSessionListener>> = new Map()
17+
private sessionPayloads: Map<string, ChatSessionOpenPayload> = new Map()
18+
19+
private initialize(): void {
20+
if (this.initialized) {
21+
return
22+
}
23+
24+
unifiedWsClient.addEventListener((message) => {
25+
if (message.domain !== 'chat' || !message.session) {
26+
return
27+
}
28+
29+
const sessionListeners = this.listeners.get(message.session)
30+
if (!sessionListeners) {
31+
return
32+
}
33+
34+
sessionListeners.forEach((listener) => {
35+
try {
36+
listener(message.data)
37+
} catch (error) {
38+
console.error('聊天会话监听器执行失败:', error)
39+
}
40+
})
41+
})
42+
43+
unifiedWsClient.onReconnect(() => {
44+
void this.reopenSessions()
45+
})
46+
47+
this.initialized = true
48+
}
49+
50+
private async reopenSessions(): Promise<void> {
51+
const reopenTargets = Array.from(this.sessionPayloads.entries())
52+
for (const [sessionId, payload] of reopenTargets) {
53+
try {
54+
await unifiedWsClient.call({
55+
domain: 'chat',
56+
method: 'session.open',
57+
session: sessionId,
58+
data: {
59+
...payload,
60+
restore: true,
61+
} as Record<string, unknown>,
62+
})
63+
} catch (error) {
64+
console.error(`恢复聊天会话失败 (${sessionId}):`, error)
65+
}
66+
}
67+
}
68+
69+
async openSession(sessionId: string, payload: ChatSessionOpenPayload): Promise<void> {
70+
this.initialize()
71+
this.sessionPayloads.set(sessionId, payload)
72+
await unifiedWsClient.call({
73+
domain: 'chat',
74+
method: 'session.open',
75+
session: sessionId,
76+
data: payload as Record<string, unknown>,
77+
})
78+
}
79+
80+
async closeSession(sessionId: string): Promise<void> {
81+
this.sessionPayloads.delete(sessionId)
82+
if (unifiedWsClient.getStatus() !== 'connected') {
83+
return
84+
}
85+
86+
try {
87+
await unifiedWsClient.call({
88+
domain: 'chat',
89+
method: 'session.close',
90+
session: sessionId,
91+
data: {},
92+
})
93+
} catch (error) {
94+
console.warn(`关闭聊天会话失败 (${sessionId}):`, error)
95+
}
96+
}
97+
98+
async sendMessage(sessionId: string, content: string, userName: string): Promise<void> {
99+
await unifiedWsClient.call({
100+
domain: 'chat',
101+
method: 'message.send',
102+
session: sessionId,
103+
data: {
104+
content,
105+
user_name: userName,
106+
},
107+
})
108+
}
109+
110+
async updateNickname(sessionId: string, userName: string): Promise<void> {
111+
const currentPayload = this.sessionPayloads.get(sessionId)
112+
if (currentPayload) {
113+
this.sessionPayloads.set(sessionId, {
114+
...currentPayload,
115+
user_name: userName,
116+
})
117+
}
118+
119+
await unifiedWsClient.call({
120+
domain: 'chat',
121+
method: 'session.update_nickname',
122+
session: sessionId,
123+
data: {
124+
user_name: userName,
125+
},
126+
})
127+
}
128+
129+
onSessionMessage(sessionId: string, listener: ChatSessionListener): () => void {
130+
this.initialize()
131+
const sessionListeners = this.listeners.get(sessionId) ?? new Set<ChatSessionListener>()
132+
sessionListeners.add(listener)
133+
this.listeners.set(sessionId, sessionListeners)
134+
135+
return () => {
136+
const currentListeners = this.listeners.get(sessionId)
137+
if (!currentListeners) {
138+
return
139+
}
140+
141+
currentListeners.delete(listener)
142+
if (currentListeners.size === 0) {
143+
this.listeners.delete(sessionId)
144+
}
145+
}
146+
}
147+
148+
onConnectionChange(listener: (connected: boolean) => void): () => void {
149+
return unifiedWsClient.onConnectionChange(listener)
150+
}
151+
152+
onStatusChange(listener: (status: ConnectionStatus) => void): () => void {
153+
return unifiedWsClient.onStatusChange(listener)
154+
}
155+
156+
async restart(): Promise<void> {
157+
await unifiedWsClient.restart()
158+
}
159+
}
160+
161+
export const chatWsClient = new ChatWsClient()

0 commit comments

Comments
 (0)