mezon-light-sdk 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -24
- package/dist/api.gen.d.ts +150 -0
- package/dist/api.gen.js +149 -0
- package/dist/client.d.ts +133 -22
- package/dist/client.js +244 -67
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +13 -1
- package/dist/google/protobuf/struct.d.ts +201 -0
- package/dist/google/protobuf/struct.js +449 -0
- package/dist/google/protobuf/wrappers.d.ts +238 -0
- package/dist/google/protobuf/wrappers.js +506 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +18 -18
- package/dist/proto/api.d.ts +24188 -0
- package/dist/proto/api.js +33287 -0
- package/dist/proto/realtime.d.ts +36125 -0
- package/dist/proto/realtime.js +13947 -0
- package/dist/session.d.ts +63 -0
- package/dist/session.js +92 -0
- package/dist/socket.d.ts +133 -11
- package/dist/socket.gen.d.ts +136 -0
- package/dist/socket.gen.js +327 -0
- package/dist/socket.js +233 -49
- package/dist/types.d.ts +44 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +110 -0
- package/dist/web_socket_adapter_pb.d.ts +69 -0
- package/dist/web_socket_adapter_pb.js +111 -0
- package/package.json +3 -4
- package/dist/message.d.ts +0 -19
- /package/dist/{message.js → types.js} +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DefaultSocket = exports.ConnectionState = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
const web_socket_adapter_pb_1 = require("./web_socket_adapter_pb");
|
|
6
|
+
/** A socket connection to Mezon server implemented with the DOM's WebSocket API. */
|
|
7
|
+
let __hasConnectedOnce = false;
|
|
8
|
+
exports.ConnectionState = {
|
|
9
|
+
DISCONNECTED: "disconnected",
|
|
10
|
+
CONNECTING: "connecting",
|
|
11
|
+
CONNECTED: "connected",
|
|
12
|
+
};
|
|
13
|
+
class DefaultSocket {
|
|
14
|
+
constructor(host, port, useSSL = false, verbose = false, adapter = new web_socket_adapter_pb_1.WebSocketAdapterPb(), sendTimeoutMs = DefaultSocket.DefaultSendTimeoutMs) {
|
|
15
|
+
this.host = host;
|
|
16
|
+
this.port = port;
|
|
17
|
+
this.useSSL = useSSL;
|
|
18
|
+
this.verbose = verbose;
|
|
19
|
+
this.adapter = adapter;
|
|
20
|
+
this.sendTimeoutMs = sendTimeoutMs;
|
|
21
|
+
this.cIds = {};
|
|
22
|
+
this.nextCid = 1;
|
|
23
|
+
this._heartbeatTimeoutMs = DefaultSocket.DefaultHeartbeatTimeoutMs;
|
|
24
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
25
|
+
}
|
|
26
|
+
generatecid() {
|
|
27
|
+
const cid = this.nextCid.toString();
|
|
28
|
+
++this.nextCid;
|
|
29
|
+
return cid;
|
|
30
|
+
}
|
|
31
|
+
isOpen() {
|
|
32
|
+
return this._connectionState === exports.ConnectionState.CONNECTED;
|
|
33
|
+
}
|
|
34
|
+
connect(session, createStatus = false, platform = "", connectTimeoutMs = DefaultSocket.DefaultConnectTimeoutMs, signal) {
|
|
35
|
+
if (this._connectionState === exports.ConnectionState.CONNECTED) {
|
|
36
|
+
return Promise.resolve(session);
|
|
37
|
+
}
|
|
38
|
+
if (this._connectionState === exports.ConnectionState.CONNECTING && this._connectPromise) {
|
|
39
|
+
return this._connectPromise;
|
|
40
|
+
}
|
|
41
|
+
this.clearConnectTimeout();
|
|
42
|
+
this._connectionState = exports.ConnectionState.CONNECTING;
|
|
43
|
+
const scheme = this.useSSL ? "wss://" : "ws://";
|
|
44
|
+
this.adapter.connect(scheme, this.host, this.port, createStatus, session.token, platform, signal);
|
|
45
|
+
this.adapter.onClose = (evt) => {
|
|
46
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
47
|
+
this.stopHeartbeatLoop();
|
|
48
|
+
this.clearConnectTimeout();
|
|
49
|
+
this.ondisconnect(evt);
|
|
50
|
+
};
|
|
51
|
+
this.adapter.onMessage = async (message) => {
|
|
52
|
+
if (this.verbose && window && window.console) {
|
|
53
|
+
console.log("Response: %o", JSON.stringify(message));
|
|
54
|
+
}
|
|
55
|
+
/** Inbound message from server. */
|
|
56
|
+
if (!message.cid) {
|
|
57
|
+
if (message.channel_message) {
|
|
58
|
+
const channelMessage = createChannelMessageFromEvent(message);
|
|
59
|
+
this.onchannelmessage(channelMessage);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (this.verbose && window && window.console) {
|
|
63
|
+
console.log("Unrecognized message received: %o", message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const executor = this.cIds[message.cid];
|
|
69
|
+
if (!executor) {
|
|
70
|
+
if (this.verbose && window && window.console) {
|
|
71
|
+
console.error("No promise executor for message: %o", message);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
delete this.cIds[message.cid];
|
|
76
|
+
if (message.error) {
|
|
77
|
+
executor.reject(message.error);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
executor.resolve(message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const connectPromise = new Promise((resolve, reject) => {
|
|
85
|
+
this.adapter.onOpen = (evt) => {
|
|
86
|
+
if (this.verbose && window && window.console) {
|
|
87
|
+
console.log(evt);
|
|
88
|
+
}
|
|
89
|
+
const isReconnect = __hasConnectedOnce;
|
|
90
|
+
__hasConnectedOnce = true;
|
|
91
|
+
this.clearConnectTimeout();
|
|
92
|
+
this._connectionState = exports.ConnectionState.CONNECTED;
|
|
93
|
+
this.startHeartbeatLoop();
|
|
94
|
+
this._connectPromise = undefined;
|
|
95
|
+
resolve(session);
|
|
96
|
+
if (isReconnect) {
|
|
97
|
+
this.onreconnect(evt);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
this.adapter.onError = (evt) => {
|
|
101
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
102
|
+
this.stopHeartbeatLoop();
|
|
103
|
+
this.clearConnectTimeout();
|
|
104
|
+
this.onerror(evt);
|
|
105
|
+
this._connectPromise = undefined;
|
|
106
|
+
this.adapter.close();
|
|
107
|
+
reject(evt);
|
|
108
|
+
};
|
|
109
|
+
this._connectTimeoutTimer = setTimeout(() => {
|
|
110
|
+
// if promise has resolved by now, the reject() is a no-op
|
|
111
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
112
|
+
this.stopHeartbeatLoop();
|
|
113
|
+
this.adapter.close();
|
|
114
|
+
this._connectPromise = undefined;
|
|
115
|
+
reject("The socket timed out when trying to connect.");
|
|
116
|
+
this._connectTimeoutTimer = undefined;
|
|
117
|
+
}, connectTimeoutMs);
|
|
118
|
+
});
|
|
119
|
+
this._connectPromise = connectPromise;
|
|
120
|
+
return this._connectPromise;
|
|
121
|
+
}
|
|
122
|
+
disconnect(fireDisconnectEvent = true) {
|
|
123
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
124
|
+
this.stopHeartbeatLoop();
|
|
125
|
+
if (this.adapter.isOpen()) {
|
|
126
|
+
this.adapter.close();
|
|
127
|
+
}
|
|
128
|
+
if (fireDisconnectEvent) {
|
|
129
|
+
this.ondisconnect({});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
setHeartbeatTimeoutMs(ms) {
|
|
133
|
+
this._heartbeatTimeoutMs = ms;
|
|
134
|
+
}
|
|
135
|
+
getHeartbeatTimeoutMs() {
|
|
136
|
+
return this._heartbeatTimeoutMs;
|
|
137
|
+
}
|
|
138
|
+
onreconnect(evt) {
|
|
139
|
+
if (this.verbose && window && window.console) {
|
|
140
|
+
console.log(evt);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
ondisconnect(evt) {
|
|
144
|
+
if (this.verbose && window && window.console) {
|
|
145
|
+
console.log(evt);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
onerror(evt) {
|
|
149
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
150
|
+
this.stopHeartbeatLoop();
|
|
151
|
+
if (this.verbose && window && window.console) {
|
|
152
|
+
console.log(evt);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
onheartbeattimeout() {
|
|
156
|
+
if (this.verbose && window && window.console) {
|
|
157
|
+
console.log("Heartbeat timeout.");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
onchannelmessage(channelMessage) {
|
|
161
|
+
if (this.verbose && window && window.console) {
|
|
162
|
+
console.log(channelMessage);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// onuserchanneladded(user: UserChannelAddedEvent) {
|
|
166
|
+
// if (this.verbose && window && window.console) {
|
|
167
|
+
// console.log(user);
|
|
168
|
+
// }
|
|
169
|
+
// }
|
|
170
|
+
send(message, sendTimeout = DefaultSocket.DefaultSendTimeoutMs) {
|
|
171
|
+
const untypedMessage = message;
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
if (!this.adapter.isOpen()) {
|
|
174
|
+
reject("Socket connection has not been established yet.");
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
if (untypedMessage.channel_message_send) {
|
|
178
|
+
untypedMessage.channel_message_send.content = JSON.stringify(untypedMessage.channel_message_send.content);
|
|
179
|
+
}
|
|
180
|
+
else if (untypedMessage.channel_message_update) {
|
|
181
|
+
untypedMessage.channel_message_update.content = JSON.stringify(untypedMessage.channel_message_update.content);
|
|
182
|
+
}
|
|
183
|
+
else if (untypedMessage.ephemeral_message_send) {
|
|
184
|
+
untypedMessage.ephemeral_message_send.message.content = JSON.stringify(untypedMessage.ephemeral_message_send.message?.content);
|
|
185
|
+
}
|
|
186
|
+
else if (untypedMessage.quick_menu_event) {
|
|
187
|
+
untypedMessage.quick_menu_event.message.content = JSON.stringify(untypedMessage.quick_menu_event.message?.content);
|
|
188
|
+
}
|
|
189
|
+
const cid = this.generatecid();
|
|
190
|
+
this.cIds[cid] = { resolve, reject };
|
|
191
|
+
if (sendTimeout !== Infinity && sendTimeout > 0) {
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
reject("The socket timed out while waiting for a response.");
|
|
194
|
+
}, sendTimeout);
|
|
195
|
+
}
|
|
196
|
+
untypedMessage.cid = cid;
|
|
197
|
+
this.adapter.send(untypedMessage);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async joinChat(clan_id, channel_id, channel_type, is_public) {
|
|
202
|
+
const response = await this.send({
|
|
203
|
+
channel_join: {
|
|
204
|
+
clan_id: clan_id,
|
|
205
|
+
channel_id: channel_id,
|
|
206
|
+
channel_type: channel_type,
|
|
207
|
+
is_public: is_public,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
return response.channel;
|
|
211
|
+
}
|
|
212
|
+
async leaveChat(clan_id, channel_id, channel_type, is_public) {
|
|
213
|
+
return this.send({
|
|
214
|
+
channel_leave: {
|
|
215
|
+
clan_id: clan_id,
|
|
216
|
+
channel_id: channel_id,
|
|
217
|
+
channel_type: channel_type,
|
|
218
|
+
is_public: is_public,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async writeChatMessage(clan_id, channel_id, mode, is_public, content, attachments, anonymous_message, mention_everyone, avatar, code, topic_id) {
|
|
223
|
+
const response = await this.send({
|
|
224
|
+
channel_message_send: {
|
|
225
|
+
clan_id: clan_id,
|
|
226
|
+
channel_id: channel_id,
|
|
227
|
+
mode: mode,
|
|
228
|
+
is_public: is_public,
|
|
229
|
+
content: content,
|
|
230
|
+
reactions: [],
|
|
231
|
+
mentions: [],
|
|
232
|
+
attachments: attachments,
|
|
233
|
+
references: [],
|
|
234
|
+
anonymous_message: anonymous_message,
|
|
235
|
+
mention_everyone: mention_everyone,
|
|
236
|
+
avatar: avatar,
|
|
237
|
+
code: code,
|
|
238
|
+
topic_id: topic_id,
|
|
239
|
+
},
|
|
240
|
+
}, Infinity);
|
|
241
|
+
return response.channel_message_ack;
|
|
242
|
+
}
|
|
243
|
+
async pingPong() {
|
|
244
|
+
if (!this.isOpen()) {
|
|
245
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
246
|
+
this.stopHeartbeatLoop();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
await this.send({ ping: {} }, this._heartbeatTimeoutMs);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
this._connectionState = exports.ConnectionState.DISCONNECTED;
|
|
254
|
+
this.stopHeartbeatLoop();
|
|
255
|
+
if (this.adapter.isOpen()) {
|
|
256
|
+
if (window && window.console) {
|
|
257
|
+
console.error("Server unreachable from heartbeat.");
|
|
258
|
+
}
|
|
259
|
+
this.onheartbeattimeout();
|
|
260
|
+
this.adapter.close();
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this.startHeartbeatLoop();
|
|
265
|
+
}
|
|
266
|
+
startHeartbeatLoop() {
|
|
267
|
+
this.stopHeartbeatLoop();
|
|
268
|
+
this._heartbeatTimer = setTimeout(() => this.pingPong(), this._heartbeatTimeoutMs);
|
|
269
|
+
}
|
|
270
|
+
stopHeartbeatLoop() {
|
|
271
|
+
if (this._heartbeatTimer !== undefined) {
|
|
272
|
+
clearTimeout(this._heartbeatTimer);
|
|
273
|
+
this._heartbeatTimer = undefined;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
clearConnectTimeout() {
|
|
277
|
+
if (this._connectTimeoutTimer !== undefined) {
|
|
278
|
+
clearTimeout(this._connectTimeoutTimer);
|
|
279
|
+
this._connectTimeoutTimer = undefined;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
exports.DefaultSocket = DefaultSocket;
|
|
284
|
+
DefaultSocket.DefaultHeartbeatTimeoutMs = 10000;
|
|
285
|
+
DefaultSocket.DefaultSendTimeoutMs = 10000;
|
|
286
|
+
DefaultSocket.DefaultConnectTimeoutMs = 30000;
|
|
287
|
+
function createChannelMessageFromEvent(message) {
|
|
288
|
+
var content, attachments;
|
|
289
|
+
try {
|
|
290
|
+
content = (0, utils_1.safeJSONParse)(message.channel_message.content);
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
console.log("content is invalid", e);
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
attachments = (0, utils_1.decodeAttachments)(message.channel_message.attachments);
|
|
297
|
+
}
|
|
298
|
+
catch (e) {
|
|
299
|
+
console.log("attachments is invalid", e);
|
|
300
|
+
}
|
|
301
|
+
var e = {
|
|
302
|
+
id: message.id || message.channel_message.message_id,
|
|
303
|
+
avatar: message.channel_message.avatar,
|
|
304
|
+
channel_id: message.channel_message.channel_id,
|
|
305
|
+
mode: message.channel_message.mode,
|
|
306
|
+
channel_label: message.channel_message.channel_label,
|
|
307
|
+
clan_id: message.channel_message.clan_id,
|
|
308
|
+
code: message.channel_message.code,
|
|
309
|
+
message_id: message.channel_message.message_id,
|
|
310
|
+
sender_id: message.channel_message.sender_id,
|
|
311
|
+
update_time: message.channel_message.update_time,
|
|
312
|
+
clan_logo: message.channel_message.clan_logo,
|
|
313
|
+
category_name: message.channel_message.category_name,
|
|
314
|
+
username: message.channel_message.username,
|
|
315
|
+
clan_nick: message.channel_message.clan_nick,
|
|
316
|
+
clan_avatar: message.channel_message.clan_avatar,
|
|
317
|
+
display_name: message.channel_message.display_name,
|
|
318
|
+
content: content,
|
|
319
|
+
attachments: attachments?.attachments,
|
|
320
|
+
hide_editted: message.channel_message.hide_editted,
|
|
321
|
+
is_public: message.channel_message.is_public,
|
|
322
|
+
create_time_seconds: message.channel_message.create_time_seconds,
|
|
323
|
+
update_time_seconds: message.channel_message.update_time_seconds,
|
|
324
|
+
topic_id: message.channel_message.topic_id,
|
|
325
|
+
};
|
|
326
|
+
return e;
|
|
327
|
+
}
|
package/dist/socket.js
CHANGED
|
@@ -1,65 +1,249 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LightSocket = void 0;
|
|
4
|
-
const
|
|
5
|
-
const mezon_js_1 = require("mezon-js");
|
|
3
|
+
exports.LightSocket = exports.SocketError = void 0;
|
|
4
|
+
const web_socket_adapter_pb_1 = require("./web_socket_adapter_pb");
|
|
6
5
|
const constants_1 = require("./constants");
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when socket operations fail.
|
|
8
|
+
*/
|
|
9
|
+
class SocketError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SocketError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.SocketError = SocketError;
|
|
16
|
+
/**
|
|
17
|
+
* Maps raw socket message data to a ChannelMessage interface.
|
|
18
|
+
*/
|
|
19
|
+
function mapToChannelMessage(message) {
|
|
20
|
+
return {
|
|
21
|
+
...message,
|
|
22
|
+
clan_id: String(message.clan_id ?? ""),
|
|
23
|
+
channel_id: String(message.channel_id ?? ""),
|
|
24
|
+
message_id: String(message.message_id ?? ""),
|
|
25
|
+
sender_id: String(message.sender_id ?? ""),
|
|
26
|
+
content: String(message.content ?? ""),
|
|
27
|
+
// reactions: [],
|
|
28
|
+
// mentions: [],
|
|
29
|
+
// attachments: Array.isArray(message.attachments) ? message.attachments : [],
|
|
30
|
+
// references: Array.isArray(message.references) ? message.references : [],
|
|
31
|
+
create_time_seconds: Number(message.create_time_seconds ?? 0),
|
|
32
|
+
update_time_seconds: Number(message.update_time_seconds ?? 0),
|
|
33
|
+
hide_editted: Boolean(message.hide_editted),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Waits for a socket to be in a ready state with exponential backoff.
|
|
38
|
+
*
|
|
39
|
+
* @param socket - The socket to wait for
|
|
40
|
+
* @param maxRetries - Maximum number of retry attempts
|
|
41
|
+
* @param initialDelay - Initial delay in milliseconds
|
|
42
|
+
* @throws {SocketError} If socket doesn't become ready within retry limit
|
|
43
|
+
*/
|
|
44
|
+
async function waitForSocketReady(socket, maxRetries = constants_1.SOCKET_READY_MAX_RETRY, initialDelay = constants_1.SOCKET_READY_RETRY_DELAY) {
|
|
45
|
+
let retryCount = 0;
|
|
46
|
+
let delay = initialDelay;
|
|
47
|
+
// Type assertion to access internal adapter
|
|
48
|
+
const socketWithAdapter = socket;
|
|
49
|
+
while (retryCount < maxRetries) {
|
|
50
|
+
if (socketWithAdapter.adapter?.isOpen()) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
54
|
+
delay *= 2; // Exponential backoff
|
|
55
|
+
retryCount++;
|
|
56
|
+
}
|
|
57
|
+
throw new SocketError(`Socket failed to connect after ${maxRetries} attempts (total wait: ~${Math.pow(2, maxRetries) * initialDelay}ms)`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* LightSocket provides a simplified interface for Mezon real-time messaging.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const socket = new LightSocket(client.client, client.session);
|
|
65
|
+
*
|
|
66
|
+
* await socket.connect({
|
|
67
|
+
* onError: (err) => console.error('Socket error:', err),
|
|
68
|
+
* onDisconnect: () => console.log('Disconnected')
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* socket.onChannelMessage((msg) => {
|
|
72
|
+
* console.log('Received message:', msg.content);
|
|
73
|
+
* });
|
|
74
|
+
*
|
|
75
|
+
* await socket.joinDMChannel('channel-123');
|
|
76
|
+
* await socket.sendDM({ channelId: 'channel-123', content: 'Hello!' });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
7
79
|
class LightSocket {
|
|
8
|
-
constructor(
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
80
|
+
constructor(_client, _session) {
|
|
81
|
+
this._client = _client;
|
|
82
|
+
this._session = _session;
|
|
83
|
+
this._socket = null;
|
|
84
|
+
this._isConnected = false;
|
|
85
|
+
this._messageHandlers = [];
|
|
11
86
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Gets whether the socket is currently connected.
|
|
89
|
+
*/
|
|
90
|
+
get isConnected() {
|
|
91
|
+
return this._isConnected;
|
|
16
92
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Gets the underlying socket instance.
|
|
95
|
+
* @throws {SocketError} If socket is not connected
|
|
96
|
+
*/
|
|
97
|
+
get socket() {
|
|
98
|
+
if (!this._socket) {
|
|
99
|
+
throw new SocketError("Socket is not connected. Call connect() first.");
|
|
100
|
+
}
|
|
101
|
+
return this._socket;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Connects to the Mezon real-time server.
|
|
105
|
+
*
|
|
106
|
+
* @param options - Connection options including error handlers
|
|
107
|
+
* @throws {SocketError} If already connected or connection fails
|
|
108
|
+
*/
|
|
109
|
+
async connect(options = {}) {
|
|
110
|
+
if (this._isConnected) {
|
|
111
|
+
throw new SocketError("Socket is already connected. Call disconnect() first.");
|
|
112
|
+
}
|
|
113
|
+
const { onError, onDisconnect, verbose = false } = options;
|
|
114
|
+
this._errorHandler = onError;
|
|
115
|
+
this._disconnectHandler = onDisconnect;
|
|
116
|
+
this._socket = this._client.createSocket(verbose, new web_socket_adapter_pb_1.WebSocketAdapterPb());
|
|
117
|
+
// Set up error handler
|
|
118
|
+
this._socket.onerror = (error) => {
|
|
119
|
+
this._errorHandler?.(error);
|
|
120
|
+
};
|
|
121
|
+
// Set up disconnect handler
|
|
122
|
+
this._socket.ondisconnect = () => {
|
|
123
|
+
this._isConnected = false;
|
|
124
|
+
this._disconnectHandler?.();
|
|
125
|
+
};
|
|
126
|
+
// Set up message handler that dispatches to all registered handlers
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
128
|
+
this._socket.onchannelmessage = (channelMessage) => {
|
|
129
|
+
if (!channelMessage) {
|
|
130
|
+
this._errorHandler?.(new SocketError("Received null or undefined channel message"));
|
|
22
131
|
return;
|
|
23
132
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
avatar: msg.avatar,
|
|
32
|
-
content: msg.content,
|
|
33
|
-
create_time: msg.create_time,
|
|
34
|
-
update_time: msg.update_time,
|
|
35
|
-
display_name: msg.display_name,
|
|
36
|
-
mentions: msg.mentions || [],
|
|
37
|
-
attachments: msg.attachments || [],
|
|
38
|
-
references: msg.references || [],
|
|
39
|
-
create_time_seconds: msg.create_time_seconds,
|
|
40
|
-
update_time_seconds: msg.update_time_seconds,
|
|
41
|
-
hide_editted: msg.hide_editted,
|
|
133
|
+
this._messageHandlers.forEach((handler) => {
|
|
134
|
+
try {
|
|
135
|
+
handler(channelMessage);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.error("Error in message handler:", error);
|
|
139
|
+
}
|
|
42
140
|
});
|
|
43
141
|
};
|
|
142
|
+
await this._socket.connect(this._session, true, "0");
|
|
143
|
+
this._isConnected = true;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Disconnects from the Mezon server.
|
|
147
|
+
*/
|
|
148
|
+
disconnect() {
|
|
149
|
+
if (this._socket) {
|
|
150
|
+
this._socket.disconnect(true);
|
|
151
|
+
this._socket = null;
|
|
152
|
+
this._isConnected = false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Registers a handler for incoming channel messages.
|
|
157
|
+
* Multiple handlers can be registered and will all receive messages.
|
|
158
|
+
*
|
|
159
|
+
* @param handler - Callback function to handle messages
|
|
160
|
+
*/
|
|
161
|
+
setChannelMessageHandler(cb) {
|
|
162
|
+
this.onChannelMessage(cb);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Registers a handler for incoming channel messages.
|
|
166
|
+
* Multiple handlers can be registered and will all receive messages.
|
|
167
|
+
*
|
|
168
|
+
* @param handler - Callback function to handle messages
|
|
169
|
+
* @returns A function to unsubscribe the handler
|
|
170
|
+
*/
|
|
171
|
+
onChannelMessage(handler) {
|
|
172
|
+
this._messageHandlers.push(handler);
|
|
173
|
+
// Return unsubscribe function
|
|
174
|
+
return () => {
|
|
175
|
+
const index = this._messageHandlers.indexOf(handler);
|
|
176
|
+
if (index !== -1) {
|
|
177
|
+
this._messageHandlers.splice(index, 1);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Joins a DM channel to receive messages from it.
|
|
183
|
+
*
|
|
184
|
+
* @param channelId - The DM channel ID to join
|
|
185
|
+
* @throws {SocketError} If socket is not ready or join fails
|
|
186
|
+
*/
|
|
187
|
+
async joinDMChannel(channelId) {
|
|
188
|
+
await waitForSocketReady(this.socket);
|
|
189
|
+
await this.socket.joinChat(constants_1.CLAN_DM, channelId, constants_1.CHANNEL_TYPE_DM, false);
|
|
44
190
|
}
|
|
45
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Joins a group channel to receive messages from it.
|
|
193
|
+
*
|
|
194
|
+
* @param channelId - The group channel ID to join
|
|
195
|
+
* @throws {SocketError} If socket is not ready or join fails
|
|
196
|
+
*/
|
|
197
|
+
async joinGroupChannel(channelId) {
|
|
46
198
|
await waitForSocketReady(this.socket);
|
|
47
|
-
await this.socket.joinChat(constants_1.CLAN_DM,
|
|
199
|
+
await this.socket.joinChat(constants_1.CLAN_DM, channelId, constants_1.CHANNEL_TYPE_GROUP, false);
|
|
48
200
|
}
|
|
49
|
-
|
|
50
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Leaves a DM channel.
|
|
203
|
+
*
|
|
204
|
+
* @param channelId - The DM channel ID to leave
|
|
205
|
+
*/
|
|
206
|
+
async leaveDMChannel(channelId) {
|
|
207
|
+
await this.socket.leaveChat(constants_1.CLAN_DM, channelId, constants_1.CHANNEL_TYPE_DM, false);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Leaves a group channel.
|
|
211
|
+
*
|
|
212
|
+
* @param channelId - The group channel ID to leave
|
|
213
|
+
*/
|
|
214
|
+
async leaveGroupChannel(channelId) {
|
|
215
|
+
await this.socket.leaveChat(constants_1.CLAN_DM, channelId, constants_1.CHANNEL_TYPE_GROUP, false);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Sends a direct message to a channel.
|
|
219
|
+
*
|
|
220
|
+
* @param options - Message options including channelId, content, and attachments
|
|
221
|
+
*/
|
|
222
|
+
async sendDM(payload) {
|
|
223
|
+
const { channelId, content, attachments, hideLink = false } = payload;
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
225
|
+
await this.socket.writeChatMessage(constants_1.CLAN_DM, channelId, constants_1.STREAM_MODE_DM, false, content, attachments, false, hideLink, "", 0);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Sends a direct message to a channel.
|
|
229
|
+
*
|
|
230
|
+
* @param options - Message options including channelId, content, and attachments
|
|
231
|
+
*/
|
|
232
|
+
async sendGroup(payload) {
|
|
233
|
+
const { channelId, content, attachments, hideLink = false } = payload;
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
235
|
+
await this.socket.writeChatMessage(constants_1.CLAN_DM, channelId, constants_1.STREAM_MODE_GROUP, false, content, attachments, false, hideLink, "", 0);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Sets the error handler for socket errors.
|
|
239
|
+
*
|
|
240
|
+
* @param handler - Error handler callback
|
|
241
|
+
*/
|
|
242
|
+
setErrorHandler(handler) {
|
|
243
|
+
this._errorHandler = handler;
|
|
244
|
+
if (this._socket) {
|
|
245
|
+
this._socket.onerror = handler;
|
|
246
|
+
}
|
|
51
247
|
}
|
|
52
248
|
}
|
|
53
249
|
exports.LightSocket = LightSocket;
|
|
54
|
-
async function waitForSocketReady(socket) {
|
|
55
|
-
let count = 0;
|
|
56
|
-
let delay = constants_1.SOCKET_READY_RETRY_DELAY;
|
|
57
|
-
while (!(socket.adapter && socket.adapter.isOpen()) &&
|
|
58
|
-
count < constants_1.SOCKET_READY_MAX_RETRY) {
|
|
59
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
60
|
-
delay *= 2;
|
|
61
|
-
count++;
|
|
62
|
-
}
|
|
63
|
-
if (!(socket.adapter && socket.adapter.isOpen()))
|
|
64
|
-
throw new Error("Socket not open");
|
|
65
|
-
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ApiMessageAttachment } from "./api.gen";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for initializing a LightClient from existing tokens.
|
|
4
|
+
*/
|
|
5
|
+
export interface ClientInitConfig {
|
|
6
|
+
/** Authentication token */
|
|
7
|
+
token: string;
|
|
8
|
+
/** Refresh token for session renewal */
|
|
9
|
+
refresh_token: string;
|
|
10
|
+
/** API URL for the Mezon server */
|
|
11
|
+
api_url: string;
|
|
12
|
+
/** User ID associated with the session */
|
|
13
|
+
user_id: string;
|
|
14
|
+
/** Server key for authentication (optional, uses default if not provided) */
|
|
15
|
+
serverkey?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Configuration for authenticating a new user.
|
|
19
|
+
*/
|
|
20
|
+
export interface AuthenticateConfig {
|
|
21
|
+
/** ID token from identity provider */
|
|
22
|
+
id_token: string;
|
|
23
|
+
/** User ID to associate with the account */
|
|
24
|
+
user_id: string;
|
|
25
|
+
/** Username for the account */
|
|
26
|
+
username: string;
|
|
27
|
+
/** Server key for authentication (optional, uses default if not provided) */
|
|
28
|
+
serverkey?: string;
|
|
29
|
+
/** Custom gateway URL (optional, uses default if not provided) */
|
|
30
|
+
gateway_url?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Options for sending a message.
|
|
34
|
+
*/
|
|
35
|
+
export interface SendMessagePayload {
|
|
36
|
+
/** The channel ID to send the message to */
|
|
37
|
+
channelId: string;
|
|
38
|
+
/** Message content */
|
|
39
|
+
content: unknown;
|
|
40
|
+
/** File/media attachments (optional) */
|
|
41
|
+
attachments?: ApiMessageAttachment[];
|
|
42
|
+
/** Whether to hide link previews (optional) */
|
|
43
|
+
hideLink?: boolean;
|
|
44
|
+
}
|
package/dist/utils.d.ts
ADDED