djs-builder 0.7.0 → 0.7.1-8.1
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 +2137 -272
- package/function/dash.js +705 -210
- package/function/function.js +255 -8
- package/function/level.js +337 -10
- package/function/log.js +407 -332
- package/handler/helper.js +46 -25
- package/handler/starter.js +47 -13
- package/package.json +23 -9
- package/views/dashboard.ejs +52 -18
- package/views/giveaways.ejs +1 -0
- package/views/guild.ejs +4 -0
- package/views/levels.ejs +672 -32
- package/views/logs.ejs +624 -0
package/function/level.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { Schema, model } = require("mongoose");
|
|
2
2
|
|
|
3
|
+
////////////////////////////////! � User Level Schema
|
|
3
4
|
const levelSchema = new Schema({
|
|
4
5
|
userId: String,
|
|
5
6
|
guildId: String,
|
|
@@ -7,15 +8,188 @@ const levelSchema = new Schema({
|
|
|
7
8
|
voice: { type: Number, default: 0 },
|
|
8
9
|
totalXP: { type: Number, default: 0 },
|
|
9
10
|
level: { type: Number, default: 0 },
|
|
11
|
+
lastMessageTimestamp: { type: Date, default: null },
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
////////////////////////////////! ⚙️ Leveling Sub-Schema
|
|
15
|
+
const levelingSchema = new Schema({
|
|
16
|
+
type: { type: String, default: "line" },
|
|
17
|
+
amount: { type: Number, default: 100 },
|
|
18
|
+
a: { type: Number, default: 5 },
|
|
19
|
+
b: { type: Number, default: 50 },
|
|
20
|
+
c: { type: Number, default: 100 },
|
|
21
|
+
customLevels: { type: Object, default: {} },
|
|
22
|
+
}, { _id: false });
|
|
23
|
+
|
|
24
|
+
////////////////////////////////! ⚙️ Guild Config Schema
|
|
25
|
+
const guildSchema = new Schema({
|
|
26
|
+
guildId: { type: String, required: true, unique: true },
|
|
27
|
+
initialized: { type: Boolean, default: false },
|
|
28
|
+
|
|
29
|
+
minXP: { type: Number, default: 3 },
|
|
30
|
+
maxXP: { type: Number, default: 8 },
|
|
31
|
+
xpMultiplier: { type: Number, default: 1 },
|
|
32
|
+
maxLevel: { type: Number, default: null },
|
|
33
|
+
cooldown: { type: Number, default: 0 },
|
|
34
|
+
roleReward: { type: Object, default: {} },
|
|
35
|
+
blacklistedChannels: { type: Array, default: [] },
|
|
36
|
+
disabled: { type: Boolean, default: false },
|
|
37
|
+
leveling: { type: levelingSchema, default: () => ({ type: "line", amount: 100, a: 5, b: 50, c: 100, customLevels: {} }) },
|
|
38
|
+
|
|
10
39
|
});
|
|
11
40
|
|
|
12
41
|
const Level = model("Level", levelSchema);
|
|
42
|
+
const levelGuild = model("levelGuild", guildSchema);
|
|
43
|
+
|
|
44
|
+
////////////////////////////////! 🗃️ Cache System
|
|
45
|
+
const configCache = new Map();
|
|
46
|
+
|
|
47
|
+
function clearConfigCache(guildId) {
|
|
48
|
+
if (guildId) {
|
|
49
|
+
configCache.delete(guildId);
|
|
50
|
+
} else {
|
|
51
|
+
configCache.clear();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function getCachedConfig(guildId) {
|
|
56
|
+
if (configCache.has(guildId)) {
|
|
57
|
+
return configCache.get(guildId);
|
|
58
|
+
}
|
|
59
|
+
const config = await levelGuild.findOne({ guildId });
|
|
60
|
+
if (config) configCache.set(guildId, config);
|
|
61
|
+
return config;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
////////////////////////////////! 🧮 Calculate XP needed for level
|
|
65
|
+
function xpNeeded(level, config = {}) {
|
|
66
|
+
const leveling = config.leveling || { type: "line", amount: 100 };
|
|
67
|
+
const type = leveling.type || "line";
|
|
68
|
+
|
|
69
|
+
if (leveling.customLevels && leveling.customLevels[level]) {
|
|
70
|
+
return leveling.customLevels[level];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
switch (type) {
|
|
74
|
+
case "exponential":
|
|
75
|
+
return Math.floor(leveling.amount * Math.pow(2, level));
|
|
76
|
+
case "balanced":
|
|
77
|
+
const a = leveling.a || 5;
|
|
78
|
+
const b = leveling.b || 50;
|
|
79
|
+
const c = leveling.c || 100;
|
|
80
|
+
return Math.floor(a * Math.pow(level, 2) + b * level + c);
|
|
81
|
+
case "custom":
|
|
82
|
+
return leveling.customLevels?.[level] || level * 1000;
|
|
83
|
+
case "line":
|
|
84
|
+
default:
|
|
85
|
+
return (level + 1) * (leveling.amount || 100);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function calculateLevel(totalXP, config = {}) {
|
|
90
|
+
let level = 0;
|
|
91
|
+
const maxLevel = config.maxLevel || null;
|
|
92
|
+
|
|
93
|
+
while (totalXP >= xpNeeded(level, config)) {
|
|
94
|
+
level++;
|
|
95
|
+
if (maxLevel && level >= maxLevel) {
|
|
96
|
+
return maxLevel;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return level;
|
|
100
|
+
}
|
|
13
101
|
|
|
14
102
|
function randomXP(min, max) {
|
|
15
103
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
16
104
|
}
|
|
17
105
|
|
|
106
|
+
////////////////////////////////* 🎮 Main setLevel Function
|
|
107
|
+
|
|
108
|
+
async function setLevel(client, guilds, callbacks = {}) {
|
|
109
|
+
client.levelGuilds = new Set(guilds.map(g => g.id));
|
|
110
|
+
client.levelDashboardGuilds = new Set(guilds.filter(g => g.Dashboard === true).map(g => g.id));
|
|
111
|
+
|
|
112
|
+
for (const guild of guilds) {
|
|
113
|
+
const trueGuild = client.guilds.cache.get(guild.id);
|
|
114
|
+
if (!trueGuild) continue;
|
|
115
|
+
|
|
116
|
+
const existingConfig = await levelGuild.findOne({ guildId: guild.id });
|
|
117
|
+
|
|
118
|
+
const { id, Dashboard, ...guildConfig } = guild;
|
|
119
|
+
|
|
120
|
+
if (!existingConfig) {
|
|
121
|
+
const newConfig = new levelGuild({ guildId: id, initialized: true, ...guildConfig });
|
|
122
|
+
await newConfig.save();
|
|
123
|
+
configCache.set(id, newConfig);
|
|
124
|
+
} else {
|
|
125
|
+
if (guild.Dashboard) {
|
|
126
|
+
await levelGuild.findOneAndUpdate(
|
|
127
|
+
{ guildId: id },
|
|
128
|
+
{ initialized: true },
|
|
129
|
+
{ new: true }
|
|
130
|
+
);
|
|
131
|
+
} else {
|
|
132
|
+
await levelGuild.findOneAndDelete({ guildId: id });
|
|
133
|
+
const newConfig = new levelGuild({ guildId: id, initialized: true, ...guildConfig });
|
|
134
|
+
await newConfig.save();
|
|
135
|
+
}
|
|
136
|
+
clearConfigCache(id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
client.on("messageCreate", async (msg) => {
|
|
141
|
+
if (msg.author.bot || !msg.guild) return;
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if (!client.levelGuilds || !client.levelGuilds.has(msg.guild.id)) return;
|
|
145
|
+
|
|
146
|
+
const guildConfig = await getCachedConfig(msg.guild.id);
|
|
147
|
+
if (!guildConfig) return;
|
|
148
|
+
|
|
149
|
+
const guildOptions = guilds.find(g => g.id === msg.guild.id) || {};
|
|
150
|
+
|
|
151
|
+
const result = await addXP(msg.author.id, msg.guild.id, {
|
|
152
|
+
...guildOptions,
|
|
153
|
+
channelId: msg.channel.id,
|
|
154
|
+
member: msg.member,
|
|
155
|
+
xpType: "text",
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (result.disabled || result.blacklisted || result.onCooldown) return;
|
|
159
|
+
|
|
160
|
+
if (callbacks.onXP) {
|
|
161
|
+
try { await callbacks.onXP(msg, result); } catch (e) { console.error("setLevel onXP error:", e); }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (result.leveledUp && callbacks.onLevelUp) {
|
|
165
|
+
try { await callbacks.onLevelUp(msg, result); } catch (e) { console.error("setLevel onLevelUp error:", e); }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (result.newRole?.length > 0 && callbacks.onRoleReward) {
|
|
169
|
+
for (const role of result.newRole) {
|
|
170
|
+
try { await callbacks.onRoleReward(msg, result, role); } catch (e) { console.error("setLevel onRoleReward error:", e); }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
////////////////////////////////! 🎮 Main addXP Function
|
|
179
|
+
|
|
180
|
+
|
|
18
181
|
async function addXP(userId, guildId, options = {}) {
|
|
182
|
+
////////////////////////////////! 🗃️ Get config from Dashboard or options
|
|
183
|
+
let config = options.Dashboard
|
|
184
|
+
? await getCachedConfig(guildId) || {}
|
|
185
|
+
: options;
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
////////////////////////////////! ❌ Check if disabled
|
|
189
|
+
if (config.disabled) {
|
|
190
|
+
return { disabled: true, newLevel: 0, oldLevel: 0, totalXP: 0, leveledUp: false };
|
|
191
|
+
}
|
|
192
|
+
|
|
19
193
|
let data = await Level.findOne({ userId, guildId });
|
|
20
194
|
if (!data) {
|
|
21
195
|
data = new Level({ userId, guildId });
|
|
@@ -23,12 +197,50 @@ async function addXP(userId, guildId, options = {}) {
|
|
|
23
197
|
|
|
24
198
|
const oldLevel = data.level || 0;
|
|
25
199
|
let leveledUp = false;
|
|
200
|
+
let roleRewardAdded = false;
|
|
201
|
+
let newRole = [];
|
|
202
|
+
|
|
203
|
+
////////////////////////////////! 🚫 Blacklist check
|
|
204
|
+
if (options.channelId && config.blacklistedChannels?.includes(options.channelId)) {
|
|
205
|
+
return {
|
|
206
|
+
newLevel: data.level,
|
|
207
|
+
oldLevel,
|
|
208
|
+
totalXP: data.totalXP,
|
|
209
|
+
leveledUp: false,
|
|
210
|
+
blacklisted: true,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
////////////////////////////////! ⏱️ Cooldown check
|
|
216
|
+
const cooldownDuration = config.cooldown ?? options.cooldown ?? 0;
|
|
217
|
+
|
|
218
|
+
if (cooldownDuration > 0) {
|
|
219
|
+
const now = new Date();
|
|
220
|
+
if (data.lastMessageTimestamp) {
|
|
221
|
+
const timeDiff = (now - data.lastMessageTimestamp) / 1000;
|
|
222
|
+
if (timeDiff < cooldownDuration) {
|
|
223
|
+
return {
|
|
224
|
+
newLevel: data.level,
|
|
225
|
+
oldLevel,
|
|
226
|
+
totalXP: data.totalXP,
|
|
227
|
+
leveledUp: false,
|
|
228
|
+
onCooldown: true,
|
|
229
|
+
remainingCooldown: Math.ceil(cooldownDuration - timeDiff),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
data.lastMessageTimestamp = now;
|
|
234
|
+
}
|
|
26
235
|
|
|
27
236
|
////////////////////////////////! 🎲 XP
|
|
28
|
-
|
|
29
|
-
options.amount_add ??
|
|
237
|
+
let xpToAdd =
|
|
238
|
+
options.amount_add ??
|
|
239
|
+
randomXP(config.minXP || 5, config.maxXP || 12);
|
|
240
|
+
|
|
241
|
+
xpToAdd = Math.floor(xpToAdd * (config.xpMultiplier || 1));
|
|
30
242
|
|
|
31
|
-
data[options.
|
|
243
|
+
data[options.xpType || "text"] += xpToAdd;
|
|
32
244
|
data.totalXP += xpToAdd;
|
|
33
245
|
|
|
34
246
|
////////////////////////////////! 🎯 Level boost
|
|
@@ -37,24 +249,70 @@ async function addXP(userId, guildId, options = {}) {
|
|
|
37
249
|
leveledUp = true;
|
|
38
250
|
}
|
|
39
251
|
|
|
40
|
-
////////////////////////////////!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
252
|
+
////////////////////////////////! 🔋 xp boost
|
|
253
|
+
if (options.totalXP_add) {
|
|
254
|
+
data.totalXP += options.totalXP_add;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (options.text_add) {
|
|
258
|
+
data.text += options.text_add;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (options.voice_add) {
|
|
262
|
+
data.voice += options.voice_add;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
////////////////////////////////! ✅ Calculate correct level
|
|
266
|
+
const newCalculatedLevel = calculateLevel(data.totalXP, config);
|
|
267
|
+
|
|
268
|
+
if (newCalculatedLevel > data.level) {
|
|
269
|
+
if (config.roleReward) {
|
|
270
|
+
const rewardLevels = Object.keys(config.roleReward)
|
|
271
|
+
.map(Number)
|
|
272
|
+
.filter(lvl => lvl === newCalculatedLevel)
|
|
273
|
+
.sort((a, b) => b - a);
|
|
274
|
+
|
|
275
|
+
if (rewardLevels.length > 0) {
|
|
276
|
+
const highestRewardLevel = rewardLevels[0];
|
|
277
|
+
newRole.push({
|
|
278
|
+
level: highestRewardLevel,
|
|
279
|
+
roleId: config.roleReward[highestRewardLevel]
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
data.level = newCalculatedLevel;
|
|
44
284
|
leveledUp = true;
|
|
45
285
|
}
|
|
46
286
|
|
|
47
287
|
await data.save();
|
|
48
288
|
|
|
289
|
+
////////////////////////////////! 🎖️ Auto add roles
|
|
290
|
+
if (options.member && newRole.length > 0) {
|
|
291
|
+
for (const role of newRole) {
|
|
292
|
+
try {
|
|
293
|
+
await options.member.roles.add(role.roleId);
|
|
294
|
+
roleRewardAdded = true;
|
|
295
|
+
} catch (err) {
|
|
296
|
+
roleRewardAdded = "Failed to add role: " + err.message;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
49
301
|
return {
|
|
50
302
|
newLevel: data.level,
|
|
51
303
|
oldLevel,
|
|
52
304
|
totalXP: data.totalXP,
|
|
53
305
|
leveledUp,
|
|
306
|
+
xpAdded: xpToAdd,
|
|
307
|
+
xpForNextLevel: xpNeeded(data.level, config),
|
|
308
|
+
remainingXP: xpNeeded(data.level, config) - data.totalXP,
|
|
309
|
+
roleRewardAdded,
|
|
310
|
+
newRole,
|
|
311
|
+
multiplierUsed: config.xpMultiplier || 1,
|
|
54
312
|
};
|
|
55
313
|
}
|
|
56
314
|
|
|
57
|
-
////////////////////////////////!
|
|
315
|
+
////////////////////////////////! 🧑🤝🧑 User data
|
|
58
316
|
async function UserLevel(userId, guildId) {
|
|
59
317
|
return (
|
|
60
318
|
(await Level.findOne({ userId, guildId })) || {
|
|
@@ -66,11 +324,80 @@ async function UserLevel(userId, guildId) {
|
|
|
66
324
|
);
|
|
67
325
|
}
|
|
68
326
|
|
|
69
|
-
////////////////////////////////! 🏆
|
|
327
|
+
////////////////////////////////! 🏆 Leaderboard
|
|
70
328
|
async function leaderboard(guildId, type = "totalXP", limit = 10) {
|
|
71
329
|
return await Level.find({ guildId })
|
|
72
330
|
.sort({ [type]: -1 })
|
|
73
331
|
.limit(limit);
|
|
74
332
|
}
|
|
75
333
|
|
|
76
|
-
|
|
334
|
+
////////////////////////////////! 🏅 Get user rank
|
|
335
|
+
async function getUserRank(userId, guildId, type = "totalXP") {
|
|
336
|
+
const allUsers = await Level.find({ guildId }).sort({ [type]: -1 });
|
|
337
|
+
const rank = allUsers.findIndex(u => u.userId === userId) + 1;
|
|
338
|
+
return rank || null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
////////////////////////////////! 🗑️ Delete user data
|
|
343
|
+
async function resetUser(userId, guildId) {
|
|
344
|
+
return await Level.findOneAndDelete({ userId, guildId });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
////////////////////////////////! 🔥 Reset all users in a guild
|
|
348
|
+
async function resetGuild(guildId) {
|
|
349
|
+
return await Level.deleteMany({ guildId });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
////////////////////////////////! 📋 Get level info
|
|
353
|
+
function getLevelInfo(level, config = {}) {
|
|
354
|
+
return {
|
|
355
|
+
level,
|
|
356
|
+
xpNeeded: xpNeeded(level, config),
|
|
357
|
+
xpForNext: xpNeeded(level + 1, config),
|
|
358
|
+
xpDifference: xpNeeded(level + 1, config) - xpNeeded(level, config),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
////////////////////////////////! ⚙️ Get Guild Config
|
|
363
|
+
async function getGuildConfig(guildId) {
|
|
364
|
+
return await getCachedConfig(guildId);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
////////////////////////////////! ✏️ Update Guild Config
|
|
368
|
+
async function updateGuildConfig(guildId, updates) {
|
|
369
|
+
const result = await levelGuild.findOneAndUpdate(
|
|
370
|
+
{ guildId },
|
|
371
|
+
{ guildId, ...updates },
|
|
372
|
+
{ upsert: true, new: true }
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
clearConfigCache(guildId);
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
////////////////////////////////! 🗑️ Delete Guild Config
|
|
380
|
+
async function deleteGuildConfig(guildId) {
|
|
381
|
+
const result = await levelGuild.findOneAndDelete({ guildId });
|
|
382
|
+
clearConfigCache(guildId);
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
module.exports = {
|
|
387
|
+
Level,
|
|
388
|
+
levelGuild,
|
|
389
|
+
addXP,
|
|
390
|
+
UserLevel,
|
|
391
|
+
leaderboard,
|
|
392
|
+
getUserRank,
|
|
393
|
+
resetUser,
|
|
394
|
+
resetGuild,
|
|
395
|
+
xpNeeded,
|
|
396
|
+
calculateLevel,
|
|
397
|
+
getLevelInfo,
|
|
398
|
+
getGuildConfig,
|
|
399
|
+
updateGuildConfig,
|
|
400
|
+
deleteGuildConfig,
|
|
401
|
+
clearConfigCache,
|
|
402
|
+
setLevel
|
|
403
|
+
};
|