simply-xp 1.3.5-beta-4 → 1.3.5-beta-6

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,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="DiscordProjectSettings">
4
+ <option name="show" value="ASK" />
5
+ <option name="description" value="" />
6
+ </component>
7
+ </project>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="JavaScriptLibraryMappings">
4
+ <file url="file://$PROJECT_DIR$" libraries="{Node.js Core}" />
5
+ </component>
6
+ </project>
package/.idea/misc.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager">
4
+ <output url="file://$PROJECT_DIR$/out" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/simply-xp.iml" filepath="$PROJECT_DIR$/.idea/simply-xp.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="JAVA_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+ <exclude-output />
5
+ <content url="file://$MODULE_DIR$" />
6
+ <orderEntry type="inheritedJdk" />
7
+ <orderEntry type="sourceFolder" forTests="false" />
8
+ </component>
9
+ </module>
@@ -0,0 +1,55 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AutoImportSettings">
4
+ <option name="autoReloadType" value="SELECTIVE" />
5
+ </component>
6
+ <component name="ChangeListManager">
7
+ <list default="true" id="38c41a1e-8ae9-4576-bb82-726fd121cb3f" name="Changes" comment="" />
8
+ <option name="SHOW_DIALOG" value="false" />
9
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
11
+ <option name="LAST_RESOLUTION" value="IGNORE" />
12
+ </component>
13
+ <component name="MarkdownSettingsMigration">
14
+ <option name="stateVersion" value="1" />
15
+ </component>
16
+ <component name="ProblemsViewState">
17
+ <option name="selectedTabId" value="CurrentFile" />
18
+ </component>
19
+ <component name="ProjectId" id="2KCg5I8izYGDdAKM7OTNH6Qxtkl" />
20
+ <component name="ProjectViewState">
21
+ <option name="hideEmptyMiddlePackages" value="true" />
22
+ <option name="showLibraryContents" value="true" />
23
+ </component>
24
+ <component name="PropertiesComponent"><![CDATA[{
25
+ "keyToString": {
26
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
27
+ "RunOnceActivity.ShowReadmeOnStart": "true",
28
+ "WebServerToolWindowFactoryState": "false",
29
+ "javascript.nodejs.core.library.configured.version": "18.12.1",
30
+ "javascript.nodejs.core.library.typings.version": "18.11.18",
31
+ "node.js.detected.package.eslint": "true",
32
+ "node.js.detected.package.tslint": "true",
33
+ "node.js.selected.package.eslint": "(autodetect)",
34
+ "node.js.selected.package.tslint": "(autodetect)",
35
+ "nodejs_package_manager_path": "npm",
36
+ "ts.external.directory.path": "C:\\Program Files\\JetBrains\\IntelliJ IDEA 2022.3.1\\plugins\\javascript-impl\\jsLanguageServicesImpl\\external",
37
+ "vue.rearranger.settings.migration": "true"
38
+ }
39
+ }]]></component>
40
+ <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
41
+ <component name="TaskManager">
42
+ <task active="true" id="Default" summary="Default task">
43
+ <changelist id="38c41a1e-8ae9-4576-bb82-726fd121cb3f" name="Changes" comment="" />
44
+ <created>1673483793432</created>
45
+ <option name="number" value="Default" />
46
+ <option name="presentableId" value="Default" />
47
+ <updated>1673483793432</updated>
48
+ <workItem from="1673483796080" duration="4036000" />
49
+ </task>
50
+ <servers />
51
+ </component>
52
+ <component name="TypeScriptGeneratedFilesManager">
53
+ <option name="version" value="3" />
54
+ </component>
55
+ </project>
package/index.d.ts CHANGED
@@ -43,7 +43,7 @@ export declare function fetch(userID: string, guildID: string): Promise<any>
43
43
  export declare function leaderboard(
44
44
  userID: string,
45
45
  guildID: string,
46
- limit: number
46
+ limit?: number
47
47
  ): Promise<any>
48
48
 
49
49
  export declare function lvlRole(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simply-xp",
3
- "version": "1.3.5-beta-4",
3
+ "version": "1.3.5-beta-6",
4
4
  "description": "A Simple, Easy and Beginner Friendly XP System",
5
5
  "main": "simplyxp.js",
6
6
  "typings": "index.d.ts",
@@ -40,9 +40,9 @@
40
40
  "url": "https://github.com/Rahuletto/simply-xp"
41
41
  },
42
42
  "dependencies": {
43
- "@napi-rs/canvas": "^0.1.30",
43
+ "@napi-rs/canvas": "^0.1.33",
44
44
  "chart.js": "^3.9.1",
45
- "mongoose": "^6.8.1"
45
+ "mongoose": "^6.8.3"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "discord.js": ">=13.12.0"
package/src/addLevel.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const levels = require('../src/models/level.js')
2
- let { roleSetup } = require('../simplyxp')
2
+ let {roleSetup} = require('../simplyxp')
3
3
 
4
4
  /**
5
5
  * @param {Discord.Message} message
@@ -8,63 +8,59 @@ let { roleSetup } = require('../simplyxp')
8
8
  * @param {number} level
9
9
  */
10
10
  async function addLevel(message, userID, guildID, level) {
11
- if (!userID) throw new Error('[XP] User ID was not provided.')
11
+ if (!userID) throw new Error('[XP] User ID was not provided.')
12
12
 
13
- if (!guildID) throw new Error('[XP] Guild ID was not provided.')
13
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
14
14
 
15
- if (!level) throw new Error('[XP] Level amount is not provided.')
15
+ if (!level) throw new Error('[XP] Level amount is not provided.')
16
16
 
17
- let { client } = message
17
+ let {client} = message
18
18
 
19
- const user = await levels.findOne({ user: userID, guild: guildID })
19
+ const user = await levels.findOne({user: userID, guild: guildID})
20
20
 
21
- if (!user) {
22
- const newUser = new levels({
23
- user: userID,
24
- guild: guildID,
25
- xp: 0,
26
- level: 0
27
- })
21
+ if (!user) {
22
+ const newUser = new levels({
23
+ user: userID,
24
+ guild: guildID,
25
+ xp: 0,
26
+ level: 0
27
+ })
28
28
 
29
- await newUser
30
- .save()
31
- .catch((e) => console.log(`[XP] Failed to save new user to database`))
29
+ await newUser.save().catch(() => console.log(`[XP] Failed to save new user to database`))
32
30
 
33
- let xp = (level * 10) ** 2
31
+ let xp = (level * 10) ** 2
34
32
 
35
- return {
36
- level: level,
37
- exp: xp
33
+ return {
34
+ level: level,
35
+ exp: xp
36
+ }
38
37
  }
39
- }
40
- let level1 = user.level
38
+ let level1 = user.level
41
39
 
42
- user.level += parseFloat(level)
43
- user.xp = (user.level * 10) ** 2
40
+ user.level += parseFloat(level)
41
+ user.xp = (user.level * 10) ** 2
44
42
 
45
- await user
46
- .save()
47
- .catch((e) =>
48
- console.log(`[XP] Failed to add Level | User: ${userID} | Err: ${e}`)
43
+ await user.save().catch((e) =>
44
+ console.log(`[XP] Failed to add Level | User: ${userID} | Err: ${e}`)
49
45
  )
50
46
 
51
- if (level1 !== level) {
52
- let data = {
53
- xp: user.xp,
54
- level: user.level,
55
- userID,
56
- guildID
57
- }
47
+ if (level1 !== level) {
48
+ let data = {
49
+ xp: user.xp,
50
+ level: user.level,
51
+ userID,
52
+ guildID
53
+ }
58
54
 
59
- let role = await roleSetup.find(client, guildID, level)
55
+ let role = await roleSetup.find(client, guildID, level)
60
56
 
61
- client.emit('levelUp', message, data, role)
62
- }
57
+ client.emit('levelUp', message, data, role)
58
+ }
63
59
 
64
- return {
65
- level: user.level,
66
- xp: user.xp
67
- }
60
+ return {
61
+ level: user.level,
62
+ xp: user.xp
63
+ }
68
64
  }
69
65
 
70
66
  module.exports = addLevel
package/src/addXP.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const levels = require('../src/models/level.js')
2
- let { roleSetup } = require('../simplyxp')
2
+ let {roleSetup} = require('../simplyxp')
3
3
 
4
4
  /**
5
5
  * @param {Discord.Message} message
@@ -9,103 +9,95 @@ let { roleSetup } = require('../simplyxp')
9
9
  */
10
10
 
11
11
  async function addXP(message, userID, guildID, xp) {
12
- if (!userID) throw new Error('[XP] User ID was not provided.')
12
+ if (!userID) throw new Error('[XP] User ID was not provided.')
13
13
 
14
- if (!guildID) throw new Error('[XP] Guild ID was not provided.')
14
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
15
15
 
16
- if (!xp) throw new Error('[XP] XP amount is not provided.')
16
+ if (!xp) throw new Error('[XP] XP amount is not provided.')
17
17
 
18
- let { client } = message
18
+ let {client} = message
19
19
 
20
- let min
21
- let max
22
- if (xp.min) {
23
- if (!xp.max)
24
- throw new Error(
25
- '[XP] XP min amount is provided but max amount is not provided.'
26
- )
20
+ let min
21
+ let max
22
+ if (xp.min) {
23
+ if (!xp.max)
24
+ throw new Error(
25
+ '[XP] XP min amount is provided but max amount is not provided.'
26
+ )
27
27
 
28
- min = Number(xp.min)
28
+ min = Number(xp.min)
29
29
 
30
- if (Number(xp.min).toString() === 'NaN')
31
- throw new Error('[XP] XP amount (min) is not a number.')
32
- }
33
-
34
- if (xp.max) {
35
- if (!xp.min)
36
- throw new Error(
37
- '[XP] XP max amount is provided but min amount is not provided.'
38
- )
30
+ if (Number(xp.min).toString() === 'NaN')
31
+ throw new Error('[XP] XP amount (min) is not a number.')
32
+ }
39
33
 
40
- max = Number(xp.max)
34
+ if (xp.max) {
35
+ if (!xp.min)
36
+ throw new Error(
37
+ '[XP] XP max amount is provided but min amount is not provided.'
38
+ )
41
39
 
42
- if (Number(xp.max).toString() === 'NaN')
43
- throw new Error('[XP] XP amount (max) is not a number.')
44
- }
40
+ max = Number(xp.max)
45
41
 
46
- if (xp.min && xp.max) {
47
- let randomNumber = Math.floor(Math.random() * (max - min) + min)
42
+ if (Number(xp.max).toString() === 'NaN')
43
+ throw new Error('[XP] XP amount (max) is not a number.')
44
+ }
48
45
 
49
- xp = randomNumber
50
- }
46
+ if (xp.min && xp.max) {
47
+ xp = Math.floor(Math.random() * (max - min) + min)
48
+ }
51
49
 
52
- const user = await levels.findOne({ user: userID, guild: guildID })
50
+ const user = await levels.findOne({user: userID, guild: guildID})
53
51
 
54
- let lvl = Math.floor(0.1 * Math.sqrt(xp))
52
+ let lvl = Math.floor(0.1 * Math.sqrt(xp))
55
53
 
56
- if (!user) {
57
- const newUser = new levels({
58
- user: userID,
59
- guild: guildID,
60
- xp: xp,
61
- level: lvl
62
- })
54
+ if (!user) {
55
+ const newUser = new levels({
56
+ user: userID,
57
+ guild: guildID,
58
+ xp: xp,
59
+ level: lvl
60
+ })
63
61
 
64
- await newUser
65
- .save()
66
- .catch((e) => console.log(`[XP] Failed to save new user to database`))
62
+ await newUser.save().catch(() => console.log(`[XP] Failed to save new user to database`))
67
63
 
68
- return {
69
- level: 0,
70
- exp: 0
64
+ return {
65
+ level: 0,
66
+ exp: 0
67
+ }
71
68
  }
72
- }
73
- let level1 = user.level
69
+ let level1 = user.level
74
70
 
75
- user.xp += parseInt(xp, 10)
76
- user.level = Math.floor(0.1 * Math.sqrt(user.xp))
71
+ user.xp += parseInt(xp, 10)
72
+ user.level = Math.floor(0.1 * Math.sqrt(user.xp))
77
73
 
78
- await user
79
- .save()
80
- .catch((e) =>
81
- console.log(`[XP] Failed to add XP | User: ${userID} | Err: ${e}`)
82
- )
74
+ await user.save().catch((e) => console.log(`[XP] Failed to add XP | User: ${userID} | Err: ${e}`))
83
75
 
84
- let level = user.level
76
+ let level = user.level
85
77
 
86
- xp = user.xp
78
+ xp = user.xp
87
79
 
88
- if (user.xp === 0 || Math.sign(user.xp) === -1) {
89
- xp = 0
90
- }
91
-
92
- if (level1 !== level) {
93
- let data = {
94
- xp,
95
- level,
96
- userID,
97
- guildID
80
+ if (user.xp === 0 || Math.sign(user.xp) === -1) {
81
+ xp = 0
98
82
  }
99
83
 
100
- let role = await roleSetup.find(client, guildID, level)
84
+ if (level1 !== level) {
85
+ let data = {
86
+ xp,
87
+ level,
88
+ userID,
89
+ guildID
90
+ }
91
+
92
+ let role = await roleSetup.find(client, guildID, level)
101
93
 
102
- client.emit('levelUp', message, data, role)
103
- }
94
+ client.emit('levelUp', message, data, role)
95
+ }
104
96
 
105
- return {
106
- level,
107
- xp
108
- }
97
+ return {
98
+ level,
99
+ xp
100
+ }
109
101
  }
110
102
 
111
103
  module.exports = addXP
package/src/charts.js CHANGED
@@ -5,85 +5,88 @@ let leaderboard = require('./leaderboard')
5
5
  * @param {import('../index').chartsOptions} options
6
6
  */
7
7
 
8
- async function charts(message, options = []) {
9
- try { require('canvas') } catch { throw '[XP] This requires canvas to be installed. \n"npm install canvas"' }
10
- const ChartJS = require('chart.js')
11
- const Canvas = require('canvas')
12
- let { client } = message
8
+ async function charts(message, options) {
9
+ try {
10
+ require('canvas')
11
+ } catch {
12
+ throw '[XP] This requires canvas to be installed. \n"npm install canvas"'
13
+ }
14
+ const ChartJS = require('chart.js')
15
+ const Canvas = require('canvas')
16
+ let {client} = message
13
17
 
14
- let data = []
15
- let pos = options?.position || 5
16
- let uzern = []
18
+ let data = []
19
+ let pos = options?.position || 5
20
+ let uzern = []
17
21
 
18
- let ctx = Canvas.createCanvas(950, 526)
19
- await leaderboard(client, message.guild.id).then((e) => {
20
- e.forEach((m) => {
21
- if (m.position <= pos) {
22
- data.push(m.xp)
23
- uzern.push(m.tag)
24
- }
22
+ let ctx = Canvas.createCanvas(950, 526)
23
+ await leaderboard(client, message.guild.id).then((e) => {
24
+ e.forEach((m) => {
25
+ if (m.position <= pos) {
26
+ data.push(m.xp)
27
+ uzern.push(m.tag)
28
+ }
29
+ })
25
30
  })
26
- })
27
31
 
28
- new ChartJS(ctx, {
29
- type: options.type || 'bar',
30
- data: {
31
- labels: uzern,
32
- datasets: [
33
- {
34
- label: 'Leaderboards',
35
- data: data,
36
- backgroundColor: [
37
- 'rgba(255, 99, 132, 0.5)',
38
- 'rgba(255, 159, 64, 0.5)',
39
- 'rgba(255, 205, 86, 0.5)',
40
- 'rgba(75, 192, 192, 0.5)',
41
- 'rgba(54, 162, 235, 0.5)',
42
- 'rgba(153, 102, 255, 0.5)',
43
- 'rgb(201, 203, 207, 0.5)'
44
- ],
45
- borderColor: [
46
- 'rgb(255, 99, 132)',
47
- 'rgb(255, 159, 64)',
48
- 'rgb(255, 205, 86)',
49
- 'rgb(75, 192, 192)',
50
- 'rgb(54, 162, 235)',
51
- 'rgb(153, 102, 255)',
52
- 'rgb(201, 203, 207)'
53
- ],
54
- borderWidth: 2
55
- }
56
- ]
57
- },
58
- options: {
59
- animation: false,
60
- plugins: {
61
- title: {
62
- display: true,
63
- text: 'XP Datasheet'
64
- }
65
- }
66
- },
67
- plugins: [
68
- {
69
- id: 'simply-xp',
70
- beforeDraw: (chart) => {
71
- const ctx = chart.canvas.getContext('2d')
72
- ctx.save()
73
- ctx.globalCompositeOperation = 'destination-over'
74
- ctx.fillStyle = options.background || '#2F3136'
75
- ctx.fillRect(0, 0, chart.width, chart.height)
76
- ctx.restore()
77
- }
78
- }
79
- ]
80
- }).update()
32
+ new ChartJS(ctx, {
33
+ type: options.type || 'bar',
34
+ data: {
35
+ labels: uzern,
36
+ datasets: [
37
+ {
38
+ label: 'Leaderboards',
39
+ data: data,
40
+ backgroundColor: [
41
+ 'rgba(255, 99, 132, 0.5)',
42
+ 'rgba(255, 159, 64, 0.5)',
43
+ 'rgba(255, 205, 86, 0.5)',
44
+ 'rgba(75, 192, 192, 0.5)',
45
+ 'rgba(54, 162, 235, 0.5)',
46
+ 'rgba(153, 102, 255, 0.5)',
47
+ 'rgb(201, 203, 207, 0.5)'
48
+ ],
49
+ borderColor: [
50
+ 'rgb(255, 99, 132)',
51
+ 'rgb(255, 159, 64)',
52
+ 'rgb(255, 205, 86)',
53
+ 'rgb(75, 192, 192)',
54
+ 'rgb(54, 162, 235)',
55
+ 'rgb(153, 102, 255)',
56
+ 'rgb(201, 203, 207)'
57
+ ],
58
+ borderWidth: 2
59
+ }
60
+ ]
61
+ },
62
+ options: {
63
+ animation: false,
64
+ plugins: {
65
+ title: {
66
+ display: true,
67
+ text: 'XP Datasheet'
68
+ }
69
+ }
70
+ },
71
+ plugins: [
72
+ {
73
+ id: 'simply-xp',
74
+ beforeDraw: (chart) => {
75
+ const ctx = chart.canvas.getContext('2d')
76
+ ctx.save()
77
+ ctx.globalCompositeOperation = 'destination-over'
78
+ ctx.fillStyle = options.background || '#2F3136'
79
+ ctx.fillRect(0, 0, chart.width, chart.height)
80
+ ctx.restore()
81
+ }
82
+ }
83
+ ]
84
+ }).update()
81
85
 
82
- const attachment = {
83
- attachment: ctx.toBuffer('image/png'),
84
- name: 'chart.png'
85
- }
86
- return attachment
86
+ return {
87
+ attachment: ctx.toBuffer('image/png'),
88
+ name: 'chart.png'
89
+ }
87
90
  }
88
91
 
89
92
  module.exports = charts
@@ -3,56 +3,50 @@ const levels = require('../src/models/level.js')
3
3
  /**
4
4
  * @param {Discord.Client} client
5
5
  * @param {string} guildID
6
- * @param {number} limit
6
+ * @param {number?} limit
7
7
  */
8
8
 
9
9
  async function leaderboard(client, guildID, limit) {
10
- if (!guildID) throw new Error('[XP] Guild ID was not provided.')
10
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
11
11
 
12
- let g = client.guilds.cache.get(guildID)
13
- if (!g) throw new Error('[XP] Guild was not found.')
12
+ let g = client.guilds.cache.get(guildID)
13
+ if (!g) throw new Error('[XP] Guild was not found.')
14
14
 
15
- let leaderboard = await levels
16
- .find({ guild: guildID })
17
- .sort([['xp', 'descending']])
15
+ let leaderboard = await levels.find({guild: guildID}).sort([['xp', 'descending']])
18
16
 
19
- let led = []
17
+ let led = []
20
18
 
21
- function shortener(count) {
22
- const COUNT_ABBRS = ['', 'k', 'M', 'T']
19
+ function shortener(count) {
20
+ const COUNT_ABBRS = ['', 'k', 'M', 'T']
23
21
 
24
- const i = 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000))
25
- let result = parseFloat((count / Math.pow(1000, i)).toFixed(2))
26
- result += `${COUNT_ABBRS[i]}`
27
- return result
28
- }
29
-
30
- var led2 = leaderboard.map(async (key) => {
31
- let user = g.members.cache.get(key.user)
32
- if (key.xp === 0) return
33
- if (!user) return
34
-
35
- let pos =
36
- leaderboard.findIndex(
37
- (i) => i.guild === key.guild && i.user === key.user
38
- ) + 1
39
-
40
- if (limit) {
41
- if (pos > Number(limit)) return
22
+ const i = 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000))
23
+ let result = parseFloat((count / Math.pow(1000, i)).toFixed(2))
24
+ result += `${COUNT_ABBRS[i]}`
25
+ return result
42
26
  }
43
27
 
44
- led.push({
45
- guildID: key.guild,
46
- userID: key.user,
47
- xp: key.xp,
48
- shortxp: shortener(key.xp),
49
- level: key.level,
50
- position: pos,
51
- username: user.user.username,
52
- tag: user.user.tag
28
+ const led2 = leaderboard.map(async (key) => {
29
+ const user = await g.members.fetch(key.user).catch(() => null)
30
+ if (!user) return levels.deleteOne({user: key.user, guild: guildID})
31
+ if (key.xp === 0) return;
32
+ let pos = leaderboard.indexOf(key) + 1
33
+
34
+ if (limit) {
35
+ if (pos > Number(limit)) return
36
+ }
37
+
38
+ led.push({
39
+ guildID: key.guild,
40
+ userID: key.user,
41
+ xp: key.xp,
42
+ shortxp: shortener(key.xp),
43
+ level: key.level,
44
+ position: pos,
45
+ username: user.user.username,
46
+ tag: user.user.tag
47
+ })
53
48
  })
54
- })
55
- return Promise.all(led2).then(() => led)
49
+ return Promise.all(led2).then(() => led)
56
50
  }
57
51
 
58
52
  module.exports = leaderboard
package/src/rank.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const levels = require('../src/models/level.js')
2
- const { join } = require('path')
2
+ const {join} = require('path')
3
3
 
4
4
  /**
5
5
  * @param {Discord.Message} message
@@ -9,288 +9,286 @@ const { join } = require('path')
9
9
  */
10
10
 
11
11
  async function rank(message, userID, guildID, options = []) {
12
- if (!userID) throw new Error('[XP] User ID was not provided.')
12
+ if (!userID) throw new Error('[XP] User ID was not provided.')
13
13
 
14
- if (!guildID) throw new Error('[XP] Guild ID was not provided.')
14
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
15
15
 
16
- const user = await levels.findOne({
17
- user: userID,
18
- guild: guildID
19
- })
20
- if (!user) throw new Error('[XP] NO_DATA | User has no XP data.')
21
-
22
- const leaderboard = await levels
23
- .find({
24
- guild: guildID
16
+ const user = await levels.findOne({
17
+ user: userID,
18
+ guild: guildID
19
+ })
20
+ if (!user) throw new Error('[XP] NO_DATA | User has no XP data.')
21
+
22
+ const leaderboard = await levels
23
+ .find({
24
+ guild: guildID
25
+ })
26
+ .sort([['xp', 'descending']])
27
+ .exec()
28
+
29
+ user.position = leaderboard.findIndex((i) => i.user === userID) + 1
30
+
31
+ let targetxp = user.level + 1
32
+
33
+ let target = targetxp * targetxp * 100
34
+
35
+ return rankCard(message, {
36
+ level: user.level,
37
+ currentXP: user.xp,
38
+ neededXP: target,
39
+ rank: user.position,
40
+ background: options.background,
41
+ color: options.color,
42
+ lvlbar: options.lvlbar,
43
+ lvlbarBg: options.lvlbarBg,
44
+ member: message.guild.members.cache.get(userID)?.user
25
45
  })
26
- .sort([['xp', 'descending']])
27
- .exec()
28
-
29
- user.position = leaderboard.findIndex((i) => i.user === userID) + 1
30
-
31
- let targetxp = user.level + 1
32
-
33
- let target = targetxp * targetxp * 100
34
-
35
- return rankCard(message, {
36
- level: user.level,
37
- currentXP: user.xp,
38
- neededXP: target,
39
- rank: user.position,
40
- background: options.background,
41
- color: options.color,
42
- lvlbar: options.lvlbar,
43
- lvlbarBg: options.lvlbarBg,
44
- member: message.guild.members.cache.get(userID)?.user
45
- })
46
-
47
- async function rankCard(message, options = []) {
48
- try {
49
- const Canvas = require('@napi-rs/canvas')
50
- Canvas.GlobalFonts.registerFromPath(
51
- join(__dirname, 'Fonts', 'Baloo-Regular.ttf'),
52
- 'Sans Serif'
53
- )
54
-
55
- function shortener(count) {
56
- const COUNT_ABBRS = [
57
- '',
58
- 'k',
59
- 'M',
60
- 'B',
61
- 'T',
62
- 'Q',
63
- 'Q+',
64
- 'S',
65
- 'S+',
66
- 'O',
67
- 'N',
68
- 'D',
69
- 'U'
70
- ]
71
-
72
- const i =
73
- 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000))
74
- let result = parseFloat((count / Math.pow(1000, i)).toFixed(2))
75
- result += `${COUNT_ABBRS[i]}`
76
- return result
77
- }
78
-
79
- const member = options.member
80
-
81
- const canvas = Canvas.createCanvas(1080, 400),
82
- ctx = canvas.getContext('2d')
83
-
84
- const name = member.tag
85
- const noSymbols = (string) => string.replace(/[\u007f-\uffff]/g, '')
86
-
87
- let fsiz = '45px'
88
- if (message.guild.name.length >= 23) {
89
- fsiz = '38px'
90
- }
91
- if (message.guild.name.length >= 40) {
92
- fsiz = '28px'
93
- }
94
- if (message.guild.name.length >= 63) {
95
- fsiz = '22px'
96
- }
97
-
98
- let BackgroundRadius = '20',
99
- BackGroundImg =
100
- options.background ||
101
- 'https://i.ibb.co/QQvMqf7/gradient.jpg',
102
- AttachmentName = 'rank.png',
103
- AttachmentDesc = 'Rank Card',
104
- Username = noSymbols(name),
105
- AvatarRoundRadius = '50',
106
- DrawLayerColor = '#000000',
107
- DrawLayerOpacity = 0.4,
108
- BoxColor = options.color || '#096DD1',
109
- LevelBarFill = options.lvlbar || '#ffffff',
110
- LevelBarBackground = options.lvlbarBg || '#ffffff',
111
- Rank = options.rank,
112
- TextEXP = shortener(options.currentXP) + ' XP',
113
- LvlText = `Level ${shortener(options.level)}`,
114
- BarRadius = '20',
115
- TextXpNeded = '{current}/{needed}',
116
- CurrentXP = options.currentXP,
117
- NeededXP = options.neededXP
118
-
119
- ctx.beginPath()
120
- ctx.moveTo(0 + Number(BackgroundRadius), 0)
121
- ctx.lineTo(0 + 1080 - Number(BackgroundRadius), 0)
122
- ctx.quadraticCurveTo(0 + 1080, 0, 0 + 1080, 0 + Number(BackgroundRadius))
123
- ctx.lineTo(0 + 1080, 0 + 400 - Number(BackgroundRadius))
124
- ctx.quadraticCurveTo(
125
- 0 + 1080,
126
- 0 + 400,
127
- 0 + 1080 - Number(BackgroundRadius),
128
- 0 + 400
129
- )
130
-
131
- ctx.lineTo(0 + Number(BackgroundRadius), 0 + 400)
132
- ctx.quadraticCurveTo(0, 0 + 400, 0, 0 + 400 - Number(BackgroundRadius))
133
- ctx.lineTo(0, 0 + Number(BackgroundRadius))
134
- ctx.quadraticCurveTo(0, 0, 0 + Number(BackgroundRadius), 0)
135
- ctx.closePath()
136
- ctx.clip()
137
- ctx.fillStyle = '#000000'
138
- ctx.fillRect(0, 0, 1080, 400)
139
- let background = await Canvas.loadImage(BackGroundImg)
140
- ctx.globalAlpha = 0.7
141
- ctx.drawImage(background, 0, 0, 1080, 400)
142
- ctx.restore()
143
-
144
- ctx.fillStyle = DrawLayerColor
145
- ctx.globalAlpha = DrawLayerOpacity
146
- ctx.fillRect(40, 0, 240, canvas.height)
147
- ctx.globalAlpha = 1
148
-
149
- function RoundedBox(ctx, x, y, width, height, radius) {
150
- ctx.beginPath()
151
- ctx.moveTo(x + radius, y)
152
- ctx.lineTo(x + width - radius, y)
153
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
154
- ctx.lineTo(x + width, y + height - radius)
155
- ctx.quadraticCurveTo(
156
- x + width,
157
- y + height,
158
- x + width - radius,
159
- y + height
160
- )
161
- ctx.lineTo(x + radius, y + height)
162
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
163
- ctx.lineTo(x, y + radius)
164
- ctx.quadraticCurveTo(x, y, x + radius, y)
165
- ctx.closePath()
166
- }
167
-
168
- let avatar = await Canvas.loadImage(member.displayAvatarURL())
169
- ctx.save()
170
- RoundedBox(ctx, 70, 30, 180, 180, Number(AvatarRoundRadius))
171
- ctx.strokeStyle = BoxColor
172
- ctx.lineWidth = 15
173
- ctx.stroke()
174
- ctx.clip()
175
- ctx.drawImage(avatar, 70, 30, 180, 180)
176
- ctx.restore()
177
-
178
- ctx.save()
179
- RoundedBox(ctx, 70, 240 + 50 + 30, 180, 50, 20)
180
- ctx.strokeStyle = '#BFC85A22'
181
- ctx.stroke()
182
- ctx.clip()
183
- ctx.fillStyle = BoxColor
184
- ctx.globalAlpha = 1
185
- ctx.fillRect(70, 320, 180, 50)
186
- ctx.globalAlpha = 1
187
- ctx.fillStyle = '#ffffff'
188
- ctx.font = '32px "Sans Serif"'
189
- ctx.textAlign = 'center'
190
- ctx.fillText(TextEXP, 160, 358)
191
- ctx.restore()
192
-
193
- ctx.save()
194
- RoundedBox(ctx, 70, 240, 180, 50, 20)
195
- ctx.strokeStyle = '#BFC85A22'
196
- ctx.stroke()
197
- ctx.clip()
198
- ctx.fillStyle = BoxColor
199
- ctx.globalAlpha = 1
200
- ctx.fillRect(70, 240, 180, 50, 50)
201
- ctx.globalAlpha = 1
202
- ctx.fillStyle = '#ffffff'
203
- ctx.font = '32px "Sans Serif"'
204
- ctx.textAlign = 'center'
205
- ctx.fillText(LvlText, 70 + 180 / 2, 278)
206
- ctx.restore()
207
-
208
- ctx.save()
209
- ctx.textAlign = 'left'
210
- ctx.fillStyle = '#ffffff'
211
- ctx.shadowColor = '#000000'
212
- ctx.shadowBlur = 15
213
- ctx.shadowOffsetX = 1
214
- ctx.shadowOffsetY = 1
215
- ctx.font = '39px "Sans Serif"'
216
- ctx.fillText(Username, 390, 80)
217
- ctx.restore()
218
-
219
- ctx.save()
220
- ctx.textAlign = 'right'
221
- ctx.fillStyle = '#ffffff'
222
- ctx.shadowColor = '#000000'
223
- ctx.shadowBlur = 15
224
- ctx.shadowOffsetX = 1
225
- ctx.shadowOffsetY = 1
226
- ctx.font = '55px "Sans Serif"'
227
- ctx.fillText('#' + Rank, canvas.width - 55, 80)
228
- ctx.restore()
229
-
230
- ctx.save()
231
- RoundedBox(ctx, 390, 305, 660, 70, Number(20))
232
- ctx.strokeStyle = '#BFC85A22'
233
- ctx.stroke()
234
- ctx.clip()
235
- ctx.fillStyle = '#ffffff'
236
- ctx.font = `${fsiz} "Sans Serif"`
237
- ctx.textAlign = 'center'
238
- ctx.fillText(message.guild.name, 720, 355)
239
- ctx.globalAlpha = 0.2
240
- ctx.fillRect(390, 305, 660, 70)
241
- ctx.restore()
242
-
243
- ctx.save()
244
- RoundedBox(ctx, 390, 145, 660, 50, Number(BarRadius))
245
- ctx.strokeStyle = '#BFC85A22'
246
- ctx.stroke()
247
- ctx.clip()
248
- ctx.fillStyle = LevelBarBackground
249
- ctx.globalAlpha = 0.2
250
- ctx.fillRect(390, 145, 660, 50, 50)
251
- ctx.restore()
252
-
253
- const percent = (100 * CurrentXP) / NeededXP
254
- const progress = (percent * 660) / 100
255
-
256
- ctx.save()
257
- RoundedBox(ctx, 390, 145, progress, 50, Number(BarRadius))
258
- ctx.strokeStyle = '#BFC85A22'
259
- ctx.stroke()
260
- ctx.clip()
261
- ctx.fillStyle = LevelBarFill
262
- ctx.globalAlpha = 0.5
263
- ctx.fillRect(390, 145, progress, 50, 50)
264
- ctx.restore()
265
-
266
- ctx.save()
267
- ctx.textAlign = 'left'
268
- ctx.fillStyle = '#ffffff'
269
- ctx.globalAlpha = 0.8
270
- ctx.font = '30px "Sans Serif"'
271
- ctx.fillText('Next Level: ' + shortener(NeededXP) + ' XP', 390, 230)
272
- ctx.restore()
273
-
274
- const latestXP = Number(CurrentXP) - Number(NeededXP)
275
- const textXPEdited = TextXpNeded.replace(/{needed}/g, shortener(NeededXP))
276
- .replace(/{current}/g, shortener(CurrentXP))
277
- .replace(/{latest}/g, latestXP)
278
- ctx.textAlign = 'center'
279
- ctx.fillStyle = '#474747'
280
- ctx.globalAlpha = 1
281
- ctx.font = '30px "Sans Serif"'
282
- ctx.fillText(textXPEdited, 730, 180)
283
-
284
- const attachment = {
285
- attachment: canvas.toBuffer('image/webp'),
286
- description: AttachmentDesc,
287
- name: AttachmentName
288
- }
289
- return attachment
290
- } catch (err) {
291
- console.log(`[XP] Error Occured. | rankCard | Error: ${err.stack}`)
46
+
47
+ async function rankCard(message, options = []) {
48
+ try {
49
+ const Canvas = require('@napi-rs/canvas')
50
+ Canvas.GlobalFonts.registerFromPath(
51
+ join(__dirname, 'Fonts', 'Baloo-Regular.ttf'),
52
+ 'Sans Serif'
53
+ )
54
+
55
+ function shortener(count) {
56
+ const COUNT_ABBRS = [
57
+ '',
58
+ 'k',
59
+ 'M',
60
+ 'B',
61
+ 'T',
62
+ 'Q',
63
+ 'Q+',
64
+ 'S',
65
+ 'S+',
66
+ 'O',
67
+ 'N',
68
+ 'D',
69
+ 'U'
70
+ ]
71
+
72
+ const i = 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000))
73
+ let result = parseFloat((count / Math.pow(1000, i)).toFixed(2))
74
+ result += `${COUNT_ABBRS[i]}`
75
+ return result
76
+ }
77
+
78
+ const member = options.member
79
+
80
+ const canvas = Canvas.createCanvas(1080, 400),
81
+ ctx = canvas.getContext('2d')
82
+
83
+ const name = member.tag
84
+ const noSymbols = (string) => string.replace(/[\u007f-\uffff]/g, '')
85
+
86
+ let fsiz = '45px'
87
+ if (message.guild.name.length >= 23) {
88
+ fsiz = '38px'
89
+ }
90
+ if (message.guild.name.length >= 40) {
91
+ fsiz = '28px'
92
+ }
93
+ if (message.guild.name.length >= 63) {
94
+ fsiz = '22px'
95
+ }
96
+
97
+ let BackgroundRadius = '20',
98
+ BackGroundImg =
99
+ options.background ||
100
+ 'https://i.ibb.co/QQvMqf7/gradient.jpg',
101
+ AttachmentName = 'rank.png',
102
+ AttachmentDesc = 'Rank Card',
103
+ Username = noSymbols(name),
104
+ AvatarRoundRadius = '50',
105
+ DrawLayerColor = '#000000',
106
+ DrawLayerOpacity = 0.4,
107
+ BoxColor = options.color || '#096DD1',
108
+ LevelBarFill = options.lvlbar || '#ffffff',
109
+ LevelBarBackground = options.lvlbarBg || '#ffffff',
110
+ Rank = options.rank,
111
+ TextEXP = shortener(options.currentXP) + ' XP',
112
+ LvlText = `Level ${shortener(options.level)}`,
113
+ BarRadius = '20',
114
+ TextXpNeded = '{current}/{needed}',
115
+ CurrentXP = options.currentXP,
116
+ NeededXP = options.neededXP
117
+
118
+ ctx.beginPath()
119
+ ctx.moveTo(Number(BackgroundRadius), 0)
120
+ ctx.lineTo(1080 - Number(BackgroundRadius), 0)
121
+ ctx.quadraticCurveTo(1080, 0, 1080, Number(BackgroundRadius))
122
+ ctx.lineTo(1080, 400 - Number(BackgroundRadius))
123
+ ctx.quadraticCurveTo(
124
+ 1080,
125
+ 400,
126
+ 1080 - Number(BackgroundRadius),
127
+ 400
128
+ )
129
+
130
+ ctx.lineTo(Number(BackgroundRadius), 400)
131
+ ctx.quadraticCurveTo(0, 400, 0, 400 - Number(BackgroundRadius))
132
+ ctx.lineTo(0, Number(BackgroundRadius))
133
+ ctx.quadraticCurveTo(0, 0, Number(BackgroundRadius), 0)
134
+ ctx.closePath()
135
+ ctx.clip()
136
+ ctx.fillStyle = '#000000'
137
+ ctx.fillRect(0, 0, 1080, 400)
138
+ let background = await Canvas.loadImage(BackGroundImg)
139
+ ctx.globalAlpha = 0.7
140
+ ctx.drawImage(background, 0, 0, 1080, 400)
141
+ ctx.restore()
142
+
143
+ ctx.fillStyle = DrawLayerColor
144
+ ctx.globalAlpha = DrawLayerOpacity
145
+ ctx.fillRect(40, 0, 240, canvas.height)
146
+ ctx.globalAlpha = 1
147
+
148
+ function RoundedBox(ctx, x, y, width, height, radius) {
149
+ ctx.beginPath()
150
+ ctx.moveTo(x + radius, y)
151
+ ctx.lineTo(x + width - radius, y)
152
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
153
+ ctx.lineTo(x + width, y + height - radius)
154
+ ctx.quadraticCurveTo(
155
+ x + width,
156
+ y + height,
157
+ x + width - radius,
158
+ y + height
159
+ )
160
+ ctx.lineTo(x + radius, y + height)
161
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
162
+ ctx.lineTo(x, y + radius)
163
+ ctx.quadraticCurveTo(x, y, x + radius, y)
164
+ ctx.closePath()
165
+ }
166
+
167
+ let avatar = await Canvas.loadImage(member.displayAvatarURL())
168
+ ctx.save()
169
+ RoundedBox(ctx, 70, 30, 180, 180, Number(AvatarRoundRadius))
170
+ ctx.strokeStyle = BoxColor
171
+ ctx.lineWidth = 15
172
+ ctx.stroke()
173
+ ctx.clip()
174
+ ctx.drawImage(avatar, 70, 30, 180, 180)
175
+ ctx.restore()
176
+
177
+ ctx.save()
178
+ RoundedBox(ctx, 70, 240 + 50 + 30, 180, 50, 20)
179
+ ctx.strokeStyle = '#BFC85A22'
180
+ ctx.stroke()
181
+ ctx.clip()
182
+ ctx.fillStyle = BoxColor
183
+ ctx.globalAlpha = 1
184
+ ctx.fillRect(70, 320, 180, 50)
185
+ ctx.globalAlpha = 1
186
+ ctx.fillStyle = '#ffffff'
187
+ ctx.font = '32px "Sans Serif"'
188
+ ctx.textAlign = 'center'
189
+ ctx.fillText(TextEXP, 160, 358)
190
+ ctx.restore()
191
+
192
+ ctx.save()
193
+ RoundedBox(ctx, 70, 240, 180, 50, 20)
194
+ ctx.strokeStyle = '#BFC85A22'
195
+ ctx.stroke()
196
+ ctx.clip()
197
+ ctx.fillStyle = BoxColor
198
+ ctx.globalAlpha = 1
199
+ ctx.fillRect(70, 240, 180, 50, 50)
200
+ ctx.globalAlpha = 1
201
+ ctx.fillStyle = '#ffffff'
202
+ ctx.font = '32px "Sans Serif"'
203
+ ctx.textAlign = 'center'
204
+ ctx.fillText(LvlText, 70 + 180 / 2, 278)
205
+ ctx.restore()
206
+
207
+ ctx.save()
208
+ ctx.textAlign = 'left'
209
+ ctx.fillStyle = '#ffffff'
210
+ ctx.shadowColor = '#000000'
211
+ ctx.shadowBlur = 15
212
+ ctx.shadowOffsetX = 1
213
+ ctx.shadowOffsetY = 1
214
+ ctx.font = '39px "Sans Serif"'
215
+ ctx.fillText(Username, 390, 80)
216
+ ctx.restore()
217
+
218
+ ctx.save()
219
+ ctx.textAlign = 'right'
220
+ ctx.fillStyle = '#ffffff'
221
+ ctx.shadowColor = '#000000'
222
+ ctx.shadowBlur = 15
223
+ ctx.shadowOffsetX = 1
224
+ ctx.shadowOffsetY = 1
225
+ ctx.font = '55px "Sans Serif"'
226
+ ctx.fillText('#' + Rank, canvas.width - 55, 80)
227
+ ctx.restore()
228
+
229
+ ctx.save()
230
+ RoundedBox(ctx, 390, 305, 660, 70, Number(20))
231
+ ctx.strokeStyle = '#BFC85A22'
232
+ ctx.stroke()
233
+ ctx.clip()
234
+ ctx.fillStyle = '#ffffff'
235
+ ctx.font = `${fsiz} "Sans Serif"`
236
+ ctx.textAlign = 'center'
237
+ ctx.fillText(message.guild.name, 720, 355)
238
+ ctx.globalAlpha = 0.2
239
+ ctx.fillRect(390, 305, 660, 70)
240
+ ctx.restore()
241
+
242
+ ctx.save()
243
+ RoundedBox(ctx, 390, 145, 660, 50, Number(BarRadius))
244
+ ctx.strokeStyle = '#BFC85A22'
245
+ ctx.stroke()
246
+ ctx.clip()
247
+ ctx.fillStyle = LevelBarBackground
248
+ ctx.globalAlpha = 0.2
249
+ ctx.fillRect(390, 145, 660, 50, 50)
250
+ ctx.restore()
251
+
252
+ const percent = (100 * CurrentXP) / NeededXP
253
+ const progress = (percent * 660) / 100
254
+
255
+ ctx.save()
256
+ RoundedBox(ctx, 390, 145, progress, 50, Number(BarRadius))
257
+ ctx.strokeStyle = '#BFC85A22'
258
+ ctx.stroke()
259
+ ctx.clip()
260
+ ctx.fillStyle = LevelBarFill
261
+ ctx.globalAlpha = 0.5
262
+ ctx.fillRect(390, 145, progress, 50, 50)
263
+ ctx.restore()
264
+
265
+ ctx.save()
266
+ ctx.textAlign = 'left'
267
+ ctx.fillStyle = '#ffffff'
268
+ ctx.globalAlpha = 0.8
269
+ ctx.font = '30px "Sans Serif"'
270
+ ctx.fillText('Next Level: ' + shortener(NeededXP) + ' XP', 390, 230)
271
+ ctx.restore()
272
+
273
+ const latestXP = Number(CurrentXP) - Number(NeededXP)
274
+ const textXPEdited = TextXpNeded.replace(/{needed}/g, shortener(NeededXP).toString())
275
+ .replace(/{current}/g, shortener(CurrentXP).toString())
276
+ .replace(/{latest}/g, latestXP.toString())
277
+ ctx.textAlign = 'center'
278
+ ctx.fillStyle = '#474747'
279
+ ctx.globalAlpha = 1
280
+ ctx.font = '30px "Sans Serif"'
281
+ ctx.fillText(textXPEdited, 730, 180)
282
+
283
+ return {
284
+ attachment: canvas.toBuffer('image/webp'),
285
+ description: AttachmentDesc,
286
+ name: AttachmentName
287
+ }
288
+ } catch (err) {
289
+ console.log(`[XP] Error Occured. | rankCard | Error: ${err.stack}`)
290
+ }
292
291
  }
293
- }
294
292
  }
295
293
 
296
294
  module.exports = rank