А почему нет?

This commit is contained in:
JonnyBro 2022-08-10 00:37:10 +05:00
parent 7bbb77937f
commit fe38bc8038
4 changed files with 107 additions and 262 deletions

View file

@ -1,5 +1,5 @@
const { SlashCommandBuilder } = require("discord.js"), const { SlashCommandBuilder } = require("discord.js"),
{ getUserXp } = require("mee6-levels-api"); Mee6Api = require("../../helpers/mee6-api");
const BaseCommand = require("../../base/BaseCommand"); const BaseCommand = require("../../base/BaseCommand");
class ImportMee6 extends BaseCommand { class ImportMee6 extends BaseCommand {
@ -32,14 +32,17 @@ class ImportMee6 extends BaseCommand {
* @param {Object} data * @param {Object} data
*/ */
async execute(client, interaction, data) { async execute(client, interaction, data) {
const level = (await getUserXp(interaction.guildId, interaction.member)).level; await interaction.deferReply();
const level = (await Mee6Api.getUserXp(interaction.guildId, interaction.member)).level;
data.memberData.level = level; data.memberData.level = level;
await data.memberData.save(); await data.memberData.save();
interaction.success("owner/debug:SUCCESS_LEVEL", { interaction.editReply({
content: interaction.translate("owner/debug:SUCCESS_LEVEL", {
username: interaction.member.toString(), username: interaction.member.toString(),
amount: level amount: level
})
}); });
} }
} }

99
helpers/mee6-api.js Normal file
View file

@ -0,0 +1,99 @@
/* eslint-disable no-constant-condition */
// Thanks to mee6-levels-api =)
const fetch = require("node-fetch");
const fetchMee6 = async args => {
const response = await fetch(`https://mee6.xyz/api/plugins/levels/leaderboard/${args}`).then(res => res.json());
if (response.statusCode !== 200) {
if (response.error && response.error.message) throw new Error(`${response.statusCode}: ${response.error.message}`);
else throw new Error(`${response.statusCode}: ${response.statusMessage}`);
}
return response;
};
class Mee6Api {
/**
* Resolves a guild or user to its ID.
* @param {{id:String}|String} guildOrUser Object to resolve to an ID.
* @returns {String} ID of the guild or user.
*/
static getId(guildOrUser) {
if (typeof guildOrUser === "string")
return guildOrUser;
else if (typeof guildOrUser.id === "string")
return guildOrUser.id;
else throw new Error("Invalid Id specified.");
}
/**
* Gets role rewards set for a guild.
* @param {{id:String}|String} guild Guild to get role rewards of.
* @returns {Promise<[Object]>} Role rewards set for this guild.
*/
static async getRoleRewards(guild) {
const guildId = this.getId(guild);
const { role_rewards } = await fetchMee6(`${guildId}?limit=1`);
return role_rewards.sort((a, b) => a.rank - b.rank).map(({ rank, ...rest }) => ({ ...rest, level: rank }));
}
/**
* Get a page of the leaderboard of a guild.
* @param {{id:String}|String} guild Guild to get the leaderboard of.
* @param {Number} limit Limit of users to fetch per page. Maximum 1000.
* @param {Number} page Number of pages to skip.
* @returns {Promise<[Object]>} Leaderboard page.
*/
static async getLeaderboardPage(guild, limit = 1000, page = 0) {
const guildId = this.getId(guild);
const { players } = await fetchMee6(`${guildId}?limit=${limit}&page=${page}`);
return players.map((user, index) => {
const { id, level, username, discriminator, avatar, message_count: messageCount } = user;
const avatarUrl = `https://cdn.discordapp.com/avatars/${id}/${avatar}`;
const [userXp, levelXp, totalXp] = user.detailed_xp;
return {
id, level, username, discriminator, avatarUrl, messageCount,
tag: `${username}#${discriminator}`,
xp: { userXp, levelXp, totalXp },
rank: (limit * page) + index + 1
};
});
}
/**
* Get the leaderboard of a guild.
* @param {{id:String}|String} guild Guild to get the leaderboard of.
* @returns {Promise<[Object]>} Leaderboard of the guild.
*/
static async getLeaderboard(guild) {
const leaderboard = [];
let pageNumber = 0, page;
while (true) {
page = await this.getLeaderboardPage(guild, 1000, pageNumber);
leaderboard.push(...page);
if (page.length < 1000) break;
pageNumber += 1;
}
return leaderboard;
}
/**
* Get the XP of an individual user in a guild.
* @param {{id:String}|String} guild Guild the user is in.
* @param {{id:String}|String} user User to get the XP of.
* @returns {Promise<Object|undefined>} XP information of the user.
*/
static async getUserXp(guild, user) {
const userId = this.getId(user);
let pageNumber = 0, page, userInfo;
while (true) {
page = await this.getLeaderboardPage(guild, 1000, pageNumber);
userInfo = page.find(u => u.id === userId);
if (page.length < 1000 || userInfo) break;
pageNumber += 1;
}
return userInfo;
}
}
module.exports = Mee6Api;

256
package-lock.json generated
View file

@ -33,7 +33,6 @@
"libsodium-wrappers": "^0.7.10", "libsodium-wrappers": "^0.7.10",
"markdown-table": "2.0.0", "markdown-table": "2.0.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"mee6-levels-api": "^1.3.0",
"moment": "^2.26.0", "moment": "^2.26.0",
"mongoose": "^5.13.14", "mongoose": "^5.13.14",
"ms": "^2.1.3", "ms": "^2.1.3",
@ -1497,11 +1496,6 @@
"url": "https://github.com/fb55/domutils?sponsor=1" "url": "https://github.com/fb55/domutils?sponsor=1"
} }
}, },
"node_modules/duplexer3": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
"integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA=="
},
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -3108,112 +3102,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mee6-levels-api": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mee6-levels-api/-/mee6-levels-api-1.3.0.tgz",
"integrity": "sha512-F7JjRPbTHARWzZavnkAJHCzS58C0Fhr4sf2Bae8N5LBay4bBUvNZbajbYrZ3eR08MuIUh7SSx+6+kl6L61BJiQ==",
"dependencies": {
"got": "^10.3.0"
}
},
"node_modules/mee6-levels-api/node_modules/@sindresorhus/is": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz",
"integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
"node_modules/mee6-levels-api/node_modules/cacheable-lookup": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz",
"integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==",
"dependencies": {
"@types/keyv": "^3.1.1",
"keyv": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/mee6-levels-api/node_modules/decompress-response": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz",
"integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==",
"dependencies": {
"mimic-response": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/mee6-levels-api/node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mee6-levels-api/node_modules/got": {
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz",
"integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==",
"dependencies": {
"@sindresorhus/is": "^2.0.0",
"@szmarczak/http-timer": "^4.0.0",
"@types/cacheable-request": "^6.0.1",
"cacheable-lookup": "^2.0.0",
"cacheable-request": "^7.0.1",
"decompress-response": "^5.0.0",
"duplexer3": "^0.1.4",
"get-stream": "^5.0.0",
"lowercase-keys": "^2.0.0",
"mimic-response": "^2.1.0",
"p-cancelable": "^2.0.0",
"p-event": "^4.0.0",
"responselike": "^2.0.0",
"to-readable-stream": "^2.0.0",
"type-fest": "^0.10.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/got?sponsor=1"
}
},
"node_modules/mee6-levels-api/node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mee6-levels-api/node_modules/type-fest": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz",
"integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/memory-pager": { "node_modules/memory-pager": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
@ -3652,39 +3540,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/p-event": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz",
"integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==",
"dependencies": {
"p-timeout": "^3.1.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
"engines": {
"node": ">=4"
}
},
"node_modules/p-timeout": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
"dependencies": {
"p-finally": "^1.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -4506,14 +4361,6 @@
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
"integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="
}, },
"node_modules/to-readable-stream": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz",
"integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==",
"engines": {
"node": ">=8"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -6003,11 +5850,6 @@
"domhandler": "^4.2.0" "domhandler": "^4.2.0"
} }
}, },
"duplexer3": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
"integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA=="
},
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -7259,78 +7101,6 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
}, },
"mee6-levels-api": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mee6-levels-api/-/mee6-levels-api-1.3.0.tgz",
"integrity": "sha512-F7JjRPbTHARWzZavnkAJHCzS58C0Fhr4sf2Bae8N5LBay4bBUvNZbajbYrZ3eR08MuIUh7SSx+6+kl6L61BJiQ==",
"requires": {
"got": "^10.3.0"
},
"dependencies": {
"@sindresorhus/is": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz",
"integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg=="
},
"cacheable-lookup": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz",
"integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==",
"requires": {
"@types/keyv": "^3.1.1",
"keyv": "^4.0.0"
}
},
"decompress-response": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz",
"integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==",
"requires": {
"mimic-response": "^2.0.0"
}
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"requires": {
"pump": "^3.0.0"
}
},
"got": {
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz",
"integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==",
"requires": {
"@sindresorhus/is": "^2.0.0",
"@szmarczak/http-timer": "^4.0.0",
"@types/cacheable-request": "^6.0.1",
"cacheable-lookup": "^2.0.0",
"cacheable-request": "^7.0.1",
"decompress-response": "^5.0.0",
"duplexer3": "^0.1.4",
"get-stream": "^5.0.0",
"lowercase-keys": "^2.0.0",
"mimic-response": "^2.1.0",
"p-cancelable": "^2.0.0",
"p-event": "^4.0.0",
"responselike": "^2.0.0",
"to-readable-stream": "^2.0.0",
"type-fest": "^0.10.0"
}
},
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
},
"type-fest": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz",
"integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw=="
}
}
},
"memory-pager": { "memory-pager": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
@ -7643,27 +7413,6 @@
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="
}, },
"p-event": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz",
"integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==",
"requires": {
"p-timeout": "^3.1.0"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="
},
"p-timeout": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
"requires": {
"p-finally": "^1.0.0"
}
},
"parent-module": { "parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -8280,11 +8029,6 @@
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
"integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="
}, },
"to-readable-stream": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz",
"integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w=="
},
"toidentifier": { "toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",

View file

@ -36,7 +36,6 @@
"libsodium-wrappers": "^0.7.10", "libsodium-wrappers": "^0.7.10",
"markdown-table": "2.0.0", "markdown-table": "2.0.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"mee6-levels-api": "^1.3.0",
"moment": "^2.26.0", "moment": "^2.26.0",
"mongoose": "^5.13.14", "mongoose": "^5.13.14",
"ms": "^2.1.3", "ms": "^2.1.3",