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.
- package/.idea/discord.xml +7 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/simply-xp.iml +9 -0
- package/.idea/workspace.xml +55 -0
- package/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/addLevel.js +38 -42
- package/src/addXP.js +65 -73
- package/src/charts.js +77 -74
- package/src/leaderboard.js +33 -39
- package/src/rank.js +277 -279
package/.idea/misc.xml
ADDED
|
@@ -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simply-xp",
|
|
3
|
-
"version": "1.3.5-beta-
|
|
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.
|
|
43
|
+
"@napi-rs/canvas": "^0.1.33",
|
|
44
44
|
"chart.js": "^3.9.1",
|
|
45
|
-
"mongoose": "^6.8.
|
|
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 {
|
|
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
|
-
|
|
11
|
+
if (!userID) throw new Error('[XP] User ID was not provided.')
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
if (!guildID) throw new Error('[XP] Guild ID was not provided.')
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
if (!level) throw new Error('[XP] Level amount is not provided.')
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
let {client} = message
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const user = await levels.findOne({user: userID, guild: guildID})
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
let xp = (level * 10) ** 2
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
43
|
-
|
|
40
|
+
user.level += parseFloat(level)
|
|
41
|
+
user.xp = (user.level * 10) ** 2
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
if (level1 !== level) {
|
|
48
|
+
let data = {
|
|
49
|
+
xp: user.xp,
|
|
50
|
+
level: user.level,
|
|
51
|
+
userID,
|
|
52
|
+
guildID
|
|
53
|
+
}
|
|
58
54
|
|
|
59
|
-
|
|
55
|
+
let role = await roleSetup.find(client, guildID, level)
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
client.emit('levelUp', message, data, role)
|
|
58
|
+
}
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 {
|
|
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
|
-
|
|
12
|
+
if (!userID) throw new Error('[XP] User ID was not provided.')
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
if (!guildID) throw new Error('[XP] Guild ID was not provided.')
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
if (!xp) throw new Error('[XP] XP amount is not provided.')
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
let {client} = message
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
+
min = Number(xp.min)
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
throw new Error('[XP] XP amount (max) is not a number.')
|
|
44
|
-
}
|
|
40
|
+
max = Number(xp.max)
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
if (Number(xp.max).toString() === 'NaN')
|
|
43
|
+
throw new Error('[XP] XP amount (max) is not a number.')
|
|
44
|
+
}
|
|
48
45
|
|
|
49
|
-
xp
|
|
50
|
-
|
|
46
|
+
if (xp.min && xp.max) {
|
|
47
|
+
xp = Math.floor(Math.random() * (max - min) + min)
|
|
48
|
+
}
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
const user = await levels.findOne({user: userID, guild: guildID})
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
let lvl = Math.floor(0.1 * Math.sqrt(xp))
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
76
|
-
|
|
71
|
+
user.xp += parseInt(xp, 10)
|
|
72
|
+
user.level = Math.floor(0.1 * Math.sqrt(user.xp))
|
|
77
73
|
|
|
78
|
-
|
|
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
|
-
|
|
76
|
+
let level = user.level
|
|
85
77
|
|
|
86
|
-
|
|
78
|
+
xp = user.xp
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
94
|
+
client.emit('levelUp', message, data, role)
|
|
95
|
+
}
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
let data = []
|
|
19
|
+
let pos = options?.position || 5
|
|
20
|
+
let uzern = []
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
package/src/leaderboard.js
CHANGED
|
@@ -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
|
-
|
|
10
|
+
if (!guildID) throw new Error('[XP] Guild ID was not provided.')
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
let g = client.guilds.cache.get(guildID)
|
|
13
|
+
if (!g) throw new Error('[XP] Guild was not found.')
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
.find({ guild: guildID })
|
|
17
|
-
.sort([['xp', 'descending']])
|
|
15
|
+
let leaderboard = await levels.find({guild: guildID}).sort([['xp', 'descending']])
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
let led = []
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
function shortener(count) {
|
|
20
|
+
const COUNT_ABBRS = ['', 'k', 'M', 'T']
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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 {
|
|
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
|
-
|
|
12
|
+
if (!userID) throw new Error('[XP] User ID was not provided.')
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
if (!guildID) throw new Error('[XP] Guild ID was not provided.')
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|