beniocord.js 2.0.4 → 2.0.6

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/Client.js CHANGED
@@ -83,6 +83,13 @@ class Client extends EventEmitter {
83
83
  response => response,
84
84
  error => this._handleAxiosError(error)
85
85
  );
86
+
87
+ setInterval(() => {
88
+ if (this._sentMessages.size > 1000) {
89
+ this._sentMessages.clear();
90
+ }
91
+ }, 30 * 60 * 1000);
92
+
86
93
  }
87
94
 
88
95
  _handleAxiosError(error) {
@@ -194,11 +201,13 @@ class Client extends EventEmitter {
194
201
  this.isConnected = true;
195
202
  this.retryCount = 0;
196
203
  this._setupSocketHandlers();
204
+ this._startHeartbeat();
197
205
  resolve();
198
206
  });
199
207
 
200
208
  this.socket.on("disconnect", (reason) => {
201
209
  this.isConnected = false;
210
+ this._stopHeartbeat(); // Para o heartbeat
202
211
  this.emit("disconnect", reason);
203
212
 
204
213
  if (reason === "io server disconnect") {
@@ -239,6 +248,7 @@ class Client extends EventEmitter {
239
248
 
240
249
  this.socket.on("reconnect", (attemptNumber) => {
241
250
  this.isConnected = true;
251
+ this._startHeartbeat();
242
252
  this.emit("reconnect", attemptNumber);
243
253
  });
244
254
 
@@ -247,6 +257,7 @@ class Client extends EventEmitter {
247
257
  });
248
258
 
249
259
  this.socket.on("reconnect_failed", () => {
260
+ this._stopHeartbeat();
250
261
  this.emit("error", new ClientError(
251
262
  "Failed to reconnect after maximum attempts",
252
263
  "RECONNECT_FAILED"
@@ -255,8 +266,87 @@ class Client extends EventEmitter {
255
266
  });
256
267
  }
257
268
 
269
+ /**
270
+ * Inicia o sistema de heartbeat
271
+ * @private
272
+ */
273
+ _startHeartbeat() {
274
+ // Para qualquer heartbeat existente
275
+ this._stopHeartbeat();
276
+
277
+ // Intervalo de 30 segundos (mesmo do frontend)
278
+ const HEARTBEAT_INTERVAL = 30000;
279
+
280
+ this.heartbeatInterval = setInterval(() => {
281
+ if (this.socket && this.isConnected) {
282
+ // Envia heartbeat simples para bots
283
+ // Bots não precisam de isPageVisible/isAppFocused
284
+ this.socket.emit("presence:heartbeat", {
285
+ status: this.status || "online",
286
+ clientType: "bot" // Identifica como bot
287
+ });
288
+ }
289
+ }, HEARTBEAT_INTERVAL);
290
+
291
+ // Envia primeiro heartbeat imediatamente
292
+ if (this.socket && this.isConnected) {
293
+ this.socket.emit("presence:heartbeat", {
294
+ status: this.status || "online",
295
+ clientType: "bot"
296
+ });
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Para o sistema de heartbeat
302
+ * @private
303
+ */
304
+ _stopHeartbeat() {
305
+ if (this.heartbeatInterval) {
306
+ clearInterval(this.heartbeatInterval);
307
+ this.heartbeatInterval = null;
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Desconecta o cliente e limpa recursos
313
+ */
314
+ disconnect() {
315
+ this._stopHeartbeat();
316
+
317
+ if (this.socket) {
318
+ // Notifica o servidor antes de desconectar
319
+ if (this.isConnected) {
320
+ this.socket.emit("presence:update", {
321
+ isPageVisible: false,
322
+ isAppFocused: false,
323
+ status: "offline",
324
+ clientType: "bot"
325
+ });
326
+ }
327
+
328
+ this._removeSocketHandlers();
329
+
330
+ this.socket.off("connect");
331
+ this.socket.off("disconnect");
332
+ this.socket.off("connect_error");
333
+ this.socket.off("reconnect");
334
+ this.socket.off("reconnect_error");
335
+ this.socket.off("reconnect_failed");
336
+
337
+ this.socket.disconnect();
338
+ this.socket = null;
339
+ }
340
+
341
+ this.isConnected = false;
342
+ this.isReady = false;
343
+ }
344
+
345
+
258
346
  _setupSocketHandlers() {
259
- this.socket.on("message:new", async (data) => {
347
+ this._removeSocketHandlers();
348
+
349
+ this.socket.on('message:new', async (data) => {
260
350
  try {
261
351
  if (this._sentMessages.has(data.id)) {
262
352
  this._sentMessages.delete(data.id);
@@ -312,6 +402,26 @@ class Client extends EventEmitter {
312
402
  data.member = member;
313
403
  data.channel = channel;
314
404
 
405
+ // 🔥 Se EU fui adicionado ao canal
406
+ if (data.memberId === this.user?.id) {
407
+ // Adiciona o canal na cache se não existir
408
+ if (channel && !this.cache.channels.has(data.channelId)) {
409
+ this.cache.channels.set(data.channelId, channel);
410
+ }
411
+
412
+ // Entra na room do canal no socket
413
+ this.socket.emit('channel:join', { channelId: data.channelId });
414
+ }
415
+
416
+ // 🔥 Se o canal já existe na cache, adiciona o membro nele
417
+ if (channel && member) {
418
+ // Assumindo que o Channel tem uma lista/Map de membros
419
+ if (!channel.members) {
420
+ channel.members = new Map();
421
+ }
422
+ channel.members.set(member.id, member);
423
+ }
424
+
315
425
  this.emit('memberJoin', data);
316
426
  console.log('join', data);
317
427
  });
@@ -327,6 +437,20 @@ class Client extends EventEmitter {
327
437
  data.member = member;
328
438
  data.channel = channel;
329
439
 
440
+ // 🔥 Se EU fui removido do canal
441
+ if (data.memberId === this.user?.id) {
442
+ // Remove o canal da cache
443
+ this.cache.channels.delete(data.channelId);
444
+
445
+ // Sai da room do canal no socket
446
+ this.socket.emit('channel:leave', { channelId: data.channelId });
447
+ }
448
+
449
+ // 🔥 Se o canal existe na cache, remove o membro dele
450
+ if (channel && member && channel.members) {
451
+ channel.members.delete(member.id);
452
+ }
453
+
330
454
  this.emit('memberLeave', data);
331
455
  console.log('leave', data);
332
456
  });
@@ -346,6 +470,24 @@ class Client extends EventEmitter {
346
470
  });
347
471
  }
348
472
 
473
+ _removeSocketHandlers() {
474
+ if (!this.socket) return;
475
+
476
+ // Remove todos os listeners customizados
477
+ this.socket.off("message:new");
478
+ this.socket.off("message:deleted");
479
+ this.socket.off("message:edited");
480
+ this.socket.off("typing:user-start");
481
+ this.socket.off("typing:user-stop");
482
+ this.socket.off("user:status-update");
483
+ this.socket.off("presence:update");
484
+ this.socket.off("member:join");
485
+ this.socket.off("member:leave");
486
+ this.socket.off("channel:update");
487
+ this.socket.off("channel:delete");
488
+ this.socket.off("rate:limited");
489
+ }
490
+
349
491
  async _processSocketMessage(data) {
350
492
  const msg = new Message(data, this);
351
493
 
@@ -447,7 +589,8 @@ class Client extends EventEmitter {
447
589
  }
448
590
 
449
591
  /**
450
- * @param {string} status - online, offline, away, dnd
592
+ * Set the user status
593
+ * @param {string} status - Status: "online", "away", "dnd", "offline"
451
594
  */
452
595
  async setStatus(status) {
453
596
  const validStatuses = ["online", "offline", "away", "dnd"];
@@ -463,13 +606,7 @@ class Client extends EventEmitter {
463
606
  this.socket.emit('status:update', { status });
464
607
  }
465
608
 
466
- /**
467
- * Envia uma mensagem para um canal
468
- * @param {string} channelId - ID do canal
469
- * @param {string|MessageEmbed} content - Conteúdo da mensagem ou MessageEmbed
470
- * @param {Object|MessageAttachment} opts - Opções adicionais
471
- * @returns {Promise<Message>}
472
- */
609
+
473
610
  // async sendMessage(channelId, content, opts = {}) {
474
611
  // return new Promise(async (resolve, reject) => {
475
612
  // try {
@@ -542,6 +679,15 @@ class Client extends EventEmitter {
542
679
  // }
543
680
  // });
544
681
  // }
682
+
683
+
684
+ /**
685
+ * Send a message
686
+ * @param {string} channelId - Channel ID
687
+ * @param {string|MessageEmbed} content - Message content or MessageEmbed
688
+ * @param {Object|MessageAttachment} opts - Extra options
689
+ * @returns {Promise<Message>}
690
+ */
545
691
  async sendMessage(channelId, content, opts = {}) {
546
692
  return new Promise(async (resolve, reject) => {
547
693
  try {
@@ -0,0 +1,8 @@
1
+ function formatUrl(url) {
2
+ const base = 'https://api.beniocord.site';
3
+ if (!url) return undefined;
4
+ if (url.startsWith(base) || url.startsWith('http')) return url;
5
+ return base + (url.startsWith('/') ? url : '/' + url);
6
+ }
7
+
8
+ module.exports = { formatUrl }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "socket.io-client": "^4.8.1"
5
5
  },
6
6
  "name": "beniocord.js",
7
- "version": "2.0.4",
7
+ "version": "2.0.6",
8
8
  "description": "Uma biblioteca leve e intuitiva para integração com APIs de bots em plataformas de mensagens, como Discord. Facilita o envio de mensagens, gerenciamento de canais e interação com usuários, proporcionando uma experiência de desenvolvimento ágil e eficiente.",
9
9
  "main": "Client.js",
10
10
  "devDependencies": {},
@@ -1,3 +1,4 @@
1
+ const { formatUrl } = require('../helpers');
1
2
  const MessageCollector = require('./MessageCollector');
2
3
 
3
4
  let client;
@@ -11,7 +12,7 @@ class Channel {
11
12
  this.type = data.type || "text";
12
13
  this.isPrivate = data.is_private;
13
14
  this.isDm = data.is_dm;
14
- this.iconUrl = data.icon_url ? 'https://api.beniocord.site' + data.icon_url : undefined;
15
+ this.iconUrl = formatUrl(data.icon_url);
15
16
  this.createdBy = data.created_by;
16
17
  this.createdAt = data.created_at;
17
18
  this.updatedAt = data.updated_at;
@@ -1,9 +1,11 @@
1
+ const { formatUrl } = require("../helpers");
2
+
1
3
  class Emoji {
2
4
  constructor(data) {
3
5
  this.id = data.id;
4
6
  this.userId = data.user_id;
5
7
  this.name = data.name;
6
- this.url = data.url ? 'https://api.beniocord.site' + data.url : undefined;
8
+ this.url = formatUrl(data.url);
7
9
  this.createdAt = data.created_at;
8
10
  this.updatedAt = data.updated_at;
9
11
  }
@@ -1,5 +1,6 @@
1
1
  const User = require("./User");
2
2
  const Channel = require("./Channel");
3
+ const { formatUrl } = require('../helpers/index');
3
4
 
4
5
  let client;
5
6
  class Message {
@@ -7,7 +8,7 @@ class Message {
7
8
  this.id = data.id;
8
9
  this.content = data.content;
9
10
  this.messageType = data.message_type || "text";
10
- this.fileUrl = data.file_url ? 'https://api.beniocord.site' + data.file_url : undefined;
11
+ this.fileUrl = formatUrl(data.file_url);
11
12
  this.fileName = data.file_name;
12
13
  this.fileSize = data.file_size;
13
14
  this.replyTo = data.reply_to;
@@ -1,9 +1,11 @@
1
+ const { formatUrl } = require("../helpers");
2
+
1
3
  class User {
2
4
  constructor(data, client) {
3
5
  this.id = data.id;
4
6
  this.username = data.username;
5
7
  this.displayName = data.display_name;
6
- this.avatarUrl = data.avatar_url ? 'https://api.beniocord.site' + data.avatar_url : undefined;
8
+ this.avatarUrl = formatUrl(data.avatar_url);
7
9
  this.status = data.status || "offline";
8
10
  this.emblems = data.emblems || [];
9
11
  this.lastSeen = data.last_seen;
@@ -1,6 +1,5 @@
1
1
  /**
2
- * MessageEmbed - Criar embeds com validação ultra rigorosa
3
- * Mesmas validações do backend para evitar rejeições
2
+ * MessageEmbed - Criar embeds
4
3
  * @class
5
4
  */
6
5
  class MessageEmbed {
@@ -483,7 +482,6 @@ class MessageEmbed {
483
482
  }
484
483
  }
485
484
 
486
- // Cores predefinidas para facilitar o uso
487
485
  MessageEmbed.Colors = {
488
486
  DEFAULT: '#5865F2',
489
487
  BLUE: '#3498DB',