beniocord.js 2.0.3 → 2.0.5

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
@@ -19,6 +19,10 @@ class ClientError extends Error {
19
19
  }
20
20
  }
21
21
 
22
+ let global = {
23
+ token: "",
24
+ apiUrl: "https://api-bots.beniocord.site"
25
+ };
22
26
  /**
23
27
  * @fires Client#ready
24
28
  * @fires Client#messageCreate
@@ -36,8 +40,9 @@ class Client extends EventEmitter {
36
40
  throw new ClientError("Valid token is required", "INVALID_TOKEN");
37
41
  }
38
42
 
39
- this.token = token.trim();
40
- this.apiUrl = "https://api-bots.beniocord.site";
43
+ // this.token = token.trim();
44
+ global.token = token.trim();
45
+ // this.apiUrl = "https://api-bots.beniocord.site";
41
46
  this.socket = null;
42
47
  this.user = null;
43
48
  this.isConnected = false;
@@ -64,12 +69,13 @@ class Client extends EventEmitter {
64
69
  this._sentMessages = new Set();
65
70
 
66
71
  this._axios = axios.create({
67
- baseURL: this.apiUrl,
72
+ baseURL: global.apiUrl,
68
73
  timeout: this.config.requestTimeout,
69
74
  headers: {
70
- 'Authorization': `Bearer ${this.token}`,
75
+ 'Authorization': `Bearer ${global.token}`,
76
+ 'x-bot-token': global.token,
71
77
  'Content-Type': 'application/json',
72
- 'Origin': this.apiUrl
78
+ 'Origin': global.apiUrl
73
79
  }
74
80
  });
75
81
 
@@ -173,9 +179,9 @@ class Client extends EventEmitter {
173
179
  ));
174
180
  }, this.config.connectionTimeout);
175
181
 
176
- this.socket = io(this.apiUrl, {
177
- auth: { token: this.token },
178
- extraHeaders: { 'Origin': this.apiUrl },
182
+ this.socket = io(global.apiUrl, {
183
+ auth: { token: global.token },
184
+ extraHeaders: { 'Origin': global.apiUrl },
179
185
  timeout: 5000,
180
186
  reconnection: true,
181
187
  reconnectionDelay: this.config.reconnectionDelay,
@@ -188,11 +194,13 @@ class Client extends EventEmitter {
188
194
  this.isConnected = true;
189
195
  this.retryCount = 0;
190
196
  this._setupSocketHandlers();
197
+ this._startHeartbeat(); // Inicia o heartbeat
191
198
  resolve();
192
199
  });
193
200
 
194
201
  this.socket.on("disconnect", (reason) => {
195
202
  this.isConnected = false;
203
+ this._stopHeartbeat(); // Para o heartbeat
196
204
  this.emit("disconnect", reason);
197
205
 
198
206
  if (reason === "io server disconnect") {
@@ -233,6 +241,7 @@ class Client extends EventEmitter {
233
241
 
234
242
  this.socket.on("reconnect", (attemptNumber) => {
235
243
  this.isConnected = true;
244
+ this._startHeartbeat(); // Reinicia o heartbeat após reconexão
236
245
  this.emit("reconnect", attemptNumber);
237
246
  });
238
247
 
@@ -241,6 +250,7 @@ class Client extends EventEmitter {
241
250
  });
242
251
 
243
252
  this.socket.on("reconnect_failed", () => {
253
+ this._stopHeartbeat();
244
254
  this.emit("error", new ClientError(
245
255
  "Failed to reconnect after maximum attempts",
246
256
  "RECONNECT_FAILED"
@@ -249,6 +259,88 @@ class Client extends EventEmitter {
249
259
  });
250
260
  }
251
261
 
262
+ /**
263
+ * Inicia o sistema de heartbeat
264
+ * @private
265
+ */
266
+ _startHeartbeat() {
267
+ // Para qualquer heartbeat existente
268
+ this._stopHeartbeat();
269
+
270
+ // Intervalo de 30 segundos (mesmo do frontend)
271
+ const HEARTBEAT_INTERVAL = 30000;
272
+
273
+ this.heartbeatInterval = setInterval(() => {
274
+ if (this.socket && this.isConnected) {
275
+ // Envia heartbeat simples para bots
276
+ // Bots não precisam de isPageVisible/isAppFocused
277
+ this.socket.emit("presence:heartbeat", {
278
+ status: this.status || "online",
279
+ clientType: "bot" // Identifica como bot
280
+ });
281
+ }
282
+ }, HEARTBEAT_INTERVAL);
283
+
284
+ // Envia primeiro heartbeat imediatamente
285
+ if (this.socket && this.isConnected) {
286
+ this.socket.emit("presence:heartbeat", {
287
+ status: this.status || "online",
288
+ clientType: "bot"
289
+ });
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Para o sistema de heartbeat
295
+ * @private
296
+ */
297
+ _stopHeartbeat() {
298
+ if (this.heartbeatInterval) {
299
+ clearInterval(this.heartbeatInterval);
300
+ this.heartbeatInterval = null;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Define o status do bot (se o servidor suportar)
306
+ * @param {string} status - Status: "online", "away", "dnd", "offline"
307
+ */
308
+ setStatus(status) {
309
+ if (!["online", "away", "dnd", "offline"].includes(status)) {
310
+ throw new ClientError("Invalid status", "INVALID_STATUS");
311
+ }
312
+
313
+ this.status = status;
314
+
315
+ if (this.socket && this.isConnected) {
316
+ this.socket.emit("status:update", { status });
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Desconecta o cliente e limpa recursos
322
+ */
323
+ disconnect() {
324
+ this._stopHeartbeat();
325
+
326
+ if (this.socket) {
327
+ // Notifica o servidor antes de desconectar
328
+ if (this.isConnected) {
329
+ this.socket.emit("presence:update", {
330
+ isPageVisible: false,
331
+ isAppFocused: false,
332
+ status: "offline",
333
+ clientType: "bot"
334
+ });
335
+ }
336
+
337
+ this.socket.disconnect();
338
+ this.socket = null;
339
+ }
340
+
341
+ this.isConnected = false;
342
+ }
343
+
252
344
  _setupSocketHandlers() {
253
345
  this.socket.on("message:new", async (data) => {
254
346
  try {
@@ -295,12 +387,34 @@ class Client extends EventEmitter {
295
387
  this.emit('presenceUpdate', data);
296
388
  });
297
389
 
298
- this.socket.on('member:join', (data) => {
390
+ this.socket.on('member:join', async (data) => {
391
+ const member = await this.fetchUser(data.memberId).catch(() => null);
392
+ const channel = await this.fetchChannel(data.channelId).catch(() => null);
393
+
394
+ if (data.addedBy) {
395
+ data.addedBy = new User(data.addedBy, this);
396
+ }
397
+
398
+ data.member = member;
399
+ data.channel = channel;
400
+
299
401
  this.emit('memberJoin', data);
402
+ console.log('join', data);
300
403
  });
301
404
 
302
- this.socket.on('member:leave', (data) => {
405
+ this.socket.on('member:leave', async (data) => {
406
+ const member = await this.fetchUser(data.memberId).catch(() => null);
407
+ const channel = await this.fetchChannel(data.channelId).catch(() => null);
408
+
409
+ if (data.removedBy) {
410
+ data.removedBy = new User(data.removedBy, this);
411
+ }
412
+
413
+ data.member = member;
414
+ data.channel = channel;
415
+
303
416
  this.emit('memberLeave', data);
417
+ console.log('leave', data);
304
418
  });
305
419
 
306
420
  this.socket.on('channel:update', (data) => {
@@ -823,8 +937,8 @@ class Client extends EventEmitter {
823
937
  /**
824
938
  * @param {string} id
825
939
  */
826
- async fetchChannel(id) {
827
- if (this.cache.channels.has(id)) {
940
+ async fetchChannel(id, force = false) {
941
+ if (!force && this.cache.channels.has(id)) {
828
942
  return this.cache.channels.get(id);
829
943
  }
830
944
 
@@ -834,7 +948,9 @@ class Client extends EventEmitter {
834
948
  this.cache.channels.set(channel.id, channel);
835
949
  return channel;
836
950
  } catch (error) {
837
- throw error instanceof ClientError ? error : new ClientError(error.message, "FETCH_CHANNEL_ERROR");
951
+ throw error instanceof ClientError
952
+ ? error
953
+ : new ClientError(error.message, "FETCH_CHANNEL_ERROR");
838
954
  }
839
955
  }
840
956
 
@@ -965,18 +1081,28 @@ class Client extends EventEmitter {
965
1081
  /**
966
1082
  * @param {string} id
967
1083
  */
968
- async fetchUser(id) {
1084
+ async fetchUser(id, force = false) {
1085
+ if (!force && this.cache.users.has(id)) {
1086
+ return this.cache.users.get(id);
1087
+ }
1088
+
969
1089
  try {
970
1090
  const res = await this._axios.get(`/api/users/${id}`);
971
1091
  const user = new User(res.data, this);
972
1092
  this.cache.users.set(user.id, user);
973
1093
  return user;
974
1094
  } catch (error) {
975
- throw error instanceof ClientError ? error : new ClientError(error.message, "FETCH_USER_ERROR");
1095
+ throw error instanceof ClientError
1096
+ ? error
1097
+ : new ClientError(error.message, "FETCH_USER_ERROR");
976
1098
  }
977
1099
  }
978
1100
 
979
- async fetchMe() {
1101
+ async fetchMe(force = false) {
1102
+ if (!force && this.user) {
1103
+ return this.user;
1104
+ }
1105
+
980
1106
  try {
981
1107
  const res = await this._axios.get('/api/users/me');
982
1108
  const user = new User(res.data, this);
@@ -984,7 +1110,9 @@ class Client extends EventEmitter {
984
1110
  this.user = user;
985
1111
  return user;
986
1112
  } catch (error) {
987
- throw error instanceof ClientError ? error : new ClientError(error.message, "FETCH_ME_ERROR");
1113
+ throw error instanceof ClientError
1114
+ ? error
1115
+ : new ClientError(error.message, "FETCH_ME_ERROR");
988
1116
  }
989
1117
  }
990
1118
 
@@ -1004,14 +1132,20 @@ class Client extends EventEmitter {
1004
1132
  /**
1005
1133
  * @param {string} id
1006
1134
  */
1007
- async fetchEmoji(id) {
1135
+ async fetchEmoji(id, force = false) {
1136
+ if (!force && this.cache.emojis.has(id)) {
1137
+ return this.cache.emojis.get(id);
1138
+ }
1139
+
1008
1140
  try {
1009
1141
  const res = await this._axios.get(`/api/emojis/${id}`);
1010
- const emoji = new Emoji(res.data, this.apiUrl);
1142
+ const emoji = new Emoji(res.data);
1011
1143
  this.cache.emojis.set(emoji.id, emoji);
1012
1144
  return emoji;
1013
1145
  } catch (error) {
1014
- throw error instanceof ClientError ? error : new ClientError(error.message, "FETCH_EMOJI_ERROR");
1146
+ throw error instanceof ClientError
1147
+ ? error
1148
+ : new ClientError(error.message, "FETCH_EMOJI_ERROR");
1015
1149
  }
1016
1150
  }
1017
1151
 
@@ -1029,7 +1163,7 @@ class Client extends EventEmitter {
1029
1163
 
1030
1164
  const res = await this._axios.get(endpoint, { params });
1031
1165
  const emojis = res.data.map(e => {
1032
- const emoji = new Emoji(e, this.apiUrl);
1166
+ const emoji = new Emoji(e);
1033
1167
  this.cache.emojis.set(emoji.id, emoji);
1034
1168
  return emoji;
1035
1169
  });
@@ -1043,13 +1177,19 @@ class Client extends EventEmitter {
1043
1177
  /**
1044
1178
  * @param {string} id
1045
1179
  */
1046
- async fetchSticker(id) {
1180
+ async fetchSticker(id, force = false) {
1181
+ if (!force && this.cache.stickers.has(id)) {
1182
+ return this.cache.stickers.get(id);
1183
+ }
1184
+
1047
1185
  try {
1048
1186
  const res = await this._axios.get(`/api/stickers/${id}`);
1049
1187
  this.cache.stickers.set(res.data.id, res.data);
1050
1188
  return res.data;
1051
1189
  } catch (error) {
1052
- throw error instanceof ClientError ? error : new ClientError(error.message, "FETCH_STICKER_ERROR");
1190
+ throw error instanceof ClientError
1191
+ ? error
1192
+ : new ClientError(error.message, "FETCH_STICKER_ERROR");
1053
1193
  }
1054
1194
  }
1055
1195
 
@@ -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.3",
7
+ "version": "2.0.5",
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,8 +1,10 @@
1
+ const { formatUrl } = require('../helpers');
1
2
  const MessageCollector = require('./MessageCollector');
2
3
 
4
+ let client;
3
5
  class Channel {
4
- constructor(data, client) {
5
- this.client = client;
6
+ constructor(data, clientInstance) {
7
+ client = clientInstance;
6
8
 
7
9
  this.id = data.id;
8
10
  this.name = data.name;
@@ -10,26 +12,26 @@ class Channel {
10
12
  this.type = data.type || "text";
11
13
  this.isPrivate = data.is_private;
12
14
  this.isDm = data.is_dm;
13
- this.iconUrl = data.icon_url ? client.apiUrl + data.icon_url : undefined;
15
+ this.iconUrl = formatUrl(data.icon_url);
14
16
  this.createdBy = data.created_by;
15
17
  this.createdAt = data.created_at;
16
18
  this.updatedAt = data.updated_at;
17
19
  }
18
20
 
19
21
  async send(content, opts = {}) {
20
- return this.client.sendMessage(this.id, content, opts);
22
+ return client.sendMessage(this.id, content, opts);
21
23
  }
22
24
 
23
25
  startTyping() {
24
- return this.client.startTyping(this.id);
26
+ return client.startTyping(this.id);
25
27
  }
26
28
 
27
29
  stopTyping() {
28
- return this.client.stopTyping(this.id);
30
+ return client.stopTyping(this.id);
29
31
  }
30
32
 
31
33
  createMessageCollector(options = {}) {
32
- return new MessageCollector(this, options);
34
+ return new MessageCollector(this, options, client);
33
35
  }
34
36
 
35
37
  }
@@ -1,9 +1,11 @@
1
+ const { formatUrl } = require("../helpers");
2
+
1
3
  class Emoji {
2
- constructor(data, api_url) {
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 ? api_url + 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,12 +1,14 @@
1
1
  const User = require("./User");
2
2
  const Channel = require("./Channel");
3
+ const { formatUrl } = require('../helpers/index');
3
4
 
5
+ let client;
4
6
  class Message {
5
- constructor(data, client) {
7
+ constructor(data, clientInstance) {
6
8
  this.id = data.id;
7
9
  this.content = data.content;
8
10
  this.messageType = data.message_type || "text";
9
- this.fileUrl = data.file_url ? client.apiUrl + data.file_url : undefined;
11
+ this.fileUrl = formatUrl(data.file_url);
10
12
  this.fileName = data.file_name;
11
13
  this.fileSize = data.file_size;
12
14
  this.replyTo = data.reply_to;
@@ -25,24 +27,25 @@ class Message {
25
27
  }
26
28
 
27
29
  // this.author = data.user ? new User(data.user, this) : null;
28
- this.author = data.user ? new User(data.user, client) : null;
29
- this.channel = data.channel ? new Channel(data.channel, client) : null;
30
+ this.author = data.user ? new User(data.user, clientInstance) : null;
31
+ this.channel = data.channel ? new Channel(data.channel, clientInstance) : null;
30
32
  // this.member = { user: this.author }
31
- this.client = client;
33
+ client = clientInstance;
32
34
  }
33
35
 
34
- async reply(content) {
35
- return this.client.sendMessage(this.channel.id, content, {
36
+ async reply(content, opts = {}) {
37
+ return client.sendMessage(this.channel.id, content, {
36
38
  replyTo: this.id,
39
+ ...opts
37
40
  });
38
41
  }
39
42
 
40
43
  async edit(content) {
41
- return this.client.editMessage(this.id, content);
44
+ return client.editMessage(this.id, content);
42
45
  }
43
46
 
44
47
  async delete() {
45
- return this.client.deleteMessage(this.id);
48
+ return client.deleteMessage(this.id);
46
49
  }
47
50
  }
48
51
 
@@ -1,11 +1,12 @@
1
1
  const EventEmitter = require('events');
2
2
 
3
3
  class MessageCollector extends EventEmitter {
4
- constructor(channel, options = {}) {
4
+ constructor(channel, options = {}, client) {
5
5
  super();
6
6
 
7
7
  this.channel = channel;
8
- this.client = channel.client;
8
+ this.client = client;
9
+ // this.client = channel.client;
9
10
  this.filter = options.filter || (() => true);
10
11
  this.time = options.time || 60000;
11
12
  this.max = options.max || Infinity;
@@ -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 ? client.apiUrl + 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',