simply-xp 1.1.0 → 1.1.5-beta

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/src/rank.js ADDED
@@ -0,0 +1,285 @@
1
+ let Discord = require('discord.js')
2
+ const levels = require('../src/models/level.js')
3
+ const { join } = require('path')
4
+
5
+ /**
6
+ * @param {Discord.Message} message
7
+ * @param {string} userID
8
+ * @param {string} guildID
9
+ * @param {import('../index').rankOptions} options
10
+ */
11
+
12
+ async function rank(message, userID, guildID, options = []) {
13
+ if (!userID) throw new Error('[XP] User ID was not provided.')
14
+
15
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
16
+
17
+ const user = await levels.findOne({
18
+ user: userID,
19
+ guild: guildID
20
+ })
21
+ if (!user) console.error('[XP] NO_DATA | User has no XP data.')
22
+
23
+ const leaderboard = await levels
24
+ .find({
25
+ guild: guildID
26
+ })
27
+ .sort([['xp', 'descending']])
28
+ .exec()
29
+
30
+ user.position = leaderboard.findIndex((i) => i.user === userID) + 1
31
+
32
+ let targetxp = user.level + 1
33
+
34
+ let target = targetxp * targetxp * 100
35
+
36
+ return rankCard(message, {
37
+ level: user.level,
38
+ currentXP: user.xp,
39
+ neededXP: target,
40
+ rank: user.position,
41
+ background: options.background,
42
+ color: options.color,
43
+ member: message.guild.members.cache.get(userID)?.user
44
+ })
45
+
46
+ async function rankCard(message, options = []) {
47
+ try {
48
+ const Canvas = require('canvas')
49
+ const { registerFont } = require('canvas')
50
+ registerFont(join(__dirname, 'Fonts', 'Poppins-SemiBold.ttf'), {
51
+ family: 'Poppins-Regular'
52
+ })
53
+ registerFont(join(__dirname, 'Fonts', 'Poppins-SemiBold.ttf'), {
54
+ family: 'Poppins-Bold'
55
+ })
56
+
57
+ function shortener(count) {
58
+ const COUNT_ABBRS = ['', 'k', 'M', 'T']
59
+
60
+ const i =
61
+ 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000))
62
+ let result = parseFloat((count / Math.pow(1000, i)).toFixed(2))
63
+ result += `${COUNT_ABBRS[i]}`
64
+ return result
65
+ }
66
+
67
+ const member = options.member
68
+
69
+ const canvas = Canvas.createCanvas(1080, 400),
70
+ ctx = canvas.getContext('2d')
71
+
72
+ const name = member.tag
73
+ const noSymbols = (string) => string.replace(/[\u007f-\uffff]/g, '')
74
+
75
+ let fsiz = '45px'
76
+ if (message.guild.name.length >= 23) {
77
+ fsiz = '38px'
78
+ }
79
+ if (message.guild.name.length >= 40) {
80
+ fsiz = '28px'
81
+ }
82
+ if (message.guild.name.length >= 63) {
83
+ fsiz = '22px'
84
+ }
85
+
86
+ let BackgroundRadius = '20',
87
+ BackGroundImg =
88
+ options.background ||
89
+ 'https://media.discordapp.net/attachments/868506665102762034/876750913866461185/photo-1579546929518-9e396f3cc809.png?width=640&height=427',
90
+ AttachmentName = 'rank.png',
91
+ Username = noSymbols(name),
92
+ AvatarRoundRadius = '50',
93
+ DrawLayerColor = '#000000',
94
+ DrawLayerOpacity = '0.4',
95
+ BoxColor = options.color || '#096DD1',
96
+ LevelBarFill = options.lvlbar || '#ffffff',
97
+ LevelBarBackground = options.lvlbarBg || '#ffffff',
98
+ Rank = options.rank,
99
+ TextEXP = shortener(options.currentXP) + ' xp',
100
+ LvlText = `Level ${shortener(options.level)}`,
101
+ BarRadius = '20',
102
+ TextXpNeded = '{current}/{needed}',
103
+ CurrentXP = options.currentXP,
104
+ NeededXP = options.neededXP
105
+
106
+ ctx.beginPath()
107
+ ctx.moveTo(0 + Number(BackgroundRadius), 0)
108
+ ctx.lineTo(0 + 1080 - Number(BackgroundRadius), 0)
109
+ ctx.quadraticCurveTo(0 + 1080, 0, 0 + 1080, 0 + Number(BackgroundRadius))
110
+ ctx.lineTo(0 + 1080, 0 + 400 - Number(BackgroundRadius))
111
+ ctx.quadraticCurveTo(
112
+ 0 + 1080,
113
+ 0 + 400,
114
+ 0 + 1080 - Number(BackgroundRadius),
115
+ 0 + 400
116
+ )
117
+
118
+ ctx.lineTo(0 + Number(BackgroundRadius), 0 + 400)
119
+ ctx.quadraticCurveTo(0, 0 + 400, 0, 0 + 400 - Number(BackgroundRadius))
120
+ ctx.lineTo(0, 0 + Number(BackgroundRadius))
121
+ ctx.quadraticCurveTo(0, 0, 0 + Number(BackgroundRadius), 0)
122
+ ctx.closePath()
123
+ ctx.clip()
124
+ ctx.fillStyle = '#000000'
125
+ ctx.fillRect(0, 0, 1080, 400)
126
+ let background = await Canvas.loadImage(BackGroundImg)
127
+ ctx.globalAlpha = 0.7
128
+ ctx.drawImage(background, 0, 0, 1080, 400)
129
+ ctx.restore()
130
+
131
+ ctx.fillStyle = DrawLayerColor
132
+ ctx.globalAlpha = DrawLayerOpacity
133
+ ctx.fillRect(40, 0, 240, canvas.height)
134
+ ctx.globalAlpha = 1
135
+
136
+ function RoundedBox(ctx, x, y, width, height, radius) {
137
+ ctx.beginPath()
138
+ ctx.moveTo(x + radius, y)
139
+ ctx.lineTo(x + width - radius, y)
140
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
141
+ ctx.lineTo(x + width, y + height - radius)
142
+ ctx.quadraticCurveTo(
143
+ x + width,
144
+ y + height,
145
+ x + width - radius,
146
+ y + height
147
+ )
148
+ ctx.lineTo(x + radius, y + height)
149
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
150
+ ctx.lineTo(x, y + radius)
151
+ ctx.quadraticCurveTo(x, y, x + radius, y)
152
+ ctx.closePath()
153
+ }
154
+
155
+ let avatar = await Canvas.loadImage(
156
+ member.displayAvatarURL({ dynamic: true, format: 'png' })
157
+ )
158
+ ctx.save()
159
+ RoundedBox(ctx, 40 + 30, 30, 180, 180, Number(AvatarRoundRadius))
160
+ ctx.strokeStyle = BoxColor
161
+ ctx.lineWidth = '10'
162
+ ctx.stroke()
163
+ ctx.clip()
164
+ ctx.drawImage(avatar, 40 + 30, 30, 180, 180)
165
+ ctx.restore()
166
+
167
+ ctx.save()
168
+ RoundedBox(ctx, 40 + 30, 30 + 180 + 30 + 50 + 30, 180, 50, 20)
169
+ ctx.strokeStyle = '#BFC85A22'
170
+ ctx.stroke()
171
+ ctx.clip()
172
+ ctx.fillStyle = BoxColor
173
+ ctx.globalAlpha = '1'
174
+ ctx.fillRect(40 + 30, 30 + 180 + 30 + 50 + 30, 180, 50)
175
+ ctx.globalAlpha = 1
176
+ ctx.fillStyle = '#ffffff'
177
+ ctx.font = '32px "Poppins-Bold"'
178
+ ctx.textAlign = 'center'
179
+ ctx.fillText(TextEXP, 40 + 30 + 180 / 2, 30 + 180 + 30 + 30 + 50 + 38)
180
+ ctx.restore()
181
+
182
+ ctx.save()
183
+ RoundedBox(ctx, 40 + 30, 30 + 180 + 30, 180, 50, 20)
184
+ ctx.strokeStyle = '#BFC85A22'
185
+ ctx.stroke()
186
+ ctx.clip()
187
+ ctx.fillStyle = BoxColor
188
+ ctx.globalAlpha = '1'
189
+ ctx.fillRect(40 + 30, 30 + 180 + 30, 180, 50, 50)
190
+ ctx.globalAlpha = 1
191
+ ctx.fillStyle = '#ffffff'
192
+ ctx.font = '32px "Poppins-Bold"'
193
+ ctx.textAlign = 'center'
194
+ ctx.fillText(LvlText, 40 + 30 + 180 / 2, 30 + 180 + 30 + 38)
195
+ ctx.restore()
196
+
197
+ ctx.save()
198
+ ctx.textAlign = 'left'
199
+ ctx.fillStyle = '#ffffff'
200
+ ctx.shadowColor = '#000000'
201
+ ctx.shadowBlur = 15
202
+ ctx.shadowOffsetX = 1
203
+ ctx.shadowOffsetY = 1
204
+ ctx.font = '39px "Poppins-Bold"'
205
+ ctx.fillText(Username, 390, 80)
206
+ ctx.restore()
207
+
208
+ ctx.save()
209
+ ctx.textAlign = 'right'
210
+ ctx.fillStyle = '#ffffff'
211
+ ctx.shadowColor = '#000000'
212
+ ctx.shadowBlur = 15
213
+ ctx.shadowOffsetX = 1
214
+ ctx.shadowOffsetY = 1
215
+ ctx.font = '55px "Poppins-Bold"'
216
+ ctx.fillText('#' + Rank, canvas.width - 50 - 5, 80)
217
+ ctx.restore()
218
+
219
+ ctx.save()
220
+ RoundedBox(ctx, 390, 305, 660, 70, Number(20))
221
+ ctx.strokeStyle = '#BFC85A22'
222
+ ctx.stroke()
223
+ ctx.clip()
224
+ ctx.fillStyle = '#ffffff'
225
+ ctx.font = `${fsiz} "Poppins-Bold"`
226
+ ctx.textAlign = 'center'
227
+ ctx.fillText(message.guild.name, 60 + 660, 355)
228
+ ctx.globalAlpha = '0.2'
229
+ ctx.fillRect(390, 305, 660, 70)
230
+ ctx.restore()
231
+
232
+ ctx.save()
233
+ RoundedBox(ctx, 390, 145, 660, 50, Number(BarRadius))
234
+ ctx.strokeStyle = '#BFC85A22'
235
+ ctx.stroke()
236
+ ctx.clip()
237
+ ctx.fillStyle = LevelBarBackground
238
+ ctx.globalAlpha = '0.2'
239
+ ctx.fillRect(390, 145, 660, 50, 50)
240
+ ctx.restore()
241
+
242
+ const percent = (100 * CurrentXP) / NeededXP
243
+ const progress = (percent * 660) / 100
244
+
245
+ ctx.save()
246
+ RoundedBox(ctx, 390, 145, progress, 50, Number(BarRadius))
247
+ ctx.strokeStyle = '#BFC85A22'
248
+ ctx.stroke()
249
+ ctx.clip()
250
+ ctx.fillStyle = LevelBarFill
251
+ ctx.globalAlpha = '0.5'
252
+ ctx.fillRect(390, 145, progress, 50, 50)
253
+ ctx.restore()
254
+
255
+ ctx.save()
256
+ ctx.textAlign = 'left'
257
+ ctx.fillStyle = '#ffffff'
258
+ ctx.globalAlpha = '0.8'
259
+ ctx.font = '30px "Poppins-Bold"'
260
+ ctx.fillText('Next Level: ' + shortener(NeededXP) + ' xp', 390, 230)
261
+ ctx.restore()
262
+
263
+ const latestXP = Number(CurrentXP) - Number(NeededXP)
264
+ const textXPEdited = TextXpNeded.replace(/{needed}/g, shortener(NeededXP))
265
+ .replace(/{current}/g, shortener(CurrentXP))
266
+ .replace(/{latest}/g, latestXP)
267
+ ctx.textAlign = 'center'
268
+ ctx.fillStyle = '#474747'
269
+ ctx.globalAlpha = 1
270
+ ctx.font = '30px "Poppins-Bold"'
271
+ ctx.fillText(textXPEdited, 730, 180)
272
+
273
+ const attachment = new Discord.MessageAttachment(
274
+ canvas.toBuffer(),
275
+ AttachmentName
276
+ )
277
+
278
+ return attachment
279
+ } catch (err) {
280
+ console.log(`[XP] Error Occured. | rankCard | Error: ${err.stack}`)
281
+ }
282
+ }
283
+ }
284
+
285
+ module.exports = rank
package/src/reset.js ADDED
@@ -0,0 +1,28 @@
1
+ const levels = require('../src/models/level.js')
2
+
3
+ /**
4
+ * @param {string} userID
5
+ * @param {string} guildID
6
+ */
7
+
8
+ async function reset(userID, guildID) {
9
+ if (!userID) throw new Error('[XP] User ID was not provided.')
10
+
11
+ if (!guildID) throw new Error('[XP] User ID was not provided.')
12
+
13
+ let uzer = await levels.findOne({ user: userID, guild: guildID })
14
+
15
+ uzer = new levels({
16
+ user: userID,
17
+ guild: guildID,
18
+ xp: 0,
19
+ lvl: 0
20
+ })
21
+ await uzer
22
+ .save()
23
+ .catch((e) => console.log(`[XP] Failed to save new use to database`))
24
+
25
+ return true
26
+ }
27
+
28
+ module.exports = reset
@@ -0,0 +1,124 @@
1
+ let Discord = require('discord.js')
2
+ const lrole = require('../src/models/lvlrole.js')
3
+
4
+ class roleSetup {
5
+ /**
6
+ * @param {Discord.Client} client
7
+ * @param {string} guildID
8
+ * @param {import('../index').lvladdOptions} options
9
+ */
10
+
11
+ static async add(client, guildID, options = []) {
12
+ let rol = await lrole.findOne({
13
+ gid: guildID,
14
+ lvlrole: {
15
+ lvl: options.level,
16
+ role: options.role
17
+ }
18
+ })
19
+
20
+ let g = client.guilds.cache.get(guildID)
21
+
22
+ let roll = g.roles.cache.find((r) => r.id === options.role)
23
+
24
+ if (roll) {
25
+ if (rol) throw new Error('Level Already Exist. Use delete')
26
+ else if (!rol) {
27
+ let newrol = await lrole.findOne({
28
+ gid: guildID
29
+ })
30
+
31
+ if (!newrol) {
32
+ newrol = new lrole({
33
+ gid: guildID,
34
+ lvlrole: []
35
+ })
36
+
37
+ await newrol.save()
38
+ }
39
+
40
+ newrol.lvlrole.push({ lvl: options.level, role: options.role })
41
+
42
+ await newrol
43
+ .save()
44
+ .catch((e) =>
45
+ console.log(`[XP] Failed to add lvlrole to database | ${e}`)
46
+ )
47
+
48
+ return true
49
+ }
50
+ } else {
51
+ throw new Error(
52
+ 'Role ID is invalid. | ' +
53
+ `Guild ID: ${guildID} | Role ID: ${options.role}`
54
+ )
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @param {Discord.Client} client
60
+ * @param {string} guildID
61
+ * @param {import('../index').lvlremoveOptions} options
62
+ */
63
+
64
+ static async remove(client, guildID, options = []) {
65
+ let rol = await lrole.find({
66
+ gid: guildID
67
+ })
68
+
69
+ if (!rol || rol.length === 0)
70
+ throw new Error('Level role with this level does not exist')
71
+ rol = rol[0].lvlrole.find((item) => item.lvl === options.level) || undefined
72
+
73
+ if (rol) {
74
+ let newrol = await lrole.findOneAndUpdate(
75
+ {
76
+ gid: guildID
77
+ },
78
+ {
79
+ $pull: { lvlrole: { lvl: options.level } }
80
+ }
81
+ )
82
+
83
+ return true
84
+ } else throw new Error('Level role with this level does not exist')
85
+ }
86
+
87
+ /**
88
+ * @param {Discord.Client} client
89
+ * @param {string} guildID
90
+ */
91
+
92
+ static async fetch(client, guildID) {
93
+ let rol = await lrole.find({
94
+ gid: guildID
95
+ })
96
+
97
+ if (!rol || rol.length === 0)
98
+ throw new Error('There is no levelRole in this guild')
99
+
100
+ return rol[0].lvlrole
101
+ }
102
+
103
+ /**
104
+ * @param {Discord.Client} client
105
+ * @param {string} guildID
106
+ * @param {string} level
107
+ */
108
+
109
+ static async find(client, guildID, level) {
110
+ let rol = await lrole.find({
111
+ gid: guildID
112
+ })
113
+
114
+ if (!rol || rol.length === 0)
115
+ throw new Error('There is no levelRole in this guild')
116
+ rol = rol[0].lvlrole.find((item) => item.lvl === level) || undefined
117
+
118
+ if (rol) {
119
+ return rol
120
+ }
121
+ }
122
+ }
123
+
124
+ module.exports = roleSetup
@@ -0,0 +1,71 @@
1
+ let Discord = require('discord.js')
2
+ const levels = require('../src/models/level.js')
3
+ let { roleSetup } = require('../simplyxp')
4
+
5
+ /**
6
+ * @param {Discord.Message} message
7
+ * @param {string} userID
8
+ * @param {string} guildID
9
+ * @param {string} level
10
+ */
11
+ async function setLevel(message, userID, guildID, level) {
12
+ if (!userID) throw new Error('[XP] User ID was not provided.')
13
+
14
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
15
+
16
+ if (!level) throw new Error('[XP] Level amount is not provided.')
17
+
18
+ let { client } = message
19
+
20
+ const user = await levels.findOne({ user: userID, guild: guildID })
21
+
22
+ if (!user) {
23
+ const newUser = new levels({
24
+ user: userID,
25
+ guild: guildID,
26
+ xp: 0,
27
+ level: 0
28
+ })
29
+
30
+ await newUser
31
+ .save()
32
+ .catch((e) => console.log(`[XP] Failed to save new user to database`))
33
+
34
+ let xp = (level * 10) ** 2
35
+
36
+ return {
37
+ level: level,
38
+ exp: xp
39
+ }
40
+ }
41
+ let level1 = user.level
42
+
43
+ user.xp = (level * 10) ** 2
44
+ user.level = Math.floor(0.1 * Math.sqrt(user.xp))
45
+
46
+ await user
47
+ .save()
48
+ .catch((e) =>
49
+ console.log(`[XP] Failed to set Level | User: ${userID} | Err: ${e}`)
50
+ )
51
+
52
+ if (level1 !== level) {
53
+ let data = {
54
+ xp: user.xp,
55
+ level: user.level,
56
+ userID,
57
+ guildID
58
+ }
59
+
60
+ let role = await roleSetup.find(client, guildID, level)
61
+
62
+ client.emit('levelUp', message, data, role)
63
+ }
64
+
65
+ return {
66
+ level: user.level,
67
+ xp: user.xp
68
+ }
69
+ }
70
+
71
+ module.exports = setLevel
package/src/setXP.js ADDED
@@ -0,0 +1,51 @@
1
+ const levels = require('../src/models/level.js')
2
+
3
+ /**
4
+ * @param {string} userID
5
+ * @param {string} guildID
6
+ * @param {string} xp
7
+ */
8
+
9
+ async function setXP(userID, guildID, xp) {
10
+ if (!userID) throw new Error('[XP] User ID was not provided.')
11
+
12
+ if (!guildID) throw new Error('[XP] Guild ID was not provided.')
13
+
14
+ if (!xp) throw new Error('[XP] XP amount is not provided.')
15
+
16
+ if (Number(xp).toString() === 'NaN')
17
+ throw new Error('[XP] XP amount is not a number.')
18
+
19
+ const user = await levels.findOne({ user: userID, guild: guildID })
20
+
21
+ let lvl = Math.floor(0.1 * Math.sqrt(xp))
22
+
23
+ if (!user) {
24
+ const newUser = new levels({
25
+ user: userID,
26
+ guild: guildID,
27
+ xp: xp,
28
+ level: lvl
29
+ })
30
+
31
+ await newUser
32
+ .save()
33
+ .catch((e) => console.log(`[XP] Failed to save new use to database`))
34
+
35
+ return {
36
+ xp: 0
37
+ }
38
+ }
39
+ user.xp = xp
40
+ user.level = Math.floor(0.1 * Math.sqrt(user.xp))
41
+
42
+ await user
43
+ .save()
44
+ .catch((e) =>
45
+ console.log(`[XP] Failed to set XP | User: ${userID} | Err: ${e}`)
46
+ )
47
+
48
+ return { xp }
49
+ }
50
+
51
+ module.exports = setXP