UNPKG

@elara-services/leveling

Version:

A package for XP/Leveling on Discord.

411 lines (410 loc) 16 kB
"use strict"; var _Leveling_listening, _Leveling_events; Object.defineProperty(exports, "__esModule", { value: true }); exports.Leveling = void 0; const tslib_1 = require("tslib"); const utils_1 = require("@elara-services/utils"); const webhooks_1 = require("@elara-services/webhooks"); const events_1 = require("events"); const services_1 = require("./services"); const api_1 = require("./services/api"); const utils_2 = require("./utils"); tslib_1.__exportStar(require("./utils"), exports); const cached = new Set(); class Leveling extends services_1.Database { constructor(client, mongodbURI, dbName = "Leveling") { super(mongodbURI, client, dbName); this.client = client; /** * The manager to configure the leveling data () */ this.api = new api_1.API(this); /** * A boolean to make sure the package only listens to the events ONCE */ _Leveling_listening.set(this, false); _Leveling_events.set(this, new events_1.EventEmitter()); } webhook(guild) { return new webhooks_1.GuildWebhook(guild); } /** * Emits when the cooldown is added for the user */ onCooldown(listener) { return tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").on("cooldown", listener); } /** * Emits when someone levels up in a server. */ onLevelUp(listener) { return tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").on("level", listener); } async send(db, channel, channelId, options) { if (channel.isDMBased()) { return; } if (!channel.isTextBased() || !channel.guild) { return; } if (db.toggles.useWebhook && channel.permissionsFor(channel.client.user.id)?.has(536870912n)) { options.webhook = { name: db.webhook.name || channel.client.user.displayName, icon: db.webhook.image || channel.client.user.displayAvatarURL({ forceStatic: true, extension: "png", }), }; return this.webhook(channel.guild) .send(channelId, options, false, false) .catch(utils_1.error); } return channel.send(options).catch(utils_1.error); } /** * Emits when someone gains xp in a server. */ onXP(listener) { return tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").on("xp", listener); } /** * Use this to start listening for events and automatically handle xp/leveling */ async start() { if (tslib_1.__classPrivateFieldGet(this, _Leveling_listening, "f")) { return true; } tslib_1.__classPrivateFieldSet(this, _Leveling_listening, true, "f"); const intents = (0, utils_2.getClientIntents)(this.client); if ((0, utils_1.hasBit)(intents, 512 /* Guild Messages */)) { this.client.on("messageCreate", (m) => void this.messageCreate(m)); } if ((0, utils_1.hasBit)(intents, 2 /* Guild Members */)) { this.client.on("guildMemberRemove", (m) => void this.guildMemberRemove(m)); } if ((0, utils_1.hasBit)(intents, 128 /* Guild Voice States */)) { this.client.on("voiceStateUpdate", (o, n) => void this.voiceStateUpdate(o, n)); } } async voiceStateUpdate(old, voice) { if (!old || !voice || !voice.guild || !voice.guild.available || !voice.member || voice.member.user.bot) { return; } // @ts-ignore const db = await this.getSettings(voice.guild.id); if (!db || !db.enabled || !db.toggles.voice) { return; } if (utils_1.is.array(db.ignore.channels) && db.ignore.channels.some((c) => [voice.channelId, voice.channel?.parentId].includes(c))) { return; // Ignore the user / XP earned in this channel. } if (utils_1.is.array(db.ignore.users) && db.ignore.users.includes(voice.member.id)) { return; // Ignore if the user is ignored. } if (utils_1.is.array(db.ignore.roles) && voice.member.roles.cache.hasAny(...db.ignore.roles)) { return; // Ignore if the user has any of the ignore roles. } if (db.toggles.voice.shouldBeUnmuted === true && [voice.deaf, voice.mute].includes(true)) { return; } // @ts-ignore const user = await this.getUser(voice.member.user.id, voice.guild.id); if (!user || user.toggles.locked) { return; } const now = Date.now(); let [voiceDuration, voiceMinutes] = [0, 0]; if (user.voice.duration) { voiceMinutes = parseInt(((now - user.voice.duration) / 60000).toFixed(0)); voiceDuration = parseInt(((now - user.voice.duration) / 1000).toFixed(0)); } const xpToGive = Math.floor(Math.min(voiceDuration * user.voice.multiplier, 20 * 25 * 3 * user.voice.multiplier)); if (!old.channelId && voice.channelId) { user.voice.multiplier = (0, utils_2.getVoiceMultiplier)(voice); user.voice.duration = now; } if (old.channelId && voice.channelId) { user.voice.multiplier = (0, utils_2.getVoiceMultiplier)(voice); } if (old.channelId && !voice.channelId) { if (db.toggles.weekly.track && utils_1.is.number(voiceMinutes)) { // @ts-ignore await this.weekly.add(voice.member.guild.id, { stats: { voice: voiceMinutes }, users: [{ userId: voice.member.id, voice: voiceMinutes }], }); } if (utils_1.is.number(xpToGive)) { await this.handleLevelups(voice.member, voice.channel ?? old.channel, xpToGive, // @ts-ignore db, voiceMinutes); } user.voice.duration = 0; user.voice.multiplier = 0; } await user.save().catch(() => null); } async guildMemberRemove(m) { // @ts-ignore const db = await this.getSettings(m.guild.id); if (!db || !db.enabled || !db.toggles.resetOnLeave) { return; } await this.dbs.users .deleteOne({ userId: m.user.id, guildId: m.guild.id }) .catch(() => null); return; } async messageCreate(message) { if (!message.inGuild() || !message.guild.available) { return; } let member = message.member; let author = message.author; // @ts-ignore const db = await this.getSettings(message.guildId); if (!db || !db.enabled) { return; } if (author.bot) { if (!message.interaction) { return; } if (!db.toggles.earnXPOnSlashCommands) { return; } author = message.interaction.user; member = await utils_1.discord.member(message.guild, author.id, true); } if (!member) { return; } if (utils_1.is.array(db.ignore.channels) && db.ignore.channels.some((c) => [message.channelId, message.channel?.parentId].includes(c))) { return; // Ignore the user / XP earned in this channel. } if (utils_1.is.array(db.ignore.users) && db.ignore.users.includes(author.id)) { return; // Ignore if the user is ignored. } if (utils_1.is.array(db.ignore.roles) && member?.roles.cache.hasAny(...db.ignore.roles)) { return; // Ignore if the user has any of the ignore roles. } let xp = (0, utils_2.random)(Math.floor(db.xp.min || 1), Math.floor(db.xp.max || 8)); const multipliers = []; if (utils_1.is.array(db.multipliers)) { for (const m of db.multipliers) { if (member.roles.cache.hasAny(...m.roles)) { multipliers.push(m.multiplier); } if (m.channels.includes(message.channelId) || m.channels.includes(message.channel?.parentId || "")) { multipliers.push(m.multiplier); } if (!utils_1.is.array(m.roles) && !utils_1.is.array(m.channels)) { multipliers.push(m.multiplier); } } } const multi = Math.max(...multipliers); if (multi > 0) { xp *= multi; } // @ts-ignore let user = await this.getUser(author.id, message.guildId); if (!user || user.toggles.locked === true) { return; } if (db.toggles.weekly.track === true) { // @ts-ignore await this.weekly.add(message.guildId, { stats: { messages: 1 }, users: [{ messages: 1, userId: author.id }], }); } // @ts-ignore user.stats = (0, utils_2.incUserStat)(user, "messages"); const find = user.cooldowns.find((c) => c.name === "xp"); let cool = utils_1.get.secs(db.cooldown || 60); if (utils_1.is.array(db.cooldowns)) { const hasCustom = db.cooldowns.find((c) => member?.roles.cache.hasAny(...c.roles)); if (hasCustom) { cool = utils_1.get.secs(hasCustom.seconds || 60); } } if (find) { const time = Date.now() - find.date; if (time < cool) { tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").emit("cooldown", message, find); await user.save().catch(() => null); return; } find.cooldown = cool; find.date = Date.now() + cool; } else { user.cooldowns.push({ name: "xp", date: Date.now() + cool, cooldown: cool, }); } user = await user.save().catch(() => null); if (!user) { return; } // @ts-ignore return this.handleLevelups(member, message.channel, xp, db); } async sendOptions(user, db, member, options, channel) { const r = await this.getOptions(options, user, member); if (!r) { return; } const users = [member.id]; if (!db.announce.channel.ping || !user.toggles.pings) { users.length = 0; } return this.send(db, channel, channel.id, { ...r, // @ts-ignore allowedMentions: { users }, allowed_mentions: { users }, }); } async getOptions(options, user, member) { if (!options.content && !options.embeds?.length) { options.content = `Congrats ${utils_1.p.user.mention}, you leveled up to **Level ${user.level}**!`; } return await (0, utils_2.parser)(options, { level: `${user.level}`, xp: `${user.xp}`, background: user.background, }, { member, guild: member.guild, user: member.user }); } /** * This is only for internal use! * */ async handleLevelups(member, currentChannel, xp, db, voiceMinutes, isLevelup) { if (!db) { // @ts-ignore db = await this.getSettings(member.guild.id); } if (!db || !db.enabled) { return null; } let roles = [...member.roles.cache.keys()]; let level = false; if (utils_1.is.boolean(isLevelup)) { level = isLevelup; } else { level = await this.api.users.appendXP(member.id, member.guild.id, xp, voiceMinutes, true); } const profile = await this.api.users.get(member.id, member.guild.id); if (!profile.status) { return; } let find; if (utils_1.is.array(db.levels)) { for (const level of db.levels.sort((a, b) => b.level - a.level)) { if (profile.data.level === level.level) { find = level; break; } if (profile.data.level >= level.level) { find = level; break; } } } const allLevelRoles = db.levels .filter((c) => c.roles.add.length) .flatMap((c) => c.roles.add); if (level === true) { const channel = member.guild.channels.resolve(db.announce.channel.channel) || currentChannel; tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").emit("xp", member, profile.data, db.toJSON(), xp); tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").emit("level", member, profile.data, db.toJSON(), profile.data.level, channel); if (db.toggles.weekly.track) { // @ts-ignore await this.weekly.add(member.guild.id, { stats: { xp }, users: [{ level: 1, userId: member.id, xp }], }); } if (find) { if (member.guild.members.me?.permissions.has(268435456n)) { roles = roles.filter((c) => !find?.roles.remove.includes(c)); if (!db.toggles.stackRoles) { roles = roles.filter((c) => !allLevelRoles.includes(c)); } roles.push(...find.roles.add); await member.edit({ roles }).catch(() => null); } } if (db.toggles.onlyRegisteredLevels === true && !find) { return; } if (cached.has(`${profile.data.level}:${member.id}`)) { return; } cached.add(`${profile.data.level}:${member.id}`); if (db.announce.channel.enabled) { if (channel && "send" in channel) { await this.sendOptions(profile.data, db, member, db.announce.channel.options, channel); } } if (db.announce.dm.enabled && profile.data.toggles.dms === true) { await member .send({ ...(await this.getOptions(db.announce.dm.options, profile.data, member)), components: [ { type: 1, components: [ { type: 2, style: 2, label: `From: ${member.guild.name}`, custom_id: "...", disabled: true, }, ], }, ], allowedMentions: profile.data.toggles.pings === true ? { parse: [], } : undefined, }) .catch(() => null); } } else { tslib_1.__classPrivateFieldGet(this, _Leveling_events, "f").emit("xp", member, profile.data, db.toJSON(), xp); if (db.toggles.weekly.track) { // @ts-ignore await this.weekly.add(member.guild.id, { stats: { xp }, users: [{ userId: member.id, xp }], }); } } } } exports.Leveling = Leveling; _Leveling_listening = new WeakMap(), _Leveling_events = new WeakMap();