apexify.js 2.3.1 → 2.3.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.
@@ -0,0 +1,644 @@
1
+ const EventEmitter = require('events');
2
+ const { updateData, find, searchMany, remove } = require('../../../database/utils');
3
+ const { levelCard, leaderBoard, xpRank } = require('../../../canvas/themes/levels-card');
4
+ const localArray = require('./cLevelsArray.json');
5
+
6
+ class LevelingSystem extends EventEmitter {
7
+ constructor(config) {
8
+ super();
9
+
10
+ this.configCollection = 'configCollection'; // Replace with your actual collection name
11
+ this.levelsCollection = 'levelsCollection'; // Replace with your actual collection name
12
+ this.XpCount = config?.XpCount || 5;
13
+ this.rate = config?.rate || 2;
14
+ this.channelId = config?.channelId || null;
15
+ this.guildId = config?.guildId || null;
16
+ this.levelsArray = config?.levelsArray || localArray;
17
+ this.levelingMessage = config?.levelUpMessage || 'Congrates {user} you level up to level {level}.'
18
+
19
+ }
20
+
21
+ async setupConfig() {
22
+ try {
23
+
24
+ if (!this.guildId || !this.channelId) {
25
+ throw new Error('Error: Please provide guildId and channelId');
26
+ }
27
+
28
+ if (!Array.isArray(this.levelsArray)) {
29
+ throw new Error('Error: levelsArray must be an array');
30
+ }
31
+
32
+ for (const levelObj of this.levelsArray) {
33
+ if (typeof levelObj !== 'object' || !('level' in levelObj) || !('xpCount' in levelObj)) {
34
+ throw new Error('Error: Invalid structure in levelsArray. Each object should have "level" and "xpCount" properties.');
35
+ }
36
+ }
37
+
38
+ const configExists = await find(this.configCollection, { guildId: this.guildId });
39
+
40
+ if (!configExists) {
41
+ await updateData(this.configCollection, { guildId: this.guildId },
42
+ {
43
+ $set: {
44
+ XpCount: this.XpCount,
45
+ rate: this.rate,
46
+ channelId: this.channelId,
47
+ guildId: this.guildId,
48
+ levelUpMessage: this.levelingMessage,
49
+ levelsArray: this.levelsArray,
50
+ },
51
+ }
52
+ );
53
+ } else {
54
+ await updateData(this.configCollection, { guildId: this.guildId },
55
+ {
56
+ $set: {
57
+ XpCount: this.XpCount,
58
+ rate: this.rate,
59
+ channelId: this.channelId,
60
+ guildId: this.guildId,
61
+ levelUpMessage: this.levelingMessage,
62
+ levelsArray: this.levelsArray,
63
+ },
64
+ }
65
+ );
66
+ }
67
+ } catch (error) {
68
+ console.error('Error in setupConfig:', error);
69
+ this.emit('error', error.message);
70
+ }
71
+ }
72
+
73
+ async disableLevel(guildId) {
74
+ try {
75
+ if (!guildId) {
76
+ throw new Error('Error: GuildId parameter is required. Usage: disbaleLevel(guildId).');
77
+ }
78
+
79
+ const search = await find(this.configCollection, { guildId });
80
+
81
+ if (!search) {
82
+ return 'No data found for this server';
83
+ }
84
+
85
+ const removeData = await remove(this.configCollection, { guildId });
86
+
87
+ const success = {
88
+ message: `Disabled LevelingSytem for serverId: ${guildId} Successfully.`,
89
+ results: removeData,
90
+ }
91
+
92
+ return success;
93
+ } catch (error) {
94
+ console.log(error.message);
95
+ throw new Error(`Error: An error occurred while disbaling leveling system.`)
96
+ }
97
+ }
98
+
99
+ async xpCard(context, guildId, userId, version = 1) {
100
+ try {
101
+ if (!context) {
102
+ throw new Error('Error: Interactio/Message parameter is required. Usage: xpCard(interaction, guildId, userId). You can replace interaction with message if prefix.');
103
+ }
104
+
105
+ if (!guildId) {
106
+ throw new Error('Error: GuildId parameter is required. Usage: xpCard(guildId, userId).');
107
+ }
108
+
109
+ if (!userId) {
110
+ throw new Error('Error: UserId parameter is required. Usage: xpCard(guildId, userId).');
111
+ }
112
+
113
+ const search = await find(this.levelsCollection, { userId, guildId });
114
+
115
+ if (!search) {
116
+ return 'No data found for this user';
117
+ }
118
+
119
+ const rank = await searchMany([
120
+ { collectionName: this.levelsCollection, displayment: null, filter: { guildId } },
121
+ ]);
122
+
123
+ const sortedLevels = rank.levelsCollection.sort((a, b) => {
124
+ return b.xpCount - a.xpCount;
125
+ });
126
+
127
+ const userIndex = sortedLevels.findIndex(user => user.userId === userId);
128
+ let userRank;
129
+
130
+ if (userIndex !== -1) {
131
+ userRank = userIndex + 1;
132
+ } else {
133
+ userRank = 'unknown';
134
+ }
135
+
136
+
137
+ const nextLevelIndex = search.level + 1;
138
+ let nextLevelData;
139
+
140
+ if (nextLevelIndex < this.levelsArray.length) {
141
+ nextLevelData = this.levelsArray[nextLevelIndex];
142
+ } else {
143
+ nextLevelData = { level: 'max', xpCount: 'max' };
144
+ }
145
+
146
+
147
+ const card = await xpRank(context, guildId, userId, search.level, userRank, search.xpCount, nextLevelData.xpCount, this.levelsArray, version, search.bannerURL)
148
+
149
+ if (!card) return 'An Error occurred while drawing users xpCard.'
150
+
151
+ return card;
152
+ } catch (error) {
153
+ console.log(error.message);
154
+ throw new Error(`An error occurred while sending xpCard.`)
155
+ }
156
+ }
157
+
158
+
159
+ async addXp(userId, guildId) {
160
+ try {
161
+ if (!guildId) {
162
+ throw new Error('Error: GuildId parameter is required. Usage: addXp(userId, guildId).');
163
+ }
164
+
165
+ if (!userId) {
166
+ throw new Error('Error: UserId parameter is required. Usage: addXp(userId, guildId).');
167
+ }
168
+
169
+ const fetchServer = await find(this.configCollection, { guildId: guildId });
170
+
171
+ if (!fetchServer) {
172
+ return `This server has no leveling system`;
173
+ }
174
+
175
+ const userRecord = await find(this.levelsCollection, { userId, guildId: guildId });
176
+
177
+ let updatedUserRecord;
178
+
179
+ if (!userRecord) {
180
+ updatedUserRecord = await updateData(this.levelsCollection, { userId, guildId: guildId }, {
181
+ $set: {
182
+ userId,
183
+ guildId: guildId,
184
+ xpCount: this.XpCount * this.rate,
185
+ level: 0,
186
+ },
187
+ });
188
+ } else {
189
+ updatedUserRecord = await updateData(this.levelsCollection, { userId, guildId: guildId }, {
190
+ $inc: { xpCount: this.XpCount * this.rate },
191
+ });
192
+
193
+ if (updatedUserRecord.data.xpCount >= this.levelsArray[this.levelsArray.length - 1].xpCount) {
194
+
195
+ await updateData(this.levelsCollection, { userId, guildId: guildId }, {
196
+ $set: {
197
+ xpCount: this.levelsArray[this.levelsArray.length - 1].xpCount,
198
+ level: this.levelsArray[this.levelsArray.length - 1].level,
199
+ },
200
+ });
201
+
202
+ return `${userId} reached maximum level`;
203
+ }
204
+
205
+ const levelUp = this.checkLevelUp(updatedUserRecord.data);
206
+ const channelData = fetchServer.channelId;
207
+
208
+ if (levelUp) {
209
+ const results = await updateData(this.levelsCollection, { userId, guildId: guildId }, {
210
+ $set: { level: updatedUserRecord.data.level },
211
+ });
212
+ const levelUpMessage = fetchServer.levelUpMessage;
213
+ let editedMessage = levelUpMessage;
214
+ if (editedMessage.includes('{user}')) {
215
+ editedMessage = editedMessage.replace('{user}', `<@${userId}>`);
216
+ }if (editedMessage.includes('{level}')) {
217
+ editedMessage = editedMessage.replace('{level}', results.data.level);
218
+ }
219
+ this.emit('levelup', editedMessage, userId, results.data.level, results.data.xpCount, channelData, updatedUserRecord.data.guildId);
220
+ }
221
+ }
222
+
223
+ const success = {
224
+ message: 'User gained xp.',
225
+ newXp: updatedUserRecord.data.xpCount,
226
+ newLevel: updatedUserRecord.data.level,
227
+ data: updatedUserRecord.data,
228
+ };
229
+
230
+ return success;
231
+ } catch (error) {
232
+ this.emit('error', error.message);
233
+ throw error;
234
+ }
235
+ }
236
+
237
+ async setUserBanner(guildId, userId, bannerURL) {
238
+ try {
239
+ if (!guildId) {
240
+ throw new Error('Error: GuildId parameter is required. Usage: setUserBanner(guildId, userId, bannerURL).');
241
+ }
242
+
243
+ if (!userId) {
244
+ throw new Error('Error: UserId parameter is required. Usage: setUserBanner(guildId, userId, bannerURL).');
245
+ }
246
+
247
+ if (!bannerURL || !bannerURL.startsWith('http')) {
248
+ throw new Error('Error: BannerURL parameter is required. Usage: setUserBanner(guildId, userId, bannerURL).');
249
+ }
250
+
251
+ const saveData = await updateData(this.levelsCollection, { userId, guildId }, { $set: { bannerURL }})
252
+
253
+ return saveData.data
254
+ } catch (error) {
255
+ console.error(error.message);
256
+ throw new Error(`Error: An error occurred while setting user banner.`)
257
+ }
258
+ }
259
+
260
+ async updateUserLevel(userId, guildId) {
261
+ try {
262
+ const userRecord = await find(this.levelsCollection, { userId, guildId });
263
+ if (!userRecord) {
264
+ console.error(`User record not found for userId: ${userId}, guildId: ${guildId}`);
265
+ return;
266
+ }
267
+
268
+ const userXp = userRecord.xpCount;
269
+
270
+ let updatedLevel = 0;
271
+ for (const levelInfo of this.levelsArray) {
272
+ if (userXp >= levelInfo.xpCount) {
273
+ updatedLevel = levelInfo.level;
274
+ } else {
275
+ break;
276
+ }
277
+ }
278
+
279
+ await updateData(this.levelsCollection, { userId, guildId }, {
280
+ $set: { level: updatedLevel },
281
+ });
282
+
283
+ return updatedLevel;
284
+ } catch (error) {
285
+ console.error("Error updating user level:", error.message);
286
+ throw error;
287
+ }
288
+ }
289
+
290
+
291
+ checkLevelUp(userRecord) {
292
+ let level = this.levelsArray.length - 1;
293
+
294
+ while (level >= 0 && userRecord.xpCount <= this.levelsArray[level].xpCount) {
295
+ level--;
296
+ }
297
+
298
+ if (level < 0) {
299
+ console.error(`Error: Invalid level data for user with xpCount: ${userRecord.xpCount}`);
300
+ this.emit('error', `Invalid level data for user with xpCount: ${userRecord.xpCount}`);
301
+ return false;
302
+ }
303
+
304
+ const nextLevelData = this.levelsArray[level];
305
+
306
+ if (userRecord.level < nextLevelData.level) {
307
+ userRecord.level = nextLevelData.level;
308
+ return true;
309
+ }
310
+
311
+ return false;
312
+ }
313
+
314
+ async userInfo(userId, guildId) {
315
+ try {
316
+ const userRecord = await find(this.levelsCollection, { userId, guildId });
317
+
318
+ if (!userRecord) {
319
+ return 'No data found for this user.';
320
+ }
321
+
322
+ return {
323
+ userId: userRecord.userId,
324
+ XpCount: userRecord.xpCount,
325
+ Level: userRecord.level,
326
+ bannerURL: userRecord.bannerURL || `user doesn't have custom banner url`,
327
+ };
328
+ } catch (error) {
329
+ console.error('Error in userInfo:', error);
330
+ this.emit('error', error.message);
331
+ return null;
332
+ }
333
+ }
334
+
335
+ async checkConfig(guildId) {
336
+ try {
337
+ const configData = await find(this.configCollection, { guildId });
338
+
339
+ if (!configData) {
340
+ return 'No config data found for this server.';
341
+ }
342
+
343
+ return {
344
+ XpCount: configData.XpCount,
345
+ rate: configData.rate,
346
+ channelId: configData.channelId,
347
+ guildId: configData.guildId,
348
+ levelsArray: configData.levelsArray,
349
+ };
350
+ } catch (error) {
351
+ console.error('Error in checkConfig:', error);
352
+ this.emit('error', error.message);
353
+ return null;
354
+ }
355
+ }
356
+
357
+ async serverLeaderBoard(guildId, topUsers = 10) {
358
+ try {
359
+ if (!guildId) {
360
+ throw new Error(`Error: GuildId must be provided in the parameter serverLeaderBoard(guildId).`)
361
+ }
362
+
363
+ if (topUsers === 0 || topUsers < -1 ) {
364
+ throw new Error('Error: serverLeaderBoard cant display 0 members.')
365
+ }
366
+
367
+ const collectionFilters = [
368
+ { collectionName: this.levelsCollection, displayment: topUsers, filter: { guildId } }
369
+ ];
370
+
371
+ const search = await searchMany(collectionFilters);
372
+
373
+ const usersList = search.levelsCollection;
374
+
375
+ if (!usersList) {
376
+ console.log('No config data found for this server.');
377
+ return 'No config data found for this server.';
378
+ }
379
+
380
+ if (!Array.isArray(usersList)) {
381
+ console.error('Error in serverLeaderBoard: usersList is not an array');
382
+ return [];
383
+ }
384
+
385
+ const sortedUsersList = usersList.sort((a, b) => b.xpCount - a.xpCount);
386
+
387
+ let topUsersData;
388
+
389
+ if (topUsers === -1) {
390
+ topUsersData = sortedUsersList;
391
+ } else {
392
+ topUsersData = sortedUsersList.slice(0, topUsers);
393
+ }
394
+
395
+ const transformedData = topUsersData.map(user => ({
396
+ userId: user.userId,
397
+ xpCount: user.xpCount,
398
+ level: user.level,
399
+ guildId: user.guildId
400
+ }));
401
+
402
+ return transformedData;
403
+ } catch (error) {
404
+ console.error('Error in serverLeaderBoard:', error);
405
+ this.emit('error', error.message);
406
+ return [];
407
+ }
408
+ }
409
+
410
+ async topGlobal(topUsers = 10) {
411
+ try {
412
+
413
+ if (topUsers === 0 || topUsers < -1 ) {
414
+ throw new Error('Error: leaderBoard cant display 0 members.')
415
+ }
416
+
417
+ const collectionFilters = [
418
+ { collectionName: this.levelsCollection, displayment: topUsers, filter: {} }
419
+ ];
420
+
421
+ const search = await searchMany(collectionFilters);
422
+ const usersList = search.levelsCollection;
423
+
424
+ if (!usersList) {
425
+ return 'No config data found for this server.';
426
+ }
427
+
428
+ if (!Array.isArray(usersList)) {
429
+ console.error('Error in leaderBoard: usersList is not an array');
430
+ return [];
431
+ }
432
+
433
+ const sortedUsersList = usersList.sort((a, b) => b.xpCount - a.xpCount);
434
+
435
+ let topUsersData;
436
+
437
+ if (topUsers === -1) {
438
+ topUsersData = sortedUsersList;
439
+ } else {
440
+ topUsersData = sortedUsersList.slice(0, topUsers);
441
+ }
442
+
443
+ const transformedData = topUsersData.map(user => ({
444
+ userId: user.userId,
445
+ XpCount: user.xpCount,
446
+ Level: user.level,
447
+ }));
448
+
449
+ return transformedData;
450
+ } catch (error) {
451
+ console.error('Error in leaderBoard:', error);
452
+ this.emit('error', error.message);
453
+ return [];
454
+ }
455
+ }
456
+
457
+ async levelUpCard(message, userId, guildId, options = {}) {
458
+
459
+ if (!message) {
460
+ throw new Error('Error: Please define message paramter in userCard(message, userId)');
461
+ }
462
+
463
+ if (!userId) {
464
+ throw new Error('Error: Please define userId paramter in userCard(message, userId)');
465
+ }
466
+
467
+ try {
468
+ const userRecord = await find(this.levelsCollection, { userId, guildId });
469
+
470
+ if (!userRecord) {
471
+ return 'No data found for this user.';
472
+ }
473
+
474
+ const attachment = await levelCard(message, userRecord, options);
475
+
476
+ return attachment;
477
+ } catch (error) {
478
+ console.error(error.message)
479
+ throw error;
480
+ }
481
+ }
482
+
483
+ async serverLeaderBoardCard(message, guildId, version = 1, colorHex = 'random') {
484
+
485
+ if (!guildId) {
486
+ throw new Error(`Error: Message must be provided in the parameter leaderBoardCard(message, guildId).`)
487
+ }
488
+
489
+ if (!guildId) {
490
+ throw new Error(`Error: GuildId must be provided in the parameter leaderBoardCard(message, guildId).`)
491
+ }
492
+
493
+ try {
494
+ const fetchServer = await find(this.configCollection, { guildId: guildId });
495
+
496
+ if (!fetchServer) {
497
+ return `This server has no leveling system`;
498
+ }
499
+
500
+ const leaderBoardData = await this.serverLeaderBoard(guildId, 5);
501
+ const card = await leaderBoard(message, leaderBoardData, version, colorHex)
502
+
503
+ return card;
504
+ } catch (error) {
505
+ console.error(error.message);
506
+ }
507
+ }
508
+
509
+ async editXp(guildId, userId, xpAmount) {
510
+ try {
511
+ if (!guildId) {
512
+ throw new Error(`Error: GuildId isn't provided. Please use editXp(guildId, userId, xpAmount).`)
513
+ }
514
+
515
+ if (!userId) {
516
+ throw new Error(`Error: UserId isn't provided. Please use editXp(guildId, userId, xpAmount).`)
517
+ }
518
+
519
+ if (!xpAmount || typeof xpAmount !== 'number') {
520
+ throw new Error('Error: Please xpAmount editXp(guidId, userId, xpAmount). xpAmount must be a number.');
521
+ }
522
+
523
+ const search = await find(this.levelsCollection, { guildId, userId });
524
+
525
+ if (!search) {
526
+ return 'This user has no leveling data'
527
+ }
528
+
529
+ const searchServer = await find(this.configCollection, { guildId });
530
+ const channelId = searchServer.channelId;
531
+ const prevLevel = search.level;
532
+
533
+ const update = await updateData(this.levelsCollection, { guildId, userId }, { $inc: { xpCount: xpAmount }})
534
+
535
+
536
+ const levelUp = await this.updateUserLevel(userId, guildId);
537
+
538
+ if (levelUp > prevLevel) {
539
+
540
+ const results = await updateData(this.levelsCollection, { userId, guildId }, {
541
+ $set: { level: updatedUserRecord.data.level },
542
+ });
543
+
544
+ const levelUpMessage = fetchServer.levelUpMessage;
545
+ let editedMessage = levelUpMessage;
546
+ if (editedMessage.includes('{user}')) {
547
+ editedMessage = editedMessage.replace('{user}', `<@${userId}>`);
548
+ }if (editedMessage.includes('{level}')) {
549
+ editedMessage = editedMessage.replace('{level}', results.data.level);
550
+ }
551
+ this.emit('levelup', editedMessage, userId, results.data.level, results.data.xpCount, channelId, updatedUserRecord.data.guildId);
552
+ }
553
+
554
+ const success = {
555
+ message: 'Updated user Xp successfully',
556
+ newXp: update.data.xpCount,
557
+ newLevel: levelUp,
558
+ }
559
+
560
+ return success;
561
+ } catch (error) {
562
+ console.error("Error editing XP:", error.message);
563
+ throw new Error("Failed to update user XP");
564
+ }
565
+ }
566
+
567
+ async removeUser(guildId, userId) {
568
+ try {
569
+ if (!guildId) {
570
+ throw new Error(`Error: GuildId isn't provided. Please use removeUser(guildId, userId).`)
571
+ }
572
+
573
+ if (!userId) {
574
+ throw new Error(`Error: UserId isn't provided. Please use removeUser(guildId, userId).`)
575
+ }
576
+
577
+ const search = await find(this.levelsCollection, { guildId, userId });
578
+
579
+ if (!search) {
580
+ return 'This user has no leveling data'
581
+ }
582
+
583
+ const deleteUser = await remove(this.levelsCollection, { guildId, userId });
584
+
585
+ const success = {
586
+ message: 'User removed successfully',
587
+ results: deleteUser,
588
+ }
589
+
590
+ return success;
591
+ } catch (error) {
592
+ console.error('Error while removing user from data due to:', error.message)
593
+ throw new Error(`Couldn't remove user from data.`)
594
+ }
595
+ }
596
+
597
+ async liveServerLeaderboard(context, guildId, channelId, timer = 10000, version = 1, colorHex) {
598
+ try {
599
+ if (!guildId) {
600
+ throw new Error(`Error: GuildId isn't provided. Please use liveServerLeaderboard(client, guildId, channelId).`);
601
+ }
602
+
603
+ if (!channelId) {
604
+ throw new Error(`Error: ChannelId isn't provided. Please use liveServerLeaderboard(client, guildId, channelId).`);
605
+ }
606
+
607
+ const fetchServer = await find(this.configCollection, { guildId: guildId });
608
+
609
+ if (!fetchServer) {
610
+ return `This server has no leveling system`;
611
+ }
612
+
613
+ const existingConfig = await find('liveLeaderBoard', { guildId, channelId });
614
+
615
+ await updateData('liveLeaderBoard', { guildId, channelId }, { $set: { guildId, channelId, version } });
616
+
617
+ const card = await this.serverLeaderBoardCard(context.client, guildId, version, colorHex);
618
+
619
+ const channel = context.client.channels.cache.get(channelId);
620
+
621
+ if (channel) {
622
+ const liveLeaderboardMessageId = existingConfig?.messageId;
623
+ const existingMessage = liveLeaderboardMessageId ? await channel.messages.fetch(liveLeaderboardMessageId).catch(() => null) : null;
624
+
625
+ if (existingMessage) {
626
+ await existingMessage.edit({ content: '# LIVE SERVER LEADERBOARD.', files: [card] });
627
+ } else {
628
+ const newMessage = await channel.send({ content: '# LIVE SERVER LEADERBOARD.', files: [card] });
629
+ await updateData('liveLeaderBoard', { guildId, channelId }, { $set: { messageId: newMessage.id } });
630
+ }
631
+ } else {
632
+ console.error('Channel not found');
633
+ }
634
+
635
+ setTimeout(() => {
636
+ this.liveServerLeaderboard(context, guildId, channelId, timer, version, colorHex);
637
+ }, timer);
638
+ } catch (error) {
639
+ console.error('Error:', error.message);
640
+ }
641
+ }
642
+ }
643
+
644
+ module.exports = LevelingSystem;